From 425d593ebe2c4136312964a434ca7bc13904679f Mon Sep 17 00:00:00 2001 From: tmbdev Date: Wed, 7 Mar 2007 20:03:40 +0000 Subject: [PATCH] top-skimming import from sf.net git-svn-id: https://tesseract-ocr.googlecode.com/svn/trunk/trunk@2 d0cd1f9f-072b-0410-8dd7-cf729c803f20 --- .cvsignore | 6 + AUTHORS | 8 + COPYING | 23 + ChangeLog | 20 + INSTALL | 229 + Makefile.am | 15 + Makefile.in | 628 ++ NEWS | 1 + README | 85 + ReleaseNotes | 78 + acinclude.m4 | 10 + aclocal.m4 | 920 +++ ccmain/Makefile.am | 41 + ccmain/Makefile.in | 636 ++ ccmain/adaptions.cpp | 1078 ++++ ccmain/adaptions.h | 109 + ccmain/applybox.cpp | 859 +++ ccmain/applybox.h | 71 + ccmain/baseapi.cpp | 395 ++ ccmain/baseapi.h | 154 + ccmain/blobcmp.cpp | 76 + ccmain/blobcmp.h | 29 + ccmain/callnet.cpp | 93 + ccmain/callnet.h | 32 + ccmain/charcut.cpp | 710 +++ ccmain/charcut.h | 119 + ccmain/charsample.cpp | 698 +++ ccmain/control.cpp | 1668 +++++ ccmain/control.h | 193 + ccmain/docqual.cpp | 1453 +++++ ccmain/docqual.h | 155 + ccmain/expandblob.cpp | 82 + ccmain/expandblob.h | 13 + ccmain/fixspace.cpp | 974 +++ ccmain/fixspace.h | 72 + ccmain/fixxht.cpp | 790 +++ ccmain/fixxht.h | 92 + ccmain/imgscale.cpp | 154 + ccmain/imgscale.h | 32 + ccmain/matmatch.cpp | 404 ++ ccmain/matmatch.h | 48 + ccmain/output.cpp | 1185 ++++ ccmain/output.h | 112 + ccmain/paircmp.cpp | 107 + ccmain/paircmp.h | 43 + ccmain/reject.cpp | 1655 +++++ ccmain/reject.h | 175 + ccmain/scaleimg.cpp | 366 ++ ccmain/scaleimg.h | 35 + ccmain/tessbox.cpp | 370 ++ ccmain/tessbox.h | 110 + ccmain/tessedit.cpp | 321 + ccmain/tessedit.h | 67 + ccmain/tessembedded.h | 38 + ccmain/tesseractmain.cpp | 311 + ccmain/tesseractmain.h | 58 + ccmain/tessvars.cpp | 38 + ccmain/tessvars.h | 48 + ccmain/tfacep.h | 121 + ccmain/tfacepp.cpp | 411 ++ ccmain/tfacepp.h | 85 + ccmain/tstruct.cpp | 511 ++ ccmain/tstruct.h | 108 + ccmain/werdit.cpp | 193 + ccmain/werdit.h | 67 + ccstruct/Makefile.am | 25 + ccstruct/Makefile.in | 587 ++ ccstruct/blckerr.h | 29 + ccstruct/blobbox.cpp | 778 +++ ccstruct/blobbox.h | 381 ++ ccstruct/blobs.cpp | 247 + ccstruct/blobs.h | 119 + ccstruct/blread.cpp | 537 ++ ccstruct/blread.h | 63 + ccstruct/callcpp.cpp | 270 + ccstruct/coutln.cpp | 604 ++ ccstruct/coutln.h | 176 + ccstruct/crakedge.h | 39 + ccstruct/genblob.cpp | 133 + ccstruct/genblob.h | 52 + ccstruct/hpddef.h | 39 + ccstruct/hpdsizes.h | 8 + ccstruct/ipoints.h | 479 ++ ccstruct/labls.cpp | 188 + ccstruct/labls.h | 38 + ccstruct/linlsq.cpp | 249 + ccstruct/linlsq.h | 102 + ccstruct/lmedsq.cpp | 453 ++ ccstruct/lmedsq.h | 84 + ccstruct/mod128.cpp | 100 + ccstruct/mod128.h | 85 + ccstruct/normalis.cpp | 176 + ccstruct/normalis.h | 108 + ccstruct/ocrblock.cpp | 368 ++ ccstruct/ocrblock.h | 228 + ccstruct/ocrrow.cpp | 216 + ccstruct/ocrrow.h | 133 + ccstruct/pageblk.cpp | 879 +++ ccstruct/pageblk.h | 318 + ccstruct/pageres.cpp | 325 + ccstruct/pageres.h | 311 + ccstruct/pdblock.cpp | 363 ++ ccstruct/pdblock.h | 181 + ccstruct/pdclass.h | 54 + ccstruct/points.cpp | 64 + ccstruct/points.h | 284 + ccstruct/polyaprx.cpp | 583 ++ ccstruct/polyaprx.h | 51 + ccstruct/polyblk.cpp | 397 ++ ccstruct/polyblk.h | 122 + ccstruct/polyblob.cpp | 344 ++ ccstruct/polyblob.h | 94 + ccstruct/polyvert.cpp | 23 + ccstruct/polyvert.h | 52 + ccstruct/poutline.cpp | 409 ++ ccstruct/poutline.h | 115 + ccstruct/quadlsq.cpp | 147 + ccstruct/quadlsq.h | 67 + ccstruct/quadratc.cpp | 21 + ccstruct/quadratc.h | 63 + ccstruct/quspline.cpp | 382 ++ ccstruct/quspline.h | 114 + ccstruct/ratngs.cpp | 262 + ccstruct/ratngs.h | 146 + ccstruct/rect.cpp | 232 + ccstruct/rect.h | 287 + ccstruct/rejctmap.cpp | 545 ++ ccstruct/rejctmap.h | 284 + ccstruct/rwpoly.cpp | 89 + ccstruct/rwpoly.h | 45 + ccstruct/statistc.cpp | 908 +++ ccstruct/statistc.h | 134 + ccstruct/stepblob.cpp | 277 + ccstruct/stepblob.h | 81 + ccstruct/txtregn.cpp | 230 + ccstruct/txtregn.h | 155 + ccstruct/vecfuncs.cpp | 63 + ccstruct/vecfuncs.h | 91 + ccstruct/werd.cpp | 997 +++ ccstruct/werd.h | 277 + ccutil/Makefile.am | 16 + ccutil/Makefile.in | 556 ++ ccutil/basedir.cpp | 104 + ccutil/basedir.h | 32 + ccutil/bits16.cpp | 30 + ccutil/bits16.h | 61 + ccutil/clst.cpp | 591 ++ ccutil/clst.h | 1078 ++++ ccutil/debugwin.cpp | 500 ++ ccutil/debugwin.h | 103 + ccutil/elst.cpp | 593 ++ ccutil/elst.h | 1146 ++++ ccutil/elst2.cpp | 602 ++ ccutil/elst2.h | 1137 ++++ ccutil/errcode.cpp | 103 + ccutil/errcode.h | 104 + ccutil/fileerr.h | 34 + ccutil/getopt.cpp | 62 + ccutil/getopt.h | 30 + ccutil/globaloc.cpp | 113 + ccutil/globaloc.h | 40 + ccutil/hashfn.cpp | 57 + ccutil/hashfn.h | 30 + ccutil/host.h | 286 + ccutil/hosthplb.h | 1 + ccutil/lsterr.h | 43 + ccutil/mainblk.cpp | 117 + ccutil/mainblk.h | 48 + ccutil/memblk.cpp | 1106 ++++ ccutil/memblk.h | 189 + ccutil/memry.cpp | 531 ++ ccutil/memry.h | 192 + ccutil/memryerr.h | 38 + ccutil/mfcpch.cpp | 5 + ccutil/mfcpch.h | 37 + ccutil/ndminx.h | 25 + ccutil/notdll.h | 28 + ccutil/nwmain.h | 176 + ccutil/ocrclass.h | 338 + ccutil/ocrshell.cpp | 759 +++ ccutil/ocrshell.h | 191 + ccutil/platform.h | 14 + ccutil/scanutils.cpp | 543 ++ ccutil/scanutils.h | 55 + ccutil/secname.h | 9 + ccutil/serialis.cpp | 112 + ccutil/serialis.h | 95 + ccutil/stderr.h | 26 + ccutil/strngs.cpp | 209 + ccutil/strngs.h | 178 + ccutil/tessclas.h | 135 + ccutil/tprintf.cpp | 136 + ccutil/tprintf.h | 35 + ccutil/unichar.cpp | 144 + ccutil/unichar.h | 76 + ccutil/varable.cpp | 695 +++ ccutil/varable.h | 412 ++ classify/Makefile.am | 24 + classify/Makefile.in | 584 ++ classify/adaptive.cpp | 534 ++ classify/adaptive.h | 199 + classify/adaptmatch.cpp | 3247 ++++++++++ classify/adaptmatch.h | 83 + classify/baseline.cpp | 58 + classify/baseline.h | 91 + classify/blobclass.cpp | 131 + classify/blobclass.h | 49 + classify/chartoname.cpp | 74 + classify/chartoname.h | 21 + classify/cluster.cpp | 2786 +++++++++ classify/cluster.h | 149 + classify/clusttool.cpp | 507 ++ classify/clusttool.h | 70 + classify/cutoffs.cpp | 67 + classify/cutoffs.h | 49 + classify/extern.h | 35 + classify/extract.cpp | 100 + classify/extract.h | 36 + classify/featdefs.cpp | 244 + classify/featdefs.h | 85 + classify/flexfx.cpp | 86 + classify/flexfx.h | 34 + classify/float2int.cpp | 126 + classify/float2int.h | 65 + classify/fpoint.cpp | 56 + classify/fpoint.h | 62 + classify/fxdefs.cpp | 74 + classify/fxdefs.h | 93 + classify/fxid.h | 69 + classify/hideedge.cpp | 35 + classify/hideedge.h | 76 + classify/intfx.cpp | 608 ++ classify/intfx.h | 63 + classify/intmatcher.cpp | 2321 +++++++ classify/intmatcher.h | 240 + classify/intproto.cpp | 1746 ++++++ classify/intproto.h | 335 + classify/kdtree.cpp | 872 +++ classify/kdtree.h | 118 + classify/mf.cpp | 106 + classify/mf.h | 43 + classify/mfdefs.cpp | 58 + classify/mfdefs.h | 67 + classify/mfoutline.cpp | 1086 ++++ classify/mfoutline.h | 277 + classify/mfx.cpp | 437 ++ classify/mfx.h | 52 + classify/normfeat.cpp | 132 + classify/normfeat.h | 63 + classify/normmatch.cpp | 300 + classify/normmatch.h | 59 + classify/ocrfeatures.cpp | 310 + classify/ocrfeatures.h | 170 + classify/outfeat.cpp | 262 + classify/outfeat.h | 76 + classify/picofeat.cpp | 297 + classify/picofeat.h | 65 + classify/protos.cpp | 468 ++ classify/protos.h | 360 ++ classify/sigmenu.cpp | 225 + classify/sigmenu.h | 39 + classify/speckle.cpp | 126 + classify/speckle.h | 69 + classify/xform2d.cpp | 63 + classify/xform2d.h | 69 + config/ac_compile_check_sizeof.m4 | 28 + config/ac_create_stdint_h.m4 | 552 ++ config/ac_define_versionlevel.m4 | 73 + config/acinclude_custom.m4 | 666 ++ config/ax_create_stdint_h.m4 | 734 +++ config/config.guess | 1317 ++++ config/config.h.in | 203 + config/config.sub | 1411 +++++ config/depcomp | 423 ++ config/install-sh | 251 + config/missing | 336 + config/mkinstalldirs | 99 + configure | 9541 +++++++++++++++++++++++++++++ configure.ac | 338 + cutil/Makefile.am | 14 + cutil/Makefile.in | 549 ++ cutil/bitvec.cpp | 122 + cutil/bitvec.h | 100 + cutil/callcpp.h | 190 + cutil/const.h | 108 + cutil/cutil.cpp | 92 + cutil/cutil.h | 159 + cutil/danerror.cpp | 145 + cutil/danerror.h | 41 + cutil/debug.cpp | 97 + cutil/debug.h | 348 ++ cutil/efio.cpp | 62 + cutil/efio.h | 32 + cutil/emalloc.cpp | 91 + cutil/emalloc.h | 72 + cutil/freelist.cpp | 85 + cutil/freelist.h | 69 + cutil/funcdefs.h | 55 + cutil/general.h | 33 + cutil/globals.cpp | 65 + cutil/globals.h | 65 + cutil/listio.cpp | 68 + cutil/listio.h | 43 + cutil/minmax.h | 40 + cutil/oldheap.cpp | 337 + cutil/oldheap.h | 126 + cutil/oldlist.cpp | 393 ++ cutil/oldlist.h | 350 ++ cutil/structures.cpp | 66 + cutil/structures.h | 112 + cutil/tessarray.cpp | 115 + cutil/tessarray.h | 166 + cutil/tordvars.cpp | 95 + cutil/tordvars.h | 60 + cutil/variables.cpp | 310 + cutil/variables.h | 168 + dict/Makefile.am | 11 + dict/Makefile.in | 539 ++ dict/choicearr.h | 96 + dict/choices.cpp | 161 + dict/choices.h | 191 + dict/context.cpp | 225 + dict/context.h | 70 + dict/dawg.cpp | 366 ++ dict/dawg.h | 296 + dict/hyphen.cpp | 70 + dict/hyphen.h | 114 + dict/matchdefs.h | 143 + dict/permdawg.cpp | 352 ++ dict/permdawg.h | 94 + dict/permnum.cpp | 483 ++ dict/permnum.h | 79 + dict/permute.cpp | 1587 +++++ dict/permute.h | 91 + dict/states.cpp | 382 ++ dict/states.h | 111 + dict/stopper.cpp | 1360 ++++ dict/stopper.h | 101 + dict/trie.cpp | 463 ++ dict/trie.h | 269 + display/Makefile.am | 14 + display/Makefile.in | 539 ++ display/cmndwin.cpp | 449 ++ display/cmndwin.h | 111 + display/pagewalk.cpp | 666 ++ display/pagewalk.h | 155 + display/pgedit.cpp | 1938 ++++++ display/pgedit.h | 176 + display/pgeditx.h | 20 + display/sbdmenu.cpp | 520 ++ display/sbdmenu.h | 415 ++ display/submen.h | 62 + display/tessio.h | 110 + display/varabled.cpp | 283 + display/varabled.h | 41 + display/varblmen.cpp | 329 + display/varblmen.h | 203 + display/varblwin.cpp | 279 + display/varblwin.h | 131 + doc/main.txt | 10 + image/Makefile.am | 10 + image/Makefile.in | 531 ++ image/bitstrm.cpp | 157 + image/bitstrm.h | 73 + image/img.h | 329 + image/imgbmp.cpp | 223 + image/imgbmp.h | 50 + image/imgerrs.h | 35 + image/imgio.cpp | 321 + image/imgio.h | 22 + image/imgs.cpp | 1619 +++++ image/imgs.h | 102 + image/imgtiff.cpp | 705 +++ image/imgtiff.h | 89 + image/imgunpk.h | 1377 +++++ phototest.tif | Bin 0 -> 38668 bytes tessdata/DangAmbigs | 39 + tessdata/blackText.params | 74 + tessdata/configs/api_config | 45 + tessdata/configs/api_resaljet | 34 + tessdata/configs/box.train | 16 + tessdata/configs/inter | 4 + tessdata/configs/oldapi_config | 36 + tessdata/configs/oldbox.train | 36 + tessdata/configs/var_api_config | 47 + tessdata/configs/var_box.train | 47 + tessdata/configs/variable_config | 72 + tessdata/confsets | 3 + tessdata/fmtable.cls | Bin 0 -> 132988 bytes tessdata/fnetwts | Bin 0 -> 751 bytes tessdata/freq-dawg | Bin 0 -> 720 bytes tessdata/inttemp | Bin 0 -> 676716 bytes tessdata/netwts | Bin 0 -> 1369167 bytes tessdata/newdiff.asccodes | 51 + tessdata/normproto | 1179 ++++ tessdata/pffmtable | 92 + tessdata/soptable.cls | 6544 ++++++++++++++++++++ tessdata/tessconfigs/batch | 79 + tessdata/tessconfigs/matdemo | 82 + tessdata/tessconfigs/old_batch | 82 + tessdata/tessconfigs/segdemo | 74 + tessdata/tessconfigs/var_batch | 82 + tessdata/test_matrix | 96 + tessdata/user-words | 920 +++ tessdata/word-dawg | Bin 0 -> 191888 bytes tesseract.dsp | 2033 ++++++ tesseract.dsw | 53 + textord/Makefile.am | 19 + textord/Makefile.in | 560 ++ textord/blkocc.cpp | 810 +++ textord/blkocc.h | 327 + textord/blobcmpl.h | 32 + textord/drawedg.cpp | 79 + textord/drawedg.h | 34 + textord/drawtord.cpp | 475 ++ textord/drawtord.h | 107 + textord/edgblob.cpp | 407 ++ textord/edgblob.h | 100 + textord/edgloop.cpp | 210 + textord/edgloop.h | 66 + textord/fpchop.cpp | 1641 +++++ textord/fpchop.h | 238 + textord/gap_map.cpp | 166 + textord/gap_map.h | 40 + textord/makerow.cpp | 2583 ++++++++ textord/makerow.h | 295 + textord/oldbasel.cpp | 1759 ++++++ textord/oldbasel.h | 195 + textord/pithsync.cpp | 696 +++ textord/pithsync.h | 134 + textord/pitsync1.cpp | 425 ++ textord/pitsync1.h | 135 + textord/scanedg.cpp | 461 ++ textord/scanedg.h | 74 + textord/sortflts.cpp | 80 + textord/sortflts.h | 64 + textord/tessout.h | 76 + textord/topitch.cpp | 2015 ++++++ textord/topitch.h | 195 + textord/tordmain.cpp | 902 +++ textord/tordmain.h | 132 + textord/tospace.cpp | 1941 ++++++ textord/tospace.h | 193 + textord/tovars.cpp | 87 + textord/tovars.h | 94 + textord/underlin.cpp | 312 + textord/underlin.h | 53 + textord/wordseg.cpp | 620 ++ textord/wordseg.h | 70 + training/Makefile.am | 40 + training/Makefile.in | 624 ++ training/cnTraining.cpp | 832 +++ training/cnTraining.dsp | 227 + training/cnTraining1.dsp | 85 + training/mergenf.cpp | 451 ++ training/mergenf.h | 106 + training/mfTraining.cpp | 1206 ++++ training/mfTraining.dsp | 269 + training/name2char.cpp | 166 + training/name2char.h | 38 + training/training.cpp | 198 + training/training.h | 130 + viewer/Makefile.am | 10 + viewer/Makefile.in | 531 ++ viewer/evntlst.cpp | 344 ++ viewer/evntlst.h | 51 + viewer/evnts.cpp | 349 ++ viewer/evnts.h | 52 + viewer/grphics.cpp | 1008 +++ viewer/grphics.h | 268 + viewer/grphshm.cpp | 472 ++ viewer/grphshm.h | 74 + viewer/sbgconst.h | 135 + viewer/sbgdefs.h | 421 ++ viewer/sbgtypes.h | 248 + viewer/showim.cpp | 84 + viewer/showim.h | 41 + wordrec/Makefile.am | 23 + wordrec/Makefile.in | 578 ++ wordrec/associate.cpp | 62 + wordrec/associate.h | 93 + wordrec/badwords.cpp | 106 + wordrec/badwords.h | 51 + wordrec/bestfirst.cpp | 512 ++ wordrec/bestfirst.h | 200 + wordrec/charsample.h | 208 + wordrec/chop.cpp | 458 ++ wordrec/chop.h | 153 + wordrec/chopper.cpp | 739 +++ wordrec/chopper.h | 104 + wordrec/closed.cpp | 136 + wordrec/closed.h | 65 + wordrec/djmenus.cpp | 118 + wordrec/djmenus.h | 33 + wordrec/drawfx.cpp | 96 + wordrec/drawfx.h | 33 + wordrec/findseam.cpp | 554 ++ wordrec/findseam.h | 69 + wordrec/gradechop.cpp | 226 + wordrec/gradechop.h | 91 + wordrec/heuristic.cpp | 194 + wordrec/heuristic.h | 120 + wordrec/makechop.cpp | 281 + wordrec/makechop.h | 69 + wordrec/matchtab.cpp | 191 + wordrec/matchtab.h | 45 + wordrec/matrix.cpp | 118 + wordrec/matrix.h | 104 + wordrec/measure.h | 135 + wordrec/metrics.cpp | 358 ++ wordrec/metrics.h | 130 + wordrec/mfvars.cpp | 51 + wordrec/mfvars.h | 27 + wordrec/msmenus.cpp | 110 + wordrec/msmenus.h | 45 + wordrec/olutil.cpp | 153 + wordrec/olutil.h | 128 + wordrec/outlines.cpp | 172 + wordrec/outlines.h | 148 + wordrec/pieces.cpp | 410 ++ wordrec/pieces.h | 154 + wordrec/plotedges.cpp | 130 + wordrec/plotedges.h | 71 + wordrec/plotseg.cpp | 112 + wordrec/plotseg.h | 73 + wordrec/render.cpp | 158 + wordrec/render.h | 90 + wordrec/seam.cpp | 482 ++ wordrec/seam.h | 136 + wordrec/split.cpp | 182 + wordrec/split.h | 115 + wordrec/tally.cpp | 68 + wordrec/tally.h | 94 + wordrec/tessinit.cpp | 108 + wordrec/tessinit.h | 46 + wordrec/tface.cpp | 273 + wordrec/tface.h | 35 + wordrec/wordclass.cpp | 277 + wordrec/wordclass.h | 64 + 539 files changed, 163745 insertions(+) create mode 100644 .cvsignore create mode 100644 AUTHORS create mode 100644 COPYING create mode 100644 ChangeLog create mode 100644 INSTALL create mode 100644 Makefile.am create mode 100644 Makefile.in create mode 100644 NEWS create mode 100644 README create mode 100644 ReleaseNotes create mode 100644 acinclude.m4 create mode 100644 aclocal.m4 create mode 100644 ccmain/Makefile.am create mode 100644 ccmain/Makefile.in create mode 100644 ccmain/adaptions.cpp create mode 100644 ccmain/adaptions.h create mode 100644 ccmain/applybox.cpp create mode 100644 ccmain/applybox.h create mode 100644 ccmain/baseapi.cpp create mode 100644 ccmain/baseapi.h create mode 100644 ccmain/blobcmp.cpp create mode 100644 ccmain/blobcmp.h create mode 100644 ccmain/callnet.cpp create mode 100644 ccmain/callnet.h create mode 100644 ccmain/charcut.cpp create mode 100644 ccmain/charcut.h create mode 100644 ccmain/charsample.cpp create mode 100644 ccmain/control.cpp create mode 100644 ccmain/control.h create mode 100644 ccmain/docqual.cpp create mode 100644 ccmain/docqual.h create mode 100644 ccmain/expandblob.cpp create mode 100644 ccmain/expandblob.h create mode 100644 ccmain/fixspace.cpp create mode 100644 ccmain/fixspace.h create mode 100644 ccmain/fixxht.cpp create mode 100644 ccmain/fixxht.h create mode 100644 ccmain/imgscale.cpp create mode 100644 ccmain/imgscale.h create mode 100644 ccmain/matmatch.cpp create mode 100644 ccmain/matmatch.h create mode 100644 ccmain/output.cpp create mode 100644 ccmain/output.h create mode 100644 ccmain/paircmp.cpp create mode 100644 ccmain/paircmp.h create mode 100644 ccmain/reject.cpp create mode 100644 ccmain/reject.h create mode 100644 ccmain/scaleimg.cpp create mode 100644 ccmain/scaleimg.h create mode 100644 ccmain/tessbox.cpp create mode 100644 ccmain/tessbox.h create mode 100644 ccmain/tessedit.cpp create mode 100644 ccmain/tessedit.h create mode 100644 ccmain/tessembedded.h create mode 100644 ccmain/tesseractmain.cpp create mode 100644 ccmain/tesseractmain.h create mode 100644 ccmain/tessvars.cpp create mode 100644 ccmain/tessvars.h create mode 100644 ccmain/tfacep.h create mode 100644 ccmain/tfacepp.cpp create mode 100644 ccmain/tfacepp.h create mode 100644 ccmain/tstruct.cpp create mode 100644 ccmain/tstruct.h create mode 100644 ccmain/werdit.cpp create mode 100644 ccmain/werdit.h create mode 100644 ccstruct/Makefile.am create mode 100644 ccstruct/Makefile.in create mode 100644 ccstruct/blckerr.h create mode 100644 ccstruct/blobbox.cpp create mode 100644 ccstruct/blobbox.h create mode 100644 ccstruct/blobs.cpp create mode 100644 ccstruct/blobs.h create mode 100644 ccstruct/blread.cpp create mode 100644 ccstruct/blread.h create mode 100644 ccstruct/callcpp.cpp create mode 100644 ccstruct/coutln.cpp create mode 100644 ccstruct/coutln.h create mode 100644 ccstruct/crakedge.h create mode 100644 ccstruct/genblob.cpp create mode 100644 ccstruct/genblob.h create mode 100644 ccstruct/hpddef.h create mode 100644 ccstruct/hpdsizes.h create mode 100644 ccstruct/ipoints.h create mode 100644 ccstruct/labls.cpp create mode 100644 ccstruct/labls.h create mode 100644 ccstruct/linlsq.cpp create mode 100644 ccstruct/linlsq.h create mode 100644 ccstruct/lmedsq.cpp create mode 100644 ccstruct/lmedsq.h create mode 100644 ccstruct/mod128.cpp create mode 100644 ccstruct/mod128.h create mode 100644 ccstruct/normalis.cpp create mode 100644 ccstruct/normalis.h create mode 100644 ccstruct/ocrblock.cpp create mode 100644 ccstruct/ocrblock.h create mode 100644 ccstruct/ocrrow.cpp create mode 100644 ccstruct/ocrrow.h create mode 100644 ccstruct/pageblk.cpp create mode 100644 ccstruct/pageblk.h create mode 100644 ccstruct/pageres.cpp create mode 100644 ccstruct/pageres.h create mode 100644 ccstruct/pdblock.cpp create mode 100644 ccstruct/pdblock.h create mode 100644 ccstruct/pdclass.h create mode 100644 ccstruct/points.cpp create mode 100644 ccstruct/points.h create mode 100644 ccstruct/polyaprx.cpp create mode 100644 ccstruct/polyaprx.h create mode 100644 ccstruct/polyblk.cpp create mode 100644 ccstruct/polyblk.h create mode 100644 ccstruct/polyblob.cpp create mode 100644 ccstruct/polyblob.h create mode 100644 ccstruct/polyvert.cpp create mode 100644 ccstruct/polyvert.h create mode 100644 ccstruct/poutline.cpp create mode 100644 ccstruct/poutline.h create mode 100644 ccstruct/quadlsq.cpp create mode 100644 ccstruct/quadlsq.h create mode 100644 ccstruct/quadratc.cpp create mode 100644 ccstruct/quadratc.h create mode 100644 ccstruct/quspline.cpp create mode 100644 ccstruct/quspline.h create mode 100644 ccstruct/ratngs.cpp create mode 100644 ccstruct/ratngs.h create mode 100644 ccstruct/rect.cpp create mode 100644 ccstruct/rect.h create mode 100644 ccstruct/rejctmap.cpp create mode 100644 ccstruct/rejctmap.h create mode 100644 ccstruct/rwpoly.cpp create mode 100644 ccstruct/rwpoly.h create mode 100644 ccstruct/statistc.cpp create mode 100644 ccstruct/statistc.h create mode 100644 ccstruct/stepblob.cpp create mode 100644 ccstruct/stepblob.h create mode 100644 ccstruct/txtregn.cpp create mode 100644 ccstruct/txtregn.h create mode 100644 ccstruct/vecfuncs.cpp create mode 100644 ccstruct/vecfuncs.h create mode 100644 ccstruct/werd.cpp create mode 100644 ccstruct/werd.h create mode 100644 ccutil/Makefile.am create mode 100644 ccutil/Makefile.in create mode 100644 ccutil/basedir.cpp create mode 100644 ccutil/basedir.h create mode 100644 ccutil/bits16.cpp create mode 100644 ccutil/bits16.h create mode 100644 ccutil/clst.cpp create mode 100644 ccutil/clst.h create mode 100644 ccutil/debugwin.cpp create mode 100644 ccutil/debugwin.h create mode 100644 ccutil/elst.cpp create mode 100644 ccutil/elst.h create mode 100644 ccutil/elst2.cpp create mode 100644 ccutil/elst2.h create mode 100644 ccutil/errcode.cpp create mode 100644 ccutil/errcode.h create mode 100644 ccutil/fileerr.h create mode 100644 ccutil/getopt.cpp create mode 100644 ccutil/getopt.h create mode 100644 ccutil/globaloc.cpp create mode 100644 ccutil/globaloc.h create mode 100644 ccutil/hashfn.cpp create mode 100644 ccutil/hashfn.h create mode 100644 ccutil/host.h create mode 100644 ccutil/hosthplb.h create mode 100644 ccutil/lsterr.h create mode 100644 ccutil/mainblk.cpp create mode 100644 ccutil/mainblk.h create mode 100644 ccutil/memblk.cpp create mode 100644 ccutil/memblk.h create mode 100644 ccutil/memry.cpp create mode 100644 ccutil/memry.h create mode 100644 ccutil/memryerr.h create mode 100644 ccutil/mfcpch.cpp create mode 100644 ccutil/mfcpch.h create mode 100644 ccutil/ndminx.h create mode 100644 ccutil/notdll.h create mode 100644 ccutil/nwmain.h create mode 100644 ccutil/ocrclass.h create mode 100644 ccutil/ocrshell.cpp create mode 100644 ccutil/ocrshell.h create mode 100644 ccutil/platform.h create mode 100644 ccutil/scanutils.cpp create mode 100644 ccutil/scanutils.h create mode 100644 ccutil/secname.h create mode 100644 ccutil/serialis.cpp create mode 100644 ccutil/serialis.h create mode 100644 ccutil/stderr.h create mode 100644 ccutil/strngs.cpp create mode 100644 ccutil/strngs.h create mode 100644 ccutil/tessclas.h create mode 100644 ccutil/tprintf.cpp create mode 100644 ccutil/tprintf.h create mode 100644 ccutil/unichar.cpp create mode 100644 ccutil/unichar.h create mode 100644 ccutil/varable.cpp create mode 100644 ccutil/varable.h create mode 100644 classify/Makefile.am create mode 100644 classify/Makefile.in create mode 100644 classify/adaptive.cpp create mode 100644 classify/adaptive.h create mode 100644 classify/adaptmatch.cpp create mode 100644 classify/adaptmatch.h create mode 100644 classify/baseline.cpp create mode 100644 classify/baseline.h create mode 100644 classify/blobclass.cpp create mode 100644 classify/blobclass.h create mode 100644 classify/chartoname.cpp create mode 100644 classify/chartoname.h create mode 100644 classify/cluster.cpp create mode 100644 classify/cluster.h create mode 100644 classify/clusttool.cpp create mode 100644 classify/clusttool.h create mode 100644 classify/cutoffs.cpp create mode 100644 classify/cutoffs.h create mode 100644 classify/extern.h create mode 100644 classify/extract.cpp create mode 100644 classify/extract.h create mode 100644 classify/featdefs.cpp create mode 100644 classify/featdefs.h create mode 100644 classify/flexfx.cpp create mode 100644 classify/flexfx.h create mode 100644 classify/float2int.cpp create mode 100644 classify/float2int.h create mode 100644 classify/fpoint.cpp create mode 100644 classify/fpoint.h create mode 100644 classify/fxdefs.cpp create mode 100644 classify/fxdefs.h create mode 100644 classify/fxid.h create mode 100644 classify/hideedge.cpp create mode 100644 classify/hideedge.h create mode 100644 classify/intfx.cpp create mode 100644 classify/intfx.h create mode 100644 classify/intmatcher.cpp create mode 100644 classify/intmatcher.h create mode 100644 classify/intproto.cpp create mode 100644 classify/intproto.h create mode 100644 classify/kdtree.cpp create mode 100644 classify/kdtree.h create mode 100644 classify/mf.cpp create mode 100644 classify/mf.h create mode 100644 classify/mfdefs.cpp create mode 100644 classify/mfdefs.h create mode 100644 classify/mfoutline.cpp create mode 100644 classify/mfoutline.h create mode 100644 classify/mfx.cpp create mode 100644 classify/mfx.h create mode 100644 classify/normfeat.cpp create mode 100644 classify/normfeat.h create mode 100644 classify/normmatch.cpp create mode 100644 classify/normmatch.h create mode 100644 classify/ocrfeatures.cpp create mode 100644 classify/ocrfeatures.h create mode 100644 classify/outfeat.cpp create mode 100644 classify/outfeat.h create mode 100644 classify/picofeat.cpp create mode 100644 classify/picofeat.h create mode 100644 classify/protos.cpp create mode 100644 classify/protos.h create mode 100644 classify/sigmenu.cpp create mode 100644 classify/sigmenu.h create mode 100644 classify/speckle.cpp create mode 100644 classify/speckle.h create mode 100644 classify/xform2d.cpp create mode 100644 classify/xform2d.h create mode 100644 config/ac_compile_check_sizeof.m4 create mode 100644 config/ac_create_stdint_h.m4 create mode 100644 config/ac_define_versionlevel.m4 create mode 100644 config/acinclude_custom.m4 create mode 100644 config/ax_create_stdint_h.m4 create mode 100755 config/config.guess create mode 100644 config/config.h.in create mode 100755 config/config.sub create mode 100755 config/depcomp create mode 100755 config/install-sh create mode 100755 config/missing create mode 100755 config/mkinstalldirs create mode 100755 configure create mode 100644 configure.ac create mode 100644 cutil/Makefile.am create mode 100644 cutil/Makefile.in create mode 100644 cutil/bitvec.cpp create mode 100644 cutil/bitvec.h create mode 100644 cutil/callcpp.h create mode 100644 cutil/const.h create mode 100644 cutil/cutil.cpp create mode 100644 cutil/cutil.h create mode 100644 cutil/danerror.cpp create mode 100644 cutil/danerror.h create mode 100644 cutil/debug.cpp create mode 100644 cutil/debug.h create mode 100644 cutil/efio.cpp create mode 100644 cutil/efio.h create mode 100644 cutil/emalloc.cpp create mode 100644 cutil/emalloc.h create mode 100644 cutil/freelist.cpp create mode 100644 cutil/freelist.h create mode 100644 cutil/funcdefs.h create mode 100644 cutil/general.h create mode 100644 cutil/globals.cpp create mode 100644 cutil/globals.h create mode 100644 cutil/listio.cpp create mode 100644 cutil/listio.h create mode 100644 cutil/minmax.h create mode 100644 cutil/oldheap.cpp create mode 100644 cutil/oldheap.h create mode 100644 cutil/oldlist.cpp create mode 100644 cutil/oldlist.h create mode 100644 cutil/structures.cpp create mode 100644 cutil/structures.h create mode 100644 cutil/tessarray.cpp create mode 100644 cutil/tessarray.h create mode 100644 cutil/tordvars.cpp create mode 100644 cutil/tordvars.h create mode 100644 cutil/variables.cpp create mode 100644 cutil/variables.h create mode 100644 dict/Makefile.am create mode 100644 dict/Makefile.in create mode 100644 dict/choicearr.h create mode 100644 dict/choices.cpp create mode 100644 dict/choices.h create mode 100644 dict/context.cpp create mode 100644 dict/context.h create mode 100644 dict/dawg.cpp create mode 100644 dict/dawg.h create mode 100644 dict/hyphen.cpp create mode 100644 dict/hyphen.h create mode 100644 dict/matchdefs.h create mode 100644 dict/permdawg.cpp create mode 100644 dict/permdawg.h create mode 100644 dict/permnum.cpp create mode 100644 dict/permnum.h create mode 100644 dict/permute.cpp create mode 100644 dict/permute.h create mode 100644 dict/states.cpp create mode 100644 dict/states.h create mode 100644 dict/stopper.cpp create mode 100644 dict/stopper.h create mode 100644 dict/trie.cpp create mode 100644 dict/trie.h create mode 100644 display/Makefile.am create mode 100644 display/Makefile.in create mode 100644 display/cmndwin.cpp create mode 100644 display/cmndwin.h create mode 100644 display/pagewalk.cpp create mode 100644 display/pagewalk.h create mode 100644 display/pgedit.cpp create mode 100644 display/pgedit.h create mode 100644 display/pgeditx.h create mode 100644 display/sbdmenu.cpp create mode 100644 display/sbdmenu.h create mode 100644 display/submen.h create mode 100644 display/tessio.h create mode 100644 display/varabled.cpp create mode 100644 display/varabled.h create mode 100644 display/varblmen.cpp create mode 100644 display/varblmen.h create mode 100644 display/varblwin.cpp create mode 100644 display/varblwin.h create mode 100644 doc/main.txt create mode 100644 image/Makefile.am create mode 100644 image/Makefile.in create mode 100644 image/bitstrm.cpp create mode 100644 image/bitstrm.h create mode 100644 image/img.h create mode 100644 image/imgbmp.cpp create mode 100644 image/imgbmp.h create mode 100644 image/imgerrs.h create mode 100644 image/imgio.cpp create mode 100644 image/imgio.h create mode 100644 image/imgs.cpp create mode 100644 image/imgs.h create mode 100644 image/imgtiff.cpp create mode 100644 image/imgtiff.h create mode 100644 image/imgunpk.h create mode 100644 phototest.tif create mode 100755 tessdata/DangAmbigs create mode 100755 tessdata/blackText.params create mode 100644 tessdata/configs/api_config create mode 100644 tessdata/configs/api_resaljet create mode 100644 tessdata/configs/box.train create mode 100755 tessdata/configs/inter create mode 100644 tessdata/configs/oldapi_config create mode 100644 tessdata/configs/oldbox.train create mode 100644 tessdata/configs/var_api_config create mode 100644 tessdata/configs/var_box.train create mode 100644 tessdata/configs/variable_config create mode 100755 tessdata/confsets create mode 100755 tessdata/fmtable.cls create mode 100755 tessdata/fnetwts create mode 100755 tessdata/freq-dawg create mode 100755 tessdata/inttemp create mode 100755 tessdata/netwts create mode 100755 tessdata/newdiff.asccodes create mode 100755 tessdata/normproto create mode 100755 tessdata/pffmtable create mode 100755 tessdata/soptable.cls create mode 100644 tessdata/tessconfigs/batch create mode 100755 tessdata/tessconfigs/matdemo create mode 100755 tessdata/tessconfigs/old_batch create mode 100755 tessdata/tessconfigs/segdemo create mode 100755 tessdata/tessconfigs/var_batch create mode 100755 tessdata/test_matrix create mode 100755 tessdata/user-words create mode 100755 tessdata/word-dawg create mode 100755 tesseract.dsp create mode 100755 tesseract.dsw create mode 100644 textord/Makefile.am create mode 100644 textord/Makefile.in create mode 100644 textord/blkocc.cpp create mode 100644 textord/blkocc.h create mode 100644 textord/blobcmpl.h create mode 100644 textord/drawedg.cpp create mode 100644 textord/drawedg.h create mode 100644 textord/drawtord.cpp create mode 100644 textord/drawtord.h create mode 100644 textord/edgblob.cpp create mode 100644 textord/edgblob.h create mode 100644 textord/edgloop.cpp create mode 100644 textord/edgloop.h create mode 100644 textord/fpchop.cpp create mode 100644 textord/fpchop.h create mode 100644 textord/gap_map.cpp create mode 100644 textord/gap_map.h create mode 100644 textord/makerow.cpp create mode 100644 textord/makerow.h create mode 100644 textord/oldbasel.cpp create mode 100644 textord/oldbasel.h create mode 100644 textord/pithsync.cpp create mode 100644 textord/pithsync.h create mode 100644 textord/pitsync1.cpp create mode 100644 textord/pitsync1.h create mode 100644 textord/scanedg.cpp create mode 100644 textord/scanedg.h create mode 100644 textord/sortflts.cpp create mode 100644 textord/sortflts.h create mode 100644 textord/tessout.h create mode 100644 textord/topitch.cpp create mode 100644 textord/topitch.h create mode 100644 textord/tordmain.cpp create mode 100644 textord/tordmain.h create mode 100644 textord/tospace.cpp create mode 100644 textord/tospace.h create mode 100644 textord/tovars.cpp create mode 100644 textord/tovars.h create mode 100644 textord/underlin.cpp create mode 100644 textord/underlin.h create mode 100644 textord/wordseg.cpp create mode 100644 textord/wordseg.h create mode 100644 training/Makefile.am create mode 100644 training/Makefile.in create mode 100644 training/cnTraining.cpp create mode 100644 training/cnTraining.dsp create mode 100755 training/cnTraining1.dsp create mode 100644 training/mergenf.cpp create mode 100644 training/mergenf.h create mode 100644 training/mfTraining.cpp create mode 100644 training/mfTraining.dsp create mode 100644 training/name2char.cpp create mode 100644 training/name2char.h create mode 100644 training/training.cpp create mode 100644 training/training.h create mode 100644 viewer/Makefile.am create mode 100644 viewer/Makefile.in create mode 100644 viewer/evntlst.cpp create mode 100644 viewer/evntlst.h create mode 100644 viewer/evnts.cpp create mode 100644 viewer/evnts.h create mode 100644 viewer/grphics.cpp create mode 100644 viewer/grphics.h create mode 100644 viewer/grphshm.cpp create mode 100644 viewer/grphshm.h create mode 100644 viewer/sbgconst.h create mode 100644 viewer/sbgdefs.h create mode 100644 viewer/sbgtypes.h create mode 100644 viewer/showim.cpp create mode 100644 viewer/showim.h create mode 100644 wordrec/Makefile.am create mode 100644 wordrec/Makefile.in create mode 100644 wordrec/associate.cpp create mode 100644 wordrec/associate.h create mode 100644 wordrec/badwords.cpp create mode 100644 wordrec/badwords.h create mode 100644 wordrec/bestfirst.cpp create mode 100644 wordrec/bestfirst.h create mode 100644 wordrec/charsample.h create mode 100644 wordrec/chop.cpp create mode 100644 wordrec/chop.h create mode 100644 wordrec/chopper.cpp create mode 100644 wordrec/chopper.h create mode 100644 wordrec/closed.cpp create mode 100644 wordrec/closed.h create mode 100644 wordrec/djmenus.cpp create mode 100644 wordrec/djmenus.h create mode 100644 wordrec/drawfx.cpp create mode 100644 wordrec/drawfx.h create mode 100644 wordrec/findseam.cpp create mode 100644 wordrec/findseam.h create mode 100644 wordrec/gradechop.cpp create mode 100644 wordrec/gradechop.h create mode 100644 wordrec/heuristic.cpp create mode 100644 wordrec/heuristic.h create mode 100644 wordrec/makechop.cpp create mode 100644 wordrec/makechop.h create mode 100644 wordrec/matchtab.cpp create mode 100644 wordrec/matchtab.h create mode 100644 wordrec/matrix.cpp create mode 100644 wordrec/matrix.h create mode 100644 wordrec/measure.h create mode 100644 wordrec/metrics.cpp create mode 100644 wordrec/metrics.h create mode 100644 wordrec/mfvars.cpp create mode 100644 wordrec/mfvars.h create mode 100644 wordrec/msmenus.cpp create mode 100644 wordrec/msmenus.h create mode 100644 wordrec/olutil.cpp create mode 100644 wordrec/olutil.h create mode 100644 wordrec/outlines.cpp create mode 100644 wordrec/outlines.h create mode 100644 wordrec/pieces.cpp create mode 100644 wordrec/pieces.h create mode 100644 wordrec/plotedges.cpp create mode 100644 wordrec/plotedges.h create mode 100644 wordrec/plotseg.cpp create mode 100644 wordrec/plotseg.h create mode 100644 wordrec/render.cpp create mode 100644 wordrec/render.h create mode 100644 wordrec/seam.cpp create mode 100644 wordrec/seam.h create mode 100644 wordrec/split.cpp create mode 100644 wordrec/split.h create mode 100644 wordrec/tally.cpp create mode 100644 wordrec/tally.h create mode 100644 wordrec/tessinit.cpp create mode 100644 wordrec/tessinit.h create mode 100644 wordrec/tface.cpp create mode 100644 wordrec/tface.h create mode 100644 wordrec/wordclass.cpp create mode 100644 wordrec/wordclass.h diff --git a/.cvsignore b/.cvsignore new file mode 100644 index 0000000000..0bd2faed9d --- /dev/null +++ b/.cvsignore @@ -0,0 +1,6 @@ +BUILD +OWNERS +Makefile +README.google +runautoconf +config_auto.h diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000000..a0a208f0d9 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,8 @@ +Ray Smith (lead developer) +Phil Cheatle +Simon Crouch +Dan Johnson +Mark Seaman +Sheelagh Huddleston +Chris Newton +... and several others. diff --git a/COPYING b/COPYING new file mode 100644 index 0000000000..20435711d7 --- /dev/null +++ b/COPYING @@ -0,0 +1,23 @@ +This package contains the Tesseract Open Source OCR Engine. +Orignally 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: +================================ +The Aspirin/MIGRAINES system is no longer used. + +Tesseract can also make use of the libtiff library. (www.libtiff.org) +Without libtiff, Tesseract can only read uncompressed and G3 compressed +TIFF files. diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000000..bcc09819ca --- /dev/null +++ b/ChangeLog @@ -0,0 +1,20 @@ +June 2006 - V1.0 of open source Tesseract checked-in. +Sep 7 2006 - 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. +Oct 4 2006 - V1.02 + Removed dependency on Aspirin. + Fixed a few missing Apache license headers. + Removed $log. +Feb 2 2007 - 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. + diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000000..a4b34144dc --- /dev/null +++ b/INSTALL @@ -0,0 +1,229 @@ +Copyright 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. + + 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. + diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000000..5ef53b5384 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,15 @@ +# TODO(luc) Add 'doc' to this list when ready +SUBDIRS = ccstruct ccutil classify cutil dict display image textord viewer wordrec ccmain training + +EXTRA_DIST = tessdata phototest.tif tesseract.dsp tesseract.dsw +#EXTRA_DIST = doc/html doc/@PACKAGE_NAME@_@PACKAGE_VERSION@.pdf doc/@PACKAGE_NAME@_@PACKAGE_VERSION@.ps.gz + +dist-hook: +# Need to remove CVS directories from directories +# added using EXTRA_DIST. $(distdir)/tessdata would in +# theory suffice. + rm -rf `find $(distdir) -name CVS` +# Also remove extra files not needed in a distribution + rm -rf `find $(distdir) -name configure.ac` + rm -rf `find $(distdir) -name acinclude.m4` + rm -rf `find $(distdir) -name aclocal.m4` diff --git a/Makefile.in b/Makefile.in new file mode 100644 index 0000000000..4017001d31 --- /dev/null +++ b/Makefile.in @@ -0,0 +1,628 @@ +# Makefile.in generated by automake 1.9.6 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005 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@ +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = . +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +INSTALL = @INSTALL@ +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@ +DIST_COMMON = README $(am__configure_deps) $(srcdir)/Makefile.am \ + $(srcdir)/Makefile.in $(top_srcdir)/config/config.h.in \ + $(top_srcdir)/configure AUTHORS COPYING ChangeLog INSTALL NEWS \ + config/config.guess config/config.sub config/depcomp \ + config/install-sh config/missing config/mkinstalldirs +subdir = . +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \ + $(top_srcdir)/config/ac_define_versionlevel.m4 \ + $(top_srcdir)/config/acinclude_custom.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 configure.status.lineno +mkinstalldirs = $(SHELL) $(top_srcdir)/config/mkinstalldirs +CONFIG_HEADER = config_auto.h +CONFIG_CLEAN_FILES = +SOURCES = +DIST_SOURCES = +RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \ + html-recursive info-recursive install-data-recursive \ + install-exec-recursive install-info-recursive \ + install-recursive installcheck-recursive installdirs-recursive \ + pdf-recursive ps-recursive uninstall-info-recursive \ + uninstall-recursive +ETAGS = etags +CTAGS = ctags +DIST_SUBDIRS = $(SUBDIRS) +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +distdir = $(PACKAGE)-$(VERSION) +top_distdir = $(distdir) +am__remove_distdir = \ + { test ! -d $(distdir) \ + || { find $(distdir) -type d ! -perm -200 -exec chmod u+w {} ';' \ + && rm -fr $(distdir); }; } +DIST_ARCHIVES = $(distdir).tar.gz +GZIP_ENV = --best +distuninstallcheck_listfiles = find . -type f -print +distcleancheck_listfiles = find . -type f -print +ACLOCAL = @ACLOCAL@ +AMDEP_FALSE = @AMDEP_FALSE@ +AMDEP_TRUE = @AMDEP_TRUE@ +AMTAR = @AMTAR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CXXRPOFLAGS = @CXXRPOFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +GNUWIN32_DIR = @GNUWIN32_DIR@ +HAVE_GNUWIN32_FALSE = @HAVE_GNUWIN32_FALSE@ +HAVE_GNUWIN32_TRUE = @HAVE_GNUWIN32_TRUE@ +HAVE_LIBTIFF_FALSE = @HAVE_LIBTIFF_FALSE@ +HAVE_LIBTIFF_TRUE = @HAVE_LIBTIFF_TRUE@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTIFF_CFLAGS = @LIBTIFF_CFLAGS@ +LIBTIFF_LIBS = @LIBTIFF_LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAINTAINER_MODE_FALSE = @MAINTAINER_MODE_FALSE@ +MAINTAINER_MODE_TRUE = @MAINTAINER_MODE_TRUE@ +MAKEINFO = @MAKEINFO@ +OBJEXT = @OBJEXT@ +OPTS = @OPTS@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_DATE = @PACKAGE_DATE@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PACKAGE_YEAR = @PACKAGE_YEAR@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +RPO_NO = @RPO_NO@ +RPO_YES = @RPO_YES@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +USING_CL_FALSE = @USING_CL_FALSE@ +USING_CL_TRUE = @USING_CL_TRUE@ +VERSION = @VERSION@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_RANLIB = @ac_ct_RANLIB@ +ac_ct_STRIP = @ac_ct_STRIP@ +am__fastdepCC_FALSE = @am__fastdepCC_FALSE@ +am__fastdepCC_TRUE = @am__fastdepCC_TRUE@ +am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@ +am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@ +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@ +datadir = @datadir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ + +# TODO(luc) Add 'doc' to this list when ready +SUBDIRS = ccstruct ccutil classify cutil dict display image textord viewer wordrec ccmain training +EXTRA_DIST = tessdata phototest.tif tesseract.dsp tesseract.dsw +all: config_auto.h + $(MAKE) $(AM_MAKEFLAGS) all-recursive + +.SUFFIXES: +am--refresh: + @: +$(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) --gnu '; \ + cd $(srcdir) && $(AUTOMAKE) --gnu \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu Makefile'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu 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) + cd $(srcdir) && $(AUTOCONF) +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS) + +config_auto.h: stamp-h1 + @if test ! -f $@; then \ + rm -f stamp-h1; \ + $(MAKE) 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) + cd $(top_srcdir) && $(AUTOHEADER) + rm -f stamp-h1 + touch $@ + +distclean-hdr: + -rm -f config_auto.h stamp-h1 +uninstall-info-am: + +# 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. +$(RECURSIVE_TARGETS): + @failcom='exit 1'; \ + for f in x $$MAKEFLAGS; do \ + case $$f in \ + *=* | --[!k]*);; \ + *k*) failcom='fail=yes';; \ + esac; \ + done; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + list='$(SUBDIRS)'; 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; \ + (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" + +mostlyclean-recursive clean-recursive distclean-recursive \ +maintainer-clean-recursive: + @failcom='exit 1'; \ + for f in x $$MAKEFLAGS; do \ + case $$f in \ + *=* | --[!k]*);; \ + *k*) failcom='fail=yes';; \ + esac; \ + done; \ + dot_seen=no; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + rev=''; for subdir in $$list; do \ + if test "$$subdir" = "."; then :; else \ + rev="$$subdir $$rev"; \ + fi; \ + done; \ + rev="$$rev ."; \ + target=`echo $@ | sed s/-recursive//`; \ + for subdir in $$rev; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done && test -z "$$fail" +tags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \ + done +ctags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \ + done + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + 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 || \ + tags="$$tags $$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique; \ + fi +ctags: CTAGS +CTAGS: ctags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(CTAGS_ARGS)$$tags$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + $(am__remove_distdir) + mkdir $(distdir) + $(mkdir_p) $(distdir)/config + @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \ + list='$(DISTFILES)'; for file in $$list; do \ + case $$file in \ + $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \ + $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \ + esac; \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + dir="/$$dir"; \ + $(mkdir_p) "$(distdir)$$dir"; \ + else \ + dir=''; \ + fi; \ + if test -d $$d/$$file; then \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$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 \ + test -d "$(distdir)/$$subdir" \ + || $(mkdir_p) "$(distdir)/$$subdir" \ + || exit 1; \ + distdir=`$(am__cd) $(distdir) && pwd`; \ + top_distdir=`$(am__cd) $(top_distdir) && pwd`; \ + (cd $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$top_distdir" \ + distdir="$$distdir/$$subdir" \ + distdir) \ + || exit 1; \ + fi; \ + done + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$(top_distdir)" distdir="$(distdir)" \ + dist-hook + -find $(distdir) -type d ! -perm -777 -exec chmod a+rwx {} \; -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 $(SHELL) $(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__remove_distdir) + +dist-bzip2: distdir + tardir=$(distdir) && $(am__tar) | bzip2 -9 -c >$(distdir).tar.bz2 + $(am__remove_distdir) + +dist-tarZ: distdir + tardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z + $(am__remove_distdir) + +dist-shar: distdir + shar $(distdir) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).shar.gz + $(am__remove_distdir) + +dist-zip: distdir + -rm -f $(distdir).zip + zip -rq $(distdir).zip $(distdir) + $(am__remove_distdir) + +dist dist-all: distdir + tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz + $(am__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) gunzip -c $(distdir).tar.gz | $(am__untar) ;;\ + *.tar.bz2*) \ + bunzip2 -c $(distdir).tar.bz2 | $(am__untar) ;;\ + *.tar.Z*) \ + uncompress -c $(distdir).tar.Z | $(am__untar) ;;\ + *.shar.gz*) \ + GZIP=$(GZIP_ENV) gunzip -c $(distdir).shar.gz | unshar ;;\ + *.zip*) \ + unzip $(distdir).zip ;;\ + esac + chmod -R a-w $(distdir); chmod a+w $(distdir) + mkdir $(distdir)/_build + mkdir $(distdir)/_inst + chmod a-w $(distdir) + dc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \ + && dc_destdir="$${TMPDIR-/tmp}/am-dc-$$$$/" \ + && cd $(distdir)/_build \ + && ../configure --srcdir=.. --prefix="$$dc_install_base" \ + $(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 + $(am__remove_distdir) + @(echo "$(distdir) archives ready for distribution: "; \ + list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \ + sed -e '1{h;s/./=/g;p;x;}' -e '$${p;x;}' +distuninstallcheck: + @cd $(distuninstallcheck_dir) \ + && test `$(distuninstallcheck_listfiles) | wc -l` -le 1 \ + || { 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 config_auto.h +installdirs: installdirs-recursive +installdirs-am: +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: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_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-recursive + +clean-am: clean-generic mostlyclean-am + +distclean: distclean-recursive + -rm -f $(am__CONFIG_DISTCLEAN_FILES) + -rm -f Makefile +distclean-am: clean-am distclean-generic distclean-hdr distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +info: info-recursive + +info-am: + +install-data-am: + +install-exec-am: + +install-info: install-info-recursive + +install-man: + +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 + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: uninstall-info-am + +uninstall-info: uninstall-info-recursive + +.PHONY: $(RECURSIVE_TARGETS) CTAGS GTAGS all all-am am--refresh check \ + check-am clean clean-generic clean-recursive ctags \ + ctags-recursive dist dist-all dist-bzip2 dist-gzip dist-hook \ + dist-shar dist-tarZ dist-zip distcheck distclean \ + distclean-generic distclean-hdr distclean-recursive \ + distclean-tags distcleancheck distdir distuninstallcheck dvi \ + dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-exec install-exec-am \ + install-info install-info-am install-man install-strip \ + installcheck installcheck-am installdirs installdirs-am \ + maintainer-clean maintainer-clean-generic \ + maintainer-clean-recursive mostlyclean mostlyclean-generic \ + mostlyclean-recursive pdf pdf-am ps ps-am tags tags-recursive \ + uninstall uninstall-am uninstall-info-am + +#EXTRA_DIST = doc/html doc/@PACKAGE_NAME@_@PACKAGE_VERSION@.pdf doc/@PACKAGE_NAME@_@PACKAGE_VERSION@.ps.gz + +dist-hook: +# Need to remove CVS directories from directories +# added using EXTRA_DIST. $(distdir)/tessdata would in +# theory suffice. + rm -rf `find $(distdir) -name CVS` +# Also remove extra files not needed in a distribution + rm -rf `find $(distdir) -name configure.ac` + rm -rf `find $(distdir) -name acinclude.m4` + rm -rf `find $(distdir) -name aclocal.m4` +# 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: diff --git a/NEWS b/NEWS new file mode 100644 index 0000000000..d2902ea6d9 --- /dev/null +++ b/NEWS @@ -0,0 +1 @@ +Stub file. To be populated at a later stage. diff --git a/README b/README new file mode 100644 index 0000000000..87eb84f442 --- /dev/null +++ b/README @@ -0,0 +1,85 @@ +Introduction +============ +This package contains the Tesseract Open Source OCR Engine. +Orignally 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: +================================ +The Aspirin/MIGRAINES system is no longer required. + +Tesseract can also make use of the libtiff library. (www.libtiff.org) +Without libtiff, Tesseract can only read uncompressed and G3 compressed +TIFF files. + + +History: +======== +The engine was 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. +A lot of the code was written in C, and then some more was written in C++. +Since then all the code has been converted to at least compile with a C++ +compiler. Currently it builds under Linux with gcc2.95 and under Windows +with VC++6. The C++ code makes heavy use of a list system using macros. +This predates stl, was portable before stl, and is more efficent than stl +lists, but has the big negative that if you do get a segmentation violation, +it is hard to debug. Another "feature" of the C/C++ split is that the C++ +data structures get converted to C data structures to call the low-level C +code. This is ugly, and the C++izing of the C code is a step towards +eliminating the conversion, but it has not happened yet. + + +Directory Structure (ordered by dependency): +============================================ +ccmain Top-level code. The main program resides in tesseractmain.cpp. +display An "editor" to view and operate on the internal structures. + (Requires a working viewer - batteries not included.) +wordrec The word-level recognizer. +textord The module that organizes(orders) text into lines and words. +classify The low-level character classifiers. +ccstruct Classes to hold information about a page as it is being processed. +viewer The client side of a client server viewing system. + Unfortunately, at this time, the server side is not available. +image Image class and processing functions. +dict Language model code. +cutil Code for file I/O, lists, heaps etc, from the old C code. +ccutil Somewhat newer code for lists, memory allocation etc from the + old C++ code. + + +About the Engine +================ +This code is a raw OCR engine. It has NO PAGE LAYOUT ANALYSIS, NO OUTPUT +FORMATTING, and NO UI. It can only process an image of a single column +and create text from it. It can detect fixed pitch vs proportional text. +Having said that, in 1995, this engine was in the top 3 in terms of character +accuracy, and it compiles and runs on both Linux and Windows. Another current +limitation is that it only recognizes English and its character set is only +US-ASCII. Training code IS included in the open source release however, and +will be included in a future release. + + +Using the Engine +================ +The usage of both Windows and Linux versions is the same. +The executable must reside in the same directory as the tessdata directory +The command line is: +tesseract batch +The image file requires an .tif extension for its type to be recognized +correctly. If a file exists with the .tif extension replaced by .uzn, then it +will be interpreted as a UNLV-style zone file. (See www.isri.unlv.edu for +details of the zone files.) + diff --git a/ReleaseNotes b/ReleaseNotes new file mode 100644 index 0000000000..f21f7afb08 --- /dev/null +++ b/ReleaseNotes @@ -0,0 +1,78 @@ +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. diff --git a/acinclude.m4 b/acinclude.m4 new file mode 100644 index 0000000000..0ed906ad7d --- /dev/null +++ b/acinclude.m4 @@ -0,0 +1,10 @@ +# Master include for AC macros. This directory structure allows +# for more flexibility with respect to CVS modules. +# +# Author: Luc Vincent + +### m4_include(config/ac_compile_check_sizeof.m4)dnl +#m4_include(config/ac_create_stdint_h.m4)dnl +#m4_include(config/ax_create_stdint_h.m4)dnl +m4_include(config/ac_define_versionlevel.m4)dnl +m4_include(config/acinclude_custom.m4)dnl diff --git a/aclocal.m4 b/aclocal.m4 new file mode 100644 index 0000000000..bddd1ef0ee --- /dev/null +++ b/aclocal.m4 @@ -0,0 +1,920 @@ +# generated automatically by aclocal 1.9.6 -*- Autoconf -*- + +# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, +# 2005 Free Software Foundation, Inc. +# This file 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. + +# Copyright (C) 2002, 2003, 2005 Free Software Foundation, Inc. +# +# This file 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. + +# AM_AUTOMAKE_VERSION(VERSION) +# ---------------------------- +# Automake X.Y traces this macro to ensure aclocal.m4 has been +# generated from the m4 files accompanying Automake X.Y. +AC_DEFUN([AM_AUTOMAKE_VERSION], [am__api_version="1.9"]) + +# AM_SET_CURRENT_AUTOMAKE_VERSION +# ------------------------------- +# Call AM_AUTOMAKE_VERSION so it can be traced. +# This function is AC_REQUIREd by AC_INIT_AUTOMAKE. +AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION], + [AM_AUTOMAKE_VERSION([1.9.6])]) + +# AM_AUX_DIR_EXPAND -*- Autoconf -*- + +# Copyright (C) 2001, 2003, 2005 Free Software Foundation, Inc. +# +# This file 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. + +# For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets +# $ac_aux_dir to `$srcdir/foo'. In other projects, it is set to +# `$srcdir', `$srcdir/..', or `$srcdir/../..'. +# +# Of course, Automake must honor this variable whenever it calls a +# tool from the auxiliary directory. The problem is that $srcdir (and +# therefore $ac_aux_dir as well) can be either absolute or relative, +# depending on how configure is run. This is pretty annoying, since +# it makes $ac_aux_dir quite unusable in subdirectories: in the top +# source directory, any form will work fine, but in subdirectories a +# relative path needs to be adjusted first. +# +# $ac_aux_dir/missing +# fails when called from a subdirectory if $ac_aux_dir is relative +# $top_srcdir/$ac_aux_dir/missing +# fails if $ac_aux_dir is absolute, +# fails when called from a subdirectory in a VPATH build with +# a relative $ac_aux_dir +# +# The reason of the latter failure is that $top_srcdir and $ac_aux_dir +# are both prefixed by $srcdir. In an in-source build this is usually +# harmless because $srcdir is `.', but things will broke when you +# start a VPATH build or use an absolute $srcdir. +# +# So we could use something similar to $top_srcdir/$ac_aux_dir/missing, +# iff we strip the leading $srcdir from $ac_aux_dir. That would be: +# am_aux_dir='\$(top_srcdir)/'`expr "$ac_aux_dir" : "$srcdir//*\(.*\)"` +# and then we would define $MISSING as +# MISSING="\${SHELL} $am_aux_dir/missing" +# This will work as long as MISSING is not called from configure, because +# unfortunately $(top_srcdir) has no meaning in configure. +# However there are other variables, like CC, which are often used in +# configure, and could therefore not use this "fixed" $ac_aux_dir. +# +# Another solution, used here, is to always expand $ac_aux_dir to an +# absolute PATH. The drawback is that using absolute paths prevent a +# configured tree to be moved without reconfiguration. + +AC_DEFUN([AM_AUX_DIR_EXPAND], +[dnl Rely on autoconf to set up CDPATH properly. +AC_PREREQ([2.50])dnl +# expand $ac_aux_dir to an absolute path +am_aux_dir=`cd $ac_aux_dir && pwd` +]) + +# AM_CONDITIONAL -*- Autoconf -*- + +# Copyright (C) 1997, 2000, 2001, 2003, 2004, 2005 +# Free Software Foundation, Inc. +# +# This file 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. + +# serial 7 + +# AM_CONDITIONAL(NAME, SHELL-CONDITION) +# ------------------------------------- +# Define a conditional. +AC_DEFUN([AM_CONDITIONAL], +[AC_PREREQ(2.52)dnl + ifelse([$1], [TRUE], [AC_FATAL([$0: invalid condition: $1])], + [$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl +AC_SUBST([$1_TRUE]) +AC_SUBST([$1_FALSE]) +if $2; then + $1_TRUE= + $1_FALSE='#' +else + $1_TRUE='#' + $1_FALSE= +fi +AC_CONFIG_COMMANDS_PRE( +[if test -z "${$1_TRUE}" && test -z "${$1_FALSE}"; then + AC_MSG_ERROR([[conditional "$1" was never defined. +Usually this means the macro was only invoked conditionally.]]) +fi])]) + + +# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 +# Free Software Foundation, Inc. +# +# This file 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. + +# serial 8 + +# There are a few dirty hacks below to avoid letting `AC_PROG_CC' be +# written in clear, in which case automake, when reading aclocal.m4, +# will think it sees a *use*, and therefore will trigger all it's +# C support machinery. Also note that it means that autoscan, seeing +# CC etc. in the Makefile, will ask for an AC_PROG_CC use... + + +# _AM_DEPENDENCIES(NAME) +# ---------------------- +# See how the compiler implements dependency checking. +# NAME is "CC", "CXX", "GCJ", or "OBJC". +# We try a few techniques and use that to set a single cache variable. +# +# We don't AC_REQUIRE the corresponding AC_PROG_CC since the latter was +# modified to invoke _AM_DEPENDENCIES(CC); we would have a circular +# dependency, and given that the user is not expected to run this macro, +# just rely on AC_PROG_CC. +AC_DEFUN([_AM_DEPENDENCIES], +[AC_REQUIRE([AM_SET_DEPDIR])dnl +AC_REQUIRE([AM_OUTPUT_DEPENDENCY_COMMANDS])dnl +AC_REQUIRE([AM_MAKE_INCLUDE])dnl +AC_REQUIRE([AM_DEP_TRACK])dnl + +ifelse([$1], CC, [depcc="$CC" am_compiler_list=], + [$1], CXX, [depcc="$CXX" am_compiler_list=], + [$1], OBJC, [depcc="$OBJC" am_compiler_list='gcc3 gcc'], + [$1], GCJ, [depcc="$GCJ" am_compiler_list='gcc3 gcc'], + [depcc="$$1" am_compiler_list=]) + +AC_CACHE_CHECK([dependency style of $depcc], + [am_cv_$1_dependencies_compiler_type], +[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'. + 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_$1_dependencies_compiler_type=none + if test "$am_compiler_list" = ""; then + am_compiler_list=`sed -n ['s/^#*\([a-zA-Z0-9]*\))$/\1/p'] < ./depcomp` + fi + 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 8's {/usr,}/bin/sh. + touch sub/conftst$i.h + done + echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf + + case $depmode in + 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 + ;; + none) break ;; + esac + # 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. + if depmode=$depmode \ + source=sub/conftest.c object=sub/conftest.${OBJEXT-o} \ + depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ + $SHELL ./depcomp $depcc -c -o sub/conftest.${OBJEXT-o} sub/conftest.c \ + >/dev/null 2>conftest.err && + grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && + grep sub/conftest.${OBJEXT-o} 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_$1_dependencies_compiler_type=$depmode + break + fi + fi + done + + cd .. + rm -rf conftest.dir +else + am_cv_$1_dependencies_compiler_type=none +fi +]) +AC_SUBST([$1DEPMODE], [depmode=$am_cv_$1_dependencies_compiler_type]) +AM_CONDITIONAL([am__fastdep$1], [ + test "x$enable_dependency_tracking" != xno \ + && test "$am_cv_$1_dependencies_compiler_type" = gcc3]) +]) + + +# AM_SET_DEPDIR +# ------------- +# Choose a directory name for dependency files. +# This macro is AC_REQUIREd in _AM_DEPENDENCIES +AC_DEFUN([AM_SET_DEPDIR], +[AC_REQUIRE([AM_SET_LEADING_DOT])dnl +AC_SUBST([DEPDIR], ["${am__leading_dot}deps"])dnl +]) + + +# AM_DEP_TRACK +# ------------ +AC_DEFUN([AM_DEP_TRACK], +[AC_ARG_ENABLE(dependency-tracking, +[ --disable-dependency-tracking speeds up one-time build + --enable-dependency-tracking do not reject slow dependency extractors]) +if test "x$enable_dependency_tracking" != xno; then + am_depcomp="$ac_aux_dir/depcomp" + AMDEPBACKSLASH='\' +fi +AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno]) +AC_SUBST([AMDEPBACKSLASH]) +]) + +# Generate code to set up dependency tracking. -*- Autoconf -*- + +# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 +# Free Software Foundation, Inc. +# +# This file 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. + +#serial 3 + +# _AM_OUTPUT_DEPENDENCY_COMMANDS +# ------------------------------ +AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS], +[for mf in $CONFIG_FILES; 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. + # So let's grep whole file. + if grep '^#.*generated by automake' $mf > /dev/null 2>&1; then + dirpart=`AS_DIRNAME("$mf")` + 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"` + # When using ansi2knr, U may be empty or an underscore; expand it + U=`sed -n 's/^U = //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' -e 's/\$U/'"$U"'/g'`; do + # Make sure the directory exists. + test -f "$dirpart/$file" && continue + fdir=`AS_DIRNAME(["$file"])` + AS_MKDIR_P([$dirpart/$fdir]) + # echo "creating $dirpart/$file" + echo '# dummy' > "$dirpart/$file" + done +done +])# _AM_OUTPUT_DEPENDENCY_COMMANDS + + +# AM_OUTPUT_DEPENDENCY_COMMANDS +# ----------------------------- +# This macro should only be invoked once -- use via AC_REQUIRE. +# +# This code is only required when automatic dependency tracking +# is enabled. FIXME. This creates each `.P' file that we will +# need in order to bootstrap the dependency handling code. +AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS], +[AC_CONFIG_COMMANDS([depfiles], + [test x"$AMDEP_TRUE" != x"" || _AM_OUTPUT_DEPENDENCY_COMMANDS], + [AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"]) +]) + +# Copyright (C) 1996, 1997, 2000, 2001, 2003, 2005 +# Free Software Foundation, Inc. +# +# This file 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. + +# serial 8 + +# AM_CONFIG_HEADER is obsolete. It has been replaced by AC_CONFIG_HEADERS. +AU_DEFUN([AM_CONFIG_HEADER], [AC_CONFIG_HEADERS($@)]) + +# Do all the work for Automake. -*- Autoconf -*- + +# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 +# Free Software Foundation, Inc. +# +# This file 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. + +# serial 12 + +# This macro actually does too much. Some checks are only needed if +# your package does certain things. But this isn't really a big deal. + +# AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE]) +# AM_INIT_AUTOMAKE([OPTIONS]) +# ----------------------------------------------- +# The call with PACKAGE and VERSION arguments is the old style +# call (pre autoconf-2.50), which is being phased out. PACKAGE +# and VERSION should now be passed to AC_INIT and removed from +# the call to AM_INIT_AUTOMAKE. +# We support both call styles for the transition. After +# the next Automake release, Autoconf can make the AC_INIT +# arguments mandatory, and then we can depend on a new Autoconf +# release and drop the old call support. +AC_DEFUN([AM_INIT_AUTOMAKE], +[AC_PREREQ([2.58])dnl +dnl Autoconf wants to disallow AM_ names. We explicitly allow +dnl the ones we care about. +m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl +AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl +AC_REQUIRE([AC_PROG_INSTALL])dnl +# test to see if srcdir already configured +if test "`cd $srcdir && pwd`" != "`pwd`" && + test -f $srcdir/config.status; then + AC_MSG_ERROR([source directory already configured; run "make distclean" there first]) +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 +AC_SUBST([CYGPATH_W]) + +# Define the identity of the package. +dnl Distinguish between old-style and new-style calls. +m4_ifval([$2], +[m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl + AC_SUBST([PACKAGE], [$1])dnl + AC_SUBST([VERSION], [$2])], +[_AM_SET_OPTIONS([$1])dnl + AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl + AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl + +_AM_IF_OPTION([no-define],, +[AC_DEFINE_UNQUOTED(PACKAGE, "$PACKAGE", [Name of package]) + AC_DEFINE_UNQUOTED(VERSION, "$VERSION", [Version number of package])])dnl + +# Some tools Automake needs. +AC_REQUIRE([AM_SANITY_CHECK])dnl +AC_REQUIRE([AC_ARG_PROGRAM])dnl +AM_MISSING_PROG(ACLOCAL, aclocal-${am__api_version}) +AM_MISSING_PROG(AUTOCONF, autoconf) +AM_MISSING_PROG(AUTOMAKE, automake-${am__api_version}) +AM_MISSING_PROG(AUTOHEADER, autoheader) +AM_MISSING_PROG(MAKEINFO, makeinfo) +AM_PROG_INSTALL_SH +AM_PROG_INSTALL_STRIP +AC_REQUIRE([AM_PROG_MKDIR_P])dnl +# We need awk for the "check" target. The system "awk" is bad on +# some platforms. +AC_REQUIRE([AC_PROG_AWK])dnl +AC_REQUIRE([AC_PROG_MAKE_SET])dnl +AC_REQUIRE([AM_SET_LEADING_DOT])dnl +_AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])], + [_AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])], + [_AM_PROG_TAR([v7])])]) +_AM_IF_OPTION([no-dependencies],, +[AC_PROVIDE_IFELSE([AC_PROG_CC], + [_AM_DEPENDENCIES(CC)], + [define([AC_PROG_CC], + defn([AC_PROG_CC])[_AM_DEPENDENCIES(CC)])])dnl +AC_PROVIDE_IFELSE([AC_PROG_CXX], + [_AM_DEPENDENCIES(CXX)], + [define([AC_PROG_CXX], + defn([AC_PROG_CXX])[_AM_DEPENDENCIES(CXX)])])dnl +]) +]) + + +# When config.status generates a header, we must update the stamp-h file. +# This file resides in the same directory as the config header +# that is generated. The stamp files are numbered to have different names. + +# Autoconf calls _AC_AM_CONFIG_HEADER_HOOK (when defined) in the +# loop where config.status creates the headers, so we can generate +# our stamp files there. +AC_DEFUN([_AC_AM_CONFIG_HEADER_HOOK], +[# Compute $1's index in $config_headers. +_am_stamp_count=1 +for _am_header in $config_headers :; do + case $_am_header in + $1 | $1:* ) + break ;; + * ) + _am_stamp_count=`expr $_am_stamp_count + 1` ;; + esac +done +echo "timestamp for $1" >`AS_DIRNAME([$1])`/stamp-h[]$_am_stamp_count]) + +# Copyright (C) 2001, 2003, 2005 Free Software Foundation, Inc. +# +# This file 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. + +# AM_PROG_INSTALL_SH +# ------------------ +# Define $install_sh. +AC_DEFUN([AM_PROG_INSTALL_SH], +[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl +install_sh=${install_sh-"$am_aux_dir/install-sh"} +AC_SUBST(install_sh)]) + +# Copyright (C) 2003, 2005 Free Software Foundation, Inc. +# +# This file 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. + +# serial 2 + +# Check whether the underlying file-system supports filenames +# with a leading dot. For instance MS-DOS doesn't. +AC_DEFUN([AM_SET_LEADING_DOT], +[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 +AC_SUBST([am__leading_dot])]) + +# Add --enable-maintainer-mode option to configure. -*- Autoconf -*- +# From Jim Meyering + +# Copyright (C) 1996, 1998, 2000, 2001, 2002, 2003, 2004, 2005 +# Free Software Foundation, Inc. +# +# This file 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. + +# serial 4 + +AC_DEFUN([AM_MAINTAINER_MODE], +[AC_MSG_CHECKING([whether to enable maintainer-specific portions of Makefiles]) + dnl maintainer-mode is disabled by default + AC_ARG_ENABLE(maintainer-mode, +[ --enable-maintainer-mode enable make rules and dependencies not useful + (and sometimes confusing) to the casual installer], + USE_MAINTAINER_MODE=$enableval, + USE_MAINTAINER_MODE=no) + AC_MSG_RESULT([$USE_MAINTAINER_MODE]) + AM_CONDITIONAL(MAINTAINER_MODE, [test $USE_MAINTAINER_MODE = yes]) + MAINT=$MAINTAINER_MODE_TRUE + AC_SUBST(MAINT)dnl +] +) + +AU_DEFUN([jm_MAINTAINER_MODE], [AM_MAINTAINER_MODE]) + +# Check to see how 'make' treats includes. -*- Autoconf -*- + +# Copyright (C) 2001, 2002, 2003, 2005 Free Software Foundation, Inc. +# +# This file 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. + +# serial 3 + +# AM_MAKE_INCLUDE() +# ----------------- +# Check to see how make treats includes. +AC_DEFUN([AM_MAKE_INCLUDE], +[am_make=${MAKE-make} +cat > confinc << 'END' +am__doit: + @echo done +.PHONY: am__doit +END +# If we don't find an include directive, just comment out the code. +AC_MSG_CHECKING([for style of include used by $am_make]) +am__include="#" +am__quote= +_am_result=none +# First try GNU make style include. +echo "include confinc" > confmf +# We grep out `Entering directory' and `Leaving directory' +# messages which can occur if `w' ends up in MAKEFLAGS. +# In particular we don't look at `^make:' because GNU make might +# be invoked under some other name (usually "gmake"), in which +# case it prints its new name instead of `make'. +if test "`$am_make -s -f confmf 2> /dev/null | grep -v 'ing directory'`" = "done"; then + am__include=include + am__quote= + _am_result=GNU +fi +# Now try BSD make style include. +if test "$am__include" = "#"; then + echo '.include "confinc"' > confmf + if test "`$am_make -s -f confmf 2> /dev/null`" = "done"; then + am__include=.include + am__quote="\"" + _am_result=BSD + fi +fi +AC_SUBST([am__include]) +AC_SUBST([am__quote]) +AC_MSG_RESULT([$_am_result]) +rm -f confinc confmf +]) + +# Copyright (C) 1999, 2000, 2001, 2003, 2005 Free Software Foundation, Inc. +# +# This file 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. + +# serial 3 + +# AM_PROG_CC_C_O +# -------------- +# Like AC_PROG_CC_C_O, but changed for automake. +AC_DEFUN([AM_PROG_CC_C_O], +[AC_REQUIRE([AC_PROG_CC_C_O])dnl +AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl +# FIXME: we rely on the cache variable name because +# there is no other way. +set dummy $CC +ac_cc=`echo $[2] | sed ['s/[^a-zA-Z0-9_]/_/g;s/^[0-9]/_/']` +if eval "test \"`echo '$ac_cv_prog_cc_'${ac_cc}_c_o`\" != yes"; then + # Losing compiler, so override with the script. + # FIXME: It is wrong to rewrite CC. + # But if we don't then we get into trouble of one sort or another. + # A longer-term fix would be to have automake use am__CC in this case, + # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" + CC="$am_aux_dir/compile $CC" +fi +]) + +# Fake the existence of programs that GNU maintainers use. -*- Autoconf -*- + +# Copyright (C) 1997, 1999, 2000, 2001, 2003, 2005 +# Free Software Foundation, Inc. +# +# This file 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. + +# serial 4 + +# AM_MISSING_PROG(NAME, PROGRAM) +# ------------------------------ +AC_DEFUN([AM_MISSING_PROG], +[AC_REQUIRE([AM_MISSING_HAS_RUN]) +$1=${$1-"${am_missing_run}$2"} +AC_SUBST($1)]) + + +# AM_MISSING_HAS_RUN +# ------------------ +# Define MISSING if not defined so far and test if it supports --run. +# If it does, set am_missing_run to use it, otherwise, to nothing. +AC_DEFUN([AM_MISSING_HAS_RUN], +[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl +test x"${MISSING+set}" = xset || MISSING="\${SHELL} $am_aux_dir/missing" +# Use eval to expand $SHELL +if eval "$MISSING --run true"; then + am_missing_run="$MISSING --run " +else + am_missing_run= + AC_MSG_WARN([`missing' script is too old or missing]) +fi +]) + +# Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc. +# +# This file 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. + +# AM_PROG_MKDIR_P +# --------------- +# Check whether `mkdir -p' is supported, fallback to mkinstalldirs otherwise. +# +# Automake 1.8 used `mkdir -m 0755 -p --' to ensure that directories +# created by `make install' are always world readable, even if the +# installer happens to have an overly restrictive umask (e.g. 077). +# This was a mistake. There are at least two reasons why we must not +# use `-m 0755': +# - it causes special bits like SGID to be ignored, +# - it may be too restrictive (some setups expect 775 directories). +# +# Do not use -m 0755 and let people choose whatever they expect by +# setting umask. +# +# We cannot accept any implementation of `mkdir' that recognizes `-p'. +# Some implementations (such as Solaris 8's) are not thread-safe: if a +# parallel make tries to run `mkdir -p a/b' and `mkdir -p a/c' +# concurrently, both version can detect that a/ is missing, but only +# one can create it and the other will error out. Consequently we +# restrict ourselves to GNU make (using the --version option ensures +# this.) +AC_DEFUN([AM_PROG_MKDIR_P], +[if mkdir -p --version . >/dev/null 2>&1 && test ! -d ./--version; then + # We used to keeping the `.' as first argument, in order to + # allow $(mkdir_p) to be used without argument. As in + # $(mkdir_p) $(somedir) + # where $(somedir) is conditionally defined. However this is wrong + # for two reasons: + # 1. if the package is installed by a user who cannot write `.' + # make install will fail, + # 2. the above comment should most certainly read + # $(mkdir_p) $(DESTDIR)$(somedir) + # so it does not work when $(somedir) is undefined and + # $(DESTDIR) is not. + # To support the latter case, we have to write + # test -z "$(somedir)" || $(mkdir_p) $(DESTDIR)$(somedir), + # so the `.' trick is pointless. + mkdir_p='mkdir -p --' +else + # On NextStep and OpenStep, the `mkdir' command does not + # recognize any option. It will interpret all options as + # directories to create, and then abort because `.' already + # exists. + for d in ./-p ./--version; + do + test -d $d && rmdir $d + done + # $(mkinstalldirs) is defined by Automake if mkinstalldirs exists. + if test -f "$ac_aux_dir/mkinstalldirs"; then + mkdir_p='$(mkinstalldirs)' + else + mkdir_p='$(install_sh) -d' + fi +fi +AC_SUBST([mkdir_p])]) + +# Helper functions for option handling. -*- Autoconf -*- + +# Copyright (C) 2001, 2002, 2003, 2005 Free Software Foundation, Inc. +# +# This file 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. + +# serial 3 + +# _AM_MANGLE_OPTION(NAME) +# ----------------------- +AC_DEFUN([_AM_MANGLE_OPTION], +[[_AM_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])]) + +# _AM_SET_OPTION(NAME) +# ------------------------------ +# Set option NAME. Presently that only means defining a flag for this option. +AC_DEFUN([_AM_SET_OPTION], +[m4_define(_AM_MANGLE_OPTION([$1]), 1)]) + +# _AM_SET_OPTIONS(OPTIONS) +# ---------------------------------- +# OPTIONS is a space-separated list of Automake options. +AC_DEFUN([_AM_SET_OPTIONS], +[AC_FOREACH([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])]) + +# _AM_IF_OPTION(OPTION, IF-SET, [IF-NOT-SET]) +# ------------------------------------------- +# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise. +AC_DEFUN([_AM_IF_OPTION], +[m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])]) + +# Check to make sure that the build environment is sane. -*- Autoconf -*- + +# Copyright (C) 1996, 1997, 2000, 2001, 2003, 2005 +# Free Software Foundation, Inc. +# +# This file 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. + +# serial 4 + +# AM_SANITY_CHECK +# --------------- +AC_DEFUN([AM_SANITY_CHECK], +[AC_MSG_CHECKING([whether build environment is sane]) +# Just in case +sleep 1 +echo timestamp > conftest.file +# 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 ( + 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 + rm -f conftest.file + 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". + AC_MSG_ERROR([ls -t appears to fail. Make sure there is not a broken +alias in your environment]) + fi + + test "$[2]" = conftest.file + ) +then + # Ok. + : +else + AC_MSG_ERROR([newly created file is older than distributed files! +Check your system clock]) +fi +AC_MSG_RESULT(yes)]) + +# Copyright (C) 2001, 2003, 2005 Free Software Foundation, Inc. +# +# This file 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. + +# AM_PROG_INSTALL_STRIP +# --------------------- +# One issue with vendor `install' (even GNU) is that you can't +# specify the program used to strip binaries. This is especially +# annoying in cross-compiling environments, where the build's strip +# is unlikely to handle the host's binaries. +# Fortunately install-sh will honor a STRIPPROG variable, so we +# always use install-sh in `make install-strip', and initialize +# STRIPPROG with the value of the STRIP variable (set by the user). +AC_DEFUN([AM_PROG_INSTALL_STRIP], +[AC_REQUIRE([AM_PROG_INSTALL_SH])dnl +# 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. +dnl Don't test for $cross_compiling = yes, because it might be `maybe'. +if test "$cross_compiling" != no; then + AC_CHECK_TOOL([STRIP], [strip], :) +fi +INSTALL_STRIP_PROGRAM="\${SHELL} \$(install_sh) -c -s" +AC_SUBST([INSTALL_STRIP_PROGRAM])]) + +# Check how to create a tarball. -*- Autoconf -*- + +# Copyright (C) 2004, 2005 Free Software Foundation, Inc. +# +# This file 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. + +# serial 2 + +# _AM_PROG_TAR(FORMAT) +# -------------------- +# Check how to create a tarball in format FORMAT. +# FORMAT should be one of `v7', `ustar', or `pax'. +# +# Substitute a variable $(am__tar) that is a command +# writing to stdout a FORMAT-tarball containing the directory +# $tardir. +# tardir=directory && $(am__tar) > result.tar +# +# Substitute a variable $(am__untar) that extract such +# a tarball read from stdin. +# $(am__untar) < result.tar +AC_DEFUN([_AM_PROG_TAR], +[# Always define AMTAR for backward compatibility. +AM_MISSING_PROG([AMTAR], [tar]) +m4_if([$1], [v7], + [am__tar='${AMTAR} chof - "$$tardir"'; am__untar='${AMTAR} xf -'], + [m4_case([$1], [ustar],, [pax],, + [m4_fatal([Unknown tar format])]) +AC_MSG_CHECKING([how to create a $1 tar archive]) +# Loop over all known methods to create a tar archive until one works. +_am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none' +_am_tools=${am_cv_prog_tar_$1-$_am_tools} +# Do not fold the above two line into one, because Tru64 sh and +# Solaris sh will not grok spaces in the rhs of `-'. +for _am_tool in $_am_tools +do + case $_am_tool in + gnutar) + for _am_tar in tar gnutar gtar; + do + AM_RUN_LOG([$_am_tar --version]) && break + done + am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"' + am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"' + am__untar="$_am_tar -xf -" + ;; + plaintar) + # Must skip GNU tar: if it does not support --format= it doesn't create + # ustar tarball either. + (tar --version) >/dev/null 2>&1 && continue + am__tar='tar chf - "$$tardir"' + am__tar_='tar chf - "$tardir"' + am__untar='tar xf -' + ;; + pax) + am__tar='pax -L -x $1 -w "$$tardir"' + am__tar_='pax -L -x $1 -w "$tardir"' + am__untar='pax -r' + ;; + cpio) + am__tar='find "$$tardir" -print | cpio -o -H $1 -L' + am__tar_='find "$tardir" -print | cpio -o -H $1 -L' + am__untar='cpio -i -H $1 -d' + ;; + none) + am__tar=false + am__tar_=false + am__untar=false + ;; + esac + + # If the value was cached, stop now. We just wanted to have am__tar + # and am__untar set. + test -n "${am_cv_prog_tar_$1}" && break + + # tar/untar a dummy directory, and stop if the command works + rm -rf conftest.dir + mkdir conftest.dir + echo GrepMe > conftest.dir/file + AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar]) + rm -rf conftest.dir + if test -s conftest.tar; then + AM_RUN_LOG([$am__untar /dev/null 2>&1 && break + fi +done +rm -rf conftest.dir + +AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool]) +AC_MSG_RESULT([$am_cv_prog_tar_$1])]) +AC_SUBST([am__tar]) +AC_SUBST([am__untar]) +]) # _AM_PROG_TAR + +m4_include([acinclude.m4]) diff --git a/ccmain/Makefile.am b/ccmain/Makefile.am new file mode 100644 index 0000000000..458f4dabff --- /dev/null +++ b/ccmain/Makefile.am @@ -0,0 +1,41 @@ +SUBDIRS = +AM_CPPFLAGS = \ + -I$(top_srcdir)/ccutil -I$(top_srcdir)/ccstruct \ + -I$(top_srcdir)/image -I$(top_srcdir)/viewer \ + -I$(top_srcdir)/ccops -I$(top_srcdir)/dict \ + -I$(top_srcdir)/classify -I$(top_srcdir)/display \ + -I$(top_srcdir)/wordrec -I$(top_srcdir)/cutil \ + -I$(top_srcdir)/textord + +EXTRA_DIST = \ + adaptions.h applybox.h baseapi.h blobcmp.h \ + callnet.h charcut.h \ + control.h docqual.h expandblob.h fixspace.h fixxht.h \ + imgscale.h matmatch.h output.h paircmp.h reject.h scaleimg.h \ + tessbox.h tessedit.h tesseractmain.h tessvars.h tfacep.h \ + tessembedded.h tfacepp.h tstruct.h werdit.h + +noinst_LIBRARIES = libtesseract_main.a +libtesseract_main_a_SOURCES = \ + tessedit.cpp adaptions.cpp applybox.cpp \ + baseapi.cpp blobcmp.cpp \ + callnet.cpp charcut.cpp charsample.cpp control.cpp \ + docqual.cpp expandblob.cpp fixspace.cpp fixxht.cpp \ + imgscale.cpp matmatch.cpp output.cpp paircmp.cpp \ + reject.cpp scaleimg.cpp tessbox.cpp tessvars.cpp \ + tfacepp.cpp tstruct.cpp werdit.cpp + +bin_PROGRAMS = tesseract +tesseract_SOURCES = tesseractmain.cpp +tesseract_LDADD = \ + libtesseract_main.a \ + ../display/libtesseract_display.a \ + ../textord/libtesseract_textord.a \ + ../wordrec/libtesseract_wordrec.a \ + ../classify/libtesseract_classify.a \ + ../dict/libtesseract_dict.a \ + ../viewer/libtesseract_viewer.a \ + ../image/libtesseract_image.a \ + ../cutil/libtesseract_cutil.a \ + ../ccstruct/libtesseract_ccstruct.a \ + ../ccutil/libtesseract_ccutil.a diff --git a/ccmain/Makefile.in b/ccmain/Makefile.in new file mode 100644 index 0000000000..ded1b7da84 --- /dev/null +++ b/ccmain/Makefile.in @@ -0,0 +1,636 @@ +# Makefile.in generated by automake 1.9.6 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005 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@ + + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = .. +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +INSTALL = @INSTALL@ +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@ +bin_PROGRAMS = tesseract$(EXEEXT) +subdir = ccmain +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \ + $(top_srcdir)/config/ac_define_versionlevel.m4 \ + $(top_srcdir)/config/acinclude_custom.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(SHELL) $(top_srcdir)/config/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config_auto.h +CONFIG_CLEAN_FILES = +LIBRARIES = $(noinst_LIBRARIES) +AR = ar +ARFLAGS = cru +libtesseract_main_a_AR = $(AR) $(ARFLAGS) +libtesseract_main_a_LIBADD = +am_libtesseract_main_a_OBJECTS = tessedit.$(OBJEXT) \ + adaptions.$(OBJEXT) applybox.$(OBJEXT) baseapi.$(OBJEXT) \ + blobcmp.$(OBJEXT) callnet.$(OBJEXT) charcut.$(OBJEXT) \ + charsample.$(OBJEXT) control.$(OBJEXT) docqual.$(OBJEXT) \ + expandblob.$(OBJEXT) fixspace.$(OBJEXT) fixxht.$(OBJEXT) \ + imgscale.$(OBJEXT) matmatch.$(OBJEXT) output.$(OBJEXT) \ + paircmp.$(OBJEXT) reject.$(OBJEXT) scaleimg.$(OBJEXT) \ + tessbox.$(OBJEXT) tessvars.$(OBJEXT) tfacepp.$(OBJEXT) \ + tstruct.$(OBJEXT) werdit.$(OBJEXT) +libtesseract_main_a_OBJECTS = $(am_libtesseract_main_a_OBJECTS) +am__installdirs = "$(DESTDIR)$(bindir)" +binPROGRAMS_INSTALL = $(INSTALL_PROGRAM) +PROGRAMS = $(bin_PROGRAMS) +am_tesseract_OBJECTS = tesseractmain.$(OBJEXT) +tesseract_OBJECTS = $(am_tesseract_OBJECTS) +tesseract_DEPENDENCIES = libtesseract_main.a \ + ../display/libtesseract_display.a \ + ../textord/libtesseract_textord.a \ + ../wordrec/libtesseract_wordrec.a \ + ../classify/libtesseract_classify.a \ + ../dict/libtesseract_dict.a ../viewer/libtesseract_viewer.a \ + ../image/libtesseract_image.a ../cutil/libtesseract_cutil.a \ + ../ccstruct/libtesseract_ccstruct.a \ + ../ccutil/libtesseract_ccutil.a +DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/config/depcomp +am__depfiles_maybe = depfiles +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +CXXLD = $(CXX) +CXXLINK = $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) \ + -o $@ +SOURCES = $(libtesseract_main_a_SOURCES) $(tesseract_SOURCES) +DIST_SOURCES = $(libtesseract_main_a_SOURCES) $(tesseract_SOURCES) +RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \ + html-recursive info-recursive install-data-recursive \ + install-exec-recursive install-info-recursive \ + install-recursive installcheck-recursive installdirs-recursive \ + pdf-recursive ps-recursive uninstall-info-recursive \ + uninstall-recursive +ETAGS = etags +CTAGS = ctags +DIST_SUBDIRS = $(SUBDIRS) +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMDEP_FALSE = @AMDEP_FALSE@ +AMDEP_TRUE = @AMDEP_TRUE@ +AMTAR = @AMTAR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CXXRPOFLAGS = @CXXRPOFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +GNUWIN32_DIR = @GNUWIN32_DIR@ +HAVE_GNUWIN32_FALSE = @HAVE_GNUWIN32_FALSE@ +HAVE_GNUWIN32_TRUE = @HAVE_GNUWIN32_TRUE@ +HAVE_LIBTIFF_FALSE = @HAVE_LIBTIFF_FALSE@ +HAVE_LIBTIFF_TRUE = @HAVE_LIBTIFF_TRUE@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTIFF_CFLAGS = @LIBTIFF_CFLAGS@ +LIBTIFF_LIBS = @LIBTIFF_LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAINTAINER_MODE_FALSE = @MAINTAINER_MODE_FALSE@ +MAINTAINER_MODE_TRUE = @MAINTAINER_MODE_TRUE@ +MAKEINFO = @MAKEINFO@ +OBJEXT = @OBJEXT@ +OPTS = @OPTS@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_DATE = @PACKAGE_DATE@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PACKAGE_YEAR = @PACKAGE_YEAR@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +RPO_NO = @RPO_NO@ +RPO_YES = @RPO_YES@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +USING_CL_FALSE = @USING_CL_FALSE@ +USING_CL_TRUE = @USING_CL_TRUE@ +VERSION = @VERSION@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_RANLIB = @ac_ct_RANLIB@ +ac_ct_STRIP = @ac_ct_STRIP@ +am__fastdepCC_FALSE = @am__fastdepCC_FALSE@ +am__fastdepCC_TRUE = @am__fastdepCC_TRUE@ +am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@ +am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@ +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@ +datadir = @datadir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +SUBDIRS = +AM_CPPFLAGS = \ + -I$(top_srcdir)/ccutil -I$(top_srcdir)/ccstruct \ + -I$(top_srcdir)/image -I$(top_srcdir)/viewer \ + -I$(top_srcdir)/ccops -I$(top_srcdir)/dict \ + -I$(top_srcdir)/classify -I$(top_srcdir)/display \ + -I$(top_srcdir)/wordrec -I$(top_srcdir)/cutil \ + -I$(top_srcdir)/textord + +EXTRA_DIST = \ + adaptions.h applybox.h baseapi.h blobcmp.h \ + callnet.h charcut.h \ + control.h docqual.h expandblob.h fixspace.h fixxht.h \ + imgscale.h matmatch.h output.h paircmp.h reject.h scaleimg.h \ + tessbox.h tessedit.h tesseractmain.h tessvars.h tfacep.h \ + tessembedded.h tfacepp.h tstruct.h werdit.h + +noinst_LIBRARIES = libtesseract_main.a +libtesseract_main_a_SOURCES = \ + tessedit.cpp adaptions.cpp applybox.cpp \ + baseapi.cpp blobcmp.cpp \ + callnet.cpp charcut.cpp charsample.cpp control.cpp \ + docqual.cpp expandblob.cpp fixspace.cpp fixxht.cpp \ + imgscale.cpp matmatch.cpp output.cpp paircmp.cpp \ + reject.cpp scaleimg.cpp tessbox.cpp tessvars.cpp \ + tfacepp.cpp tstruct.cpp werdit.cpp + +tesseract_SOURCES = tesseractmain.cpp +tesseract_LDADD = \ + libtesseract_main.a \ + ../display/libtesseract_display.a \ + ../textord/libtesseract_textord.a \ + ../wordrec/libtesseract_wordrec.a \ + ../classify/libtesseract_classify.a \ + ../dict/libtesseract_dict.a \ + ../viewer/libtesseract_viewer.a \ + ../image/libtesseract_image.a \ + ../cutil/libtesseract_cutil.a \ + ../ccstruct/libtesseract_ccstruct.a \ + ../ccutil/libtesseract_ccutil.a + +all: all-recursive + +.SUFFIXES: +.SUFFIXES: .cpp .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 \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu ccmain/Makefile'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu 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 + +clean-noinstLIBRARIES: + -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) +libtesseract_main.a: $(libtesseract_main_a_OBJECTS) $(libtesseract_main_a_DEPENDENCIES) + -rm -f libtesseract_main.a + $(libtesseract_main_a_AR) libtesseract_main.a $(libtesseract_main_a_OBJECTS) $(libtesseract_main_a_LIBADD) + $(RANLIB) libtesseract_main.a +install-binPROGRAMS: $(bin_PROGRAMS) + @$(NORMAL_INSTALL) + test -z "$(bindir)" || $(mkdir_p) "$(DESTDIR)$(bindir)" + @list='$(bin_PROGRAMS)'; for p in $$list; do \ + p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \ + if test -f $$p \ + ; then \ + f=`echo "$$p1" | sed 's,^.*/,,;$(transform);s/$$/$(EXEEXT)/'`; \ + echo " $(INSTALL_PROGRAM_ENV) $(binPROGRAMS_INSTALL) '$$p' '$(DESTDIR)$(bindir)/$$f'"; \ + $(INSTALL_PROGRAM_ENV) $(binPROGRAMS_INSTALL) "$$p" "$(DESTDIR)$(bindir)/$$f" || exit 1; \ + else :; fi; \ + done + +uninstall-binPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(bin_PROGRAMS)'; for p in $$list; do \ + f=`echo "$$p" | sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \ + echo " rm -f '$(DESTDIR)$(bindir)/$$f'"; \ + rm -f "$(DESTDIR)$(bindir)/$$f"; \ + done + +clean-binPROGRAMS: + -test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS) +tesseract$(EXEEXT): $(tesseract_OBJECTS) $(tesseract_DEPENDENCIES) + @rm -f tesseract$(EXEEXT) + $(CXXLINK) $(tesseract_LDFLAGS) $(tesseract_OBJECTS) $(tesseract_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/adaptions.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/applybox.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/baseapi.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/blobcmp.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/callnet.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/charcut.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/charsample.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/control.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/docqual.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/expandblob.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fixspace.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fixxht.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imgscale.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/matmatch.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/output.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/paircmp.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/reject.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/scaleimg.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tessbox.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tessedit.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tesseractmain.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tessvars.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tfacepp.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tstruct.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/werdit.Po@am__quote@ + +.cpp.o: +@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ $< + +.cpp.obj: +@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` +uninstall-info-am: + +# 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. +$(RECURSIVE_TARGETS): + @failcom='exit 1'; \ + for f in x $$MAKEFLAGS; do \ + case $$f in \ + *=* | --[!k]*);; \ + *k*) failcom='fail=yes';; \ + esac; \ + done; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + list='$(SUBDIRS)'; 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; \ + (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" + +mostlyclean-recursive clean-recursive distclean-recursive \ +maintainer-clean-recursive: + @failcom='exit 1'; \ + for f in x $$MAKEFLAGS; do \ + case $$f in \ + *=* | --[!k]*);; \ + *k*) failcom='fail=yes';; \ + esac; \ + done; \ + dot_seen=no; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + rev=''; for subdir in $$list; do \ + if test "$$subdir" = "."; then :; else \ + rev="$$subdir $$rev"; \ + fi; \ + done; \ + rev="$$rev ."; \ + target=`echo $@ | sed s/-recursive//`; \ + for subdir in $$rev; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done && test -z "$$fail" +tags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \ + done +ctags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \ + done + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + 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 || \ + tags="$$tags $$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique; \ + fi +ctags: CTAGS +CTAGS: ctags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(CTAGS_ARGS)$$tags$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +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)'; for file in $$list; do \ + case $$file in \ + $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \ + $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \ + esac; \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + dir="/$$dir"; \ + $(mkdir_p) "$(distdir)$$dir"; \ + else \ + dir=''; \ + fi; \ + if test -d $$d/$$file; then \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$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 \ + test -d "$(distdir)/$$subdir" \ + || $(mkdir_p) "$(distdir)/$$subdir" \ + || exit 1; \ + distdir=`$(am__cd) $(distdir) && pwd`; \ + top_distdir=`$(am__cd) $(top_distdir) && pwd`; \ + (cd $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$top_distdir" \ + distdir="$$distdir/$$subdir" \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-recursive +all-am: Makefile $(LIBRARIES) $(PROGRAMS) +installdirs: installdirs-recursive +installdirs-am: + for dir in "$(DESTDIR)$(bindir)"; 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: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_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-recursive + +clean-am: clean-binPROGRAMS clean-generic clean-noinstLIBRARIES \ + mostlyclean-am + +distclean: distclean-recursive + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +info: info-recursive + +info-am: + +install-data-am: + +install-exec-am: install-binPROGRAMS + +install-info: install-info-recursive + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-compile mostlyclean-generic + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: uninstall-binPROGRAMS uninstall-info-am + +uninstall-info: uninstall-info-recursive + +.PHONY: $(RECURSIVE_TARGETS) CTAGS GTAGS all all-am check check-am \ + clean clean-binPROGRAMS clean-generic clean-noinstLIBRARIES \ + clean-recursive ctags ctags-recursive distclean \ + distclean-compile distclean-generic distclean-recursive \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-binPROGRAMS install-data \ + install-data-am install-exec install-exec-am install-info \ + install-info-am install-man install-strip installcheck \ + installcheck-am installdirs installdirs-am maintainer-clean \ + maintainer-clean-generic maintainer-clean-recursive \ + mostlyclean mostlyclean-compile mostlyclean-generic \ + mostlyclean-recursive pdf pdf-am ps ps-am tags tags-recursive \ + uninstall uninstall-am uninstall-binPROGRAMS uninstall-info-am + +# 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: diff --git a/ccmain/adaptions.cpp b/ccmain/adaptions.cpp new file mode 100644 index 0000000000..913dd43b2b --- /dev/null +++ b/ccmain/adaptions.cpp @@ -0,0 +1,1078 @@ +/********************************************************************** + * 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. + * + **********************************************************************/ + +#include "mfcpch.h" +#ifdef __UNIX__ +#include +#endif +#include +#include +#include "tessbox.h" +#include "tessvars.h" +#include "memry.h" +#include "mainblk.h" +#include "charcut.h" +#include "imgs.h" +#include "scaleimg.h" +#include "reject.h" +#include "control.h" +#include "adaptions.h" +#include "stopper.h" +#include "charsample.h" +#include "matmatch.h" +#include "secname.h" + +INT32 demo_word = 0; + +#define EXTERN + +EXTERN BOOL_VAR (tessedit_reject_ems, FALSE, "Reject all m's"); +EXTERN BOOL_VAR (tessedit_reject_suspect_ems, FALSE, "Reject suspect m's"); + +EXTERN double_VAR (tessedit_cluster_t1, 0.20, +"t1 threshold for clustering samples"); +EXTERN double_VAR (tessedit_cluster_t2, 0.40, +"t2 threshold for clustering samples"); +EXTERN double_VAR (tessedit_cluster_t3, 0.12, +"Extra threshold for clustering samples, only keep a new sample if best score greater than this value"); +EXTERN double_VAR (tessedit_cluster_accept_fraction, 0.80, +"Largest fraction of characters in cluster for it to be used for adaption"); +EXTERN INT_VAR (tessedit_cluster_min_size, 3, +"Smallest number of samples in a cluster for it to be used for adaption"); +EXTERN BOOL_VAR (tessedit_cluster_debug, FALSE, +"Generate and print debug information for adaption by clustering"); +EXTERN BOOL_VAR (tessedit_use_best_sample, FALSE, +"Use best sample from cluster when adapting"); +EXTERN BOOL_VAR (tessedit_test_cluster_input, FALSE, +"Set reject map to enable cluster input to be measured"); + +EXTERN BOOL_VAR (tessedit_matrix_match, TRUE, "Use matrix matcher"); +EXTERN BOOL_VAR (tessedit_mm_use_non_adaption_set, FALSE, +"Don't try to adapt to characters on this list"); +EXTERN STRING_VAR (tessedit_non_adaption_set, ",.;:'~@*", +"Characters to be avoided when adapting"); +EXTERN BOOL_VAR (tessedit_mm_adapt_using_prototypes, TRUE, +"Use prototypes when adapting"); +EXTERN BOOL_VAR (tessedit_mm_use_prototypes, TRUE, +"Use prototypes as clusters are built"); +EXTERN BOOL_VAR (tessedit_mm_use_rejmap, FALSE, +"Adapt to characters using reject map"); +EXTERN BOOL_VAR (tessedit_mm_all_rejects, FALSE, +"Adapt to all characters using, matrix matcher"); +EXTERN BOOL_VAR (tessedit_mm_only_match_same_char, FALSE, +"Only match samples against clusters for the same character"); +EXTERN BOOL_VAR (tessedit_process_rns, FALSE, "Handle m - rn ambigs"); + +EXTERN BOOL_VAR (tessedit_demo_adaption, FALSE, +"Display cut images and matrix match for demo purposes"); +EXTERN INT_VAR (tessedit_demo_word1, 62, +"Word number of first word to display"); +EXTERN INT_VAR (tessedit_demo_word2, 64, +"Word number of second word to display"); +EXTERN STRING_VAR (tessedit_demo_file, "academe", +"Name of document containing demo words"); + +BOOL8 word_adaptable( //should we adapt? + WERD_RES *word, + UINT16 mode) { + 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) { + return FALSE; + } + + if (flags.bit (ADAPTABLE_WERD)) + status |= word->tess_would_adapt; + + if (flags.bit (ACCEPTABLE_WERD)) + status |= word->tess_accepted; + + 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)) + return FALSE; + + if (flags.bit (CHECK_ONE_ELL_CONFLICT) && one_ell_conflict (word, FALSE)) + return FALSE; + + if (flags.bit (CHECK_SPACES) && + (strchr (word->best_choice->string ().string (), ' ') != NULL)) + return FALSE; + +// if (flags.bit (CHECK_AMBIG_WERD) && test_ambig_word (word)) + if (flags.bit (CHECK_AMBIG_WERD) && + !NoDangerousAmbig(word->best_choice->string().string(), NULL)) + return FALSE; + + return status; + +} + + +void collect_ems_for_adaption(WERD_RES *word, + CHAR_SAMPLES_LIST *char_clusters, + CHAR_SAMPLE_LIST *chars_waiting) { + PBLOB_LIST *blobs = word->outword->blob_list (); + PBLOB_IT blob_it(blobs); + INT16 i; + CHAR_SAMPLE *sample; + PIXROW_LIST *pixrow_list; + PIXROW_IT pixrow_it; + IMAGELINE *imlines; // lines of the image + BOX pix_box; // box of imlines + // extent + WERD copy_outword; // copy to denorm + PBLOB_IT copy_blob_it; + OUTLINE_IT copy_outline_it; + INT32 resolution = page_image.get_res (); + + if (tessedit_reject_ems || tessedit_reject_suspect_ems) + return; // Do nothing + + if (word->word->bounding_box ().height () > resolution / 3) + return; + + if (tessedit_demo_adaption) + // Make sure not set + tessedit_display_mm.set_value (FALSE); + + if (word_adaptable (word, tessedit_em_adaption_mode) + && word->reject_map.reject_count () == 0 + && (strchr (word->best_choice->string ().string (), 'm') != NULL + || (tessedit_process_rns + && strstr (word->best_choice->string ().string (), + "rn") != NULL))) { + if (tessedit_process_rns + && strstr (word->best_choice->string ().string (), "rn") != NULL) { + copy_outword = *(word->outword); + copy_blob_it.set_to_list (copy_outword.blob_list ()); + i = 0; + while (word->best_choice->string ()[i] != '\0') { + if (word->best_choice->string ()[i] == 'r' + && word->best_choice->string ()[i + 1] == 'n') { + copy_outline_it.set_to_list (copy_blob_it.data ()-> + out_list ()); + copy_outline_it.add_list_after (copy_blob_it. + data_relative (1)-> + out_list ()); + copy_blob_it.forward (); + delete (copy_blob_it.extract ()); + i++; + } + copy_blob_it.forward (); + i++; + } + } + else + copy_outword = *(word->outword); + + copy_outword.baseline_denormalise (&word->denorm); + char_clip_word(©_outword, page_image, pixrow_list, imlines, pix_box); + pixrow_it.set_to_list (pixrow_list); + pixrow_it.move_to_first (); + + blob_it.move_to_first (); + for (i = 0; + word->best_choice->string ()[i] != '\0'; + i++, pixrow_it.forward (), blob_it.forward ()) { + + if (word->best_choice->string ()[i] == 'm' + || (word->best_choice->string ()[i] == 'r' + && word->best_choice->string ()[i + 1] == 'n')) { + #ifndef SECURE_NAMES + if (tessedit_cluster_debug) + tprintf ("Sample %c for adaption found in %s, index %d\n", + word->best_choice->string ()[i], + word->best_choice->string ().string (), i); + #endif + if (tessedit_matrix_match) { + sample = clip_sample (pixrow_it.data (), + imlines, + pix_box, + copy_outword.flag (W_INVERSE), + word->best_choice->string ()[i]); + + if (sample == NULL) { //Clip failed + #ifndef SECURE_NAMES + tprintf ("Unable to clip sample from %s, index %d\n", + word->best_choice->string ().string (), i); + #endif + if (word->best_choice->string ()[i] == 'r') + i++; + + continue; + } + } + else + sample = new CHAR_SAMPLE (blob_it.data (), + &word->denorm, + word->best_choice->string ()[i]); + + cluster_sample(sample, char_clusters, chars_waiting); + + if (word->best_choice->string ()[i] == 'r') + i++; // Skip next character + } + } + delete[]imlines; // Free array of imlines + delete pixrow_list; + } +} + + +void collect_characters_for_adaption(WERD_RES *word, + CHAR_SAMPLES_LIST *char_clusters, + CHAR_SAMPLE_LIST *chars_waiting) { + PBLOB_LIST *blobs = word->outword->blob_list (); + PBLOB_IT blob_it(blobs); + INT16 i; + CHAR_SAMPLE *sample; + PIXROW_LIST *pixrow_list; + PIXROW_IT pixrow_it; + IMAGELINE *imlines; // lines of the image + BOX pix_box; // box of imlines + // extent + WERD copy_outword; // copy to denorm + INT32 resolution = page_image.get_res (); + + if (word->word->bounding_box ().height () > resolution / 3) + return; + + if (tessedit_demo_adaption) + // Make sure not set + tessedit_display_mm.set_value (FALSE); + + if ((word_adaptable (word, tessedit_cluster_adaption_mode) + && word->reject_map.reject_count () == 0) || tessedit_mm_use_rejmap) { + if (tessedit_test_cluster_input && !tessedit_mm_use_rejmap) + return; // Reject map set to acceptable + /* Collect information about good matches */ + copy_outword = *(word->outword); + copy_outword.baseline_denormalise (&word->denorm); + char_clip_word(©_outword, page_image, pixrow_list, imlines, pix_box); + pixrow_it.set_to_list (pixrow_list); + pixrow_it.move_to_first (); + + blob_it.move_to_first (); + for (i = 0; + word->best_choice->string ()[i] != '\0'; + i++, pixrow_it.forward (), blob_it.forward ()) { + + if (!(tessedit_mm_use_non_adaption_set + && STRING (tessedit_non_adaption_set).contains (word-> + best_choice-> + string ()[i])) + || (tessedit_mm_use_rejmap && word->reject_map[i].accepted ())) { + #ifndef SECURE_NAMES + if (tessedit_cluster_debug) + tprintf ("Sample %c for adaption found in %s, index %d\n", + word->best_choice->string ()[i], + word->best_choice->string ().string (), i); + #endif + sample = clip_sample (pixrow_it.data (), + imlines, + pix_box, + copy_outword.flag (W_INVERSE), + word->best_choice->string ()[i]); + + if (sample == NULL) { //Clip failed + #ifndef SECURE_NAMES + tprintf ("Unable to clip sample from %s, index %d\n", + word->best_choice->string ().string (), i); + #endif + continue; + } + cluster_sample(sample, char_clusters, chars_waiting); + } + } + delete[]imlines; // Free array of imlines + delete pixrow_list; + } + else if (tessedit_test_cluster_input && !tessedit_mm_use_rejmap) + // Set word to all rejects + word->reject_map.rej_word_tess_failure (); + +} + + +void cluster_sample(CHAR_SAMPLE *sample, + CHAR_SAMPLES_LIST *char_clusters, + CHAR_SAMPLE_LIST *chars_waiting) { + CHAR_SAMPLES *best_cluster = NULL; + CHAR_SAMPLES_IT c_it = char_clusters; + CHAR_SAMPLE_IT cw_it = chars_waiting; + float score; + float best_score = MAX_INT32; + + if (c_it.empty ()) + c_it.add_to_end (new CHAR_SAMPLES (sample)); + else { + for (c_it.mark_cycle_pt (); !c_it.cycled_list (); c_it.forward ()) { + score = c_it.data ()->match_score (sample); + if (score < best_score) { + best_score = score; + best_cluster = c_it.data (); + } + } + + if (tessedit_cluster_debug) + tprintf ("Sample's best score %f\n", best_score); + + if (best_score < tessedit_cluster_t1) { + if (best_score > tessedit_cluster_t3 || tessedit_mm_use_prototypes) { + best_cluster->add_sample (sample); + check_wait_list(chars_waiting, sample, best_cluster); + #ifndef SECURE_NAMES + if (tessedit_cluster_debug) + tprintf ("Sample added to an existing cluster\n"); + #endif + } + else { + #ifndef SECURE_NAMES + if (tessedit_cluster_debug) + tprintf + ("Sample dropped, good match to an existing cluster\n"); + #endif + } + } + else if (best_score > tessedit_cluster_t2) { + c_it.add_to_end (new CHAR_SAMPLES (sample)); + #ifndef SECURE_NAMES + if (tessedit_cluster_debug) + tprintf ("New cluster created for this sample\n"); + #endif + } + else { + cw_it.add_to_end (sample); + if (tessedit_cluster_debug) + tprintf ("Sample added to the wait list\n"); + } + } +} + + +void check_wait_list(CHAR_SAMPLE_LIST *chars_waiting, + CHAR_SAMPLE *sample, + CHAR_SAMPLES *best_cluster) { + CHAR_SAMPLE *wait_sample; + CHAR_SAMPLE *test_sample = sample; + CHAR_SAMPLE_IT cw_it = chars_waiting; + CHAR_SAMPLE_LIST add_list; //Samples added to best cluster + CHAR_SAMPLE_IT add_it = &add_list; + float score; + + add_list.clear (); + + if (!cw_it.empty ()) { + do { + if (!add_list.empty ()) { + add_it.forward (); + test_sample = add_it.extract (); + best_cluster->add_sample (test_sample); + } + + for (cw_it.mark_cycle_pt (); + !cw_it.cycled_list (); cw_it.forward ()) { + wait_sample = cw_it.data (); + if (tessedit_mm_use_prototypes) + score = best_cluster->match_score (wait_sample); + else + score = sample->match_sample (wait_sample, FALSE); + if (score < tessedit_cluster_t1) { + if (score > tessedit_cluster_t3 + || tessedit_mm_use_prototypes) { + add_it.add_after_stay_put (cw_it.extract ()); + #ifndef SECURE_NAMES + if (tessedit_cluster_debug) + tprintf + ("Wait sample added to an existing cluster\n"); + #endif + } + else { + #ifndef SECURE_NAMES + if (tessedit_cluster_debug) + tprintf + ("Wait sample dropped, good match to an existing cluster\n"); + #endif + } + } + } + } + while (!add_list.empty ()); + } +} + + +void complete_clustering(CHAR_SAMPLES_LIST *char_clusters, + CHAR_SAMPLE_LIST *chars_waiting) { + CHAR_SAMPLES *best_cluster; + CHAR_SAMPLES_IT c_it = char_clusters; + CHAR_SAMPLE_IT cw_it = chars_waiting; + CHAR_SAMPLE *sample; + INT32 total_sample_count = 0; + + while (!cw_it.empty ()) { + cw_it.move_to_first (); + sample = cw_it.extract (); + best_cluster = new CHAR_SAMPLES (sample); + c_it.add_to_end (best_cluster); + check_wait_list(chars_waiting, sample, best_cluster); + } + + for (c_it.mark_cycle_pt (); !c_it.cycled_list (); c_it.forward ()) { + c_it.data ()->assign_to_char (); + if (tessedit_use_best_sample) + c_it.data ()->find_best_sample (); + else if (tessedit_mm_adapt_using_prototypes) + c_it.data ()->build_prototype (); + + if (tessedit_cluster_debug) + total_sample_count += c_it.data ()->n_samples (); + } + #ifndef SECURE_NAMES + if (tessedit_cluster_debug) + tprintf ("Clustering completed, %d samples in all\n", total_sample_count); + #endif + +#ifndef GRAPHICS_DISABLED + if (tessedit_demo_adaption) + display_cluster_prototypes(char_clusters); +#endif + +} + + +void adapt_to_good_ems(WERD_RES *word, + CHAR_SAMPLES_LIST *char_clusters, + CHAR_SAMPLE_LIST *chars_waiting) { + PBLOB_LIST *blobs = word->outword->blob_list (); + PBLOB_IT blob_it(blobs); + INT16 i; + CHAR_SAMPLE *sample; + CHAR_SAMPLES_IT c_it = char_clusters; + CHAR_SAMPLE_IT cw_it = chars_waiting; + float score; + float best_score; + char best_char; + CHAR_SAMPLES *best_cluster; + PIXROW_LIST *pixrow_list; + PIXROW_IT pixrow_it; + IMAGELINE *imlines; // lines of the image + BOX pix_box; // box of imlines + // extent + WERD copy_outword; // copy to denorm + BOX b_box; + PBLOB_IT copy_blob_it; + OUTLINE_IT copy_outline_it; + PIXROW *pixrow = NULL; + + static INT32 word_number = 0; + +#ifndef GRAPHICS_DISABLED + WINDOW demo_win = NULL; +#endif + + INT32 resolution = page_image.get_res (); + + if (word->word->bounding_box ().height () > resolution / 3) + return; + + word_number++; + + if (strchr (word->best_choice->string ().string (), 'm') == NULL + && (tessedit_process_rns + && strstr (word->best_choice->string ().string (), "rn") == NULL)) + return; + + if (tessedit_reject_ems) + reject_all_ems(word); + else if (tessedit_reject_suspect_ems) + reject_suspect_ems(word); + else { + if (char_clusters->length () == 0) { + #ifndef SECURE_NAMES + if (tessedit_cluster_debug) + tprintf ("No clusters to use for em adaption\n"); + #endif + return; + } + + if (!cw_it.empty ()) { + complete_clustering(char_clusters, chars_waiting); + print_em_stats(char_clusters, chars_waiting); + } + + if ((!word_adaptable (word, tessedit_em_adaption_mode) || + word->reject_map.reject_count () != 0) + && (strchr (word->best_choice->string ().string (), 'm') != NULL + || (tessedit_process_rns + && strstr (word->best_choice->string ().string (), + "rn") != NULL))) { + if (tessedit_process_rns + && strstr (word->best_choice->string ().string (), + "rn") != NULL) { + copy_outword = *(word->outword); + copy_blob_it.set_to_list (copy_outword.blob_list ()); + i = 0; + while (word->best_choice->string ()[i] != '\0') { + if (word->best_choice->string ()[i] == 'r' + && word->best_choice->string ()[i + 1] == 'n') { + copy_outline_it.set_to_list (copy_blob_it.data ()-> + out_list ()); + copy_outline_it.add_list_after (copy_blob_it. + data_relative (1)-> + out_list ()); + copy_blob_it.forward (); + delete (copy_blob_it.extract ()); + i++; + } + copy_blob_it.forward (); + i++; + } + } + else + copy_outword = *(word->outword); + + copy_outword.baseline_denormalise (&word->denorm); + copy_blob_it.set_to_list (copy_outword.blob_list ()); + char_clip_word(©_outword, page_image, pixrow_list, imlines, pix_box); + pixrow_it.set_to_list (pixrow_list); + pixrow_it.move_to_first (); + + // For debugging only + b_box = copy_outword.bounding_box (); + pixrow = pixrow_it.data (); + + blob_it.move_to_first (); + copy_blob_it.move_to_first (); + for (i = 0; + word->best_choice->string ()[i] != '\0'; + i++, pixrow_it.forward (), blob_it.forward (), + copy_blob_it.forward ()) { + if ((word->best_choice->string ()[i] == 'm' + || (word->best_choice->string ()[i] == 'r' + && word->best_choice->string ()[i + 1] == 'n')) + && !word->reject_map[i].perm_rejected ()) { + if (tessedit_cluster_debug) + tprintf ("Sample %c to check found in %s, index %d\n", + word->best_choice->string ()[i], + word->best_choice->string ().string (), i); + + if (tessedit_demo_adaption) + tprintf + ("Sample %c to check found in %s (%d), index %d\n", + word->best_choice->string ()[i], + word->best_choice->string ().string (), word_number, + i); + + if (tessedit_matrix_match) { + BOX copy_box = copy_blob_it.data ()->bounding_box (); + + sample = clip_sample (pixrow_it.data (), + imlines, + pix_box, + copy_outword.flag (W_INVERSE), + word->best_choice->string ()[i]); + + //Clip failed + if (sample == NULL) { + tprintf + ("Unable to clip sample from %s, index %d\n", + word->best_choice->string ().string (), i); + #ifndef SECURE_NAMES + if (tessedit_cluster_debug) + tprintf ("Sample rejected (no sample)\n"); + #endif + word->reject_map[i].setrej_mm_reject (); + if (word->best_choice->string ()[i] == 'r') { + word->reject_map[i + 1].setrej_mm_reject (); + i++; + } + continue; + } + } + else + sample = new CHAR_SAMPLE (blob_it.data (), + &word->denorm, + word->best_choice-> + string ()[i]); + + best_score = MAX_INT32; + best_char = '\0'; + best_cluster = NULL; + + for (c_it.mark_cycle_pt (); + !c_it.cycled_list (); c_it.forward ()) { + if (c_it.data ()->character () != '\0') { + score = c_it.data ()->match_score (sample); + if (score < best_score) { + best_cluster = c_it.data (); + best_score = score; + best_char = c_it.data ()->character (); + } + } + } + + if (best_score > tessedit_cluster_t1) { + #ifndef SECURE_NAMES + if (tessedit_cluster_debug) + tprintf ("Sample rejected (score %f)\n", best_score); + if (tessedit_demo_adaption) + tprintf ("Sample rejected (score %f)\n", best_score); + #endif + word->reject_map[i].setrej_mm_reject (); + if (word->best_choice->string ()[i] == 'r') + word->reject_map[i + 1].setrej_mm_reject (); + } + else { + if (word->best_choice->string ()[i] == best_char) { + #ifndef SECURE_NAMES + if (tessedit_cluster_debug) + tprintf ("Sample accepted (score %f)\n", + best_score); + if (tessedit_demo_adaption) + tprintf ("Sample accepted (score %f)\n", + best_score); + #endif + word->reject_map[i].setrej_mm_accept (); + if (word->best_choice->string ()[i] == 'r') + word->reject_map[i + 1].setrej_mm_accept (); + } + else { + #ifndef SECURE_NAMES + if (tessedit_cluster_debug) + tprintf ("Sample rejected (char %c, score %f)\n", + best_char, best_score); + if (tessedit_demo_adaption) + tprintf ("Sample rejected (char %c, score %f)\n", + best_char, best_score); + #endif + word->reject_map[i].setrej_mm_reject (); + if (word->best_choice->string ()[i] == 'r') + word->reject_map[i + 1].setrej_mm_reject (); + } + } + + if (tessedit_demo_adaption) { + if (strcmp (imagebasename.string (), + tessedit_demo_file.string ()) != 0 + || word_number == tessedit_demo_word1 + || word_number == tessedit_demo_word2) { +#ifndef GRAPHICS_DISABLED + demo_win = + display_clip_image(©_outword, + page_image, + pixrow_list, + pix_box); +#endif + demo_word = word_number; + best_cluster->match_score (sample); + demo_word = 0; + } + } + if (word->best_choice->string ()[i] == 'r') + i++; // Skip next character + } + } + delete[]imlines; // Free array of imlines + delete pixrow_list; + } + } +} + + +void adapt_to_good_samples(WERD_RES *word, + CHAR_SAMPLES_LIST *char_clusters, + CHAR_SAMPLE_LIST *chars_waiting) { + PBLOB_LIST *blobs = word->outword->blob_list (); + PBLOB_IT blob_it(blobs); + INT16 i; + CHAR_SAMPLE *sample; + CHAR_SAMPLES_IT c_it = char_clusters; + CHAR_SAMPLE_IT cw_it = chars_waiting; + float score; + float best_score; + char best_char; + CHAR_SAMPLES *best_cluster; + PIXROW_LIST *pixrow_list; + PIXROW_IT pixrow_it; + IMAGELINE *imlines; // lines of the image + BOX pix_box; // box of imlines + // extent + WERD copy_outword; // copy to denorm + BOX b_box; + PBLOB_IT copy_blob_it; + PIXROW *pixrow = NULL; + + static INT32 word_number = 0; + +#ifndef GRAPHICS_DISABLED + WINDOW demo_win = NULL; +#endif + + INT32 resolution = page_image.get_res (); + + word_number++; + + if (tessedit_test_cluster_input) + return; + + if (word->word->bounding_box ().height () > resolution / 3) + return; + + if (char_clusters->length () == 0) { + #ifndef SECURE_NAMES + if (tessedit_cluster_debug) + tprintf ("No clusters to use for adaption\n"); + #endif + return; + } + + if (!cw_it.empty ()) { + complete_clustering(char_clusters, chars_waiting); + print_em_stats(char_clusters, chars_waiting); + } + + if ((!word_adaptable (word, tessedit_cluster_adaption_mode) + && word->reject_map.reject_count () != 0) || tessedit_mm_use_rejmap) { + if (tessedit_cluster_debug) { + tprintf ("\nChecking: \"%s\" MAP ", + word->best_choice->string ().string ()); + word->reject_map.print (debug_fp); + tprintf ("\n"); + } + + copy_outword = *(word->outword); + copy_outword.baseline_denormalise (&word->denorm); + copy_blob_it.set_to_list (copy_outword.blob_list ()); + char_clip_word(©_outword, page_image, pixrow_list, imlines, pix_box); + pixrow_it.set_to_list (pixrow_list); + pixrow_it.move_to_first (); + + // For debugging only + b_box = copy_outword.bounding_box (); + pixrow = pixrow_it.data (); + + blob_it.move_to_first (); + copy_blob_it.move_to_first (); + for (i = 0; + word->best_choice->string ()[i] != '\0'; + i++, pixrow_it.forward (), blob_it.forward (), + copy_blob_it.forward ()) { + if (word->reject_map[i].recoverable () + || (tessedit_mm_all_rejects && word->reject_map[i].rejected ())) { + BOX copy_box = copy_blob_it.data ()->bounding_box (); + + if (tessedit_cluster_debug) + tprintf ("Sample %c to check found in %s, index %d\n", + word->best_choice->string ()[i], + word->best_choice->string ().string (), i); + + if (tessedit_demo_adaption) + tprintf ("Sample %c to check found in %s (%d), index %d\n", + word->best_choice->string ()[i], + word->best_choice->string ().string (), + word_number, i); + + sample = clip_sample (pixrow_it.data (), + imlines, + pix_box, + copy_outword.flag (W_INVERSE), + word->best_choice->string ()[i]); + + if (sample == NULL) { //Clip failed + tprintf ("Unable to clip sample from %s, index %d\n", + word->best_choice->string ().string (), i); + #ifndef SECURE_NAMES + if (tessedit_cluster_debug) + tprintf ("Sample rejected (no sample)\n"); + #endif + word->reject_map[i].setrej_mm_reject (); + + continue; + } + + best_score = MAX_INT32; + best_char = '\0'; + best_cluster = NULL; + + for (c_it.mark_cycle_pt (); + !c_it.cycled_list (); c_it.forward ()) { + if (c_it.data ()->character () != '\0') { + score = c_it.data ()->match_score (sample); + if (score < best_score) { + best_cluster = c_it.data (); + best_score = score; + best_char = c_it.data ()->character (); + } + } + } + + if (best_score > tessedit_cluster_t1) { + #ifndef SECURE_NAMES + if (tessedit_cluster_debug) + tprintf ("Sample rejected (score %f)\n", best_score); + if (tessedit_demo_adaption) + tprintf ("Sample rejected (score %f)\n", best_score); + #endif + word->reject_map[i].setrej_mm_reject (); + } + else { + if (word->best_choice->string ()[i] == best_char) { + #ifndef SECURE_NAMES + if (tessedit_cluster_debug) + tprintf ("Sample accepted (score %f)\n", best_score); + if (tessedit_demo_adaption) + tprintf ("Sample accepted (score %f)\n", best_score); + #endif + if (tessedit_test_adaption) + word->reject_map[i].setrej_minimal_rej_accept (); + else + word->reject_map[i].setrej_mm_accept (); + } + else { + #ifndef SECURE_NAMES + if (tessedit_cluster_debug) + tprintf ("Sample rejected (char %c, score %f)\n", + best_char, best_score); + if (tessedit_demo_adaption) + tprintf ("Sample rejected (char %c, score %f)\n", + best_char, best_score); + #endif + word->reject_map[i].setrej_mm_reject (); + } + } + + if (tessedit_demo_adaption) { + if (strcmp (imagebasename.string (), + tessedit_demo_file.string ()) != 0 + || word_number == tessedit_demo_word1 + || word_number == tessedit_demo_word2) { +#ifndef GRAPHICS_DISABLED + demo_win = + display_clip_image(©_outword, + page_image, + pixrow_list, + pix_box); +#endif + demo_word = word_number; + best_cluster->match_score (sample); + demo_word = 0; + } + } + } + } + delete[]imlines; // Free array of imlines + delete pixrow_list; + + if (tessedit_cluster_debug) { + tprintf ("\nFinal: \"%s\" MAP ", + word->best_choice->string ().string ()); + word->reject_map.print (debug_fp); + tprintf ("\n"); + } + } +} + + +void print_em_stats(CHAR_SAMPLES_LIST *char_clusters, + CHAR_SAMPLE_LIST *chars_waiting) { + CHAR_SAMPLES_IT c_it = char_clusters; + + if (!tessedit_cluster_debug) + return; + #ifndef SECURE_NAMES + tprintf ("There are %d clusters and %d samples waiting\n", + char_clusters->length (), chars_waiting->length ()); + + for (c_it.mark_cycle_pt (); !c_it.cycled_list (); c_it.forward ()) + c_it.data ()->print (debug_fp); + #endif + tprintf ("\n"); +} + + +CHAR_SAMPLE *clip_sample( //lines of the image + PIXROW *pixrow, + IMAGELINE *imlines, + BOX pix_box, //box of imlines extent + BOOL8 white_on_black, + char c) { + BOX b_box = pixrow->bounding_box (); + float baseline_pos = 0; + INT32 resolution = page_image.get_res (); + + if (!b_box.null_box ()) { + ASSERT_HOST (b_box.width () < page_image.get_xsize () && + b_box.height () < page_image.get_ysize ()); + + if (b_box.width () > resolution || b_box.height () > resolution) { + tprintf ("clip sample: sample too big (%d x %d)\n", + b_box.width (), b_box.height ()); + + return NULL; + } + + IMAGE *image = new (IMAGE); + if (image->create (b_box.width (), b_box.height (), 1) == -1) { + tprintf ("clip sample: create image failed (%d x %d)\n", + b_box.width (), b_box.height ()); + + delete image; + return NULL; + } + + if (!white_on_black) + invert_image(image); // Set background to white + pixrow->char_clip_image (imlines, pix_box, NULL, *image, baseline_pos); + if (white_on_black) + invert_image(image); //invert white on black for scaling &NN + return new CHAR_SAMPLE (image, c); + } + else + return NULL; +} + + +#ifndef GRAPHICS_DISABLED +void display_cluster_prototypes(CHAR_SAMPLES_LIST *char_clusters) { + INT16 proto_number = 0; + CHAR_SAMPLES_IT c_it = char_clusters; + char title[WINDOWNAMESIZE]; + + for (c_it.mark_cycle_pt (); !c_it.cycled_list (); c_it.forward ()) { + proto_number++; + + #ifndef SECURE_NAMES + tprintf ("Displaying proto number %d\n", proto_number); + #endif + + if (c_it.data ()->prototype () != NULL) { + sprintf (title, "Proto - %d", proto_number); + display_image (c_it.data ()->prototype ()->make_image (), + title, (proto_number - 1) * 400, 0, FALSE); + } + } +} +#endif + +// ********************************************************************* +// Simplistic routines to test the effect of rejecting ems and fullstops +// ********************************************************************* + +void reject_all_ems(WERD_RES *word) { + INT16 i; + + for (i = 0; word->best_choice->string ()[i] != '\0'; i++) { + if (word->best_choice->string ()[i] == 'm') + // reject all ems + word->reject_map[i].setrej_mm_reject (); + } +} + + +void reject_all_fullstops(WERD_RES *word) { + INT16 i; + + for (i = 0; word->best_choice->string ()[i] != '\0'; i++) { + if (word->best_choice->string ()[i] == '.') + // reject all fullstops + word->reject_map[i].setrej_mm_reject (); + } +} + + +void reject_suspect_ems(WERD_RES *word) { + INT16 i; + + if (!word_adaptable (word, tessedit_cluster_adaption_mode)) + for (i = 0; word->best_choice->string ()[i] != '\0'; i++) { + if (word->best_choice->string ()[i] == 'm' && suspect_em (word, i)) + // reject all ems + word->reject_map[i].setrej_mm_reject (); + } +} + + +void reject_suspect_fullstops(WERD_RES *word) { + INT16 i; + + for (i = 0; word->best_choice->string ()[i] != '\0'; i++) { + if (word->best_choice->string ()[i] == '.' + && suspect_fullstop (word, i)) + // reject all commas + word->reject_map[i].setrej_mm_reject (); + } +} + + +BOOL8 suspect_em(WERD_RES *word, INT16 index) { + PBLOB_LIST *blobs = word->outword->blob_list (); + PBLOB_IT blob_it(blobs); + INT16 j; + + for (j = 0; j < index; j++) + blob_it.forward (); + + return (blob_it.data ()->out_list ()->length () != 1); +} + + +BOOL8 suspect_fullstop(WERD_RES *word, INT16 i) { + float aspect_ratio; + PBLOB_LIST *blobs = word->outword->blob_list (); + PBLOB_IT blob_it(blobs); + INT16 j; + BOX box; + INT16 width; + INT16 height; + + for (j = 0; j < i; j++) + blob_it.forward (); + + box = blob_it.data ()->bounding_box (); + + width = box.width (); + height = box.height (); + + aspect_ratio = ((width > height) ? ((float) width) / height : + ((float) height) / width); + + return (aspect_ratio > tessed_fullstop_aspect_ratio); +} diff --git a/ccmain/adaptions.h b/ccmain/adaptions.h new file mode 100644 index 0000000000..5719b9060c --- /dev/null +++ b/ccmain/adaptions.h @@ -0,0 +1,109 @@ +/********************************************************************** + * File: adaptions.h (Formerly adaptions.h) + * 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. + * + **********************************************************************/ + +#ifndef ADAPTIONS_H +#define ADAPTIONS_H + +#include "charsample.h" +#include "charcut.h" +#include "notdll.h" + +extern BOOL_VAR_H (tessedit_reject_ems, FALSE, "Reject all m's"); +extern BOOL_VAR_H (tessedit_reject_suspect_ems, FALSE, "Reject suspect m's"); +extern double_VAR_H (tessedit_cluster_t1, 0.20, +"t1 threshold for clustering samples"); +extern double_VAR_H (tessedit_cluster_t2, 0.40, +"t2 threshold for clustering samples"); +extern double_VAR_H (tessedit_cluster_t3, 0.12, +"Extra threshold for clustering samples, only keep a new sample if best score greater than this value"); +extern double_VAR_H (tessedit_cluster_accept_fraction, 0.80, +"Largest fraction of characters in cluster for it to be used for adaption"); +extern INT_VAR_H (tessedit_cluster_min_size, 3, +"Smallest number of samples in a cluster for it to be used for adaption"); +extern BOOL_VAR_H (tessedit_cluster_debug, FALSE, +"Generate and print debug information for adaption by clustering"); +extern BOOL_VAR_H (tessedit_use_best_sample, FALSE, +"Use best sample from cluster when adapting"); +extern BOOL_VAR_H (tessedit_test_cluster_input, FALSE, +"Set reject map to enable cluster input to be measured"); +extern BOOL_VAR_H (tessedit_matrix_match, TRUE, "Use matrix matcher"); +extern BOOL_VAR_H (tessedit_old_matrix_match, FALSE, "Use matrix matcher"); +extern BOOL_VAR_H (tessedit_mm_use_non_adaption_set, FALSE, +"Don't try to adapt to characters on this list"); +extern STRING_VAR_H (tessedit_non_adaption_set, ",.;:'~@*", +"Characters to be avoided when adapting"); +extern BOOL_VAR_H (tessedit_mm_adapt_using_prototypes, TRUE, +"Use prototypes when adapting"); +extern BOOL_VAR_H (tessedit_mm_use_prototypes, TRUE, +"Use prototypes as clusters are built"); +extern BOOL_VAR_H (tessedit_mm_use_rejmap, FALSE, +"Adapt to characters using reject map"); +extern BOOL_VAR_H (tessedit_mm_all_rejects, FALSE, +"Adapt to all characters using, matrix matcher"); +extern BOOL_VAR_H (tessedit_mm_only_match_same_char, FALSE, +"Only match samples against clusters for the same character"); +extern BOOL_VAR_H (tessedit_process_rns, FALSE, "Handle m - rn ambigs"); +extern BOOL_VAR_H (tessedit_demo_adaption, FALSE, +"Display cut images and matrix match for demo purposes"); +extern INT_VAR_H (tessedit_demo_word1, 62, +"Word number of first word to display"); +extern INT_VAR_H (tessedit_demo_word2, 64, +"Word number of second word to display"); +extern STRING_VAR_H (tessedit_demo_file, "academe", +"Name of document containing demo words"); +BOOL8 word_adaptable( //should we adapt? + WERD_RES *word, + UINT16 mode); +void collect_ems_for_adaption(WERD_RES *word, + CHAR_SAMPLES_LIST *char_clusters, + CHAR_SAMPLE_LIST *chars_waiting); +void collect_characters_for_adaption(WERD_RES *word, + CHAR_SAMPLES_LIST *char_clusters, + CHAR_SAMPLE_LIST *chars_waiting); +void cluster_sample(CHAR_SAMPLE *sample, + CHAR_SAMPLES_LIST *char_clusters, + CHAR_SAMPLE_LIST *chars_waiting); +void check_wait_list(CHAR_SAMPLE_LIST *chars_waiting, + CHAR_SAMPLE *sample, + CHAR_SAMPLES *best_cluster); +void complete_clustering(CHAR_SAMPLES_LIST *char_clusters, + CHAR_SAMPLE_LIST *chars_waiting); +void adapt_to_good_ems(WERD_RES *word, + CHAR_SAMPLES_LIST *char_clusters, + CHAR_SAMPLE_LIST *chars_waiting); +void adapt_to_good_samples(WERD_RES *word, + CHAR_SAMPLES_LIST *char_clusters, + CHAR_SAMPLE_LIST *chars_waiting); +void print_em_stats(CHAR_SAMPLES_LIST *char_clusters, + CHAR_SAMPLE_LIST *chars_waiting); + //lines of the image +CHAR_SAMPLE *clip_sample(PIXROW *pixrow, + IMAGELINE *imlines, + BOX pix_box, //box of imlines extent + BOOL8 white_on_black, + char c); +void display_cluster_prototypes(CHAR_SAMPLES_LIST *char_clusters); +void reject_all_ems(WERD_RES *word); +void reject_all_fullstops(WERD_RES *word); +void reject_suspect_ems(WERD_RES *word); +void reject_suspect_fullstops(WERD_RES *word); +BOOL8 suspect_em(WERD_RES *word, INT16 index); +BOOL8 suspect_fullstop(WERD_RES *word, INT16 i); +#endif diff --git a/ccmain/applybox.cpp b/ccmain/applybox.cpp new file mode 100644 index 0000000000..41b4822596 --- /dev/null +++ b/ccmain/applybox.cpp @@ -0,0 +1,859 @@ +/********************************************************************** + * 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. + * + **********************************************************************/ +/* +define SECURE_NAMES for code versions which go to UNLV to stop tessedit +including all the newdiff stuff (which contains lots of text indicating +what measures we are interested in. +*/ +/* #define SECURE_NAMES done in secnames.h when necessary*/ + +#include "mfcpch.h" +#include "applybox.h" +#include +#include +#ifdef __UNIX__ +#include +#include +#endif +#include "mainblk.h" +#include "genblob.h" +#include "fixxht.h" +#include "control.h" +#include "tessbox.h" +#include "globals.h" +#include "secname.h" + +#define SECURE_NAMES +#ifndef SECURE_NAMES +#include "wordstats.h" +#endif + +#define EXTERN +EXTERN BOOL_VAR (applybox_rebalance, TRUE, "Drop dead"); +EXTERN INT_VAR (applybox_debug, 0, "Debug level"); +EXTERN STRING_VAR (applybox_test_exclusions, "|", +"Chars ignored for testing"); +EXTERN double_VAR (applybox_error_band, 0.15, "Err band as fract of xht"); + +/************************************************************************* + * The code re-assigns outlines to form words each with ONE labelled blob. + * Noise is left in UNLABELLED words. The chars on the page are checked crudely + * for sensible position relative to baseline and xht. Failed boxes are + * compensated for by duplicating other believable instances of the character. + * + * The box file is assumed to contain box definitions, one per line, of the + * following format: + * ... arbitrary trailing fields unused + * + * The approach taken is to search the WHOLE page for stuff overlapping each box. + * - This is not too inefficient and is SAFE. + * - We can detect overlapping blobs as we will be attempting to put a blob + * from a LABELLED word into the current word. + * - When all the boxes have been processed we can detect any stuff which is + * being ignored - it is the unlabelled words left on the page. + * + * A box should only overlap one row. + * + * A warning is given if the box is on the same row as the previous box, but NOT + * on the same row as the previous blob. + * + * Any OUTLINE which overlaps the box is put into the new word. + * + * ascender chars must ascend above xht significantly + * xht chars must not rise above row xht significantly + * bl chars must not descend below baseline significantly + * descender chars must descend below baseline significantly + * + * ?? Certain chars are DROPPED - to limit the training data. + * + *************************************************************************/ + +void apply_boxes(BLOCK_LIST *block_list //real blocks + ) { + INT16 boxfile_lineno = 0; + INT16 boxfile_charno = 0; + BOX box; //boxfile box + char ch[2]; //correct ch from boxfile + ROW *row; + ROW *prev_row = NULL; + INT16 prev_box_right = MAX_INT16; + INT16 block_id; + INT16 row_id; + INT16 box_count = 0; + INT16 box_failures = 0; + INT16 labels_ok; + INT16 rows_ok; + INT16 bad_blobs; + INT16 tgt_char_counts[128]; //No. of box samples + // INT16 labelled_char_counts[128]; //No. of unique labelled samples + INT16 i; + INT16 rebalance_count = 0; + char min_char; + INT16 min_samples; + INT16 final_labelled_blob_count; + + for (i = 0; i < 128; i++) + tgt_char_counts[i] = 0; + + FILE* box_file; + STRING filename = imagefile; + filename += ".box"; + if (!(box_file = fopen (filename.string(), "r"))) { + CANTOPENFILE.error ("read_next_box", EXIT, + "Cant open box file %s %d", + filename.string(), errno); + } + + ch[1] = '\0'; + clear_any_old_text(block_list); + while (read_next_box (box_file, &box, &ch[0])) { + box_count++; + tgt_char_counts[ch[0]]++; + row = find_row_of_box (block_list, box, block_id, row_id); + if (box.left () < prev_box_right) { + boxfile_lineno++; + boxfile_charno = 1; + } + else + boxfile_charno++; + + if (row == NULL) { + box_failures++; + report_failed_box (boxfile_lineno, boxfile_charno, box, ch, + "FAILURE! box overlaps no blobs or blobs in multiple rows"); + } + else { + if ((box.left () >= prev_box_right) && (row != prev_row)) + report_failed_box (boxfile_lineno, boxfile_charno, box, ch, + "WARNING! false row break"); + box_failures += resegment_box (row, box, ch, block_id, row_id, + boxfile_lineno, boxfile_charno); + prev_row = row; + } + prev_box_right = box.right (); + } + tidy_up(block_list, + labels_ok, + rows_ok, + bad_blobs, + tgt_char_counts, + rebalance_count, + min_char, + min_samples, + final_labelled_blob_count); + tprintf ("APPLY_BOXES:\n"); + tprintf (" Boxes read from boxfile: %6d\n", box_count); + tprintf (" Initially labelled blobs: %6d in %d rows\n", + labels_ok, rows_ok); + tprintf (" Box failures detected: %6d\n", box_failures); + tprintf (" Duped blobs for rebalance:%6d\n", rebalance_count); + tprintf (" \"%c\" has fewest samples:%6d\n", min_char, min_samples); + tprintf (" Total unlabelled words: %6d\n", + bad_blobs); + tprintf (" Final labelled words: %6d\n", + final_labelled_blob_count); +} + + +void clear_any_old_text( //remove correct text + BLOCK_LIST *block_list //real blocks + ) { + BLOCK_IT block_it(block_list); + ROW_IT row_it; + WERD_IT word_it; + + for (block_it.mark_cycle_pt (); + !block_it.cycled_list (); block_it.forward ()) { + row_it.set_to_list (block_it.data ()->row_list ()); + for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) { + word_it.set_to_list (row_it.data ()->word_list ()); + for (word_it.mark_cycle_pt (); + !word_it.cycled_list (); word_it.forward ()) { + word_it.data ()->set_text (""); + } + } + } +} + + +BOOL8 read_next_box(FILE* box_file, // + BOX *box, + char *ch) { + char buff[256]; //boxfile read buffer + char *buffptr = buff; + STRING box_filename; + static INT16 line = 0; + INT32 x_min; + INT32 y_min; + INT32 x_max; + INT32 y_max; + INT32 count = 0; + + while (!feof (box_file)) { + fgets (buff, sizeof (buff) - 1, box_file); + line++; + + /* Check for blank lines in box file */ + for (buffptr = buff; isspace (*buffptr); buffptr++) + ; + if (*buffptr != '\0') { + count = + sscanf (buff, + "%c " INT32FORMAT " " INT32FORMAT " " INT32FORMAT " " + INT32FORMAT, ch, &x_min, &y_min, &x_max, &y_max); + if (count != 5) { + tprintf ("Box file format error on line %i ignored\n", line); + } + else { + *box = BOX (ICOORD (x_min, y_min), ICOORD (x_max, y_max)); + return TRUE; //read a box ok + } + } + } + return FALSE; //EOF +} + + +ROW *find_row_of_box( // + BLOCK_LIST *block_list, //real blocks + BOX box, //from boxfile + INT16 &block_id, + INT16 &row_id_to_process) { + BLOCK_IT block_it(block_list); + BLOCK *block; + ROW_IT row_it; + ROW *row; + ROW *row_to_process = NULL; + INT16 row_id; + WERD_IT word_it; + WERD *word; + BOOL8 polyg; + PBLOB_IT blob_it; + PBLOB *blob; + OUTLINE_IT outline_it; + OUTLINE *outline; + + /* + Find row to process - error if box REALLY overlaps more than one row. (I.e + it overlaps blobs in the row - not just overlaps the bounding box of the + whole row.) + */ + + block_id = 0; + for (block_it.mark_cycle_pt (); + !block_it.cycled_list (); block_it.forward ()) { + block_id++; + row_id = 0; + block = block_it.data (); + if (block->bounding_box ().overlap (box)) { + row_it.set_to_list (block->row_list ()); + for (row_it.mark_cycle_pt (); + !row_it.cycled_list (); row_it.forward ()) { + row_id++; + row = row_it.data (); + if (row->bounding_box ().overlap (box)) { + word_it.set_to_list (row->word_list ()); + for (word_it.mark_cycle_pt (); + !word_it.cycled_list (); word_it.forward ()) { + word = word_it.data (); + polyg = word->flag (W_POLYGON); + if (word->bounding_box ().overlap (box)) { + blob_it.set_to_list (word->gblob_list ()); + for (blob_it.mark_cycle_pt (); + !blob_it.cycled_list (); blob_it.forward ()) { + blob = blob_it.data (); + if (gblob_bounding_box (blob, polyg). + overlap (box)) { + outline_it. + set_to_list (gblob_out_list + (blob, polyg)); + for (outline_it.mark_cycle_pt (); + !outline_it.cycled_list (); + outline_it.forward ()) { + outline = outline_it.data (); + if (goutline_bounding_box + (outline, polyg).major_overlap (box)) { + if ((row_to_process == NULL) || + (row_to_process == row)) { + row_to_process = row; + row_id_to_process = row_id; + } + else + /* RETURN ERROR Box overlaps blobs in more than one row */ + return NULL; + } + } + } + } + } + } + } + } + } + } + return row_to_process; +} + + +INT16 resegment_box( // + ROW *row, + BOX box, + char *ch, + INT16 block_id, + INT16 row_id, + INT16 boxfile_lineno, + INT16 boxfile_charno) { + WERD_IT word_it; + WERD *word; + WERD *new_word = NULL; + BOOL8 polyg = false; + PBLOB_IT blob_it; + PBLOB_IT new_blob_it; + PBLOB *blob; + PBLOB *new_blob; + OUTLINE_IT outline_it; + OUTLINE_LIST dummy; // Just to initialize new_outline_it. + OUTLINE_IT new_outline_it = &dummy; + OUTLINE *outline; + BOX new_word_box; + float word_x_centre; + float baseline; + INT16 error_count = 0; //number of chars lost + + word_it.set_to_list (row->word_list ()); + for (word_it.mark_cycle_pt (); !word_it.cycled_list (); word_it.forward ()) { + word = word_it.data (); + polyg = word->flag (W_POLYGON); + if (word->bounding_box ().overlap (box)) { + blob_it.set_to_list (word->gblob_list ()); + for (blob_it.mark_cycle_pt (); + !blob_it.cycled_list (); blob_it.forward ()) { + blob = blob_it.data (); + if (gblob_bounding_box (blob, polyg).overlap (box)) { + outline_it.set_to_list (gblob_out_list (blob, polyg)); + for (outline_it.mark_cycle_pt (); + !outline_it.cycled_list (); outline_it.forward ()) { + outline = outline_it.data (); + if (goutline_bounding_box (outline, polyg). + major_overlap (box)) { + if (strlen (word->text ()) > 0) { + if (error_count == 0) { + error_count = 1; + if (applybox_debug > 4) + report_failed_box (boxfile_lineno, + boxfile_charno, + box, ch, + "FAILURE! box overlaps blob in labelled word"); + } + if (applybox_debug > 4) + tprintf + ("APPLY_BOXES: ALSO ignoring corrupted char blk:%d row:%d \"%s\"\n", + block_id, row_id, + word_it.data ()->text ()); + word_it.data ()->set_text (""); + //UN label it + error_count++; + } + + if (error_count == 0) { + if (new_word == NULL) { + /* Make a new word with a single blob */ + new_word = word->shallow_copy (); + new_word->set_text (ch); + if (polyg) + new_blob = new PBLOB; + else + new_blob = (PBLOB *) new C_BLOB; + new_blob_it.set_to_list (new_word-> + gblob_list ()); + new_blob_it.add_to_end (new_blob); + new_outline_it. + set_to_list (gblob_out_list + (new_blob, polyg)); + } + new_outline_it.add_to_end (outline_it. + extract ()); + //move blob + } + } + } + //no outlines in blob + if (outline_it.empty ()) + //so delete blob + delete blob_it.extract (); + } + } + if (blob_it.empty ()) //no blobs in word + //so delete word + delete word_it.extract (); + } + } + if (error_count > 0) + return error_count; + + if (new_word != NULL) { + gblob_sort_list (new_word->gblob_list (), polyg); + word_it.add_to_end (new_word); + new_word_box = new_word->bounding_box (); + word_x_centre = (new_word_box.left () + new_word_box.right ()) / 2.0f; + baseline = row->base_line (word_x_centre); + + if (STRING (chs_caps_ht).contains (ch[0]) && + (new_word_box.top () < + baseline + (1 + applybox_error_band) * row->x_height ())) { + report_failed_box (boxfile_lineno, boxfile_charno, box, ch, + "FAILURE! caps-ht char didn't ascend"); + new_word->set_text (""); + return 1; + } + if (STRING (chs_odd_top).contains (ch[0]) && + (new_word_box.top () < + baseline + (1 - applybox_error_band) * row->x_height ())) { + report_failed_box (boxfile_lineno, boxfile_charno, box, ch, + "FAILURE! Odd top char below xht"); + new_word->set_text (""); + return 1; + } + if (STRING (chs_x_ht).contains (ch[0]) && + ((new_word_box.top () > + baseline + (1 + applybox_error_band) * row->x_height ()) || + (new_word_box.top () < + baseline + (1 - applybox_error_band) * row->x_height ()))) { + report_failed_box (boxfile_lineno, boxfile_charno, box, ch, + "FAILURE! x-ht char didn't have top near xht"); + new_word->set_text (""); + return 1; + } + if (STRING (chs_non_ambig_bl).contains (ch[0]) && + ((new_word_box.bottom () < + baseline - applybox_error_band * row->x_height ()) || + (new_word_box.bottom () > + baseline + applybox_error_band * row->x_height ()))) { + report_failed_box (boxfile_lineno, boxfile_charno, box, ch, + "FAILURE! non ambig BL char didnt have bottom near baseline"); + new_word->set_text (""); + return 1; + } + if (STRING (chs_odd_bot).contains (ch[0]) && + (new_word_box.bottom () > + baseline + applybox_error_band * row->x_height ())) { + report_failed_box (boxfile_lineno, boxfile_charno, box, ch, + "FAILURE! Odd bottom char above baseline"); + new_word->set_text (""); + return 1; + } + if (STRING (chs_desc).contains (ch[0]) && + (new_word_box.bottom () > + baseline - applybox_error_band * row->x_height ())) { + report_failed_box (boxfile_lineno, boxfile_charno, box, ch, + "FAILURE! Descender doesn't descend"); + new_word->set_text (""); + return 1; + } + return 0; + } + else { + report_failed_box (boxfile_lineno, boxfile_charno, box, ch, + "FAILURE! Couldn't find any blobs"); + return 1; + } +} + + +/************************************************************************* + * tidy_up() + * - report >1 block + * - sort the words in each row. + * - report any rows with no labelled words. + * - report any remaining unlabelled words + * - report total labelled words + * + *************************************************************************/ +void tidy_up( // + BLOCK_LIST *block_list, //real blocks + INT16 &ok_char_count, + INT16 &ok_row_count, + INT16 &unlabelled_words, + INT16 *tgt_char_counts, + INT16 &rebalance_count, + char &min_char, + INT16 &min_samples, + INT16 &final_labelled_blob_count) { + BLOCK_IT block_it(block_list); + ROW_IT row_it; + ROW *row; + WERD_IT word_it; + WERD *word; + WERD *duplicate_word; + INT16 block_idx = 0; + INT16 row_idx; + INT16 all_row_idx = 0; + BOOL8 row_ok; + BOOL8 rebalance_needed = FALSE; + //No. of unique labelled samples + INT16 labelled_char_counts[128]; + INT16 i; + char ch; + char prev_ch = '\0'; + BOOL8 at_dupe_of_prev_word; + ROW *prev_row = NULL; + INT16 left; + INT16 prev_left = -1; + + for (i = 0; i < 128; i++) + labelled_char_counts[i] = 0; + + ok_char_count = 0; + ok_row_count = 0; + unlabelled_words = 0; + if ((applybox_debug > 4) && (block_it.length () != 1)) + + tprintf ("APPLY_BOXES: More than one block??\n"); + + for (block_it.mark_cycle_pt (); + !block_it.cycled_list (); block_it.forward ()) { + block_idx++; + row_idx = 0; + row_ok = FALSE; + row_it.set_to_list (block_it.data ()->row_list ()); + for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) { + row_idx++; + all_row_idx++; + row = row_it.data (); + word_it.set_to_list (row->word_list ()); + word_it.sort (word_comparator); + for (word_it.mark_cycle_pt (); + !word_it.cycled_list (); word_it.forward ()) { + word = word_it.data (); + if (strlen (word->text ()) == 0) { + unlabelled_words++; + if (applybox_debug > 4) { + tprintf + ("APPLY_BOXES: Unlabelled word blk:%d row:%d allrows:%d\n", + block_idx, row_idx, all_row_idx); + } + } + else { + if (word->gblob_list ()->length () != 1) + tprintf + ("APPLY_BOXES: FATALITY - MULTIBLOB Labelled word blk:%d row:%d allrows:%d\n", + block_idx, row_idx, all_row_idx); + + ok_char_count++; + labelled_char_counts[*word->text ()]++; + row_ok = TRUE; + } + } + if ((applybox_debug > 4) && (!row_ok)) { + tprintf + ("APPLY_BOXES: Row with no labelled words blk:%d row:%d allrows:%d\n", + block_idx, row_idx, all_row_idx); + } + else + ok_row_count++; + } + } + + min_samples = 9999; + for (i = 0; i < 128; i++) { + if (tgt_char_counts[i] > labelled_char_counts[i]) { + if (labelled_char_counts[i] <= 1) { + tprintf + ("APPLY_BOXES: FATALITY - %d labelled samples of \"%c\" - target is %d\n", + labelled_char_counts[i], (char) i, tgt_char_counts[i]); + } + else { + rebalance_needed = TRUE; + if (applybox_debug > 0) + tprintf + ("APPLY_BOXES: REBALANCE REQD \"%c\" - target of %d from %d labelled samples\n", + (char) i, tgt_char_counts[i], labelled_char_counts[i]); + } + } + if ((min_samples > labelled_char_counts[i]) && (tgt_char_counts[i] > 0)) { + min_samples = labelled_char_counts[i]; + min_char = (char) i; + } + } + + while (applybox_rebalance && rebalance_needed) { + block_it.set_to_list (block_list); + for (block_it.mark_cycle_pt (); + !block_it.cycled_list (); block_it.forward ()) { + row_it.set_to_list (block_it.data ()->row_list ()); + for (row_it.mark_cycle_pt (); + !row_it.cycled_list (); row_it.forward ()) { + row = row_it.data (); + word_it.set_to_list (row->word_list ()); + for (word_it.mark_cycle_pt (); + !word_it.cycled_list (); word_it.forward ()) { + word = word_it.data (); + left = word->bounding_box ().left (); + ch = *word->text (); + at_dupe_of_prev_word = ((row == prev_row) && + (left = prev_left) && + (ch == prev_ch)); + if ((ch != '\0') && + (labelled_char_counts[ch] > 1) && + (tgt_char_counts[ch] > labelled_char_counts[ch]) && + (!at_dupe_of_prev_word)) { + /* Duplicate the word to rebalance the labelled samples */ + if (applybox_debug > 9) { + tprintf ("Duping \"%c\" from ", ch); + word->bounding_box ().print (); + } + duplicate_word = new WERD; + *duplicate_word = *word; + word_it.add_after_then_move (duplicate_word); + rebalance_count++; + labelled_char_counts[ch]++; + } + prev_row = row; + prev_left = left; + prev_ch = ch; + } + } + } + rebalance_needed = FALSE; + for (i = 0; i < 128; i++) { + if ((tgt_char_counts[i] > labelled_char_counts[i]) && + (labelled_char_counts[i] > 1)) { + rebalance_needed = TRUE; + break; + } + } + } + + /* Now final check - count labelled blobs */ + final_labelled_blob_count = 0; + block_it.set_to_list (block_list); + for (block_it.mark_cycle_pt (); + !block_it.cycled_list (); block_it.forward ()) { + row_it.set_to_list (block_it.data ()->row_list ()); + for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) { + row = row_it.data (); + word_it.set_to_list (row->word_list ()); + word_it.sort (word_comparator); + for (word_it.mark_cycle_pt (); + !word_it.cycled_list (); word_it.forward ()) { + word = word_it.data (); + if ((strlen (word->text ()) == 1) && + (word->gblob_list ()->length () == 1)) + final_labelled_blob_count++; + } + } + } +} + + +void report_failed_box(INT16 boxfile_lineno, + INT16 boxfile_charno, + BOX box, + char *box_ch, + const char *err_msg) { + if (applybox_debug > 4) + tprintf ("APPLY_BOXES: boxfile %1d/%1d/%s ((%1d,%1d),(%1d,%1d)): %s\n", + boxfile_lineno, + boxfile_charno, + box_ch, + box.left (), box.bottom (), box.right (), box.top (), err_msg); +} + + +void apply_box_training(BLOCK_LIST *block_list) { + BLOCK_IT block_it(block_list); + ROW_IT row_it; + ROW *row; + WERD_IT word_it; + WERD *word; + WERD *bln_word; + WERD copy_outword; // copy to denorm + PBLOB_IT blob_it; + DENORM denorm; + INT16 count = 0; + char ch[2]; + + ch[1] = '\0'; + + tprintf ("Generating training data\n"); + for (block_it.mark_cycle_pt (); + !block_it.cycled_list (); block_it.forward ()) { + row_it.set_to_list (block_it.data ()->row_list ()); + for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) { + row = row_it.data (); + word_it.set_to_list (row->word_list ()); + for (word_it.mark_cycle_pt (); + !word_it.cycled_list (); word_it.forward ()) { + word = word_it.data (); + if ((strlen (word->text ()) == 1) && + (word->gblob_list ()->length () == 1)) { + /* Here is a word with a single char label and a single blob so train on it */ + bln_word = + make_bln_copy (word, row, row->x_height (), &denorm); + blob_it.set_to_list (bln_word->blob_list ()); + ch[0] = *word->text (); + tess_training_tester (blob_it.data (), + //single blob + &denorm, TRUE, //correct + ch, //correct ASCII char + 1, //ASCII length + NULL); + copy_outword = *(bln_word); + copy_outword.baseline_denormalise (&denorm); + blob_it.set_to_list (copy_outword.blob_list ()); + ch[0] = *word->text (); + delete bln_word; + count++; + } + } + } + } + tprintf ("Generated training data for %d blobs\n", count); +} + + +void apply_box_testing(BLOCK_LIST *block_list) { + BLOCK_IT block_it(block_list); + ROW_IT row_it; + ROW *row; + INT16 row_count = 0; + WERD_IT word_it; + WERD *word; + WERD *bln_word; + INT16 word_count = 0; + PBLOB_IT blob_it; + DENORM denorm; + INT16 count = 0; + char ch[2]; + WERD *outword; //bln best choice + //segmentation + WERD_CHOICE *best_choice; //tess output + WERD_CHOICE *raw_choice; //top choice permuter + //detailed results + BLOB_CHOICE_LIST_CLIST blob_choices; + INT16 char_count = 0; + INT16 correct_count = 0; + INT16 err_count = 0; + INT16 rej_count = 0; + #ifndef SECURE_NAMES + WERDSTATS wordstats; //As from newdiff + #endif + char tess_rej_str[3]; + char tess_long_str[3]; + + ch[1] = '\0'; + strcpy (tess_rej_str, "|A"); + strcpy (tess_long_str, "|B"); + + for (block_it.mark_cycle_pt (); + !block_it.cycled_list (); block_it.forward ()) { + row_it.set_to_list (block_it.data ()->row_list ()); + for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) { + row = row_it.data (); + row_count++; + word_count = 0; + word_it.set_to_list (row->word_list ()); + for (word_it.mark_cycle_pt (); + !word_it.cycled_list (); word_it.forward ()) { + word = word_it.data (); + word_count++; + if ((strlen (word->text ()) == 1) && + !STRING (applybox_test_exclusions).contains (*word->text ()) + && (word->gblob_list ()->length () == 1)) { + /* Here is a word with a single char label and a single blob so test it */ + bln_word = + make_bln_copy (word, row, row->x_height (), &denorm); + blob_it.set_to_list (bln_word->blob_list ()); + ch[0] = *word->text (); + char_count++; + best_choice = tess_segment_pass1 (bln_word, + &denorm, + tess_default_matcher, + raw_choice, + &blob_choices, outword); + + /* + Test for TESS screw up on word. Recog_word has already ensured that the + choice list, outword blob lists and best_choice string are the same + length. A TESS screw up is indicated by a blank filled or 0 length string. + */ + if ((best_choice->string ().length () == 0) || + (strspn (best_choice->string ().string (), " ") == + best_choice->string ().length ())) { + rej_count++; + tprintf ("%d:%d: \"%s\" -> TESS FAILED\n", + row_count, word_count, ch); + #ifndef SECURE_NAMES + wordstats.word (tess_rej_str, 2, ch, 1); + #endif + } + else { + if ((best_choice->string ().length () != + outword->blob_list ()->length ()) || + (best_choice->string ().length () != + blob_choices.length ())) { + tprintf + ("ASSERT FAIL String:\"%s\"; Strlen=%d; #Blobs=%d; #Choices=%d\n", + best_choice->string ().string (), + best_choice->string ().length (), + outword->blob_list ()->length (), + blob_choices.length ()); + } + ASSERT_HOST (best_choice->string ().length () == + outword->blob_list ()->length ()); + ASSERT_HOST (best_choice->string ().length () == + blob_choices.length ()); + fix_quotes ((char *) best_choice->string ().string (), + //turn to double + outword, &blob_choices); + if (strcmp (best_choice->string ().string (), ch) != 0) { + err_count++; + tprintf ("%d:%d: \"%s\" -> \"%s\"\n", + row_count, word_count, ch, + best_choice->string ().string ()); + } + else + correct_count++; + #ifndef SECURE_NAMES + if (best_choice->string ().length () > 2) + wordstats.word (tess_long_str, 2, ch, 1); + else + wordstats.word ((char *) best_choice->string (). + string (), + best_choice->string ().length (), ch, + 1); + #endif + } + delete bln_word; + delete outword; + delete best_choice; + delete raw_choice; + blob_choices.deep_clear (); + count++; + } + } + } + } + #ifndef SECURE_NAMES + wordstats.print (1, 100.0); + wordstats.conf_matrix (); + tprintf ("Tested %d chars: %d correct; %d rejected by tess; %d errs\n", + char_count, correct_count, rej_count, err_count); + #endif +} diff --git a/ccmain/applybox.h b/ccmain/applybox.h new file mode 100644 index 0000000000..14533f2f59 --- /dev/null +++ b/ccmain/applybox.h @@ -0,0 +1,71 @@ +/********************************************************************** + * File: applybox.h (Formerly applybox.h) + * 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. + * + **********************************************************************/ + +#ifndef APPLYBOX_H +#define APPLYBOX_H + +#include "varable.h" +#include "ocrblock.h" +#include "ocrrow.h" +#include "notdll.h" + +extern BOOL_VAR_H (applybox_rebalance, TRUE, "Drop dead"); +extern INT_VAR_H (applybox_debug, 0, "Debug level"); +extern STRING_VAR_H (applybox_test_exclusions, "|", +"Chars ignored for testing"); +extern double_VAR_H (applybox_error_band, 0.15, "Err band as fract of xht"); +void apply_boxes(BLOCK_LIST *block_list //real blocks + ); +void clear_any_old_text( //remove correct text + BLOCK_LIST *block_list //real blocks + ); +BOOL8 read_next_box(FILE* box_file, // + BOX *box, + char *ch); +ROW *find_row_of_box( // + BLOCK_LIST *block_list, //real blocks + BOX box, //from boxfile + INT16 &block_id, + INT16 &row_id_to_process); +INT16 resegment_box( // + ROW *row, + BOX box, + char *ch, + INT16 block_id, + INT16 row_id, + INT16 boxfile_lineno, + INT16 boxfile_charno); +void tidy_up( // + BLOCK_LIST *block_list, //real blocks + INT16 &ok_char_count, + INT16 &ok_row_count, + INT16 &unlabelled_words, + INT16 *tgt_char_counts, + INT16 &rebalance_count, + char &min_char, + INT16 &min_samples, + INT16 &final_labelled_blob_count); +void report_failed_box(INT16 boxfile_lineno, + INT16 boxfile_charno, + BOX box, + char *box_ch, + const char *err_msg); +void apply_box_training(BLOCK_LIST *block_list); +void apply_box_testing(BLOCK_LIST *block_list); +#endif diff --git a/ccmain/baseapi.cpp b/ccmain/baseapi.cpp new file mode 100644 index 0000000000..0d76398767 --- /dev/null +++ b/ccmain/baseapi.cpp @@ -0,0 +1,395 @@ +/********************************************************************** + * 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 "baseapi.h" + +#include "tessedit.h" +#include "pageres.h" +#include "tessvars.h" +#include "control.h" +#include "applybox.h" +#include "pgedit.h" +#include "varabled.h" +#include "adaptmatch.h" + +BOOL_VAR(tessedit_resegment_from_boxes, FALSE, + "Take segmentation and labeling from box file"); +BOOL_VAR(tessedit_train_from_boxes, FALSE, + "Generate training data from boxed chars"); + +// Minimum sensible image size to be worth running tesseract. +const int kMinRectSize = 10; + +// Start tesseract. +// The datapath must be the name of the data directory or some other file +// in which the data directory resides (for instance argv[0].) +// The configfile is the name of a file in the tessconfigs directory +// (eg batch) or NULL to run on defaults. +// Outputbase may also be NULL, and is the basename of various output files. +// If the output of any of these files is enabled, then a name nmust be given. +// If numeric_mode is true, only possible digits and roman numbers are +// returned. Returns 0 if successful. Crashes if not. +// The argc and argv may be 0 and NULL respectively. They are used for +// providing config files for debug/display purposes. +// TODO(rays) get the facts straight. Is it OK to call +// it more than once? Make it properly check for errors and return them. +int TessBaseAPI::Init(const char* datapath, const char* outputbase, + const char* configfile, bool numeric_mode, + int argc, char* argv[]) { + int result = init_tesseract(datapath, outputbase, configfile, argc, argv); + bln_numericmode.set_value(numeric_mode); + return result; +} + +// 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 (in future will be coded +// as UTF8 and) must be freed with the delete [] operator. +char* TessBaseAPI::TesseractRect(const UINT8* imagedata, + int bytes_per_pixel, + int bytes_per_line, + int left, int top, + int width, int height) { + if (width < kMinRectSize || height < kMinRectSize) + return NULL; // Nothing worth doing. + + // Copy/Threshold the image to the tesseract global page_image. + CopyImageToTesseract(imagedata, bytes_per_pixel, bytes_per_line, + left, top, width, height); + + return RecognizeToString(); +} + +// Call between pages or documents etc to free up memory and forget +// adaptive data. +void TessBaseAPI::ClearAdaptiveClassifier() { + ResetAdaptiveClassifier(); +} + +// Close down tesseract and free up memory. +void TessBaseAPI::End() { + ResetAdaptiveClassifier(); + end_tesseract(); +} + +// Dump the internal binary image to a PGM file. +void TessBaseAPI::DumpPGM(const char* filename) { + IMAGELINE line; + line.init(page_image.get_xsize()); + FILE *fp = fopen(filename, "w"); + fprintf(fp, "P5 " INT32FORMAT " " INT32FORMAT " 255\n", page_image.get_xsize(), + page_image.get_ysize()); + for (int j = page_image.get_ysize()-1; j >= 0 ; --j) { + page_image.get_line(0, j, page_image.get_xsize(), &line, 0); + for (int i = 0; i < page_image.get_xsize(); ++i) { + UINT8 b = line.pixels[i] ? 255 : 0; + fwrite(&b, 1, 1, fp); + } + } + fclose(fp); +} + +// Copy the given image rectangle to Tesseract, with adaptive thresholding +// if the image is not already binary. +void TessBaseAPI::CopyImageToTesseract(const UINT8* imagedata, + int bytes_per_pixel, + int bytes_per_line, + int left, int top, + int width, int height) { + if (bytes_per_pixel > 0) { + // Threshold grey or color. + int* thresholds = new int[bytes_per_pixel]; + int* hi_values = new int[bytes_per_pixel]; + + // Compute the thresholds. + OtsuThreshold(imagedata, bytes_per_pixel, bytes_per_line, + left, top, left + width, top + height, + thresholds, hi_values); + + // Threshold the image to the tesseract global page_image. + ThresholdRect(imagedata, bytes_per_pixel, bytes_per_line, + left, top, width, height, + thresholds, hi_values); + delete [] thresholds; + delete [] hi_values; + } else { + CopyBinaryRect(imagedata, bytes_per_line, left, top, width, height); + } +} + +// Compute 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. +// thresholds and hi_values are assumed to be of bytes_per_pixel size. +void TessBaseAPI::OtsuThreshold(const UINT8* imagedata, + int bytes_per_pixel, + int bytes_per_line, + int left, int top, int right, int bottom, + int* thresholds, + int* hi_values) { + // Of all channels with no good hi_value, keep the best so we can always + // produce at least one answer. + int best_hi_value = 0; + int best_hi_index = 0; + bool any_good_hivalue = false; + double best_hi_dist = 0.0; + + for (int ch = 0; ch < bytes_per_pixel; ++ch) { + thresholds[ch] = 0; + hi_values[ch] = -1; + // Compute the histogram of the image rectangle. + int histogram[256]; + HistogramRect(imagedata + ch, bytes_per_pixel, bytes_per_line, + left, top, right, bottom, histogram); + int H; + int best_omega_0; + int best_t = OtsuStats(histogram, &H, &best_omega_0); + // 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; + } + } + } + if (!any_good_hivalue) { + // Use the best of the ones that were not good enough. + hi_values[best_hi_index] = best_hi_value; + } +} + +// Compute the histogram for the given image rectangle, and the given +// channel. (Channel pointed to by imagedata.) Each channel is always +// one byte per pixel. +// Bytes per pixel is used to skip channels not being +// counted with this call in a multi-channel (pixel-major) image. +// Histogram is always a 256 element array to count occurrences of +// each pixel value. +void TessBaseAPI::HistogramRect(const UINT8* imagedata, + int bytes_per_pixel, + int bytes_per_line, + int left, int top, int right, int bottom, + int* histogram) { + int width = right - left; + memset(histogram, 0, sizeof(*histogram) * 256); + const UINT8* pix = imagedata + + top*bytes_per_line + + left*bytes_per_pixel; + for (int y = top; y < bottom; ++y) { + for (int x = 0; x < width; ++x) { + ++histogram[pix[x * bytes_per_pixel]]; + } + pix += bytes_per_line; + } +} + +// Compute the Otsu threshold(s) for the given histogram. +// Also returns H = total count in histogram, and +// omega0 = count of histogram below threshold. +int TessBaseAPI::OtsuStats(const int* histogram, + int* H_out, + int* omega0_out) { + int H = 0; + double mu_T = 0.0; + for (int i = 0; i < 256; ++i) { + H += histogram[i]; + mu_T += 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 < 255; ++t) { + omega_0 += histogram[t]; + mu_t += t * static_cast(histogram[t]); + if (omega_0 == 0) + continue; + omega_1 = H - omega_0; + 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; +} + +// Threshold the given grey or color image into the tesseract global +// image ready for recognition. Requires thresholds and hi_value +// produced by OtsuThreshold above. +void TessBaseAPI::ThresholdRect(const UINT8* imagedata, + int bytes_per_pixel, + int bytes_per_line, + int left, int top, + int width, int height, + const int* thresholds, + const int* hi_values) { + IMAGELINE line; + page_image.create(width, height, 1); + line.init(width); + // For each line in the image, fill the IMAGELINE class and put it into the + // Tesseract global page_image. Note that Tesseract stores images with the + // bottom at y=0 and 0 is black, so we need 2 kinds of inversion. + const UINT8* data = imagedata + top*bytes_per_line + left*bytes_per_pixel; + for (int y = height - 1 ; y >= 0; --y) { + const UINT8* pix = data; + for (int x = 0; x < width; ++x, pix += bytes_per_pixel) { + line.pixels[x] = 1; + for (int ch = 0; ch < bytes_per_pixel; ++ch) { + if (hi_values[ch] >= 0 && + (pix[ch] > thresholds[ch]) == (hi_values[ch] == 0)) { + line.pixels[x] = 0; + break; + } + } + } + page_image.put_line(0, y, width, &line, 0); + data += bytes_per_line; + } +} + +// Cut out the requested rectangle of the binary image to the +// tesseract global image ready for recognition. +void TessBaseAPI::CopyBinaryRect(const UINT8* imagedata, + int bytes_per_line, + int left, int top, + int width, int height) { + // Copy binary image, cutting out the required rectangle. + IMAGE image; + image.capture(const_cast(imagedata), + bytes_per_line*8, top + height, 1); + page_image.create(width, height, 1); + copy_sub_image(&image, left, top, width, height, &page_image, 0, 0, false); +} + +// Low-level function to recognize the current global image to a string. +char* TessBaseAPI::RecognizeToString() { + BLOCK_LIST block_list; + + FindLines(&block_list); + + // Now run the main recognition. + PAGE_RES* page_res = Recognize(&block_list, NULL); + + return TesseractToText(page_res); +} + +// Find lines from the image making the BLOCK_LIST. +void TessBaseAPI::FindLines(BLOCK_LIST* block_list) { + STRING input_file = "noname.tif"; + // The following call creates a full-page block and then runs connected + // component analysis and text line creation. + pgeditor_read_file(input_file, block_list); +} + +// Recognize the tesseract global image and return the result as Tesseract +// internal structures. +PAGE_RES* TessBaseAPI::Recognize(BLOCK_LIST* block_list, ETEXT_DESC* monitor) { + if (tessedit_resegment_from_boxes) + apply_boxes(block_list); + if (edit_variables) + start_variables_editor(); + + PAGE_RES* page_res = new PAGE_RES(block_list); + if (interactive_mode) { + pgeditor_main(block_list); //pgeditor user I/F + } else if (tessedit_train_from_boxes) { + apply_box_training(block_list); + } else { + // Now run the main recognition. + recog_all_words(page_res, monitor); + } + return page_res; +} + +// Make a text string from the internal data structures. +// The input page_res is deleted. +char* TessBaseAPI::TesseractToText(PAGE_RES* page_res) { + if (page_res != NULL) { + int total_length = 2; + PAGE_RES_IT page_res_it(page_res); + // 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_length += choice->string().length() + 1; + } + } + 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(); + WERD_CHOICE* choice = word->best_choice; + if (choice != NULL) { + strcpy(ptr, choice->string().string()); + ptr += strlen(ptr); + if (word->word->flag(W_EOL)) + *ptr++ = '\n'; + else + *ptr++ = ' '; + } + } + *ptr++ = '\n'; + *ptr = '\0'; + delete page_res; + return result; + } + return NULL; +} + diff --git a/ccmain/baseapi.h b/ccmain/baseapi.h new file mode 100644 index 0000000000..591d415d8d --- /dev/null +++ b/ccmain/baseapi.h @@ -0,0 +1,154 @@ +/////////////////////////////////////////////////////////////////////// +// 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 THIRD_PARTY_TESSERACT_CCMAIN_BASEAPI_H__ +#define THIRD_PARTY_TESSERACT_CCMAIN_BASEAPI_H__ + +#include + +#include "host.h" +#include "ocrclass.h" + +class PAGE_RES; +class BLOCK_LIST; + +// Base class for all tesseract APIs. +// Specific classes can add ability to work on different inputs or produce +// different outputs. + +class TessBaseAPI { + public: + // Start tesseract. + // The datapath must be the name of the data directory or some other file + // in which the data directory resides (for instance argv[0].) + // The configfile is the name of a file in the tessconfigs directory + // (eg batch) or NULL to run on defaults. + // Outputbase may also be NULL, and is the basename of various output files. + // If the output of any of these files is enabled, then a name must be given. + // If numeric_mode is true, only possible digits and roman numbers are + // returned. Returns 0 if successful. Crashes if not. + // The argc and argv may be 0 and NULL respectively. They are used for + // providing config files for debug/display purposes. + // TODO(rays) get the facts straight. Is it OK to call + // it more than once? Make it properly check for errors and return them. + static int Init(const char* datapath, const char* outputbase, + const char* configfile, bool numeric_mode, + int argc, char* argv[]); + + // 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 (in future will be coded + // as UTF8 and) must be freed with the delete [] operator. + static char* TesseractRect(const UINT8* 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. + static void ClearAdaptiveClassifier(); + + // Close down tesseract and free up memory. + static void End(); + + // Dump the internal binary image to a PGM file. + static void DumpPGM(const char* filename); + + protected: + // Copy the given image rectangle to Tesseract, with adaptive thresholding + // if the image is not already binary. + static void CopyImageToTesseract(const UINT8* imagedata, + int bytes_per_pixel, + int bytes_per_line, + int left, int top, int width, int height); + + // Compute 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. + // thresholds and hi_values are assumed to be of bytes_per_pixel size. + static void OtsuThreshold(const UINT8* imagedata, + int bytes_per_pixel, + int bytes_per_line, + int left, int top, int right, int bottom, + int* thresholds, + int* hi_values); + + // Compute the histogram for the given image rectangle, and the given + // channel. (Channel pointed to by imagedata.) Each channel is always + // one byte per pixel. + // Bytes per pixel is used to skip channels not being + // counted with this call in a multi-channel (pixel-major) image. + // Histogram is always a 256 element array to count occurrences of + // each pixel value. + static void HistogramRect(const UINT8* imagedata, + int bytes_per_pixel, + int bytes_per_line, + int left, int top, int right, int bottom, + int* histogram); + + // Compute the Otsu threshold(s) for the given histogram. + // Also returns H = total count in histogram, and + // omega0 = count of histogram below threshold. + static int OtsuStats(const int* histogram, + int* H_out, + int* omega0_out); + + // Threshold the given grey or color image into the tesseract global + // image ready for recognition. Requires thresholds and hi_value + // produced by OtsuThreshold above. + static void ThresholdRect(const UINT8* imagedata, + int bytes_per_pixel, + int bytes_per_line, + int left, int top, + int width, int height, + const int* thresholds, + const int* hi_values); + + // Cut out the requested rectangle of the binary image to the + // tesseract global image ready for recognition. + static void CopyBinaryRect(const UINT8* imagedata, + int bytes_per_line, + int left, int top, + int width, int height); + + // Low-level function to recognize the current global image to a string. + static char* RecognizeToString(); + + // Find lines from the image making the BLOCK_LIST. + static void FindLines(BLOCK_LIST* block_list); + + // Recognize the tesseract global image and return the result as Tesseract + // internal structures. + static PAGE_RES* Recognize(BLOCK_LIST* block_list, ETEXT_DESC* monitor); + + // Convert (and free) the internal data structures into a text string. + static char* TesseractToText(PAGE_RES* page_res); +}; + +#endif // THIRD_PARTY_TESSERACT_CCMAIN_BASEAPI_H__ diff --git a/ccmain/blobcmp.cpp b/ccmain/blobcmp.cpp new file mode 100644 index 0000000000..c0c8e34e86 --- /dev/null +++ b/ccmain/blobcmp.cpp @@ -0,0 +1,76 @@ +/********************************************************************** + * File: blobcmp.c (Formerly blobcmp.c) + * Description: Code to compare blobs using the adaptive matcher. + * Author: Ray Smith + * Created: Wed Apr 21 09:28:51 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 "mfcpch.h" +#include "fxdefs.h" +#include "ocrfeatures.h" +#include "intmatcher.h" +#include "intproto.h" +#include "adaptive.h" +#include "adaptmatch.h" +#include "const.h" +#include "tessvars.h" + +#define CMP_CLASS 'x' + +/********************************************************************** + * compare_tess_blobs + * + * Match 2 blobs using the adaptive classifier. + **********************************************************************/ +float compare_tess_blobs(TBLOB *blob1, + TEXTROW *row1, + TBLOB *blob2, + TEXTROW *row2) { + int fcount; /*number of features */ + ADAPT_TEMPLATES ad_templates; + LINE_STATS line_stats1, line_stats2; + INT_FEATURE_ARRAY int_features; + FEATURE_SET float_features; + INT_RESULT_STRUCT int_result; /*output */ + + BIT_VECTOR AllProtosOn = NewBitVector (MAX_NUM_PROTOS); + BIT_VECTOR AllConfigsOn = NewBitVector (MAX_NUM_CONFIGS); + set_all_bits (AllProtosOn, WordsInVectorOfSize (MAX_NUM_PROTOS)); + set_all_bits (AllConfigsOn, WordsInVectorOfSize (MAX_NUM_CONFIGS)); + + EnterClassifyMode; + ad_templates = NewAdaptedTemplates (); + GetLineStatsFromRow(row1, &line_stats1); + /*copy baseline stuff */ + GetLineStatsFromRow(row2, &line_stats2); + MakeNewAdaptedClass(blob1, &line_stats1, CMP_CLASS, ad_templates); + fcount = GetAdaptiveFeatures (blob2, &line_stats2, + int_features, &float_features); + if (fcount > 0) { + SetBaseLineMatch(); + IntegerMatcher (ClassForClassId (ad_templates->Templates, CMP_CLASS), + AllProtosOn, AllConfigsOn, fcount, fcount, + int_features, 0, 0, &int_result, testedit_match_debug); + FreeFeatureSet(float_features); + if (int_result.Rating < 0) + int_result.Rating = MAX_FLOAT32; + } + + free_adapted_templates(ad_templates); + FreeBitVector(AllConfigsOn); + FreeBitVector(AllProtosOn); + + return fcount > 0 ? int_result.Rating * fcount : MAX_FLOAT32; +} diff --git a/ccmain/blobcmp.h b/ccmain/blobcmp.h new file mode 100644 index 0000000000..8ead234128 --- /dev/null +++ b/ccmain/blobcmp.h @@ -0,0 +1,29 @@ +/********************************************************************** + * File: blobcmp.c + * Description: Code to compare blobs using the adaptive matcher. + * Author: Ray Smith + * Created: Wed Apr 21 09:28:51 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 BLOBCMP_H +#define BLOBCMP_H + +#include "tstruct.h" + +float compare_tess_blobs(TBLOB *blob1, + TEXTROW *row1, + TBLOB *blob2, + TEXTROW *row2); +#endif diff --git a/ccmain/callnet.cpp b/ccmain/callnet.cpp new file mode 100644 index 0000000000..506ed57520 --- /dev/null +++ b/ccmain/callnet.cpp @@ -0,0 +1,93 @@ +/********************************************************************** + * File: callnet.cpp (Formerly callnet.c) + * Description: Interface to Neural Net matcher + * Author: Phil Cheatle + * Created: Wed Nov 18 10:35:00 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 "mfcpch.h" +#include "errcode.h" +//#include "nmatch.h" +#include "globals.h" + +#define OUTPUT_NODES 94 + +const ERRCODE NETINIT = "NN init error"; + +//extern "C" +//{ +//extern char* demodir; /* where program lives */ + +void init_net() { /* Initialise net */ +#ifdef ASPIRIN_INCLUDED + char wts_filename[256]; + + if (nmatch_init_network () != 0) { + NETINIT.error ("Init_net", EXIT, "Errcode %s", nmatch_error_string ()); + } + strcpy(wts_filename, demodir); + strcat (wts_filename, "tessdata/netwts"); + + if (nmatch_load_network (wts_filename) != 0) { + NETINIT.error ("Init_net", EXIT, "Weights failed, Errcode %s", + nmatch_error_string ()); + } +#endif +} + + +void callnet( /* Apply image to net */ + float *input_vector, + char *top, + float *top_score, + char *next, + float *next_score) { +#ifdef ASPIRIN_INCLUDED + float *output_vector; + int i; + int max_out_i = 0; + int next_max_out_i = 0; + float max_out = -9; + float next_max_out = -9; + + nmatch_set_input(input_vector); + nmatch_propagate_forward(); + output_vector = nmatch_get_output (); + + /* Now find top two choices */ + + for (i = 0; i < OUTPUT_NODES; i++) { + if (output_vector[i] > max_out) { + next_max_out = max_out; + max_out = output_vector[i]; + next_max_out_i = max_out_i; + max_out_i = i; + } + else { + if (output_vector[i] > next_max_out) { + next_max_out = output_vector[i]; + next_max_out_i = i; + } + } + } + *top = max_out_i + '!'; + *next = next_max_out_i + '!'; + *top_score = max_out; + *next_score = next_max_out; +#endif +} + + +//}; diff --git a/ccmain/callnet.h b/ccmain/callnet.h new file mode 100644 index 0000000000..528bd96c75 --- /dev/null +++ b/ccmain/callnet.h @@ -0,0 +1,32 @@ +/********************************************************************** + * File: callnet.h (Formerly callnet.h) + * Description: Interface to Neural Net matcher + * Author: Phil Cheatle + * Created: Wed Nov 18 10:35:00 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 CALLNET_H +#define CALLNET_H + +// extern "C" { +void init_net(); /* Initialise net */ +void callnet( /* Apply image to net */ + float *input_vector, + char *top, + float *top_score, + char *next, + float *next_score); +// }; +#endif diff --git a/ccmain/charcut.cpp b/ccmain/charcut.cpp new file mode 100644 index 0000000000..5bbb3fa3ee --- /dev/null +++ b/ccmain/charcut.cpp @@ -0,0 +1,710 @@ +/********************************************************************** + * File: charcut.cpp (Formerly charclip.c) + * Description: Code for character clipping + * Author: Phil Cheatle + * Created: Wed Nov 11 08:35:15 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 "mfcpch.h" +#include "charcut.h" +#include "imgs.h" +#include "showim.h" +#include "evnts.h" +#include "notdll.h" + +#define LARGEST(a,b) ( (a) > (b) ? (a) : (b) ) +#define SMALLEST(a,b) ( (a) > (b) ? (b) : (a) ) +#define BUG_OFFSET 1 +#define EXTERN + +EXTERN INT_VAR (pix_word_margin, 3, "How far outside word BB to grow"); + +extern IMAGE page_image; + +ELISTIZE (PIXROW) +/************************************************************************* + * PIXROW::PIXROW() + * + * Constructor for a specified size PIXROW from a blob + *************************************************************************/ +PIXROW::PIXROW(INT16 pos, INT16 count, PBLOB *blob) { + OUTLINE_LIST *outline_list; + OUTLINE_IT outline_it; + POLYPT_LIST *pts_list; + POLYPT_IT pts_it; + INT16 i; + FCOORD pt; + FCOORD vec; + float y_coord; + INT16 x_coord; + + row_offset = pos; + row_count = count; + min = (INT16 *) alloc_mem (count * sizeof (INT16)); + max = (INT16 *) alloc_mem (count * sizeof (INT16)); + outline_list = blob->out_list (); + outline_it.set_to_list (outline_list); + + for (i = 0; i < count; i++) { + min[i] = MAX_INT16 - 1; + max[i] = -MAX_INT16 + 1; + y_coord = row_offset + i + 0.5; + for (outline_it.mark_cycle_pt (); + !outline_it.cycled_list (); outline_it.forward ()) { + pts_list = outline_it.data ()->polypts (); + pts_it.set_to_list (pts_list); + for (pts_it.mark_cycle_pt (); + !pts_it.cycled_list (); pts_it.forward ()) { + pt = pts_it.data ()->pos; + vec = pts_it.data ()->vec; + if ((vec.y () != 0) && + (((pt.y () <= y_coord) && (pt.y () + vec.y () >= y_coord)) + || ((pt.y () >= y_coord) + && (pt.y () + vec.y () <= y_coord)))) { + /* The segment crosses y_coord so find x-point and check for min/max. */ + x_coord = (INT16) floor ((y_coord - + pt.y ()) * vec.x () / vec.y () + + pt.x () + 0.5); + if (x_coord < min[i]) + min[i] = x_coord; + x_coord--; //to get pix to left of line + if (x_coord > max[i]) + max[i] = x_coord; + } + } + } + } +} + + +/************************************************************************* + * PIXROW::plot() + * + * Draw the PIXROW + *************************************************************************/ + +#ifndef GRAPHICS_DISABLED +void PIXROW::plot(WINDOW fd //where to paint + ) const { + INT16 i; + INT16 y_coord; + + for (i = 0; i < row_count; i++) { + y_coord = row_offset + i; + if (min[i] <= max[i]) { + rectangle (fd, min[i], y_coord, max[i] + 1, y_coord + 1); + } + } +} +#endif + +/************************************************************************* + * PIXROW::bounding_box() + * + * Generate bounding box for blob image + *************************************************************************/ + +bool PIXROW::bad_box( //return true if box exceeds image + int xsize, + int ysize) const { + BOX bbox = bounding_box (); + if (bbox.left () < 0 || bbox.right () > xsize + || bbox.top () > ysize || bbox.bottom () < 0) { + tprintf("Box (%d,%d)->(%d,%d) bad compared to %d,%d\n", + bbox.left(),bbox.bottom(), bbox.right(), bbox.top(), + xsize, ysize); + return true; + } + return false; +} + + +/************************************************************************* + * PIXROW::bounding_box() + * + * Generate bounding box for blob image + *************************************************************************/ + +BOX PIXROW::bounding_box() const { + INT16 i; + INT16 y_coord; + INT16 min_x = MAX_INT16 - 1; + INT16 min_y = MAX_INT16 - 1; + INT16 max_x = -MAX_INT16 + 1; + INT16 max_y = -MAX_INT16 + 1; + + for (i = 0; i < row_count; i++) { + y_coord = row_offset + i; + if (min[i] <= max[i]) { + if (y_coord < min_y) + min_y = y_coord; + if (y_coord + 1 > max_y) + max_y = y_coord + 1; + if (min[i] < min_x) + min_x = min[i]; + if (max[i] + 1 > max_x) + max_x = max[i] + 1; + } + } + if (min_x > max_x || min_y > max_y) + return BOX (); + else + return BOX (ICOORD (min_x, min_y), ICOORD (max_x, max_y)); +} + + +/************************************************************************* + * PIXROW::contract() + * + * Reduce the mins and maxs so that they end on black pixels + *************************************************************************/ + +void PIXROW::contract( //image array + IMAGELINE *imlines, + INT16 x_offset, //of pixels[0] + INT16 foreground_colour //0 or 1 + ) { + INT16 i; + UINT8 *line_pixels; + + for (i = 0; i < row_count; i++) { + if (min[i] > max[i]) + continue; + + line_pixels = imlines[i].pixels; + while (line_pixels[min[i] - x_offset] != foreground_colour) { + if (min[i] == max[i]) { + min[i] = MAX_INT16 - 1; + max[i] = -MAX_INT16 + 1; + goto nextline; + } + else + min[i]++; + } + while (line_pixels[max[i] - x_offset] != foreground_colour) { + if (min[i] == max[i]) { + min[i] = MAX_INT16 - 1; + max[i] = -MAX_INT16 + 1; + goto nextline; + } + else + max[i]--; + } + nextline:; + //goto label! + } +} + + +/************************************************************************* + * PIXROW::extend() + * + * 1 pixel extension in each direction to cover extra black area + *************************************************************************/ + +BOOL8 PIXROW::extend( //image array + IMAGELINE *imlines, + BOX &imbox, + PIXROW *prev, //for prev blob + PIXROW *next, //for next blob + INT16 foreground_colour) { + INT16 i; + INT16 x_offset = imbox.left (); + INT16 limit; + INT16 left_limit; + INT16 right_limit; + UINT8 *pixels = NULL; + UINT8 *pixels_below = NULL; //row below current + UINT8 *pixels_above = NULL; //row above current + BOOL8 changed = FALSE; + + pixels_above = imlines[0].pixels; + for (i = 0; i < row_count; i++) { + pixels_below = pixels; + pixels = pixels_above; + if (i < (row_count - 1)) + pixels_above = imlines[i + 1].pixels; + else + pixels_above = NULL; + + /* Extend Left by one pixel*/ + if (prev == NULL || prev->max[i] < prev->min[i]) + limit = imbox.left (); + else + limit = prev->max[i] + 1; + if ((min[i] <= max[i]) && + (min[i] > limit) && + (pixels[min[i] - 1 - x_offset] == foreground_colour)) { + min[i]--; + changed = TRUE; + } + + /* Extend Right by one pixel*/ + if (next == NULL || next->min[i] > next->max[i]) + limit = imbox.right () - 1;//-1 to index inside pix + else + limit = next->min[i] - 1; + if ((min[i] <= max[i]) && + (max[i] < limit) && + (pixels[max[i] + 1 - x_offset] == foreground_colour)) { + max[i]++; + changed = TRUE; + } + + /* Extend down by one row */ + if (pixels_below != NULL) { + if (min[i] < min[i - 1]) { //row goes left of row below + if (prev == NULL || prev->max[i - 1] < prev->min[i - 1]) + left_limit = min[i]; + else + left_limit = LARGEST (min[i], prev->max[i - 1] + 1); + } + else + left_limit = min[i - 1]; + + if (max[i] > max[i - 1]) { //row goes right of row below + if (next == NULL || next->min[i - 1] > next->max[i - 1]) + right_limit = max[i]; + else + right_limit = SMALLEST (max[i], next->min[i - 1] - 1); + } + else + right_limit = max[i - 1]; + + while ((left_limit <= right_limit) && + (pixels_below[left_limit - x_offset] != foreground_colour)) + left_limit++; //find black extremity + + if ((left_limit <= right_limit) && (left_limit < min[i - 1])) { + min[i - 1] = left_limit; //widen left if poss + changed = TRUE; + } + + while ((left_limit <= right_limit) && + (pixels_below[right_limit - x_offset] != foreground_colour)) + right_limit--; //find black extremity + + if ((left_limit <= right_limit) && (right_limit > max[i - 1])) { + max[i - 1] = right_limit;//widen right if poss + changed = TRUE; + } + } + + /* Extend up by one row */ + if (pixels_above != NULL) { + if (min[i] < min[i + 1]) { //row goes left of row above + if (prev == NULL || prev->min[i + 1] > prev->max[i + 1]) + left_limit = min[i]; + else + left_limit = LARGEST (min[i], prev->max[i + 1] + 1); + } + else + left_limit = min[i + 1]; + + if (max[i] > max[i + 1]) { //row goes right of row above + if (next == NULL || next->min[i + 1] > next->max[i + 1]) + right_limit = max[i]; + else + right_limit = SMALLEST (max[i], next->min[i + 1] - 1); + } + else + right_limit = max[i + 1]; + + while ((left_limit <= right_limit) && + (pixels_above[left_limit - x_offset] != foreground_colour)) + left_limit++; //find black extremity + + if ((left_limit <= right_limit) && (left_limit < min[i + 1])) { + min[i + 1] = left_limit; //widen left if poss + changed = TRUE; + } + + while ((left_limit <= right_limit) && + (pixels_above[right_limit - x_offset] != foreground_colour)) + right_limit--; //find black extremity + + if ((left_limit <= right_limit) && (right_limit > max[i + 1])) { + max[i + 1] = right_limit;//widen right if poss + changed = TRUE; + } + } + } + return changed; +} + + +/************************************************************************* + * PIXROW::char_clip_image() + * Cut out a sub image for a character + *************************************************************************/ + +void PIXROW::char_clip_image( //box of imlines extnt + IMAGELINE *imlines, + BOX &im_box, + ROW *row, //row containing word + IMAGE &clip_image, //unscaled sq subimage + float &baseline_pos //baseline ht in image + ) { + INT16 clip_image_xsize; //sub image x size + INT16 clip_image_ysize; //sub image y size + INT16 x_shift; //from pixrow to subim + INT16 y_shift; //from pixrow to subim + BOX char_pix_box; //bbox of char pixels + INT16 y_dest; + INT16 x_min; + INT16 x_max; + INT16 x_min_dest; + INT16 x_max_dest; + INT16 x_width; + INT16 y; + + clip_image_xsize = clip_image.get_xsize (); + clip_image_ysize = clip_image.get_ysize (); + + char_pix_box = bounding_box (); + /* + The y shift is calculated by first finding the coord of the bottom of the + image relative to the image lines. Then reducing this so by the amount + relative to the clip image size, necessary to vertically position the + character. + */ + y_shift = char_pix_box.bottom () - row_offset - + (INT16) floor ((clip_image_ysize - char_pix_box.height () + 0.5) / 2); + + /* + The x_shift is the shift to be applied to the page coord in the pixrow to + generate a centred char in the clip image. Thus the left hand edge of the + char is shifted to the margin width of the centred character. + */ + x_shift = char_pix_box.left () - + (INT16) floor ((clip_image_xsize - char_pix_box.width () + 0.5) / 2); + + for (y = 0; y < row_count; y++) { + /* + Check that there is something in this row of the source that will fit in the + sub image. If there is, reduce x range if necessary, then copy it + */ + y_dest = y - y_shift; + if ((min[y] <= max[y]) && (y_dest >= 0) && (y_dest < clip_image_ysize)) { + x_min = min[y]; + x_min_dest = x_min - x_shift; + if (x_min_dest < 0) { + x_min = x_min - x_min_dest; + x_min_dest = 0; + } + x_max = max[y]; + x_max_dest = x_max - x_shift; + if (x_max_dest > clip_image_xsize - 1) { + x_max = x_max - (x_max_dest - (clip_image_xsize - 1)); + x_max_dest = clip_image_xsize - 1; + } + x_width = x_max - x_min + 1; + if (x_width > 0) { + x_min -= im_box.left (); + //offset pixel ptr + imlines[y].pixels += x_min; + clip_image.put_line (x_min_dest, y_dest, x_width, imlines + y, + 0); + imlines[y].init (); //reset pixel ptr + } + } + } + /* + Baseline position relative to clip image: First find the baseline relative + to the page origin at the x coord of the centre of the character. Then + make this relative to the character bottom. Finally shift by the margin + between the bottom of the character and the bottom of the clip image. + */ + if (row == NULL) + baseline_pos = 0; //Not needed + else + baseline_pos = row->base_line ((char_pix_box.left () + + char_pix_box.right ()) / 2.0) + - char_pix_box.bottom () + + ((clip_image_ysize - char_pix_box.height ()) / 2); +} + + +/************************************************************************* + * char_clip_word() + * + * Generate a PIXROW_LIST with one element for each blob in the word, together + * with the image lines for the whole word. + *************************************************************************/ + +void char_clip_word( // + WERD *word, //word to be processed + IMAGE &bin_image, //whole image + PIXROW_LIST *&pixrow_list, //pixrows built + IMAGELINE *&imlines, //lines cut from image + BOX &pix_box //box defining imlines + ) { + BOX word_box = word->bounding_box (); + PBLOB_LIST *blob_list; + PBLOB_IT blob_it; + PIXROW_IT pixrow_it; + INT16 pix_offset; //Y pos of pixrow[0] + INT16 row_height; //No of pix rows + INT16 imlines_x_offset; + PIXROW *prev; + PIXROW *next; + PIXROW *current; + BOOL8 changed; //still improving + BOOL8 just_changed; //still improving + INT16 iteration_count = 0; + INT16 foreground_colour; + + if (word->flag (W_INVERSE)) + foreground_colour = 1; + else + foreground_colour = 0; + + /* Define region for max pixrow expansion */ + pix_box = word_box; + pix_box.move_bottom_edge (-pix_word_margin); + pix_box.move_top_edge (pix_word_margin); + pix_box.move_left_edge (-pix_word_margin); + pix_box.move_right_edge (pix_word_margin); + pix_box -= BOX (ICOORD (0, 0 + BUG_OFFSET), + ICOORD (bin_image.get_xsize (), + bin_image.get_ysize () - BUG_OFFSET)); + + /* Generate pixrows list */ + + pix_offset = pix_box.bottom (); + row_height = pix_box.height (); + blob_list = word->blob_list (); + blob_it.set_to_list (blob_list); + + pixrow_list = new PIXROW_LIST; + pixrow_it.set_to_list (pixrow_list); + + for (blob_it.mark_cycle_pt (); !blob_it.cycled_list (); blob_it.forward ()) { + PIXROW *row = new PIXROW (pix_offset, row_height, blob_it.data ()); + ASSERT_HOST (!row-> + bad_box (bin_image.get_xsize (), bin_image.get_ysize ())); + pixrow_it.add_after_then_move (row); + } + + imlines = generate_imlines (bin_image, pix_box); + + /* Contract pixrows - shrink min and max back to black pixels */ + + imlines_x_offset = pix_box.left (); + + pixrow_it.move_to_first (); + for (pixrow_it.mark_cycle_pt (); + !pixrow_it.cycled_list (); pixrow_it.forward ()) { + ASSERT_HOST (!pixrow_it.data ()-> + bad_box (bin_image.get_xsize (), bin_image.get_ysize ())); + pixrow_it.data ()->contract (imlines, imlines_x_offset, + foreground_colour); + ASSERT_HOST (!pixrow_it.data ()-> + bad_box (bin_image.get_xsize (), bin_image.get_ysize ())); + } + + /* Expand pixrows iteratively 1 pixel at a time */ + do { + changed = FALSE; + pixrow_it.move_to_first (); + prev = NULL; + current = NULL; + next = pixrow_it.data (); + for (pixrow_it.mark_cycle_pt (); + !pixrow_it.cycled_list (); pixrow_it.forward ()) { + prev = current; + current = next; + if (pixrow_it.at_last ()) + next = NULL; + else + next = pixrow_it.data_relative (1); + just_changed = current->extend (imlines, pix_box, prev, next, + foreground_colour); + ASSERT_HOST (!current-> + bad_box (bin_image.get_xsize (), + bin_image.get_ysize ())); + changed = changed || just_changed; + } + iteration_count++; + } + while (changed); +} + + +/************************************************************************* + * generate_imlines() + * Get an array of IMAGELINES holding a portion of an image + *************************************************************************/ + +IMAGELINE *generate_imlines( //get some imagelines + IMAGE &bin_image, //from here + BOX &pix_box) { + IMAGELINE *imlines; //array of lines + int i; + + imlines = new IMAGELINE[pix_box.height ()]; + for (i = 0; i < pix_box.height (); i++) { + imlines[i].init (pix_box.width ()); + //coord to start at + bin_image.fast_get_line (pix_box.left (), + pix_box.bottom () + i + BUG_OFFSET, + //line to get + pix_box.width (), //width to get + imlines + i); //dest imline + } + return imlines; +} + + +/************************************************************************* + * display_clip_image() + * All the boring user interface bits to let you see what's going on + *************************************************************************/ + +#ifndef GRAPHICS_DISABLED +WINDOW display_clip_image(WERD *word, //word to be processed + IMAGE &bin_image, //whole image + PIXROW_LIST *pixrow_list, //pixrows built + BOX &pix_box //box of subimage + ) { + WINDOW clip_window; //window for debug + BOX word_box = word->bounding_box (); + int border = word_box.height () / 2; + BOX display_box = word_box; + + display_box.move_bottom_edge (-border); + display_box.move_top_edge (border); + display_box.move_left_edge (-border); + display_box.move_right_edge (border); + display_box -= BOX (ICOORD (0, 0 - BUG_OFFSET), + ICOORD (bin_image.get_xsize (), + bin_image.get_ysize () - BUG_OFFSET)); + + pgeditor_msg ("Creating Clip window..."); + clip_window = + create_window ("Clipped Blobs", + SCROLLINGWIN, + editor_word_xpos, editor_word_ypos, + 3 * (word_box.width () + 2 * border), + 3 * (word_box.height () + 2 * border), + //window width,height + // xmin, xmax + display_box.left (), display_box.right (), + display_box.bottom () - BUG_OFFSET, + display_box.top () - BUG_OFFSET, + // ymin, ymax + TRUE, FALSE, FALSE, TRUE); // down event & key only + pgeditor_msg ("Creating Clip window...Done"); + + clear_view_surface(clip_window); + show_sub_image (&bin_image, + display_box.left (), + display_box.bottom (), + display_box.width (), + display_box.height (), + clip_window, + display_box.left (), display_box.bottom () - BUG_OFFSET); + + word->plot (clip_window, RED); + word_box.plot (clip_window, INT_HOLLOW, TRUE, BLUE, BLUE); + pix_box.plot (clip_window, INT_HOLLOW, TRUE, BLUE, BLUE); + plot_pixrows(pixrow_list, clip_window); + overlap_picture_ops(TRUE); + return clip_window; +} + + +/************************************************************************* + * display_images() + * Show a pair of clip and scaled character images and wait for key before + * continuing. + *************************************************************************/ + +void display_images(IMAGE &clip_image, IMAGE &scaled_image) { + WINDOW clip_im_window; //window for debug + WINDOW scale_im_window; //window for debug + INT16 i; + GRAPHICS_EVENT event; // c; + + // xmin xmax ymin ymax + clip_im_window = create_window ("Clipped Blob", SCROLLINGWIN, editor_word_xpos - 20, editor_word_ypos - 100, 5 * clip_image.get_xsize (), 5 * clip_image.get_ysize (), 0, clip_image.get_xsize (), 0, clip_image.get_ysize (), + TRUE, FALSE, FALSE, TRUE); // down event & key only + + clear_view_surface(clip_im_window); + show_sub_image (&clip_image, + 0, 0, + clip_image.get_xsize (), clip_image.get_ysize (), + clip_im_window, 0, 0); + + line_color_index(clip_im_window, RED); + for (i = 1; i < clip_image.get_xsize (); i++) { + move2d (clip_im_window, i, 0); + draw2d (clip_im_window, i, clip_image.get_xsize ()); + } + for (i = 1; i < clip_image.get_ysize (); i++) { + move2d (clip_im_window, 0, i); + draw2d (clip_im_window, clip_image.get_xsize (), i); + } + + // xmin xmax ymin ymax + scale_im_window = create_window ("Scaled Blob", SCROLLINGWIN, editor_word_xpos + 300, editor_word_ypos - 100, 5 * scaled_image.get_xsize (), 5 * scaled_image.get_ysize (), 0, scaled_image.get_xsize (), 0, scaled_image.get_ysize (), + TRUE, FALSE, FALSE, TRUE); // down event & key only + + clear_view_surface(scale_im_window); + show_sub_image (&scaled_image, + 0, 0, + scaled_image.get_xsize (), scaled_image.get_ysize (), + scale_im_window, 0, 0); + + line_color_index(scale_im_window, RED); + for (i = 1; i < scaled_image.get_xsize (); i++) { + move2d (scale_im_window, i, 0); + draw2d (scale_im_window, i, scaled_image.get_xsize ()); + } + for (i = 1; i < scaled_image.get_ysize (); i++) { + move2d (scale_im_window, 0, i); + draw2d (scale_im_window, scaled_image.get_xsize (), i); + } + + overlap_picture_ops(TRUE); + await_event(scale_im_window, TRUE, ANY_EVENT, &event); + destroy_window(clip_im_window); + destroy_window(scale_im_window); +} + + +/************************************************************************* + * plot_pixrows() + * Display a list of pixrows + *************************************************************************/ + +void plot_pixrows( //plot for all blobs + PIXROW_LIST *pixrow_list, + WINDOW win) { + PIXROW_IT pixrow_it(pixrow_list); + INT16 colour = RED; + + for (pixrow_it.mark_cycle_pt (); + !pixrow_it.cycled_list (); pixrow_it.forward ()) { + if (colour > RED + 7) + colour = RED; + + perimeter_color_index (win, (COLOUR) colour); + interior_style(win, INT_HOLLOW, TRUE); + pixrow_it.data ()->plot (win); + colour++; + } +} +#endif diff --git a/ccmain/charcut.h b/ccmain/charcut.h new file mode 100644 index 0000000000..b447516232 --- /dev/null +++ b/ccmain/charcut.h @@ -0,0 +1,119 @@ +/********************************************************************** + * File: charcut.h (Formerly charclip.h) + * Description: Code for character clipping + * Author: Phil Cheatle + * Created: Wed Nov 11 08:35:15 GMT 1992 + * + * (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 CHARCUT_H +#define CHARCUT_H + +#include "pgedit.h" +#include "notdll.h" +#include "notdll.h" + +/************************************************************************* + * CLASS PIXROW + * + * This class describes the pixels occupied by a blob. It uses two arrays, (min + * and max), each with one element per row, to identify the min and max x + * coordinates of the black pixels in the character on that row of the image. + * The number of rows used to describe the blob is held in row_count - note that + * some rows may be unoccupied - signified by max < min. The page coordinate of + * the row defined by min[0] and max[0] is held in row_offset. + *************************************************************************/ + +class PIXROW:public ELIST_LINK +{ + public: + INT16 row_offset; //y coord of min[0] + INT16 row_count; //length of arrays + INT16 *min; //array of min x + INT16 *max; //array of max x + + PIXROW() { //empty constructor + row_offset = 0; + row_count = 0; + min = NULL; + max = NULL; + } + PIXROW( //specified size + INT16 pos, + INT16 count, + PBLOB *blob); + + ~PIXROW () { //destructor + if (min != NULL) + free_mem(min); + if (max != NULL) + free_mem(max); + max = NULL; + } + + void plot( //use current settings + WINDOW fd) const; //where to paint + + BOX bounding_box() const; //return bounding box + //return true if box exceeds image + bool bad_box(int xsize, int ysize) const; + + void contract( //force end on black + IMAGELINE *imlines, //image array + INT16 x_offset, //of pixels[0] + INT16 foreground_colour); //0 or 1 + + //image array + BOOL8 extend(IMAGELINE *imlines, + BOX &imbox, + PIXROW *prev, //for prev blob + PIXROW *next, //for next blob + INT16 foreground_colour); //0 or 1 + + //box of imlines extnt + void char_clip_image(IMAGELINE *imlines, + BOX &im_box, + ROW *row, //row containing word + IMAGE &clip_image, //unscaled char image + float &baseline_pos); //baseline ht in image + +}; + +ELISTIZEH (PIXROW) +extern INT_VAR_H (pix_word_margin, 3, "How far outside word BB to grow"); +extern BOOL_VAR_H (show_char_clipping, TRUE, "Show clip image window?"); +extern INT_VAR_H (net_image_width, 40, "NN input image width"); +extern INT_VAR_H (net_image_height, 36, "NN input image height"); +extern INT_VAR_H (net_image_x_height, 22, "NN input image x_height"); +void char_clip_word( // + WERD *word, //word to be processed + IMAGE &bin_image, //whole image + PIXROW_LIST *&pixrow_list, //pixrows built + IMAGELINE *&imlines, //lines cut from image + BOX &pix_box //box defining imlines + ); +IMAGELINE *generate_imlines( //get some imagelines + IMAGE &bin_image, //from here + BOX &pix_box); + //word to be processed +WINDOW display_clip_image(WERD *word, + IMAGE &bin_image, //whole image + PIXROW_LIST *pixrow_list, //pixrows built + BOX &pix_box //box of subimage + ); +void display_images(IMAGE &clip_image, IMAGE &scaled_image); +void plot_pixrows( //plot for all blobs + PIXROW_LIST *pixrow_list, + WINDOW win); +#endif diff --git a/ccmain/charsample.cpp b/ccmain/charsample.cpp new file mode 100644 index 0000000000..84cd79075c --- /dev/null +++ b/ccmain/charsample.cpp @@ -0,0 +1,698 @@ +/********************************************************************** + * File: charsample.cpp (Formerly charsample.c) + * Description: Class to contain character samples and match scores + * to be used for adaption + * Author: Chris Newton + * Created: Thu Oct 7 13:40:37 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 "mfcpch.h" +#include +#include +#include +#ifdef __UNIX__ +#include +#include +#endif +#include "memry.h" +#include "tessvars.h" +#include "statistc.h" +#include "charsample.h" +#include "paircmp.h" +#include "matmatch.h" +#include "adaptions.h" +#include "secname.h" +#include "notdll.h" + +extern INT32 demo_word; // Hack for demos + +ELISTIZE (CHAR_SAMPLE) ELISTIZE (CHAR_SAMPLES) CHAR_SAMPLE::CHAR_SAMPLE () { + sample_blob = NULL; + sample_denorm = NULL; + sample_image = NULL; + ch = '\0'; + n_samples_matched = 0; + total_match_scores = 0.0; + sumsq_match_scores = 0.0; +} + + +CHAR_SAMPLE::CHAR_SAMPLE(PBLOB *blob, DENORM *denorm, char c) { + sample_blob = blob; + sample_denorm = denorm; + sample_image = NULL; + ch = c; + n_samples_matched = 0; + total_match_scores = 0.0; + sumsq_match_scores = 0.0; +} + + +CHAR_SAMPLE::CHAR_SAMPLE(IMAGE *image, char c) { + sample_blob = NULL; + sample_denorm = NULL; + sample_image = image; + ch = c; + n_samples_matched = 0; + total_match_scores = 0.0; + sumsq_match_scores = 0.0; +} + + +float CHAR_SAMPLE::match_sample( // Update match scores + CHAR_SAMPLE *test_sample, + BOOL8 updating) { + float score1; + float score2; + IMAGE *image = test_sample->image (); + + if (sample_blob != NULL && test_sample->blob () != NULL) { + PBLOB *blob = test_sample->blob (); + DENORM *denorm = test_sample->denorm (); + + score1 = compare_bln_blobs (sample_blob, sample_denorm, blob, denorm); + score2 = compare_bln_blobs (blob, denorm, sample_blob, sample_denorm); + + score1 = (score1 > score2) ? score1 : score2; + } + else if (sample_image != NULL && image != NULL) { + CHAR_PROTO *sample = new CHAR_PROTO (this); + + score1 = matrix_match (sample_image, image); + delete sample; + } + else + return BAD_SCORE; + + if ((tessedit_use_best_sample || tessedit_cluster_debug) && updating) { + n_samples_matched++; + total_match_scores += score1; + sumsq_match_scores += score1 * score1; + } + return score1; +} + + +double CHAR_SAMPLE::mean_score() { + if (n_samples_matched > 0) + return (total_match_scores / n_samples_matched); + else + return BAD_SCORE; +} + + +double CHAR_SAMPLE::variance() { + double mean = mean_score (); + + if (n_samples_matched > 0) { + return (sumsq_match_scores / n_samples_matched) - mean * mean; + } + else + return BAD_SCORE; +} + + +void CHAR_SAMPLE::print(FILE *f) { + if (!tessedit_cluster_debug) + return; + + if (n_samples_matched > 0) + fprintf (f, + "%c - sample matched against " INT32FORMAT + " blobs, mean: %f, var: %f\n", ch, n_samples_matched, + mean_score (), variance ()); + else + fprintf (f, "No matches for this sample (%c)\n", ch); +} + + +void CHAR_SAMPLE::reset_match_statistics() { + n_samples_matched = 0; + total_match_scores = 0.0; + sumsq_match_scores = 0.0; +} + + +CHAR_SAMPLES::CHAR_SAMPLES() { + type = UNKNOWN; + samples.clear (); + ch = '\0'; + best_sample = NULL; + proto = NULL; +} + + +CHAR_SAMPLES::CHAR_SAMPLES(CHAR_SAMPLE *sample) { + CHAR_SAMPLE_IT sample_it = &samples; + + ASSERT_HOST (sample->image () != NULL || sample->blob () != NULL); + + if (sample->image () != NULL) + type = IMAGE_CLUSTER; + else if (sample->blob () != NULL) + type = BLOB_CLUSTER; + + samples.clear (); + sample_it.add_to_end (sample); + if (tessedit_mm_only_match_same_char) + ch = sample->character (); + else + ch = '\0'; + best_sample = NULL; + proto = NULL; +} + + +void CHAR_SAMPLES::add_sample(CHAR_SAMPLE *sample) { + CHAR_SAMPLE_IT sample_it = &samples; + + if (tessedit_use_best_sample || tessedit_cluster_debug) + for (sample_it.mark_cycle_pt (); + !sample_it.cycled_list (); sample_it.forward ()) { + sample_it.data ()->match_sample (sample, TRUE); + sample->match_sample (sample_it.data (), TRUE); + } + + sample_it.add_to_end (sample); + + if (tessedit_mm_use_prototypes && type == IMAGE_CLUSTER) + if (samples.length () == tessedit_mm_prototype_min_size) + this->build_prototype (); + else if (samples.length () > tessedit_mm_prototype_min_size) + this->add_sample_to_prototype (sample); +} + + +void CHAR_SAMPLES::add_sample_to_prototype(CHAR_SAMPLE *sample) { + BOOL8 rebuild = FALSE; + INT32 new_xsize = proto->x_size (); + INT32 new_ysize = proto->y_size (); + INT32 sample_xsize = sample->image ()->get_xsize (); + INT32 sample_ysize = sample->image ()->get_ysize (); + + if (sample_xsize > new_xsize) { + new_xsize = sample_xsize; + rebuild = TRUE; + } + if (sample_ysize > new_ysize) { + new_ysize = sample_ysize; + rebuild = TRUE; + } + + if (rebuild) + proto->enlarge_prototype (new_xsize, new_ysize); + + proto->add_sample (sample); +} + + +void CHAR_SAMPLES::build_prototype() { + CHAR_SAMPLE_IT sample_it = &samples; + CHAR_SAMPLE *sample; + INT32 proto_xsize = 0; + INT32 proto_ysize = 0; + + if (type != IMAGE_CLUSTER + || samples.length () < tessedit_mm_prototype_min_size) + return; + + for (sample_it.mark_cycle_pt (); + !sample_it.cycled_list (); sample_it.forward ()) { + sample = sample_it.data (); + if (sample->image ()->get_xsize () > proto_xsize) + proto_xsize = sample->image ()->get_xsize (); + if (sample->image ()->get_ysize () > proto_ysize) + proto_ysize = sample->image ()->get_ysize (); + } + + proto = new CHAR_PROTO (proto_xsize, proto_ysize, 0, 0, '\0'); + + for (sample_it.mark_cycle_pt (); + !sample_it.cycled_list (); sample_it.forward ()) + this->add_sample_to_prototype (sample_it.data ()); + +} + + +void CHAR_SAMPLES::find_best_sample() { + CHAR_SAMPLE_IT sample_it = &samples; + double score; + double best_score = MAX_INT32; + + if (ch == '\0' || samples.length () < tessedit_mm_prototype_min_size) + return; + + for (sample_it.mark_cycle_pt (); + !sample_it.cycled_list (); sample_it.forward ()) { + score = sample_it.data ()->mean_score (); + if (score < best_score) { + best_score = score; + best_sample = sample_it.data (); + } + } + #ifndef SECURE_NAMES + if (tessedit_cluster_debug) { + tprintf ("Best sample for this %c cluster:\n", ch); + best_sample->print (debug_fp); + } + #endif +} + + +float CHAR_SAMPLES::match_score(CHAR_SAMPLE *sample) { + if (tessedit_mm_only_match_same_char && sample->character () != ch) + return BAD_SCORE; + + if (tessedit_use_best_sample && best_sample != NULL) + return best_sample->match_sample (sample, FALSE); + else if ((tessedit_mm_use_prototypes + || tessedit_mm_adapt_using_prototypes) && proto != NULL) + return proto->match_sample (sample); + else + return this->nn_match_score (sample); +} + + +float CHAR_SAMPLES::nn_match_score(CHAR_SAMPLE *sample) { + CHAR_SAMPLE_IT sample_it = &samples; + float score; + float min_score = MAX_INT32; + + for (sample_it.mark_cycle_pt (); + !sample_it.cycled_list (); sample_it.forward ()) { + score = sample_it.data ()->match_sample (sample, FALSE); + if (score < min_score) + min_score = score; + } + + return min_score; +} + + +void CHAR_SAMPLES::assign_to_char() { + STATS char_frequency(FIRST_CHAR, LAST_CHAR); + CHAR_SAMPLE_IT sample_it = &samples; + INT32 i; + INT32 max_index = 0; + INT32 max_freq = 0; + + if (samples.length () == 0 || tessedit_mm_only_match_same_char) + return; + + for (sample_it.mark_cycle_pt (); + !sample_it.cycled_list (); sample_it.forward ()) + char_frequency.add ((INT32) sample_it.data ()->character (), 1); + + for (i = FIRST_CHAR; i <= LAST_CHAR; i++) + if (char_frequency.pile_count (i) > max_freq) { + max_index = i; + max_freq = char_frequency.pile_count (i); + } + + if (samples.length () >= tessedit_cluster_min_size + && max_freq > samples.length () * tessedit_cluster_accept_fraction) + ch = (char) max_index; +} + + +void CHAR_SAMPLES::print(FILE *f) { + CHAR_SAMPLE_IT sample_it = &samples; + + fprintf (f, "Collected " INT32FORMAT " samples\n", samples.length ()); + + #ifndef SECURE_NAMES + if (tessedit_cluster_debug) + for (sample_it.mark_cycle_pt (); + !sample_it.cycled_list (); sample_it.forward ()) + sample_it.data ()->print (f); + + if (ch == '\0') + fprintf (f, "\nCluster not used for adaption\n"); + else + fprintf (f, "\nCluster used to adapt to '%c's\n", ch); + #endif +} + + +CHAR_PROTO::CHAR_PROTO() { + xsize = 0; + ysize = 0; + ch = '\0'; + nsamples = 0; + proto_data = NULL; + proto = NULL; +} + + +CHAR_PROTO::CHAR_PROTO(INT32 x_size, + INT32 y_size, + INT32 n_samples, + float initial_value, + char c) { + INT32 x; + INT32 y; + + xsize = x_size; + ysize = y_size; + ch = c; + nsamples = n_samples; + + ALLOC_2D_ARRAY(xsize, ysize, proto_data, proto, float); + + for (y = 0; y < ysize; y++) + for (x = 0; x < xsize; x++) + proto[x][y] = initial_value; +} + + +CHAR_PROTO::CHAR_PROTO(CHAR_SAMPLE *sample) { + INT32 x; + INT32 y; + IMAGELINE imline_s; + + if (sample->image () == NULL) { + xsize = 0; + ysize = 0; + ch = '\0'; + nsamples = 0; + proto_data = NULL; + proto = NULL; + } + else { + ch = sample->character (); + xsize = sample->image ()->get_xsize (); + ysize = sample->image ()->get_ysize (); + nsamples = 1; + + ALLOC_2D_ARRAY(xsize, ysize, proto_data, proto, float); + + for (y = 0; y < ysize; y++) { + sample->image ()->fast_get_line (0, y, xsize, &imline_s); + for (x = 0; x < xsize; x++) + if (imline_s.pixels[x] == BINIM_WHITE) + proto[x][y] = 1.0; + else + proto[x][y] = -1.0; + } + } +} + + +CHAR_PROTO::~CHAR_PROTO () { + if (proto_data != NULL) + FREE_2D_ARRAY(proto_data, proto); +} + + +float CHAR_PROTO::match_sample(CHAR_SAMPLE *test_sample) { + CHAR_PROTO *test_proto; + float score; + + if (test_sample->image () != NULL) { + test_proto = new CHAR_PROTO (test_sample); + if (xsize > test_proto->x_size ()) + score = this->match (test_proto); + else { + demo_word = -demo_word; // Flag different call + score = test_proto->match (this); + } + } + else + return BAD_SCORE; + + delete test_proto; + + return score; +} + + +float CHAR_PROTO::match(CHAR_PROTO *test_proto) { + INT32 xsize2 = test_proto->x_size (); + INT32 y_size; + INT32 y_size2; + INT32 x_offset; + INT32 y_offset; + INT32 x; + INT32 y; + CHAR_PROTO *match_proto; + float score; + float sum = 0.0; + + ASSERT_HOST (xsize >= xsize2); + + x_offset = (xsize - xsize2) / 2; + + if (ysize < test_proto->y_size ()) { + y_size = test_proto->y_size (); + y_size2 = ysize; + y_offset = (y_size - y_size2) / 2; + + match_proto = new CHAR_PROTO (xsize, + y_size, + nsamples * test_proto->n_samples (), + 0, '\0'); + + for (y = 0; y < y_offset; y++) { + for (x = 0; x < xsize2; x++) { + match_proto->data ()[x + x_offset][y] = + test_proto->data ()[x][y] * nsamples; + sum += match_proto->data ()[x + x_offset][y]; + } + } + + for (y = y_offset + y_size2; y < y_size; y++) { + for (x = 0; x < xsize2; x++) { + match_proto->data ()[x + x_offset][y] = + test_proto->data ()[x][y] * nsamples; + sum += match_proto->data ()[x + x_offset][y]; + } + } + + for (y = y_offset; y < y_offset + y_size2; y++) { + for (x = 0; x < x_offset; x++) { + match_proto->data ()[x][y] = proto[x][y - y_offset] * + test_proto->n_samples (); + sum += match_proto->data ()[x][y]; + } + + for (x = x_offset + xsize2; x < xsize; x++) { + match_proto->data ()[x][y] = proto[x][y - y_offset] * + test_proto->n_samples (); + sum += match_proto->data ()[x][y]; + } + + for (x = x_offset; x < x_offset + xsize2; x++) { + match_proto->data ()[x][y] = + proto[x][y - y_offset] * test_proto->data ()[x - x_offset][y]; + sum += match_proto->data ()[x][y]; + } + } + } + else { + y_size = ysize; + y_size2 = test_proto->y_size (); + y_offset = (y_size - y_size2) / 2; + + match_proto = new CHAR_PROTO (xsize, + y_size, + nsamples * test_proto->n_samples (), + 0, '\0'); + + for (y = 0; y < y_offset; y++) + for (x = 0; x < xsize; x++) { + match_proto->data ()[x][y] = + proto[x][y] * test_proto->n_samples (); + sum += match_proto->data ()[x][y]; + } + + for (y = y_offset + y_size2; y < y_size; y++) + for (x = 0; x < xsize; x++) { + match_proto->data ()[x][y] = + proto[x][y] * test_proto->n_samples (); + sum += match_proto->data ()[x][y]; + } + + for (y = y_offset; y < y_offset + y_size2; y++) { + for (x = 0; x < x_offset; x++) { + match_proto->data ()[x][y] = + proto[x][y] * test_proto->n_samples (); + sum += match_proto->data ()[x][y]; + } + + for (x = x_offset + xsize2; x < xsize; x++) { + match_proto->data ()[x][y] = + proto[x][y] * test_proto->n_samples (); + sum += match_proto->data ()[x][y]; + } + + for (x = x_offset; x < x_offset + xsize2; x++) { + match_proto->data ()[x][y] = proto[x][y] * + test_proto->data ()[x - x_offset][y - y_offset]; + sum += match_proto->data ()[x][y]; + } + } + } + + score = (1.0 - sum / + (xsize * y_size * nsamples * test_proto->n_samples ())); + + if (tessedit_mm_debug) { + if (score < 0) { + tprintf ("Match score %f\n", score); + tprintf ("x: %d, y: %d, ns: %d, nt: %d, dx %d, dy: %d\n", + xsize, y_size, nsamples, test_proto->n_samples (), + x_offset, y_offset); + for (y = 0; y < y_size; y++) { + tprintf ("\n%d", y); + for (x = 0; x < xsize; x++) + tprintf ("\t%d", match_proto->data ()[x][y]); + + } + tprintf ("\n"); + fflush(debug_fp); + } + } + +#ifndef GRAPHICS_DISABLED + if (tessedit_display_mm) { + tprintf ("Match score %f\n", score); + display_images (this->make_image (), + test_proto->make_image (), match_proto->make_image ()); + } + else if (demo_word != 0) { + if (demo_word > 0) + display_image (test_proto->make_image (), "Test sample", + 300, 400, FALSE); + else + display_image (this->make_image (), "Test sample", 300, 400, FALSE); + + display_image (match_proto->make_image (), "Best match", + 700, 400, TRUE); + } +#endif + + delete match_proto; + + return score; +} + + +void CHAR_PROTO::enlarge_prototype(INT32 new_xsize, INT32 new_ysize) { + float *old_proto_data = proto_data; + float **old_proto = proto; + INT32 old_xsize = xsize; + INT32 old_ysize = ysize; + INT32 x_offset; + INT32 y_offset; + INT32 x; + INT32 y; + + ASSERT_HOST (new_xsize >= xsize && new_ysize >= ysize); + + xsize = new_xsize; + ysize = new_ysize; + ALLOC_2D_ARRAY(xsize, ysize, proto_data, proto, float); + x_offset = (xsize - old_xsize) / 2; + y_offset = (ysize - old_ysize) / 2; + + for (y = 0; y < y_offset; y++) + for (x = 0; x < xsize; x++) + proto[x][y] = nsamples; + + for (y = y_offset + old_ysize; y < ysize; y++) + for (x = 0; x < xsize; x++) + proto[x][y] = nsamples; + + for (y = y_offset; y < y_offset + old_ysize; y++) { + for (x = 0; x < x_offset; x++) + proto[x][y] = nsamples; + + for (x = x_offset + old_xsize; x < xsize; x++) + proto[x][y] = nsamples; + + for (x = x_offset; x < x_offset + old_xsize; x++) + proto[x][y] = old_proto[x - x_offset][y - y_offset]; + } + + FREE_2D_ARRAY(old_proto_data, old_proto); +} + + +void CHAR_PROTO::add_sample(CHAR_SAMPLE *sample) { + INT32 x_offset; + INT32 y_offset; + INT32 x; + INT32 y; + IMAGELINE imline_s; + INT32 sample_xsize = sample->image ()->get_xsize (); + INT32 sample_ysize = sample->image ()->get_ysize (); + + x_offset = (xsize - sample_xsize) / 2; + y_offset = (ysize - sample_ysize) / 2; + + ASSERT_HOST (x_offset >= 0 && y_offset >= 0); + + for (y = 0; y < y_offset; y++) + for (x = 0; x < xsize; x++) + proto[x][y]++; // Treat pixels outside the + // range as white + for (y = y_offset + sample_ysize; y < ysize; y++) + for (x = 0; x < xsize; x++) + proto[x][y]++; + + for (y = y_offset; y < y_offset + sample_ysize; y++) { + sample->image ()->fast_get_line (0, + y - y_offset, sample_xsize, &imline_s); + for (x = x_offset; x < x_offset + sample_xsize; x++) { + if (imline_s.pixels[x - x_offset] == BINIM_WHITE) + proto[x][y]++; + else + proto[x][y]--; + } + + for (x = 0; x < x_offset; x++) + proto[x][y]++; + + for (x = x_offset + sample_xsize; x < xsize; x++) + proto[x][y]++; + } + + nsamples++; +} + + +IMAGE *CHAR_PROTO::make_image() { + IMAGE *image; + IMAGELINE imline_p; + INT32 x; + INT32 y; + + ASSERT_HOST (nsamples != 0); + + image = new (IMAGE); + image->create (xsize, ysize, 8); + + for (y = 0; y < ysize; y++) { + image->fast_get_line (0, y, xsize, &imline_p); + + for (x = 0; x < xsize; x++) { + imline_p.pixels[x] = 128 + + (UINT8) ((proto[x][y] * 128.0) / (0.00001 + nsamples)); + } + + image->fast_put_line (0, y, xsize, &imline_p); + } + return image; +} diff --git a/ccmain/control.cpp b/ccmain/control.cpp new file mode 100644 index 0000000000..663a2e9fb1 --- /dev/null +++ b/ccmain/control.cpp @@ -0,0 +1,1668 @@ +/****************************************************************** + * 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 "mfcpch.h" +#include "mainblk.h" +#include +#include +#ifdef __UNIX__ +#include +#include +#include +#endif +#include +#include "ocrclass.h" +#include "werdit.h" +#include "drawfx.h" +#include "tfacep.h" +#include "tessbox.h" +#include "tessvars.h" +//#include "fxtop.h" +#include "pgedit.h" +#include "reject.h" +#include "adaptions.h" +#include "charcut.h" +#include "fixxht.h" +#include "fixspace.h" +#include "genblob.h" +#include "docqual.h" +#include "control.h" +#include "secname.h" +#include "output.h" +#include "callcpp.h" +#include "notdll.h" +#include "tordvars.h" +#include "adaptmatch.h" + +#define MIN_FONT_ROW_COUNT 8 +#define MAX_XHEIGHT_DIFF 3 + +#define EXTERN +//extern "C" { +//EXTERN BOOL_VAR(tessedit_small_match,FALSE,"Use small matrix matcher"); + +//extern FILE* matcher_fp; +//extern FILE* correct_fp; +//}; +BOOL_VAR (tessedit_small_match, FALSE, "Use small matrix matcher"); +EXTERN BOOL_VAR (tessedit_print_text, FALSE, "Write text to stdout"); +EXTERN BOOL_VAR (tessedit_draw_words, FALSE, "Draw source words"); +EXTERN BOOL_VAR (tessedit_draw_outwords, FALSE, "Draw output words"); +EXTERN BOOL_VAR (tessedit_training_wiseowl, FALSE, "Call WO to learn blobs"); +EXTERN BOOL_VAR (tessedit_training_tess, FALSE, "Call Tess to learn blobs"); +EXTERN BOOL_VAR (tessedit_matcher_is_wiseowl, FALSE, "Call WO to classify"); +EXTERN BOOL_VAR (tessedit_dump_choices, FALSE, "Dump char choices"); +EXTERN BOOL_VAR (tessedit_fix_fuzzy_spaces, TRUE, +"Try to improve fuzzy spaces"); +EXTERN BOOL_VAR (tessedit_unrej_any_wd, FALSE, +"Dont bother with word plausibility"); +EXTERN BOOL_VAR (tessedit_fix_hyphens, TRUE, "Crunch double hyphens?"); + +EXTERN BOOL_VAR (tessedit_reject_fullstops, FALSE, "Reject all fullstops"); +EXTERN BOOL_VAR (tessedit_reject_suspect_fullstops, FALSE, +"Reject suspect fullstops"); +EXTERN BOOL_VAR (tessedit_redo_xheight, TRUE, "Check/Correct x-height"); +EXTERN BOOL_VAR (tessedit_cluster_adaption_on, TRUE, +"Do our own adaption - ems only"); +EXTERN BOOL_VAR (tessedit_enable_doc_dict, TRUE, +"Add words to the document dictionary"); +EXTERN BOOL_VAR (word_occ_first, FALSE, "Do word occ before re-est xht"); +EXTERN BOOL_VAR (tessedit_debug_fonts, FALSE, "Output font info per char"); +EXTERN BOOL_VAR (tessedit_xht_fiddles_on_done_wds, TRUE, +"Apply xht fix up even if done"); +EXTERN BOOL_VAR (tessedit_xht_fiddles_on_no_rej_wds, TRUE, +"Apply xht fix up even in no rejects"); +EXTERN INT_VAR (x_ht_check_word_occ, 2, "Check Char Block occupancy"); +EXTERN INT_VAR (x_ht_stringency, 1, "How many confirmed a/n to accept?"); +EXTERN BOOL_VAR (x_ht_quality_check, TRUE, "Dont allow worse quality"); +EXTERN BOOL_VAR (tessedit_debug_block_rejection, FALSE, +"Block and Row stats"); +EXTERN INT_VAR (debug_x_ht_level, 0, "Reestimate debug"); +EXTERN BOOL_VAR (rej_use_xht, TRUE, "Individual rejection control"); +EXTERN BOOL_VAR (debug_acceptable_wds, FALSE, "Dump word pass/fail chk"); + +EXTERN STRING_VAR (chs_leading_punct, "('`\"", "Leading punctuation"); +EXTERN +STRING_VAR (chs_trailing_punct1, ").,;:?!", "1st Trailing punctuation"); +EXTERN STRING_VAR (chs_trailing_punct2, ")'`\"", +"2nd Trailing punctuation"); + +EXTERN double_VAR (quality_rej_pc, 0.08, +"good_quality_doc lte rejection limit"); +EXTERN double_VAR (quality_blob_pc, 0.0, +"good_quality_doc gte good blobs limit"); +EXTERN double_VAR (quality_outline_pc, 1.0, +"good_quality_doc lte outline error limit"); +EXTERN double_VAR (quality_char_pc, 0.95, +"good_quality_doc gte good char limit"); +EXTERN INT_VAR (quality_min_initial_alphas_reqd, 2, +"alphas in a good word"); + +EXTERN BOOL_VAR (tessedit_tess_adapt_to_rejmap, FALSE, +"Use reject map to control Tesseract adaption"); +EXTERN INT_VAR (tessedit_tess_adaption_mode, 0x27, +"Adaptation decision algorithm for tess"); +EXTERN INT_VAR (tessedit_em_adaption_mode, 0, +"Adaptation decision algorithm for ems matrix matcher"); +EXTERN BOOL_VAR (tessedit_cluster_adapt_after_pass1, FALSE, +"Adapt using clusterer after pass 1"); +EXTERN BOOL_VAR (tessedit_cluster_adapt_after_pass2, FALSE, +"Adapt using clusterer after pass 1"); +EXTERN BOOL_VAR (tessedit_cluster_adapt_after_pass3, FALSE, +"Adapt using clusterer after pass 1"); +EXTERN BOOL_VAR (tessedit_cluster_adapt_before_pass1, FALSE, +"Adapt using clusterer before Tess adaping during pass 1"); +EXTERN INT_VAR (tessedit_cluster_adaption_mode, 0, +"Adaptation decision algorithm for matrix matcher"); +EXTERN BOOL_VAR (tessedit_adaption_debug, FALSE, +"Generate and print debug information for adaption"); +EXTERN BOOL_VAR (tessedit_minimal_rej_pass1, FALSE, +"Do minimal rejection on pass 1 output"); +EXTERN BOOL_VAR (tessedit_test_adaption, FALSE, +"Test adaption criteria"); +EXTERN BOOL_VAR (tessedit_global_adaption, FALSE, +"Adapt to all docs over time"); +EXTERN BOOL_VAR (tessedit_matcher_log, FALSE, "Log matcher activity"); +EXTERN INT_VAR (tessedit_test_adaption_mode, 3, +"Adaptation decision algorithm for tess"); + +EXTERN BOOL_VAR (test_pt, FALSE, "Test for point"); +EXTERN double_VAR (test_pt_x, 99999.99, "xcoord"); +EXTERN double_VAR (test_pt_y, 99999.99, "ycoord"); + +extern int MatcherDebugLevel; +extern int display_ratings; +extern int number_debug; +extern int adjust_debug; +/* +extern "C" { + extern int MatcherDebugLevel; + extern int display_ratings; + extern int number_debug; + extern int adjust_debug; +// extern int LearningDebugLevel; + }; +*/ +FILE *choice_file = NULL; //Choice file ptr + +CLISTIZEH (PBLOB) CLISTIZE (PBLOB) +/* DEBUGGING */ +INT16 blob_count(WERD *w) { + return w->blob_list ()->length (); +} + + +/********************************************************************** + * recog_pseudo_word + * + * Make a word from the selected blobs and run Tess on them. + **********************************************************************/ + +void recog_pseudo_word( //recognize blobs + BLOCK_LIST *block_list, //blocks to check + BOX &selection_box) { + WERD *word; + ROW *pseudo_row; //row of word + BLOCK *pseudo_block; //block of word + + word = make_pseudo_word (block_list, selection_box, + pseudo_block, pseudo_row); + if (word != NULL) { + recog_interactive(pseudo_block, pseudo_row, word); + delete word; + } +} + + +/********************************************************************** + * recog_interactive + * + * Recognize a single word in interactive mode. + **********************************************************************/ + +BOOL8 recog_interactive( //recognize blobs + BLOCK *, //block + ROW *row, //row of word + WERD *word //word to recognize + ) { + WERD_RES word_res(word); + INT16 char_qual; + INT16 good_char_qual; + + classify_word_pass2(&word_res, row); + #ifndef SECURE_NAMES + if (tessedit_debug_quality_metrics) { + word_char_quality(&word_res, 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, row), + word_outline_errs (&word_res), char_qual, good_char_qual); + } + #endif + return TRUE; +} + + +/********************************************************************** + * recog_all_words() + * + * Walk the current block list applying the specified word processor function + * to all words + **********************************************************************/ + +void recog_all_words( //process words + PAGE_RES *page_res, //page structure + volatile ETEXT_DESC *monitor //progress monitor + ) { + //reset page iterator + PAGE_RES_IT page_res_it(page_res); + INT16 chars_in_word; + INT16 rejects_in_word; + CHAR_SAMPLES_LIST em_clusters; + CHAR_SAMPLE_LIST ems_waiting; + CHAR_SAMPLES_LIST char_clusters; + CHAR_SAMPLE_LIST chars_waiting; + INT16 blob_quality = 0; + INT16 outline_errs = 0; + INT16 doc_blob_quality = 0; + INT16 doc_outline_errs = 0; + INT16 doc_char_quality = 0; + INT16 all_char_quality; + INT16 accepted_all_char_quality; + INT16 good_char_count = 0; + INT16 doc_good_char_quality = 0; + const STRING *wordstr; + const char *text; + int i; + + BOOL8 good_quality_doc; + UINT8 permuter_type; + + INT32 tess_adapt_mode = 0; + INT32 word_count; //count of words in doc + INT32 word_index; //current word + + if (tessedit_minimal_rej_pass1) { + tessedit_test_adaption.set_value (TRUE); + tessedit_minimal_rejection.set_value (TRUE); + } + + if (tessedit_cluster_adapt_before_pass1) { + tess_adapt_mode = tessedit_tess_adaption_mode; + tessedit_tess_adaption_mode.set_value (0); + tessedit_tess_adapt_to_rejmap.set_value (TRUE); + } + + /* Pass 1 */ + word_count = 0; + if (monitor != NULL) { + monitor->ocr_alive = TRUE; + while (page_res_it.word () != NULL) { + word_count++; + page_res_it.forward (); + } + page_res_it.restart_page (); + } + else + word_count = 1; + + word_index = 0; + int dict_words = 0; + while (page_res_it.word () != NULL) { + set_global_loc_code(LOC_PASS1); + word_index++; + if (monitor != NULL) { + monitor->ocr_alive = TRUE; + monitor->progress = 30 + 50 * word_index / word_count; + if ((monitor->end_time != 0 && clock() > monitor->end_time) || + (monitor->cancel != NULL && (*monitor->cancel)(monitor->cancel_this, + dict_words))) + return; + } + classify_word_pass1 (page_res_it.word (), + page_res_it.row ()->row, FALSE, NULL, NULL); + + if (tessedit_test_adaption && !tessedit_minimal_rejection) { + if (!word_adaptable (page_res_it.word (), + tessedit_test_adaption_mode)) + page_res_it.word ()->reject_map.rej_word_tess_failure (); + //FAKE PERM REJ + else { + wordstr = &(page_res_it.word ()->best_choice->string ()); + /* Override rejection mechanisms for this word */ + text = wordstr->string (); + for (i = 0; text[i] != '\0'; i++) { + if ((text[i] != ' ') + && page_res_it.word ()->reject_map[i].rejected ()) + page_res_it.word ()->reject_map[i]. + setrej_minimal_rej_accept(); + } + } + } + + if ((tessedit_cluster_adapt_after_pass1 + || tessedit_cluster_adapt_after_pass3 + || tessedit_cluster_adapt_before_pass1) + && tessedit_cluster_adaption_mode != 0) { + collect_characters_for_adaption (page_res_it.word (), + &char_clusters, &chars_waiting); + } + // Count dict words. + if (page_res_it.word()->best_choice->permuter() == USER_DAWG_PERM) + ++dict_words; + page_res_it.forward (); + } + + if (tessedit_cluster_adapt_before_pass1) + tessedit_tess_adaption_mode.set_value (tess_adapt_mode); + + page_res_it.restart_page (); + while ((tessedit_cluster_adapt_after_pass1 + || tessedit_cluster_adapt_before_pass1) + && page_res_it.word () != NULL) { + if (monitor != NULL) + monitor->ocr_alive = TRUE; + if (tessedit_cluster_adapt_after_pass1) + adapt_to_good_samples (page_res_it.word (), + &char_clusters, &chars_waiting); + else + classify_word_pass1 (page_res_it.word (), + page_res_it.row ()->row, + TRUE, &char_clusters, &chars_waiting); + + page_res_it.forward (); + } + + /* Pass 2 */ + page_res_it.restart_page (); + word_index = 0; + while (!tessedit_test_adaption && page_res_it.word () != NULL) { + set_global_loc_code(LOC_PASS2); + word_index++; + if (monitor != NULL) { + monitor->ocr_alive = TRUE; + monitor->progress = 80 + 10 * word_index / word_count; + if ((monitor->end_time != 0 && clock() > monitor->end_time) || + (monitor->cancel != NULL && (*monitor->cancel)(monitor->cancel_this, + dict_words))) + return; + } + classify_word_pass2 (page_res_it.word (), page_res_it.row ()->row); + + if (tessedit_em_adaption_mode > 0) + collect_ems_for_adaption (page_res_it.word (), + &em_clusters, &ems_waiting); + + if (tessedit_cluster_adapt_after_pass2 + && tessedit_cluster_adaption_mode != 0) + collect_characters_for_adaption (page_res_it.word (), + &char_clusters, &chars_waiting); + page_res_it.forward (); + } + + /* Another pass */ + set_global_loc_code(LOC_FUZZY_SPACE); + + if (!tessedit_test_adaption && tessedit_fix_fuzzy_spaces + && !tessedit_word_for_word) + fix_fuzzy_spaces(monitor, word_count, page_res); + + if (!tessedit_test_adaption && tessedit_em_adaption_mode != 0) + // Initially ems only + print_em_stats(&em_clusters, &ems_waiting); + + /* Pass 3 - used for checking confusion sets */ + page_res_it.restart_page (); + word_index = 0; + while (!tessedit_test_adaption && page_res_it.word () != NULL) { + set_global_loc_code(LOC_MM_ADAPT); + word_index++; + if (monitor != NULL) { + monitor->ocr_alive = TRUE; + monitor->progress = 95 + 5 * word_index / word_count; + } + check_debug_pt (page_res_it.word (), 70); + /* Use good matches to sort out confusions */ + + if (tessedit_em_adaption_mode != 0) + adapt_to_good_ems (page_res_it.word (), &em_clusters, &ems_waiting); + + if (tessedit_cluster_adapt_after_pass2 + && tessedit_cluster_adaption_mode != 0) + adapt_to_good_samples (page_res_it.word (), + &char_clusters, &chars_waiting); + + if (tessedit_reject_fullstops + && strchr (page_res_it.word ()->best_choice->string ().string (), + '.') != NULL) + reject_all_fullstops (page_res_it.word ()); + else if (tessedit_reject_suspect_fullstops + && strchr (page_res_it.word ()->best_choice->string (). + string (), '.') != NULL) + reject_suspect_fullstops (page_res_it.word ()); + + page_res_it.rej_stat_word (); + chars_in_word = page_res_it.word ()->reject_map.length (); + rejects_in_word = page_res_it.word ()->reject_map.reject_count (); + + blob_quality = word_blob_quality (page_res_it.word (), + page_res_it.row ()->row); + doc_blob_quality += blob_quality; + outline_errs = word_outline_errs (page_res_it.word ()); + doc_outline_errs += outline_errs; + word_char_quality (page_res_it.word (), + page_res_it.row ()->row, + &all_char_quality, &accepted_all_char_quality); + doc_char_quality += all_char_quality; + permuter_type = page_res_it.word ()->best_choice->permuter (); + if ((permuter_type == SYSTEM_DAWG_PERM) || + (permuter_type == FREQ_DAWG_PERM) || + (permuter_type == USER_DAWG_PERM)) { + good_char_count += chars_in_word - rejects_in_word; + doc_good_char_quality += accepted_all_char_quality; + } + check_debug_pt (page_res_it.word (), 80); + if (tessedit_reject_bad_qual_wds && + (blob_quality == 0) && (outline_errs >= chars_in_word)) + page_res_it.word ()->reject_map.rej_word_bad_quality (); + check_debug_pt (page_res_it.word (), 90); + page_res_it.forward (); + } + + page_res_it.restart_page (); + while (!tessedit_test_adaption + && tessedit_cluster_adapt_after_pass3 && page_res_it.word () != NULL) { + if (monitor != NULL) + monitor->ocr_alive = TRUE; + if (tessedit_cluster_adaption_mode != 0) + adapt_to_good_samples (page_res_it.word (), + &char_clusters, &chars_waiting); + page_res_it.forward (); + } + + #ifndef SECURE_NAMES + 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 / (float) page_res->char_count, doc_blob_quality, + doc_blob_quality / (float) page_res->char_count, doc_outline_errs, + doc_outline_errs / (float) page_res->char_count, doc_char_quality, + doc_char_quality / (float) page_res->char_count, + doc_good_char_quality, + good_char_count > + 0 ? doc_good_char_quality / (float) good_char_count : 0.0); + } + #endif + good_quality_doc = + (page_res->rej_count / (float) page_res->char_count <= quality_rej_pc) + && + (doc_blob_quality / (float) page_res->char_count >= quality_blob_pc) && + (doc_outline_errs / (float) page_res->char_count <= quality_outline_pc) && + (doc_char_quality / (float) page_res->char_count >= quality_char_pc); + + /* 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); + } + font_recognition_pass(page_res_it); + + /* 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. + // output_pass (page_res_it, false); +} + + +/********************************************************************** + * classify_word_pass1 + * + * Baseline normalize the word and pass it to Tess. + **********************************************************************/ + +void classify_word_pass1( //recog one word + WERD_RES *word, //word to do + ROW *row, + BOOL8 cluster_adapt, + CHAR_SAMPLES_LIST *char_clusters, + CHAR_SAMPLE_LIST *chars_waiting) { + WERD *bln_word; //baseline norm copy + //detailed results + BLOB_CHOICE_LIST_CLIST blob_choices; + BOOL8 adapt_ok; + const char *rejmap; + INT16 index; + STRING mapstr = ""; + char *match_string; + char word_string[1024]; + + if (matcher_fp != NULL) { + fgets (word_string, 1023, correct_fp); + if ((match_string = strchr (word_string, '\r')) != NULL) + *match_string = '\0'; + if ((match_string = strchr (word_string, '\n')) != NULL) + *match_string = '\0'; + if (word_string[0] != '\0') { + word->word->set_text (word_string); + word_answer = (char *) word->word->text (); + } + else + word_answer = NULL; + } + + check_debug_pt (word, 0); + matcher_pass = 0; + bln_word = make_bln_copy (word->word, row, row->x_height (), &word->denorm); + + word->best_choice = tess_segment_pass1 (bln_word, &word->denorm, + tess_default_matcher, + word->raw_choice, &blob_choices, + word->outword); + + /* + Test for TESS screw up on word. Recog_word has already ensured that the + choice list, outword blob lists and best_choice string are the same + length. A TESS screw up is indicated by a blank filled or 0 length string. + */ + if ((word->best_choice->string ().length () == 0) || + (strspn (word->best_choice->string ().string (), " ") == + word->best_choice->string ().length ())) { + word->done = FALSE; //Try again on pass2 - adaption may help + word->tess_failed = TRUE; + word->reject_map.initialise (word->best_choice->string ().length ()); + word->reject_map.rej_word_tess_failure (); + } + else { + word->tess_failed = FALSE; + if ((word->best_choice->string ().length () != + word->outword->blob_list ()->length ()) || + (word->best_choice->string ().length () != blob_choices.length ())) { + tprintf + ("ASSERT FAIL String:\"%s\"; Strlen=%d; #Blobs=%d; #Choices=%d\n", + word->best_choice->string ().string (), + word->best_choice->string ().length (), + word->outword->blob_list ()->length (), blob_choices.length ()); + } + ASSERT_HOST (word->best_choice->string ().length () == + word->outword->blob_list ()->length ()); + ASSERT_HOST (word->best_choice->string ().length () == + blob_choices.length ()); + + /* + The adaption step used to be here. It has been moved to after + make_reject_map so that we know whether the word will be accepted in the + first pass or not. This move will PREVENT adaption to words containing + double quotes because the word will not be identical to what tess thinks + its best choice is. (See CurrentBestChoiceIs in + danj/microfeatures/stopper.c which is used by AdaptableWord in + danj/microfeatures/adaptmatch.c) + */ + + if (word->word->flag (W_REP_CHAR)) { + fix_rep_char(word); + } + else { + fix_quotes ((char *) word->best_choice->string ().string (), + //turn to double + word->outword, &blob_choices); + if (tessedit_fix_hyphens) + //turn 2 to 1 + fix_hyphens ((char *) word->best_choice->string ().string (), word->outword, &blob_choices); + record_certainty (word->best_choice->certainty (), 1); + //accounting + + word->tess_accepted = tess_acceptable_word (word->best_choice, + word->raw_choice); + + word->tess_would_adapt = tess_adaptable_word (word->outword, + word->best_choice, + word->raw_choice); + // Also sets word->done flag + make_reject_map (word, &blob_choices, row, 1); + + adapt_ok = word_adaptable (word, tessedit_tess_adaption_mode); + + if (cluster_adapt) + adapt_to_good_samples(word, char_clusters, chars_waiting); + + if (adapt_ok || tessedit_tess_adapt_to_rejmap) { + if (!tessedit_tess_adapt_to_rejmap) + rejmap = NULL; + else { + ASSERT_HOST (word->reject_map.length () == + word->best_choice->string ().length ()); + + for (index = 0; index < word->reject_map.length (); index++) { + if (adapt_ok || word->reject_map[index].accepted ()) + mapstr += '1'; + else + mapstr += '0'; + } + rejmap = mapstr.string (); + } + + //adapt to it + tess_adapter (word->outword, &word->denorm, word->best_choice->string ().string (), word->raw_choice->string ().string (), rejmap); + } + + if (tessedit_enable_doc_dict) + tess_add_doc_word (word->best_choice); + set_word_fonts(word, &blob_choices); + } + } + if (tessedit_print_text) { + write_cooked_text (bln_word, word->best_choice->string (), + word->done, FALSE, stdout); + } + delete bln_word; + blob_choices.deep_clear (); +} + + +/********************************************************************** + * classify_word_pass2 + * + * Control what to do with the word in pass 2 + **********************************************************************/ + +void classify_word_pass2( //word to do + WERD_RES *word, + ROW *row) { + BOOL8 done_this_pass = FALSE; + WERD_RES new_x_ht_word (word->word); + float new_x_ht = 0.0; + INT16 old_xht_reject_count; + INT16 new_xht_reject_count; + INT16 old_xht_accept_count; + INT16 new_xht_accept_count; + BOOL8 accept_new_x_ht = FALSE; + INT16 old_chs_in_wd; + INT16 new_chs_in_wd; + INT16 old_word_quality; + INT16 new_word_quality; + INT16 dummy; + + set_global_subloc_code(SUBLOC_NORM); + check_debug_pt (word, 30); + if (!word->done || + tessedit_training_tess || + tessedit_training_wiseowl || tessedit_dump_choices) { + word->x_height = row->x_height (); + word->caps_height = 0.0; + if (word->outword != NULL) { + delete word->outword; //get rid of junk + delete word->best_choice; + delete word->raw_choice; + } + match_word_pass2 (word, row, row->x_height ()); + done_this_pass = TRUE; + check_debug_pt (word, 40); + } + + if (!word->tess_failed && !word->word->flag (W_REP_CHAR)) { + set_global_subloc_code(SUBLOC_FIX_XHT); + if ((tessedit_xht_fiddles_on_done_wds || !word->done) && + (tessedit_xht_fiddles_on_no_rej_wds || + (word->reject_map.reject_count () > 0))) { + if ((x_ht_check_word_occ >= 2) && word_occ_first) + check_block_occ(word); + + if (tessedit_redo_xheight) + re_estimate_x_ht(word, &new_x_ht); + + if (((x_ht_check_word_occ >= 2) && !word_occ_first) || + ((x_ht_check_word_occ >= 1) && (new_x_ht > 0))) + check_block_occ(word); + } + if (new_x_ht > 0) { + old_chs_in_wd = word->reject_map.length (); + + /* Re-estimated x_ht error suggests a rematch is worthwhile. */ + new_x_ht_word.x_height = new_x_ht; + new_x_ht_word.caps_height = 0.0; + match_word_pass2 (&new_x_ht_word, row, new_x_ht_word.x_height); + if (!new_x_ht_word.tess_failed) { + if ((x_ht_check_word_occ >= 1) && word_occ_first) + check_block_occ(&new_x_ht_word); + + re_estimate_x_ht(&new_x_ht_word, &new_x_ht); + + if ((x_ht_check_word_occ >= 1) && !word_occ_first) + check_block_occ(&new_x_ht_word); + + old_xht_reject_count = word->reject_map.reject_count (); + old_xht_accept_count = old_chs_in_wd - old_xht_reject_count; + new_xht_reject_count = new_x_ht_word.reject_map.reject_count (); + new_chs_in_wd = new_x_ht_word.reject_map.length (); + new_xht_accept_count = new_chs_in_wd - new_xht_reject_count; + accept_new_x_ht = + ((new_xht_accept_count > old_xht_accept_count) || + ((new_xht_accept_count == old_xht_accept_count) && + (new_xht_accept_count > 0))) && + (!new_x_ht_word.guessed_x_ht || + !new_x_ht_word.guessed_caps_ht); + + if (accept_new_x_ht && x_ht_quality_check) { + word_char_quality(word, row, &old_word_quality, &dummy); + word_char_quality(&new_x_ht_word, row, &new_word_quality, &dummy); + if (old_word_quality > new_word_quality) + accept_new_x_ht = FALSE; + } + + if (accept_new_x_ht && (x_ht_stringency > 0)) { + accept_new_x_ht = + (count_alphanums (&new_x_ht_word) > x_ht_stringency); + if (!accept_new_x_ht && rej_use_xht) { + if (debug_x_ht_level >= 1) + tprintf + ("Failed stringency test so reject original word\n"); + word->reject_map.rej_word_xht_fixup (); + } + } + + #ifndef SECURE_NAMES + if (debug_x_ht_level >= 1) { + tprintf ("New XHT Match:: %s ", + word->best_choice->string ().string ()); + word->reject_map.print (debug_fp); + tprintf (" -> %s ", + new_x_ht_word.best_choice->string ().string ()); + new_x_ht_word.reject_map.print (debug_fp); + tprintf (" %s->%s %s %s\n", + word->guessed_x_ht ? "GUESS" : "CERT", + new_x_ht_word.guessed_x_ht ? "GUESS" : "CERT", + new_x_ht > 0.1 ? "STILL DOUBT" : "OK", + accept_new_x_ht ? "ACCEPTED" : ""); + } + #endif + } + if (accept_new_x_ht) { + /* + The new x_ht is deemed superior so put the final results in the real word + and destroy the old results + */ + delete word->outword; //get rid of junk + word->outword = new_x_ht_word.outword; + word->denorm = new_x_ht_word.denorm; + delete word->best_choice; + word->best_choice = new_x_ht_word.best_choice; + delete word->raw_choice; + word->raw_choice = new_x_ht_word.raw_choice; + word->reject_map = new_x_ht_word.reject_map; + word->done = new_x_ht_word.done; + done_this_pass = TRUE; + } + else { + /* + The new x_ht is no better, so destroy the copy word and put any uncertain + x or cap ht estimate back to default. (I.e. dont blame me if its bad!) + Conditionally, use any ammended block occ chars. + */ + //get rid of junk + delete new_x_ht_word.outword; + delete new_x_ht_word.best_choice; + delete new_x_ht_word.raw_choice; + } + //to keep new destructor happy + new_x_ht_word.outword = NULL; + //to keep new destructor happy + new_x_ht_word.best_choice = NULL; + //to keep new destructor happy + new_x_ht_word.raw_choice = NULL; + + if (rej_mostly_reject_mode == 2) { + reject_mostly_rejects(word); + tprintf ("Rejecting mostly rejects on %s ", + word->best_choice->string ().string ()); + } + } + + set_global_subloc_code(SUBLOC_NORM); + + if (done_this_pass && !word->done && tessedit_save_stats) + SaveBadWord (word->best_choice->string ().string (), + word->best_choice->certainty ()); + record_certainty (word->best_choice->certainty (), 2); + //accounting + } +#ifndef GRAPHICS_DISABLED + if (tessedit_draw_outwords) { + if (fx_win == NO_WINDOW) + create_fx_win(); + clear_fx_win(); + word->outword->plot (fx_win); + make_picture_current(fx_win); + } +#endif + + set_global_subloc_code(SUBLOC_NORM); + if (tessedit_print_text) { + write_cooked_text (word->outword, word->best_choice->string (), + word->done, done_this_pass, stdout); + } + check_debug_pt (word, 50); +} + + +/********************************************************************** + * match_word_pass2 + * + * Baseline normalize the word and pass it to Tess. + **********************************************************************/ + +void match_word_pass2( //recog one word + WERD_RES *word, //word to do + ROW *row, + float x_height) { + WERD *bln_word; //baseline norm copy + //detailed results + BLOB_CHOICE_LIST_CLIST blob_choices; + + set_global_subsubloc_code(SUBSUBLOC_OTHER); + if (matcher_fp != NULL) { + word_answer = (char *) word->word->text (); + if (word_answer != NULL && word_answer[0] == '\0') + word_answer = NULL; + } + matcher_pass = 0; + bln_word = make_bln_copy (word->word, row, x_height, &word->denorm); + set_global_subsubloc_code(SUBSUBLOC_TESS); + if (tessedit_training_tess) + word->best_choice = correct_segment_pass2 (bln_word, + &word->denorm, + tess_default_matcher, + tess_training_tester, + word->raw_choice, + &blob_choices, word->outword); + else if (tessedit_dump_choices) + word->best_choice = test_segment_pass2 (bln_word, + &word->denorm, + tess_default_matcher, + choice_dump_tester, + word->raw_choice, + &blob_choices, word->outword); + // else if (tessedit_training_wiseowl) + // best_choice=correct_segment_pass2( word, &denorm, + // tess_default_matcher,wo_learn, + // raw_choice,&blob_choices,outword); + // else if (tessedit_matcher_is_wiseowl) + // best_choice=tess_segment_pass2( word, &denorm, wo_classify, + // raw_choice, &blob_choices, outword); + else { + word->best_choice = tess_segment_pass2 (bln_word, &word->denorm, + tess_default_matcher, + word->raw_choice, &blob_choices, + word->outword); + } + set_global_subsubloc_code(SUBSUBLOC_OTHER); + /* + Test for TESS screw up on word. Recog_word has already ensured that the + choice list, outword blob lists and best_choice string are the same + length. A TESS screw up is indicated by a blank filled or 0 length string. + */ + if ((word->best_choice->string ().length () == 0) || + (strspn (word->best_choice->string ().string (), " ") == + word->best_choice->string ().length ())) { + word->tess_failed = TRUE; + word->reject_map.initialise (word->best_choice->string ().length ()); + word->reject_map.rej_word_tess_failure (); + // tprintf("Empty word produced\n"); + } + else { + if ((word->best_choice->string ().length () != + word->outword->blob_list ()->length ()) || + (word->best_choice->string ().length () != blob_choices.length ())) { + tprintf + ("ASSERT FAIL String:\"%s\"; Strlen=%d; #Blobs=%d; #Choices=%d\n", + word->best_choice->string ().string (), + word->best_choice->string ().length (), + word->outword->blob_list ()->length (), blob_choices.length ()); + } + ASSERT_HOST (word->best_choice->string ().length () == + word->outword->blob_list ()->length ()); + ASSERT_HOST (word->best_choice->string ().length () == + blob_choices.length ()); + + word->tess_failed = FALSE; + if (word->word->flag (W_REP_CHAR)) { + fix_rep_char(word); + } + else { + fix_quotes ((char *) word->best_choice->string ().string (), + word->outword, &blob_choices); + if (tessedit_fix_hyphens) + fix_hyphens ((char *) word->best_choice->string ().string (), + word->outword, &blob_choices); + /* Dont trust fix_quotes! - though I think I've fixed the bug */ + if ((word->best_choice->string ().length () != + word->outword->blob_list ()->length ()) || + (word->best_choice->string ().length () != + blob_choices.length ())) { + #ifndef SECURE_NAMES + tprintf + ("POST FIX_QUOTES FAIL String:\"%s\"; Strlen=%d; #Blobs=%d; #Choices=%d\n", + word->best_choice->string ().string (), + word->best_choice->string ().length (), + word->outword->blob_list ()->length (), + blob_choices.length ()); + #endif + + } + ASSERT_HOST (word->best_choice->string ().length () == + word->outword->blob_list ()->length ()); + ASSERT_HOST (word->best_choice->string ().length () == + blob_choices.length ()); + + word->tess_accepted = tess_acceptable_word (word->best_choice, + word->raw_choice); + + make_reject_map (word, &blob_choices, row, 2); + } + } + blob_choices.deep_clear (); + delete bln_word; + assert (word->raw_choice != NULL); +} + + +/************************************************************************* + * fix_rep_char() + * The word is a repeated char. Find the repeated char character. Make a reject + * string which rejects any char other than the voted char. Set the word to done + * to stop rematching it. + * + *************************************************************************/ +void fix_rep_char( //Repeated char word + WERD_RES *word //word to do + ) { + struct REP_CH + { + char ch; + int count; + }; + + REP_CH *rep_ch; //array of char counts + int word_len; + int rep_ch_count = 0; //how many unique chs + const char *word_str; //the repeated chs + int i, j; + int total = 0; + int max = 0; + char maxch = ' '; //Most common char + + word_str = word->best_choice->string ().string (); + word_len = strlen (word_str); + rep_ch = (REP_CH *) alloc_mem (word_len * sizeof (REP_CH)); + for (i = 0; i < word_len; i++) { + for (j = 0; j < rep_ch_count && rep_ch[j].ch != word_str[i]; j++); + if (j < rep_ch_count) + rep_ch[j].count++; + else { + rep_ch[rep_ch_count].ch = word_str[i]; + rep_ch[rep_ch_count].count = 1; + rep_ch_count++; + } + } + + for (j = 0; j < rep_ch_count; j++) { + total += rep_ch[j].count; + if ((rep_ch[j].count > max) && (rep_ch[j].ch != ' ')) { + max = rep_ch[j].count; + maxch = rep_ch[j].ch; + } + } + // tprintf( "REPEATED CHAR %s len=%d total=%d choice=%c\n", + // word_str, word_len, total, maxch ); + free_mem(rep_ch); + + word->reject_map.initialise (word_len); + for (i = 0; i < word_len; i++) { + if (word_str[i] != maxch) + //rej unrecognised blobs + word->reject_map[i].setrej_bad_repetition (); + } + word->done = TRUE; +} + + +/********************************************************************** + * fix_quotes + * + * Change pairs of quotes to double quotes. + **********************************************************************/ + +void fix_quotes( //make double quotes + char *string, //string to fix + WERD *word, //word to do //char choices + BLOB_CHOICE_LIST_CLIST *blob_choices) { + char *ptr; //string ptr + //blobs + PBLOB_IT blob_it = word->blob_list (); + //choices + BLOB_CHOICE_LIST_C_IT choice_it = blob_choices; + BLOB_CHOICE_IT it1; //first choices + BLOB_CHOICE_IT it2; //second choices + + for (ptr = string; + *ptr != '\0'; ptr++, blob_it.forward (), choice_it.forward ()) { + if ((*ptr == '\'' || *ptr == '`') + && (*(ptr + 1) == '\'' || *(ptr + 1) == '`')) { + *ptr = '"'; //turn to double + strcpy (ptr + 1, ptr + 2); //shuffle up + merge_blobs (blob_it.data (), blob_it.data_relative (1)); + blob_it.forward (); + delete blob_it.extract (); //get rid of spare + + it1.set_to_list (choice_it.data ()); + it2.set_to_list (choice_it.data_relative (1)); + if (it1.data ()->certainty () < it2.data ()->certainty ()) { + choice_it.forward (); + //get rid of spare + delete choice_it.extract (); + } + else { + //get rid of spare + delete choice_it.extract (); + choice_it.forward (); + } + } + } +} + + +/********************************************************************** + * fix_hyphens + * + * Change pairs of hyphens to a single hyphen if the bounding boxes touch + * Typically a long dash which has been segmented. + **********************************************************************/ + +void fix_hyphens( //crunch double hyphens + char *string, //string to fix + WERD *word, //word to do //char choices + BLOB_CHOICE_LIST_CLIST *blob_choices) { + char *ptr; //string ptr + //blobs + PBLOB_IT blob_it = word->blob_list (); + //choices + BLOB_CHOICE_LIST_C_IT choice_it = blob_choices; + BLOB_CHOICE_IT it1; //first choices + BLOB_CHOICE_IT it2; //second choices + + for (ptr = string; + *ptr != '\0'; ptr++, blob_it.forward (), choice_it.forward ()) { + if ((*ptr == '-' || *ptr == '~') && + (*(ptr + 1) == '-' || *(ptr + 1) == '~') && + (blob_it.data ()->bounding_box ().right () >= + blob_it.data_relative (1)->bounding_box ().left ())) { + *ptr = '-'; //turn to single hyphen + strcpy (ptr + 1, ptr + 2); //shuffle up + merge_blobs (blob_it.data (), blob_it.data_relative (1)); + blob_it.forward (); + delete blob_it.extract (); //get rid of spare + + it1.set_to_list (choice_it.data ()); + it2.set_to_list (choice_it.data_relative (1)); + if (it1.data ()->certainty () < it2.data ()->certainty ()) { + choice_it.forward (); + //get rid of spare + delete choice_it.extract (); + } + else { + //get rid of spare + delete choice_it.extract (); + choice_it.forward (); + } + } + } +} + + +/********************************************************************** + * merge_blobs + * + * Add the outlines from blob2 to blob1. Blob2 is emptied but not deleted. + **********************************************************************/ + +void merge_blobs( //combine 2 blobs + PBLOB *blob1, //dest blob + PBLOB *blob2 //source blob + ) { + OUTLINE_IT outline_it = blob1->out_list (); + //iterator + + outline_it.move_to_last (); //go to end + //do it + outline_it.add_list_after (blob2->out_list ()); +} + + +/********************************************************************** + * choice_dump_tester + * + * Matcher tester function which generates .chc file entries. + * Called via test_segment_pass2 for every blob tested by tess in a word. + * (But only for words for which a correct segmentation could be found.) + **********************************************************************/ + +void choice_dump_tester( //dump chars in word + PBLOB *, //blob + DENORM *, //de-normaliser + BOOL8 correct, //ly segmented + char *text, //correct text + INT32 count, //chars in text + BLOB_CHOICE_LIST *ratings //list of results + ) { + STRING choice_file_name; + BLOB_CHOICE *blob_choice; + BLOB_CHOICE_IT it; + char source_chars[20]; + char correct_char[3]; + + if (choice_file == NULL) { + choice_file_name = imagebasename + ".chc"; + if (!(choice_file = fopen (choice_file_name.string (), "w"))) { + CANTOPENFILE.error ("choice_dump_tester", EXIT, "%s %d", + choice_file_name.string (), errno); + } + } + + if ((count == 0) || (text == NULL) || (text[0] == '\0')) { + strcpy (source_chars, "$$"); + strcpy (correct_char, "$$"); + } + else { + strncpy(source_chars, text, count); + source_chars[count] = '\0'; + if (correct) { + correct_char[0] = text[0]; + correct_char[1] = '\0'; + } + else { + strcpy (correct_char, "$$"); + } + } + fprintf (choice_file, "%s\t%s", source_chars, correct_char); + + it.set_to_list (ratings); + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + blob_choice = it.data (); + if ((blob_choice->char_class () >= '!') && + (blob_choice->char_class () <= '~')) + fprintf (choice_file, "\t%c\t%f\t%f", + blob_choice->char_class (), + blob_choice->rating (), blob_choice->certainty ()); + } + fprintf (choice_file, "\n"); +} + + +/************************************************************************* + * make_bln_copy() + * + * Generate a baseline normalised copy of the source word. The copy is done so + * that whatever format the original word is in, a polygonal bln version is + * generated as output. + *************************************************************************/ + +WERD *make_bln_copy(WERD *src_word, ROW *row, float x_height, DENORM *denorm) { + WERD *result; + + // if (wordit_linearc && !src_word->flag(W_POLYGON)) + // { + // larc_word = src_word->larc_copy( row->x_height() ); + // result = larc_word->poly_copy( row->x_height() ); + // delete larc_word; + // } + // else + result = src_word->poly_copy (row->x_height ()); + + // if (tessedit_draw_words) + // { + // if ( la_win == NO_WINDOW ) + // create_la_win(); + // result->plot( la_win ); + // } + result->baseline_normalise_x (row, x_height, denorm); + return result; +} + + +ACCEPTABLE_WERD_TYPE acceptable_word_string(const char *s) { + int i = 0; + int leading_punct_count; + int upper_count = 0; + int hyphen_pos = -1; + ACCEPTABLE_WERD_TYPE word_type = AC_UNACCEPTABLE; + + if (strlen (s) > 20) + return word_type; + + /* Single Leading punctuation char*/ + + if ((s[i] != '\0') && (STRING (chs_leading_punct).contains (s[i]))) + i++; + leading_punct_count = i; + + /* Initial cap */ + while (isupper (s[i])) { + i++; + upper_count++; + } + if (upper_count > 1) + word_type = AC_UPPER_CASE; + else { + /* Lower case word, possibly with an initial cap */ + while (islower (s[i])) { + i++; + } + if (i - leading_punct_count < quality_min_initial_alphas_reqd) + goto not_a_word; + /* + Allow a single hyphen in a lower case word + - dont trust upper case - I've seen several cases of "H" -> "I-I" + */ + if (s[i] == '-') { + hyphen_pos = i++; + if (s[i] != '\0') { + while (islower (s[i])) { + i++; + } + if (i < hyphen_pos + 3) + goto not_a_word; + } + } + else { + /* Allow "'s" in NON hyphenated lower case words */ + if ((s[i] == '\'') && (s[i + 1] == 's')) + i += 2; + } + if (upper_count > 0) + word_type = AC_INITIAL_CAP; + else + word_type = AC_LOWER_CASE; + } + + /* Up to two different, constrained trailing punctuation chars */ + if ((s[i] != '\0') && (STRING (chs_trailing_punct1).contains (s[i]))) + i++; + if ((s[i] != '\0') && + (s[i - 1] != s[i]) && (STRING (chs_trailing_punct2).contains (s[i]))) + i++; + + if (s[i] != '\0') + word_type = AC_UNACCEPTABLE; + + not_a_word: + + if (word_type == AC_UNACCEPTABLE) { + /* Look for abbreviation string */ + i = 0; + if (isupper (s[0])) { + word_type = AC_UC_ABBREV; + while ((s[i] != '\0') && isupper (s[i]) && (s[i + 1] == '.')) + i += 2; + } + else if (islower (s[0])) { + word_type = AC_LC_ABBREV; + while ((s[i] != '\0') && islower (s[i]) && (s[i + 1] == '.')) + i += 2; + } + if (s[i] != '\0') + word_type = AC_UNACCEPTABLE; + } + + return word_type; +} + + +/* DEBUGGING ROUTINE */ + +BOOL8 check_debug_pt(WERD_RES *word, int location) { + BOOL8 show_map_detail = FALSE; + INT16 i; + + #ifndef SECURE_NAMES + if (!test_pt) + return FALSE; + + tessedit_rejection_debug.set_value (FALSE); + debug_x_ht_level.set_value (0); + tessedit_cluster_debug.set_value (FALSE); + nn_debug.set_value (FALSE); + nn_reject_debug.set_value (FALSE); + + 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 (20); + tessedit_cluster_debug.set_value (TRUE); + nn_debug.set_value (TRUE); + nn_reject_debug.set_value (TRUE); + tprintf ("\n\nTESTWD::"); + switch (location) { + case 0: + tprintf ("classify_word_pass1 start\n"); + word->word->print (debug_fp); + 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; + } + tprintf (" \"%s\" ", word->best_choice->string ().string ()); + word->reject_map.print (debug_fp); + tprintf ("\n"); + if (show_map_detail) { + tprintf ("\"%s\"\n", word->best_choice->string ().string ()); + for (i = 0; word->best_choice->string ()[i] != '\0'; i++) { + tprintf ("**** \"%c\" ****\n", word->best_choice->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"); + return TRUE; + } + else + #endif + return FALSE; +} + + +/********************************************************************** + * set_word_fonts + * + * Get the fonts for the word. + **********************************************************************/ + +void set_word_fonts( //good chars in word + WERD_RES *word, //word to adapt to //detailed results + BLOB_CHOICE_LIST_CLIST *blob_choices) { + INT32 index; //char index + char choice_char; //char from word + INT8 config; //font of char + //character iterator + BLOB_CHOICE_LIST_C_IT char_it = blob_choices; + BLOB_CHOICE_IT choice_it; //choice iterator + STATS fonts (0, 32); //font counters + static INT8 italic_table[32] = { + 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 + }; + static INT8 bold_table[32] = { + 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 + }; + static INT8 font_table[32] = { + 2, 2, 2, 2, + -1, -1, -1, -1, + 0, 0, 0, 0, + 1, 1, 1, 1, + 3, 3, 3, 3, + 4, 4, 4, 4, + 5, 5, 5, 5, + 2, 2, 2, 2 + }; + + word->italic = 0; + word->bold = 0; + for (char_it.mark_cycle_pt (), index = 0; + !char_it.cycled_list (); char_it.forward (), index++) { + choice_char = word->best_choice->string ()[index]; + choice_it.set_to_list (char_it.data ()); + for (choice_it.mark_cycle_pt (); !choice_it.cycled_list (); + choice_it.forward ()) { + if (choice_it.data ()->char_class () == choice_char) { + config = choice_it.data ()->config (); + if (tessedit_debug_fonts) + tprintf ("%c(%d=%d%c%c)", + choice_char, config, (config & 31) >> 2, + config & 2 ? 'N' : 'B', config & 1 ? 'N' : 'I'); + if (config != -1) { + config &= 31; + word->italic += italic_table[config]; + word->bold += bold_table[config]; + if (font_table[config] != -1) + fonts.add (font_table[config], 1); + } + break; + } + } + } + find_modal_font (&fonts, &word->font1, &word->font1_count); + find_modal_font (&fonts, &word->font2, &word->font2_count); + if (tessedit_debug_fonts) + tprintf ("\n"); + /* if (word->font1_count>0) + { + for (char_it.mark_cycle_pt(),index=0; + !char_it.cycled_list();char_it.forward(),index++) + { + choice_char=word->best_choice->string()[index]; + choice_it.set_to_list(char_it.data()); + for (choice_it.mark_cycle_pt();!choice_it.cycled_list();choice_it.forward()) + { + if (choice_it.data()->char_class()==choice_char) + { + config=choice_it.data()->config(); + if (config!=-1 && font_table[config&31]==word->font1) + { + word->italic+=italic_table[config]; + word->bold+=bold_table[config]; + } + break; + } + } + } + }*/ +} + + +/********************************************************************** + * font_recognition_pass + * + * Smooth the fonts for the document. + **********************************************************************/ + +void font_recognition_pass( //good chars in word + PAGE_RES_IT &page_res_it) { + INT32 length; //of word + INT32 count; //of a feature + INT8 doc_font; //modal font + INT8 doc_font_count; //modal font + INT32 doc_italic; //total italics + INT32 doc_bold; //total bolds + ROW_RES *row = NULL; //current row + WERD_RES *word; //current word + STATS fonts (0, 32); //font counters + STATS doc_fonts (0, 32); //font counters + + doc_italic = 0; + doc_bold = 0; + page_res_it.restart_page (); + while (page_res_it.word () != NULL) { + if (row != page_res_it.row ()) { + if (row != NULL) { + find_modal_font (&fonts, &row->font1, &row->font1_count); + find_modal_font (&fonts, &row->font2, &row->font2_count); + } + row = page_res_it.row (); //current row + fonts.clear (); //clear counters + row->italic = 0; + row->bold = 0; + } + word = page_res_it.word (); + row->italic += word->italic; + row->bold += word->bold; + fonts.add (word->font1, word->font1_count); + fonts.add (word->font2, word->font2_count); + doc_italic += word->italic; + doc_bold += word->bold; + doc_fonts.add (word->font1, word->font1_count); + doc_fonts.add (word->font2, word->font2_count); + page_res_it.forward (); + } + if (row != NULL) { + find_modal_font (&fonts, &row->font1, &row->font1_count); + find_modal_font (&fonts, &row->font2, &row->font2_count); + } + find_modal_font(&doc_fonts, &doc_font, &doc_font_count); + /* + row=NULL; + page_res_it.restart_page(); + while (page_res_it.word() != NULL) + { + if (row!=page_res_it.row()) + { + row2=row; + row=page_res_it.row(); + if (row->font1_countrow->x_height()-row2->row->x_height(); + if (hdiff<0) + hdiff=-hdiff; + if (hdiffrow->x_height()-row2->row->x_height(); + if (hdiff<0) + hdiff=-hdiff; + if (hdiffitalic=italic; + row->bold=bold; + find_modal_font(&fonts,&row->font1,&row->font1_count); + find_modal_font(&fonts,&row->font2,&row->font2_count); + } + else + page_res_it.forward(); + } + else + page_res_it.forward(); + }*/ + + page_res_it.restart_page (); + while (page_res_it.word () != NULL) { + row = page_res_it.row (); //current row + word = page_res_it.word (); + length = word->best_choice->string ().length (); + + count = word->italic; + if (count < 0) + count = -count; + if (!(count == length || length > 3 && count >= length * 3 / 4)) + word->italic = doc_italic > 0 ? 1 : -1; + + count = word->bold; + if (count < 0) + count = -count; + if (!(count == length || length > 3 && count >= length * 3 / 4)) + word->bold = doc_bold > 0 ? 1 : -1; + + count = word->font1_count; + if (!(count == length || length > 3 && count >= length * 3 / 4)) { + word->font1 = doc_font; + word->font1_count = doc_font_count; + } + + page_res_it.forward (); + } +} + + +/********************************************************************** + * add_in_one_row + * + * Add into the stats for one row. + **********************************************************************/ + +void add_in_one_row( //good chars in word + ROW_RES *row, //current row + STATS *fonts, //font stats + INT8 *italic, //output count + INT8 *bold //output count + ) { + WERD_RES *word; //current word + WERD_RES_IT word_it = &row->word_res_list; + + for (word_it.mark_cycle_pt (); !word_it.cycled_list (); word_it.forward ()) { + word = word_it.data (); + *italic += word->italic; + *bold += word->bold; + if (word->font1_count > 0) + fonts->add (word->font1, word->font1_count); + if (word->font2_count > 0) + fonts->add (word->font2, word->font2_count); + + } +} + + +/********************************************************************** + * find_modal_font + * + * Find the modal font and remove from the stats. + **********************************************************************/ + +void find_modal_font( //good chars in word + STATS *fonts, //font stats + INT8 *font_out, //output font + INT8 *font_count //output count + ) { + INT8 font; //font index + INT32 count; //pile couat + + if (fonts->get_total () > 0) { + font = (INT8) 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; + } +} diff --git a/ccmain/control.h b/ccmain/control.h new file mode 100644 index 0000000000..a660a91ea3 --- /dev/null +++ b/ccmain/control.h @@ -0,0 +1,193 @@ +/********************************************************************** + * 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. + * + **********************************************************************/ + +#ifndef CONTROL_H +#define CONTROL_H + +#include "varable.h" +#include "ocrblock.h" +//#include "epapdest.h" +#include "ratngs.h" +#include "statistc.h" +//#include "epapconv.h" +#include "ocrshell.h" +#include "pageres.h" +#include "charsample.h" +#include "notdll.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. +}; + +typedef BOOL8 (*BLOB_REJECTOR) (PBLOB *, BLOB_CHOICE_IT *, void *); + +extern INT_VAR_H (tessedit_single_match, FALSE, "Top choice only from CP"); +//extern BOOL_VAR_H(tessedit_small_match,FALSE,"Use small matrix matcher"); +extern BOOL_VAR_H (tessedit_print_text, FALSE, "Write text to stdout"); +extern BOOL_VAR_H (tessedit_draw_words, FALSE, "Draw source words"); +extern BOOL_VAR_H (tessedit_draw_outwords, FALSE, "Draw output words"); +extern BOOL_VAR_H (tessedit_training_wiseowl, FALSE, +"Call WO to learn blobs"); +extern BOOL_VAR_H (tessedit_training_tess, FALSE, "Call Tess to learn blobs"); +extern BOOL_VAR_H (tessedit_matcher_is_wiseowl, FALSE, "Call WO to classify"); +extern BOOL_VAR_H (tessedit_dump_choices, FALSE, "Dump char choices"); +extern BOOL_VAR_H (tessedit_fix_fuzzy_spaces, TRUE, +"Try to improve fuzzy spaces"); +extern BOOL_VAR_H (tessedit_unrej_any_wd, FALSE, +"Dont bother with word plausibility"); +extern BOOL_VAR_H (tessedit_fix_hyphens, TRUE, "Crunch double hyphens?"); +extern BOOL_VAR_H (tessedit_reject_fullstops, FALSE, "Reject all fullstops"); +extern BOOL_VAR_H (tessedit_reject_suspect_fullstops, FALSE, +"Reject suspect fullstops"); +extern BOOL_VAR_H (tessedit_redo_xheight, TRUE, "Check/Correct x-height"); +extern BOOL_VAR_H (tessedit_cluster_adaption_on, TRUE, +"Do our own adaption - ems only"); +extern BOOL_VAR_H (tessedit_enable_doc_dict, TRUE, +"Add words to the document dictionary"); +extern BOOL_VAR_H (word_occ_first, FALSE, "Do word occ before re-est xht"); +extern BOOL_VAR_H (tessedit_xht_fiddles_on_done_wds, TRUE, +"Apply xht fix up even if done"); +extern BOOL_VAR_H (tessedit_xht_fiddles_on_no_rej_wds, TRUE, +"Apply xht fix up even in no rejects"); +extern INT_VAR_H (x_ht_check_word_occ, 2, "Check Char Block occupancy"); +extern INT_VAR_H (x_ht_stringency, 1, "How many confirmed a/n to accept?"); +extern BOOL_VAR_H (x_ht_quality_check, TRUE, "Dont allow worse quality"); +extern BOOL_VAR_H (tessedit_debug_block_rejection, FALSE, +"Block and Row stats"); +extern INT_VAR_H (debug_x_ht_level, 0, "Reestimate debug"); +extern BOOL_VAR_H (rej_use_xht, TRUE, "Individual rejection control"); +extern BOOL_VAR_H (debug_acceptable_wds, FALSE, "Dump word pass/fail chk"); +extern STRING_VAR_H (chs_leading_punct, "('`\"", "Leading punctuation"); +extern +STRING_VAR_H (chs_trailing_punct1, ").,;:?!", "1st Trailing punctuation"); +extern STRING_VAR_H (chs_trailing_punct2, ")'`\"", +"2nd Trailing punctuation"); +extern double_VAR_H (quality_rej_pc, 0.08, +"good_quality_doc lte rejection limit"); +extern double_VAR_H (quality_blob_pc, 0.0, +"good_quality_doc gte good blobs limit"); +extern double_VAR_H (quality_outline_pc, 1.0, +"good_quality_doc lte outline error limit"); +extern double_VAR_H (quality_char_pc, 0.95, +"good_quality_doc gte good char limit"); +extern INT_VAR_H (quality_min_initial_alphas_reqd, 2, +"alphas in a good word"); +extern BOOL_VAR_H (tessedit_tess_adapt_to_rejmap, FALSE, +"Use reject map to control Tesseract adaption"); +extern INT_VAR_H (tessedit_tess_adaption_mode, 3, +"Adaptation decision algorithm for tess"); +extern INT_VAR_H (tessedit_em_adaption_mode, 62, +"Adaptation decision algorithm for ems matrix matcher"); +extern BOOL_VAR_H (tessedit_cluster_adapt_after_pass1, FALSE, +"Adapt using clusterer after pass 1"); +extern BOOL_VAR_H (tessedit_cluster_adapt_after_pass2, FALSE, +"Adapt using clusterer after pass 1"); +extern BOOL_VAR_H (tessedit_cluster_adapt_after_pass3, FALSE, +"Adapt using clusterer after pass 1"); +extern BOOL_VAR_H (tessedit_cluster_adapt_before_pass1, FALSE, +"Adapt using clusterer before Tess adaping during pass 1"); +extern INT_VAR_H (tessedit_cluster_adaption_mode, 0, +"Adaptation decision algorithm for matrix matcher"); +extern BOOL_VAR_H (tessedit_adaption_debug, FALSE, +"Generate and print debug information for adaption"); +extern BOOL_VAR_H (tessedit_minimal_rej_pass1, FALSE, +"Do minimal rejection on pass 1 output"); +extern BOOL_VAR_H (tessedit_test_adaption, FALSE, +"Test adaption criteria"); +extern BOOL_VAR_H (tessedit_global_adaption, FALSE, +"Adapt to all docs over time"); +extern BOOL_VAR_H (tessedit_matcher_log, FALSE, "Log matcher activity"); +extern INT_VAR_H (tessedit_test_adaption_mode, 3, +"Adaptation decision algorithm for tess"); +extern BOOL_VAR_H (test_pt, FALSE, "Test for point"); +extern double_VAR_H (test_pt_x, 99999.99, "xcoord"); +extern double_VAR_H (test_pt_y, 99999.99, "ycoord"); +void recog_pseudo_word( //recognize blobs + BLOCK_LIST *block_list, //blocks to check + BOX &selection_box); +BOOL8 recog_interactive( //recognize blobs + BLOCK *, //block + ROW *row, //row of word + WERD *word //word to recognize + ); +void recog_all_words( //process words + PAGE_RES *page_res, //page structure + volatile ETEXT_DESC *monitor //progress monitor + ); +void classify_word_pass1( //recog one word + WERD_RES *word, //word to do + ROW *row, + BOOL8 cluster_adapt, + CHAR_SAMPLES_LIST *char_clusters, + CHAR_SAMPLE_LIST *chars_waiting); + //word to do +void classify_word_pass2(WERD_RES *word, ROW *row); +void match_word_pass2( //recog one word + WERD_RES *word, //word to do + ROW *row, + float x_height); +void fix_rep_char( //Repeated char word + WERD_RES *word //word to do + ); +void fix_quotes( //make double quotes + char *string, //string to fix + WERD *word, //word to do //char choices + BLOB_CHOICE_LIST_CLIST *blob_choices); +void fix_hyphens( //crunch double hyphens + char *string, //string to fix + WERD *word, //word to do //char choices + BLOB_CHOICE_LIST_CLIST *blob_choices); +void merge_blobs( //combine 2 blobs + PBLOB *blob1, //dest blob + PBLOB *blob2 //source blob + ); +void choice_dump_tester( //dump chars in word + PBLOB *, //blob + DENORM *, //de-normaliser + BOOL8 correct, //ly segmented + char *text, //correct text + INT32 count, //chars in text + BLOB_CHOICE_LIST *ratings //list of results + ); +WERD *make_bln_copy(WERD *src_word, ROW *row, float x_height, DENORM *denorm); +ACCEPTABLE_WERD_TYPE acceptable_word_string(const char *s); +BOOL8 check_debug_pt(WERD_RES *word, int location); +void set_word_fonts( //good chars in word + WERD_RES *word, //word to adapt to //detailed results + BLOB_CHOICE_LIST_CLIST *blob_choices); +void font_recognition_pass( //good chars in word + PAGE_RES_IT &page_res_it); +void add_in_one_row( //good chars in word + ROW_RES *row, //current row + STATS *fonts, //font stats + INT8 *italic, //output count + INT8 *bold //output count + ); +void find_modal_font( //good chars in word + STATS *fonts, //font stats + INT8 *font_out, //output font + INT8 *font_count //output count + ); +#endif diff --git a/ccmain/docqual.cpp b/ccmain/docqual.cpp new file mode 100644 index 0000000000..a889ca4ca5 --- /dev/null +++ b/ccmain/docqual.cpp @@ -0,0 +1,1453 @@ +/****************************************************************** + * 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. + * + **********************************************************************/ + +#include "mfcpch.h" +#include +#include "docqual.h" +#include "tstruct.h" +#include "tfacep.h" +#include "reject.h" +#include "tessvars.h" +#include "genblob.h" +#include "secname.h" + +#define EXTERN + +EXTERN STRING_VAR (outlines_odd, "%| ", "Non standard number of outlines"); +EXTERN STRING_VAR (outlines_2, "ij!?%\":;", +"Non standard number of outlines"); +EXTERN BOOL_VAR (docqual_excuse_outline_errs, FALSE, +"Allow outline errs in unrejection?"); +EXTERN BOOL_VAR (tessedit_good_quality_unrej, TRUE, +"Reduce rejection on good docs"); +EXTERN BOOL_VAR (tessedit_use_reject_spaces, TRUE, "Reject spaces?"); +EXTERN double_VAR (tessedit_reject_doc_percent, 65.00, +"%rej allowed before rej whole doc"); +EXTERN double_VAR (tessedit_reject_block_percent, 45.00, +"%rej allowed before rej whole block"); +EXTERN double_VAR (tessedit_reject_row_percent, 40.00, +"%rej allowed before rej whole row"); +EXTERN double_VAR (tessedit_whole_wd_rej_row_percent, 70.00, +"%of row rejects in whole word rejects which prevents whole row rejection"); +EXTERN BOOL_VAR (tessedit_preserve_blk_rej_perfect_wds, TRUE, +"Only rej partially rejected words in block rejection"); +EXTERN BOOL_VAR (tessedit_preserve_row_rej_perfect_wds, TRUE, +"Only rej partially rejected words in row rejection"); +EXTERN BOOL_VAR (tessedit_dont_blkrej_good_wds, FALSE, +"Use word segmentation quality metric"); +EXTERN BOOL_VAR (tessedit_dont_rowrej_good_wds, FALSE, +"Use word segmentation quality metric"); +EXTERN INT_VAR (tessedit_preserve_min_wd_len, 2, +"Only preserve wds longer than this"); +EXTERN BOOL_VAR (tessedit_row_rej_good_docs, TRUE, +"Apply row rejection to good docs"); +EXTERN double_VAR (tessedit_good_doc_still_rowrej_wd, 1.1, +"rej good doc wd if more than this fraction rejected"); +EXTERN BOOL_VAR (tessedit_reject_bad_qual_wds, TRUE, +"Reject all bad quality wds"); +EXTERN BOOL_VAR (tessedit_debug_doc_rejection, FALSE, "Page stats"); +EXTERN BOOL_VAR (tessedit_debug_quality_metrics, FALSE, +"Output data to debug file"); +EXTERN BOOL_VAR (bland_unrej, FALSE, "unrej potential with no chekcs"); +EXTERN double_VAR (quality_rowrej_pc, 1.1, +"good_quality_doc gte good char limit"); + +EXTERN BOOL_VAR (unlv_tilde_crunching, TRUE, +"Mark v.bad words for tilde crunch"); +EXTERN BOOL_VAR (crunch_early_merge_tess_fails, TRUE, "Before word crunch?"); +EXTERN BOOL_EVAR (crunch_early_convert_bad_unlv_chs, FALSE, +"Take out ~^ early?"); + +EXTERN double_VAR (crunch_terrible_rating, 80.0, "crunch rating lt this"); +EXTERN BOOL_VAR (crunch_terrible_garbage, TRUE, "As it says"); +EXTERN double_VAR (crunch_poor_garbage_cert, -9.0, +"crunch garbage cert lt this"); +EXTERN double_VAR (crunch_poor_garbage_rate, 60, +"crunch garbage rating lt this"); + +EXTERN double_VAR (crunch_pot_poor_rate, 40, +"POTENTIAL crunch rating lt this"); +EXTERN double_VAR (crunch_pot_poor_cert, -8.0, +"POTENTIAL crunch cert lt this"); +EXTERN BOOL_VAR (crunch_pot_garbage, TRUE, "POTENTIAL crunch garbage"); + +EXTERN double_VAR (crunch_del_rating, 60, "POTENTIAL crunch rating lt this"); +EXTERN double_VAR (crunch_del_cert, -10.0, "POTENTIAL crunch cert lt this"); +EXTERN double_VAR (crunch_del_min_ht, 0.7, "Del if word ht lt xht x this"); +EXTERN double_VAR (crunch_del_max_ht, 3.0, "Del if word ht gt xht x this"); +EXTERN double_VAR (crunch_del_min_width, 3.0, +"Del if word width lt xht x this"); +EXTERN double_VAR (crunch_del_high_word, 1.5, +"Del if word gt xht x this above bl"); +EXTERN double_VAR (crunch_del_low_word, 0.5, +"Del if word gt xht x this below bl"); +EXTERN double_VAR (crunch_small_outlines_size, 0.6, "Small if lt xht x this"); + +EXTERN INT_VAR (crunch_rating_max, 10, "For adj length in rating per ch"); +EXTERN INT_VAR (crunch_pot_indicators, 1, +"How many potential indicators needed"); + +EXTERN BOOL_VAR (crunch_leave_ok_strings, TRUE, +"Dont touch sensible strings"); +EXTERN BOOL_VAR (crunch_accept_ok, TRUE, "Use acceptability in okstring"); +EXTERN BOOL_VAR (crunch_leave_accept_strings, FALSE, +"Dont pot crunch sensible strings"); +EXTERN BOOL_VAR (crunch_include_numerals, FALSE, "Fiddle alpha figures"); +EXTERN INT_VAR (crunch_leave_lc_strings, 4, +"Dont crunch words with long lower case strings"); +EXTERN INT_VAR (crunch_leave_uc_strings, 4, +"Dont crunch words with long lower case strings"); +EXTERN INT_VAR (crunch_long_repetitions, 3, +"Crunch words with long repetitions"); + +EXTERN INT_VAR (crunch_debug, 0, "As it says"); + +/************************************************************************* + * word_blob_quality() + * How many blobs in the outword are identical to those of the inword? + * ASSUME blobs in both initial word and outword are in ascending order of + * left hand blob edge. + *************************************************************************/ +INT16 word_blob_quality( //Blob seg changes + WERD_RES *word, + ROW *row) { + WERD *bln_word; //BL norm init word + TWERD *tessword; //tess format + WERD *init_word; //BL norm init word + PBLOB_IT outword_it; + PBLOB_IT initial_it; + INT16 i; + INT16 init_blobs_left; + INT16 match_count = 0; + BOOL8 matched; + BOX out_box; + PBLOB *test_blob; + DENORM denorm; + float bln_xht; + + if (word->word->gblob_list ()->empty ()) + return 0; + //xht used for blnorm + bln_xht = bln_x_height / word->denorm.scale (); + bln_word = make_bln_copy (word->word, row, bln_xht, &denorm); + /* + NOTE: Need to convert to tess format and back again to ensure that the + same float -> int rounding of coords is done to source wd as out wd before + comparison + */ + // if (!bln_word->flag(W_POLYGON)) + // tprintf( "NON POLYGON BLN WERD\n"); + tessword = make_tess_word (bln_word, NULL); + //convert word + init_word = make_ed_word (tessword, bln_word); + // if (!init_word->flag(W_POLYGON)) + // tprintf( "NON POLYGON INIT WERD\n"); + // tprintf( "SOURCE BLOBS-AFTER TESS:\n"); + // print_boxes( init_word ); + // tprintf( "OUTPUT BLOBS:\n"); + // print_boxes( word->outword ); + + initial_it.set_to_list (init_word->blob_list ()); + init_blobs_left = initial_it.length (); + outword_it.set_to_list (word->outword->blob_list ()); + delete bln_word; + delete_word(tessword); //get rid of it + + for (outword_it.mark_cycle_pt (); + !outword_it.cycled_list (); outword_it.forward ()) { + out_box = outword_it.data ()->bounding_box (); + + /* Skip any initial blobs LEFT of current outword blob */ + while (!initial_it.at_last () && + (initial_it.data ()->bounding_box ().left () < out_box.left ())) { + initial_it.forward (); + init_blobs_left--; + } + + /* See if current outword blob matches any initial blob with the same left + coord. (Normally only one but possibly more - in unknown order) */ + + i = 0; + matched = FALSE; + do { + test_blob = initial_it.data_relative (i++); + matched = crude_match_blobs (test_blob, outword_it.data ()); + if (matched) + match_count++; + } + while (!matched && + (init_blobs_left - i > 0) && + (i < 129) && + !initial_it.at_last () && + test_blob->bounding_box ().left () == out_box.left ()); + } + delete init_word; + return match_count; +} + + +/************************************************************************* + * crude_match_blobs() + * Check bounding boxes are the same and the number of outlines are the same. + *************************************************************************/ +BOOL8 crude_match_blobs(PBLOB *blob1, PBLOB *blob2) { + BOX box1 = blob1->bounding_box (); + BOX box2 = blob2->bounding_box (); + + if (box1.contains (box2) && + box2.contains (box1) && + (blob1->out_list ()->length () == blob1->out_list ()->length ())) + return TRUE; + else + return FALSE; +} + + +INT16 word_outline_errs( //Outline count errs + WERD_RES *word) { + PBLOB_IT outword_it; + INT16 i = 0; + INT16 err_count = 0; + + outword_it.set_to_list (word->outword->blob_list ()); + + for (outword_it.mark_cycle_pt (); + !outword_it.cycled_list (); outword_it.forward ()) { + err_count += count_outline_errs (word->best_choice->string ()[i], + outword_it.data ()->out_list ()-> + length ()); + 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 word_char_quality( //Blob seg changes + WERD_RES *word, + ROW *row, + INT16 *match_count, + INT16 *accepted_match_count) { + WERD *bln_word; //BL norm init word + TWERD *tessword; //tess format + WERD *init_word; //BL norm init word + PBLOB_IT outword_it; + PBLOB_IT initial_it; + INT16 i; + INT16 init_blobs_left; + BOOL8 matched; + BOX out_box; + PBLOB *test_blob; + DENORM denorm; + float bln_xht; + INT16 j = 0; + + *match_count = 0; + *accepted_match_count = 0; + if (word->word->gblob_list ()->empty ()) + return; + + //xht used for blnorm + bln_xht = bln_x_height / word->denorm.scale (); + bln_word = make_bln_copy (word->word, row, bln_xht, &denorm); + /* + NOTE: Need to convert to tess format and back again to ensure that the + same float -> int rounding of coords is done to source wd as out wd before + comparison + */ + tessword = make_tess_word (bln_word, NULL); + //convert word + init_word = make_ed_word (tessword, bln_word); + delete bln_word; + delete_word(tessword); //get rid of it + // tprintf( "SOURCE BLOBS-AFTER TESS:\n"); + // print_boxes( init_word ); + // tprintf( "OUTPUT BLOBS:\n"); + // print_boxes( word->outword ); + + initial_it.set_to_list (init_word->blob_list ()); + init_blobs_left = initial_it.length (); + outword_it.set_to_list (word->outword->blob_list ()); + + for (outword_it.mark_cycle_pt (); + !outword_it.cycled_list (); outword_it.forward ()) { + out_box = outword_it.data ()->bounding_box (); + + /* Skip any initial blobs LEFT of current outword blob */ + while (!initial_it.at_last () && + (initial_it.data ()->bounding_box ().left () < out_box.left ())) { + initial_it.forward (); + init_blobs_left--; + } + + /* See if current outword blob matches any initial blob with the same left + coord. (Normally only one but possibly more - in unknown order) */ + + i = 0; + matched = FALSE; + do { + test_blob = initial_it.data_relative (i++); + matched = crude_match_blobs (test_blob, outword_it.data ()); + if (matched && + (count_outline_errs (word->best_choice->string ()[j], + outword_it.data ()->out_list ()->length ()) + == 0)) { + (*match_count)++; + if (word->reject_map[j].accepted ()) + (*accepted_match_count)++; + } + } + while (!matched && + (init_blobs_left - i > 0) && + (i < 129) && + !initial_it.at_last () && + test_blob->bounding_box ().left () == out_box.left ()); + j++; + } + delete init_word; +} + + +/************************************************************************* + * unrej_good_chs() + * Unreject POTENTIAL rejects if the blob passes the blob and outline checks + *************************************************************************/ +void unrej_good_chs(WERD_RES *word, ROW *row) { + WERD *bln_word; //BL norm init word + TWERD *tessword; //tess format + WERD *init_word; //BL norm init word + PBLOB_IT outword_it; + PBLOB_IT initial_it; + INT16 i; + INT16 init_blobs_left; + BOOL8 matched; + BOX out_box; + PBLOB *test_blob; + DENORM denorm; + float bln_xht; + INT16 j = 0; + + if (word->word->gblob_list ()->empty ()) + return; + + //xht used for blnorm + bln_xht = bln_x_height / word->denorm.scale (); + bln_word = make_bln_copy (word->word, row, bln_xht, &denorm); + /* + NOTE: Need to convert to tess format and back again to ensure that the + same float -> int rounding of coords is done to source wd as out wd before + comparison + */ + tessword = make_tess_word (bln_word, NULL); + //convert word + init_word = make_ed_word (tessword, bln_word); + delete bln_word; + delete_word(tessword); //get rid of it + + initial_it.set_to_list (init_word->blob_list ()); + init_blobs_left = initial_it.length (); + outword_it.set_to_list (word->outword->blob_list ()); + + for (outword_it.mark_cycle_pt (); + !outword_it.cycled_list (); outword_it.forward ()) { + out_box = outword_it.data ()->bounding_box (); + + /* Skip any initial blobs LEFT of current outword blob */ + while (!initial_it.at_last () && + (initial_it.data ()->bounding_box ().left () < out_box.left ())) { + initial_it.forward (); + init_blobs_left--; + } + + /* See if current outword blob matches any initial blob with the same left + coord. (Normally only one but possibly more - in unknown order) */ + + i = 0; + matched = FALSE; + do { + test_blob = initial_it.data_relative (i++); + matched = crude_match_blobs (test_blob, outword_it.data ()); + if (matched && + (word->reject_map[j].accept_if_good_quality ()) && + (docqual_excuse_outline_errs || + (count_outline_errs (word->best_choice->string ()[j], + outword_it.data ()->out_list ()-> + length ()) == 0))) + word->reject_map[j].setrej_quality_accept (); + } + while (!matched && + (init_blobs_left - i > 0) && + (i < 129) && + !initial_it.at_last () && + test_blob->bounding_box ().left () == out_box.left ()); + j++; + } + delete init_word; +} + + +void print_boxes(WERD *word) { + PBLOB_IT it; + BOX box; + + it.set_to_list (word->blob_list ()); + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + box = it.data ()->bounding_box (); + box.print (); + } +} + + +INT16 count_outline_errs(char c, INT16 outline_count) { + int expected_outline_count; + + if (STRING (outlines_odd).contains (c)) + return 0; //Dont 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 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); + + page_res_it.restart_page (); + while (page_res_it.word () != NULL) { + insert_rej_cblobs (page_res_it.word ()); + page_res_it.forward (); + } + + 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 + * - CANT do it in a single pass without a bit of fiddling + * - keep it simple but inefficient + *************************************************************************/ +void 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->best_choice->string ().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 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; + 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); + #ifndef SECURE_NAMES + 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); + } + #endif + } + else { + #ifndef SECURE_NAMES + 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); + #endif + + /* Walk blocks testing for block rejection */ + + page_res_it.restart_page (); + while (page_res_it.word () != NULL) { + current_block = page_res_it.block (); + if (current_block->block->text_region () != NULL) + block_no = current_block->block->text_region ()->id_no (); + else + block_no = -1; + if ((page_res_it.block ()->char_count > 0) && + ((page_res_it.block ()->rej_count * 100.0 / + page_res_it.block ()->char_count) > + tessedit_reject_block_percent)) { + #ifndef SECURE_NAMES + if (tessedit_debug_block_rejection) + tprintf ("REJECTING BLOCK %d #chars: %d; #Rejects: %d\n", + block_no, + page_res_it.block ()->char_count, + page_res_it.block ()->rej_count); + #endif + prev_word_rejected = FALSE; + while ((page_res_it.word () != NULL) && + (page_res_it.block () == current_block)) { + if (tessedit_preserve_blk_rej_perfect_wds) { + rej_word = + (page_res_it.word ()->reject_map.reject_count () > 0) + || (page_res_it.word ()->reject_map.length () < + tessedit_preserve_min_wd_len); + if (rej_word && tessedit_dont_blkrej_good_wds + && !(page_res_it.word ()->reject_map.length () < + tessedit_preserve_min_wd_len) + && + (acceptable_word_string + (page_res_it.word ()->best_choice->string (). + string ()) != AC_UNACCEPTABLE)) { + word_char_quality (page_res_it.word (), + page_res_it.row ()->row, + &char_quality, + &accepted_char_quality); + rej_word = char_quality != + page_res_it.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 ()) && + (page_res_it.word ()->word->space () == 1)) + page_res_it.word ()->reject_spaces = TRUE; + page_res_it.word ()->reject_map.rej_word_block_rej (); + } + prev_word_rejected = rej_word; + page_res_it.forward (); + } + } + else { + #ifndef SECURE_NAMES + 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); + #endif + + /* 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 ((page_res_it.row ()->char_count > 0) && + ((page_res_it.row ()->rej_count * 100.0 / + page_res_it.row ()->char_count) > + tessedit_reject_row_percent) && + ((page_res_it.row ()->whole_word_rej_count * 100.0 / + page_res_it.row ()->rej_count) < + tessedit_whole_wd_rej_row_percent)) { + #ifndef SECURE_NAMES + if (tessedit_debug_block_rejection) + tprintf + ("REJECTING ROW %d #chars: %d; #Rejects: %d\n", + row_no, page_res_it.row ()->char_count, + page_res_it.row ()->rej_count); + #endif + prev_word_rejected = FALSE; + while ((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 = + page_res_it.word ()->reject_map. + reject_count () / + (float) page_res_it.word ()->reject_map. + length () > tessedit_good_doc_still_rowrej_wd; + } + + /* Preserve perfect words anyway */ + else if (tessedit_preserve_row_rej_perfect_wds) { + rej_word = + (page_res_it.word ()->reject_map. + reject_count () > 0) + || (page_res_it.word ()->reject_map. + length () < tessedit_preserve_min_wd_len); + if (rej_word && tessedit_dont_rowrej_good_wds + && !(page_res_it.word ()->reject_map. + length () < + tessedit_preserve_min_wd_len) + && + (acceptable_word_string + (page_res_it.word ()->best_choice-> + string ().string ()) != AC_UNACCEPTABLE)) { + word_char_quality (page_res_it.word (), + page_res_it.row ()->row, + &char_quality, + &accepted_char_quality); + rej_word = char_quality != + page_res_it.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 ()) + && (page_res_it.word ()->word->space () == + 1)) + page_res_it.word ()->reject_spaces = TRUE; + page_res_it.word ()->reject_map. + rej_word_row_rej(); + } + prev_word_rejected = rej_word; + page_res_it.forward (); + } + } + else { + #ifndef SECURE_NAMES + if (tessedit_debug_block_rejection) + tprintf + ("NOT REJECTING ROW %d #chars: %d # Rejects: %d; \n", + row_no, page_res_it.row ()->char_count, + page_res_it.row ()->rej_count); + #endif + while ((page_res_it.word () != NULL) && + (page_res_it.row () == current_row)) + page_res_it.forward (); + } + } + } + } + } +} + + +/************************************************************************* + * reject_whole_page() + * Dont 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; +} + + +void 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; + int dict_type; + BOOL8 ok_dict_word; + + page_res_it.restart_page (); + while (page_res_it.word () != NULL) { + word = page_res_it.word (); + + if (crunch_early_convert_bad_unlv_chs) + convert_bad_unlv_chs(word); + + if (crunch_early_merge_tess_fails) + merge_tess_fails(word); + + if (word->reject_map.accept_count () != 0) { + found_terrible_word = FALSE; + //Forget earlier potential crunches + prev_potential_marked = FALSE; + } + else { + dict_type = dict_word (word->best_choice->string ().string ()); + ok_dict_word = (dict_type > 0) && (dict_type != DOC_DAWG_PERM); + 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->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->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->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->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->string ().string ()); + } + } + } + page_res_it.forward (); + } +} + + +BOOL8 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->string ().length () == 0) || + (strspn (word->best_choice->string ().string (), " ") == + word->best_choice->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->string ().string ()); + } + return TRUE; + } + else + return FALSE; +} + + +BOOL8 potential_word_crunch(WERD_RES *word, + GARBAGE_LEVEL garbage_level, + BOOL8 ok_dict_word) { + float rating_per_ch; + int adjusted_len; + char *str = (char *) word->best_choice->string ().string (); + BOOL8 word_crunchable; + int poor_indicator_count = 0; + + word_crunchable = + !crunch_leave_accept_strings || + (word->reject_map.length () < 3) || + ((acceptable_word_string (str) == 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->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->string ().string ()); + } + poor_indicator_count++; + } + + if (garbage_level != G_OK) { + if (crunch_debug > 2) { + tprintf ("Potential garbage on \"%s\"\n", + word->best_choice->string ().string ()); + } + poor_indicator_count++; + } + return (poor_indicator_count >= crunch_pot_indicators); +} + + +void 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->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->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->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) + merge_tess_fails(word); + page_res_it.forward (); + } +} + + +void convert_bad_unlv_chs( //word to do + WERD_RES *word_res) { + char *ptr; //string ptr + int i; + + ptr = (char *) word_res->best_choice->string ().string (); + for (i = 0; i < word_res->reject_map.length (); i++) { + if (ptr[i] == '~') { + ptr[i] = '-'; + if (word_res->reject_map[i].accepted ()) + word_res->reject_map[i].setrej_unlv_rej (); + } + if (ptr[i] == '^') { + ptr[i] = ' '; + if (word_res->reject_map[i].accepted ()) + word_res->reject_map[i].setrej_unlv_rej (); + } + } +} + + +/********************************************************************** + * merge_tess_fails + * + * Change pairs of tess failures to a single one + **********************************************************************/ + +void merge_tess_fails( //word to do + WERD_RES *word_res) { + char *ptr; //string ptr + PBLOB_IT blob_it; //blobs + int i = 0; + int len; + + len = strlen (word_res->best_choice->string ().string ()); + ASSERT_HOST (word_res->reject_map.length () == len); + ASSERT_HOST (word_res->outword->blob_list ()->length () == len); + + ptr = (char *) word_res->best_choice->string ().string (); + blob_it = word_res->outword->blob_list (); + while (*ptr != '\0') { + if ((*ptr == ' ') && (*(ptr + 1) == ' ')) { + strcpy (ptr + 1, ptr + 2); //shuffle up + word_res->reject_map.remove_pos (i); + merge_blobs (blob_it.data_relative (1), blob_it.data ()); + delete blob_it.extract (); //get rid of spare + } + else { + i++; + ptr++; + } + blob_it.forward (); + } + len = strlen (word_res->best_choice->string ().string ()); + ASSERT_HOST (word_res->reject_map.length () == len); + ASSERT_HOST (word_res->outword->blob_list ()->length () == len); +} + + +GARBAGE_LEVEL garbage_word(WERD_RES *word, BOOL8 ok_dict_word) { + enum STATES + { + JUNK, + FIRST_UPPER, + FIRST_LOWER, + FIRST_NUM, + SUBSEQUENT_UPPER, + SUBSEQUENT_LOWER, + SUBSEQUENT_NUM + }; + char *str = (char *) word->best_choice->string ().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; + char last_char = ' '; + 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++) { + len++; + if (isupper (*str)) { + 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 == *str) { + alpha_repetition_count++; + if (longest_alpha_repetition_count < alpha_repetition_count) { + longest_alpha_repetition_count = alpha_repetition_count; + } + } + else { + last_char = *str; + alpha_repetition_count = 1; + } + break; + case FIRST_NUM: + isolated_digits++; + default: + state = FIRST_UPPER; + last_char = *str; + alpha_repetition_count = 1; + upper_string_count = 1; + break; + } + } + else if (islower (*str)) { + 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 == *str) { + alpha_repetition_count++; + if (longest_alpha_repetition_count < alpha_repetition_count) { + longest_alpha_repetition_count = alpha_repetition_count; + } + } + else { + last_char = *str; + alpha_repetition_count = 1; + } + break; + case FIRST_NUM: + isolated_digits++; + default: + state = FIRST_LOWER; + last_char = *str; + alpha_repetition_count = 1; + lower_string_count = 1; + break; + } + } + else if (isdigit (*str)) { + 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 (*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 (str) != 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 (str) != 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->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 word_deletable(WERD_RES *word, INT16 &delete_mode) { + int word_len = word->reject_map.length (); + float rating_per_ch; + BOX 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; + } + + box = word->outword->bounding_box (); + if (box.height () < crunch_del_min_ht * bln_x_height) { + delete_mode = 4; + return CR_DELETE; + } + + if (noise_outlines (word->outword)) { + 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 () < bln_baseline_offset - crunch_del_low_word * bln_x_height) { + delete_mode = 9; + return CR_LOOSE_SPACE; + } + + if (box.bottom () > + bln_baseline_offset + crunch_del_high_word * bln_x_height) { + delete_mode = 10; + return CR_LOOSE_SPACE; + } + + if (box.height () > crunch_del_max_ht * bln_x_height) { + delete_mode = 11; + return CR_LOOSE_SPACE; + } + + if (box.width () < crunch_del_min_width * bln_x_height) { + delete_mode = 3; + return CR_LOOSE_SPACE; + } + + delete_mode = 0; + return CR_NONE; +} + + +INT16 failure_count(WERD_RES *word) { + char *str = (char *) word->best_choice->string ().string (); + int tess_rejs = 0; + + for (; *str != '\0'; str++) { + if (*str == ' ') + tess_rejs++; + } + return tess_rejs; +} + + +BOOL8 noise_outlines(WERD *word) { + PBLOB_IT blob_it; + OUTLINE_IT outline_it; + BOX box; //BB of outline + INT16 outline_count = 0; + INT16 small_outline_count = 0; + INT16 max_dimension; + float small_limit = bln_x_height * crunch_small_outlines_size; + + blob_it.set_to_list (word->blob_list ()); + for (blob_it.mark_cycle_pt (); !blob_it.cycled_list (); blob_it.forward ()) { + outline_it.set_to_list (blob_it.data ()->out_list ()); + for (outline_it.mark_cycle_pt (); + !outline_it.cycled_list (); outline_it.forward ()) { + outline_count++; + box = outline_it.data ()->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); +} + + +/************************************************************************* + * insert_rej_cblobs() + * Put rejected word blobs back into the outword. + * NOTE!!! AFTER THIS THE CHOICES LIST WILL NOT HAVE THE CORRECT NUMBER + * OF ELEMENTS. + *************************************************************************/ +void insert_rej_cblobs( //word to do + WERD_RES *word) { + PBLOB_IT blob_it; //blob iterator + PBLOB_IT rej_blob_it; + const STRING *wordstr; + int old_len; + int rej_len; + char new_str[512]; + REJMAP new_map; + int i = 0; //new_str index + int j = 0; //old_str index + int new_len; + + gblob_sort_list (word->outword->rej_blob_list (), TRUE); + rej_blob_it.set_to_list (word->outword->rej_blob_list ()); + if (rej_blob_it.empty ()) + return; + rej_len = rej_blob_it.length (); + blob_it.set_to_list (word->outword->blob_list ()); + wordstr = &(word->best_choice->string ()); + old_len = wordstr->length (); + ASSERT_HOST (word->reject_map.length () == old_len); + ASSERT_HOST (blob_it.length () == old_len); + if ((old_len + rej_len) > 511) + return; //Word is garbage anyway prevent abort + new_map.initialise (old_len + rej_len); + + while (!rej_blob_it.empty ()) { + if ((j >= old_len) || + (rej_blob_it.data ()->bounding_box ().left () <= + blob_it.data ()->bounding_box ().left ())) { + /* Insert reject blob */ + if (j >= old_len) + blob_it.add_to_end (rej_blob_it.extract ()); + else + blob_it.add_before_stay_put (rej_blob_it.extract ()); + if (!rej_blob_it.empty ()) + rej_blob_it.forward (); + new_str[i] = ' '; + new_map[i].setrej_rej_cblob (); + i++; + } + else { + new_str[i] = (*wordstr)[j]; + new_map[i] = word->reject_map[j]; + i++; + j++; + blob_it.forward (); + } + } + /* Add any extra normal blobs to strings */ + while (j < wordstr->length ()) { + new_str[i] = (*wordstr)[j]; + new_map[i] = word->reject_map[j]; + i++; + j++; + } + new_str[i] = '\0'; + /* + tprintf( + "\nOld len %d; New len %d; New str \"%s\"; New map \"%s\"\n", + old_len, i, new_str, new_map ); + */ + ASSERT_HOST (i == blob_it.length ()); + ASSERT_HOST (i == old_len + rej_len); + word->reject_map = new_map; + *((STRING *) wordstr) = new_str; + new_len = strlen (word->best_choice->string ().string ()); + ASSERT_HOST (word->reject_map.length () == new_len); + ASSERT_HOST (word->outword->blob_list ()->length () == new_len); +} diff --git a/ccmain/docqual.h b/ccmain/docqual.h new file mode 100644 index 0000000000..f0d340619c --- /dev/null +++ b/ccmain/docqual.h @@ -0,0 +1,155 @@ +/****************************************************************** + * 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" +#include "notdll.h" + +enum GARBAGE_LEVEL +{ + G_NEVER_CRUNCH, + G_OK, + G_DODGY, + G_TERRIBLE +}; + +extern STRING_VAR_H (outlines_odd, "%| ", "Non standard number of outlines"); +extern STRING_VAR_H (outlines_2, "ij!?%\":;", +"Non standard number of outlines"); +extern BOOL_VAR_H (docqual_excuse_outline_errs, FALSE, +"Allow outline errs in unrejection?"); +extern BOOL_VAR_H (tessedit_good_quality_unrej, TRUE, +"Reduce rejection on good docs"); +extern BOOL_VAR_H (tessedit_use_reject_spaces, TRUE, "Reject spaces?"); +extern double_VAR_H (tessedit_reject_doc_percent, 65.00, +"%rej allowed before rej whole doc"); +extern double_VAR_H (tessedit_reject_block_percent, 45.00, +"%rej allowed before rej whole block"); +extern double_VAR_H (tessedit_reject_row_percent, 40.00, +"%rej allowed before rej whole row"); +extern double_VAR_H (tessedit_whole_wd_rej_row_percent, 70.00, +"%of row rejects in whole word rejects which prevents whole row rejection"); +extern BOOL_VAR_H (tessedit_preserve_blk_rej_perfect_wds, TRUE, +"Only rej partially rejected words in block rejection"); +extern BOOL_VAR_H (tessedit_preserve_row_rej_perfect_wds, TRUE, +"Only rej partially rejected words in row rejection"); +extern BOOL_VAR_H (tessedit_dont_blkrej_good_wds, FALSE, +"Use word segmentation quality metric"); +extern BOOL_VAR_H (tessedit_dont_rowrej_good_wds, FALSE, +"Use word segmentation quality metric"); +extern INT_VAR_H (tessedit_preserve_min_wd_len, 2, +"Only preserve wds longer than this"); +extern BOOL_VAR_H (tessedit_row_rej_good_docs, TRUE, +"Apply row rejection to good docs"); +extern double_VAR_H (tessedit_good_doc_still_rowrej_wd, 1.1, +"rej good doc wd if more than this fraction rejected"); +extern BOOL_VAR_H (tessedit_reject_bad_qual_wds, TRUE, +"Reject all bad quality wds"); +extern BOOL_VAR_H (tessedit_debug_doc_rejection, FALSE, "Page stats"); +extern BOOL_VAR_H (tessedit_debug_quality_metrics, FALSE, +"Output data to debug file"); +extern BOOL_VAR_H (bland_unrej, FALSE, "unrej potential with no chekcs"); +extern double_VAR_H (quality_rowrej_pc, 1.1, +"good_quality_doc gte good char limit"); +extern BOOL_VAR_H (unlv_tilde_crunching, TRUE, +"Mark v.bad words for tilde crunch"); +extern BOOL_VAR_H (crunch_early_merge_tess_fails, TRUE, +"Before word crunch?"); +extern BOOL_VAR_H (crunch_early_convert_bad_unlv_chs, FALSE, +"Take out ~^ early?"); +extern double_VAR_H (crunch_terrible_rating, 80.0, "crunch rating lt this"); +extern BOOL_VAR_H (crunch_terrible_garbage, TRUE, "As it says"); +extern double_VAR_H (crunch_poor_garbage_cert, -9.0, +"crunch garbage cert lt this"); +extern double_VAR_H (crunch_poor_garbage_rate, 60, +"crunch garbage rating lt this"); +extern double_VAR_H (crunch_pot_poor_rate, 40, +"POTENTIAL crunch rating lt this"); +extern double_VAR_H (crunch_pot_poor_cert, -8.0, +"POTENTIAL crunch cert lt this"); +extern BOOL_VAR_H (crunch_pot_garbage, TRUE, "POTENTIAL crunch garbage"); +extern double_VAR_H (crunch_del_rating, 60, +"POTENTIAL crunch rating lt this"); +extern double_VAR_H (crunch_del_cert, -10.0, "POTENTIAL crunch cert lt this"); +extern double_VAR_H (crunch_del_min_ht, 0.7, "Del if word ht lt xht x this"); +extern double_VAR_H (crunch_del_max_ht, 3.0, "Del if word ht gt xht x this"); +extern double_VAR_H (crunch_del_min_width, 3.0, +"Del if word width lt xht x this"); +extern double_VAR_H (crunch_del_high_word, 1.5, +"Del if word gt xht x this above bl"); +extern double_VAR_H (crunch_del_low_word, 0.5, +"Del if word gt xht x this below bl"); +extern double_VAR_H (crunch_small_outlines_size, 0.6, +"Small if lt xht x this"); +extern INT_VAR_H (crunch_rating_max, 10, "For adj length in rating per ch"); +extern INT_VAR_H (crunch_pot_indicators, 1, +"How many potential indicators needed"); +extern BOOL_VAR_H (crunch_leave_ok_strings, TRUE, +"Dont touch sensible strings"); +extern BOOL_VAR_H (crunch_accept_ok, TRUE, "Use acceptability in okstring"); +extern BOOL_VAR_H (crunch_leave_accept_strings, FALSE, +"Dont pot crunch sensible strings"); +extern BOOL_VAR_H (crunch_include_numerals, FALSE, "Fiddle alpha figures"); +extern INT_VAR_H (crunch_leave_lc_strings, 4, +"Dont crunch words with long lower case strings"); +extern INT_VAR_H (crunch_leave_uc_strings, 4, +"Dont crunch words with long lower case strings"); +extern INT_VAR_H (crunch_long_repetitions, 3, +"Crunch words with long repetitions"); +extern INT_VAR_H (crunch_debug, 0, "As it says"); +INT16 word_blob_quality( //Blob seg changes + WERD_RES *word, + ROW *row); +BOOL8 crude_match_blobs(PBLOB *blob1, PBLOB *blob2); +INT16 word_outline_errs( //Outline count errs + WERD_RES *word); +void word_char_quality( //Blob seg changes + WERD_RES *word, + ROW *row, + INT16 *match_count, + INT16 *accepted_match_count); +void unrej_good_chs(WERD_RES *word, ROW *row); +void print_boxes(WERD *word); +INT16 count_outline_errs(char c, INT16 outline_count); +void quality_based_rejection(PAGE_RES_IT &page_res_it, BOOL8 good_quality_doc); +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 reject_whole_page(PAGE_RES_IT &page_res_it); +void tilde_crunch(PAGE_RES_IT &page_res_it); +BOOL8 terrible_word_crunch(WERD_RES *word, GARBAGE_LEVEL garbage_level); +BOOL8 potential_word_crunch(WERD_RES *word, + GARBAGE_LEVEL garbage_level, + BOOL8 ok_dict_word); +void tilde_delete(PAGE_RES_IT &page_res_it); + //word to do +void convert_bad_unlv_chs(WERD_RES *word_res); + //word to do +void merge_tess_fails(WERD_RES *word_res); +GARBAGE_LEVEL garbage_word(WERD_RES *word, BOOL8 ok_dict_word); +CRUNCH_MODE word_deletable(WERD_RES *word, INT16 &delete_mode); +INT16 failure_count(WERD_RES *word); +BOOL8 noise_outlines(WERD *word); + //word to do +void insert_rej_cblobs(WERD_RES *word); +#endif diff --git a/ccmain/expandblob.cpp b/ccmain/expandblob.cpp new file mode 100644 index 0000000000..f80236a89d --- /dev/null +++ b/ccmain/expandblob.cpp @@ -0,0 +1,82 @@ +/************************************************************************** + * Revision 5.1 89/07/27 11:46:53 11:46:53 ray () + * (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 "mfcpch.h" +#include "expandblob.h" +#include "tessclas.h" +#include "const.h" +#include "structures.h" +#include "freelist.h" + +/*********************************************************************** +free_blob(blob) frees the blob and everything it is connected to, +i.e. outlines, nodes, edgepts, bytevecs, ratings etc +*************************************************************************/ +void free_blob( /*blob to free */ + register TBLOB *blob) { + if (blob == NULL) + return; /*duff blob */ + free_tree (blob->outlines); /*do the tree of outlines */ + oldblob(blob); /*free the actual blob */ +} + + +/*************************************************************************** +free_tree(outline) frees the current outline +and then its sub-tree +*****************************************************************************/ +void free_tree( /*outline to draw */ + register TESSLINE *outline) { + if (outline == NULL) + return; /*duff outline */ + if (outline->next != NULL) + free_tree (outline->next); + if (outline->child != NULL) + free_tree (outline->child); /*and sub-tree */ + free_outline(outline); /*free the outline */ +} + + +/******************************************************************************* +free_outline(outline) frees an outline and anything connected to it +*********************************************************************************/ +void free_outline( /*outline to free */ + register TESSLINE *outline) { + if (outline->compactloop != NULL) + /*no compact loop */ + memfree (outline->compactloop); + + if (outline->loop != NULL) + free_loop (outline->loop); + + oldoutline(outline); +} + + +/********************************************************************************* +free_loop(startpt) frees all the elements of the closed loop +starting at startpt +***********************************************************************************/ +void free_loop( /*outline to free */ + register EDGEPT *startpt) { + register EDGEPT *edgept; /*current point */ + + if (startpt == NULL) + return; + edgept = startpt; + do { + edgept = oldedgept (edgept); /*free it and move on */ + } + while (edgept != startpt); +} diff --git a/ccmain/expandblob.h b/ccmain/expandblob.h new file mode 100644 index 0000000000..6d80c288db --- /dev/null +++ b/ccmain/expandblob.h @@ -0,0 +1,13 @@ +#ifndef EXPANDBLOB_H +#define EXPANDBLOB_H + +#include "tessclas.h" + +void free_blob(register TBLOB *blob); + +void free_tree(register TESSLINE *outline); + +void free_outline(register TESSLINE *outline); + +void free_loop(register EDGEPT *startpt); +#endif diff --git a/ccmain/fixspace.cpp b/ccmain/fixspace.cpp new file mode 100644 index 0000000000..c3e8439a30 --- /dev/null +++ b/ccmain/fixspace.cpp @@ -0,0 +1,974 @@ +/****************************************************************** + * 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 "mfcpch.h" +#include +#include "reject.h" +#include "statistc.h" +#include "genblob.h" +#include "control.h" +#include "fixspace.h" +#include "tessvars.h" +#include "tessbox.h" +#include "secname.h" + +#define EXTERN + +EXTERN BOOL_VAR (fixsp_check_for_fp_noise_space, TRUE, +"Try turning noise to space in fixed pitch"); +EXTERN BOOL_VAR (fixsp_fp_eval, TRUE, "Use alternate evaluation for fp"); +EXTERN BOOL_VAR (fixsp_noise_score_fixing, TRUE, "More sophisticated?"); +EXTERN INT_VAR (fixsp_non_noise_limit, 1, +"How many non-noise blbs either side?"); +EXTERN double_VAR (fixsp_small_outlines_size, 0.28, "Small if lt xht x this"); + +EXTERN BOOL_VAR (fixsp_ignore_punct, TRUE, "In uniform spacing calc"); +EXTERN BOOL_VAR (fixsp_numeric_fix, TRUE, "Try to deal with numeric punct"); +EXTERN BOOL_VAR (fixsp_prefer_joined_1s, TRUE, "Arbitrary boost"); +EXTERN BOOL_VAR (tessedit_test_uniform_wd_spacing, FALSE, +"Limit context word spacing"); +EXTERN BOOL_VAR (tessedit_prefer_joined_punct, FALSE, +"Reward punctation joins"); +EXTERN INT_VAR (fixsp_done_mode, 1, "What constitues done for spacing"); +EXTERN INT_VAR (debug_fix_space_level, 0, "Contextual fixspace debug"); +EXTERN STRING_VAR (numeric_punctuation, ".,", +"Punct. chs expected WITHIN numbers"); + +#define PERFECT_WERDS 999 +#define MAXSPACING 128 /*max expected spacing in pix */ + +/************************************************************************* + * 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. + *************************************************************************/ + +void fix_fuzzy_spaces( //find fuzzy words + volatile ETEXT_DESC *monitor, //progress monitor + INT32 word_count, //count of words in doc + PAGE_RES *page_res) { + BLOCK_RES_IT block_res_it; //iterators + 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; //DONT 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); + 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 (!word_res_it_from.at_last ()) { + word_res_it_to = word_res_it_from; + prevent_null_wd_fixsp = + word_res->word->gblob_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; + } + 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->gblob_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->gblob_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); + 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); + //Last word in row + } + } + } +} + + +void fix_fuzzy_space_list( //space explorer + WERD_RES_LIST &best_perm, + ROW *row) { + INT16 best_score; + WERD_RES_LIST current_perm; + INT16 current_score; + BOOL8 improved = FALSE; + + //default score + best_score = eval_word_spacing (best_perm); + + 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); + 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); + 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); +} + + +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 = new WERD_RES (*src_wd); + new_wd->combination = FALSE; + new_wd->part_of_combo = FALSE; + new_it.add_after_then_move (new_wd); + } + } +} + + +void match_current_words(WERD_RES_LIST &words, ROW *row) { + WERD_RES_IT word_it(&words); + WERD_RES *word; + + for (word_it.mark_cycle_pt (); !word_it.cycled_list (); word_it.forward ()) { + word = word_it.data (); + if ((!word->part_of_combo) && (word->outword == NULL)) + classify_word_pass2(word, row); + } +} + + +/************************************************************************* + * 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 prefered. + * + * 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 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; + 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 didnt end in a 1 when the first char of this word is a digit + AND it didnt 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, + word->best_choice->string ()[0])) || + (prev_char_digit && + ((word_done && + (word->best_choice->string ()[0] == '1')) || + (!word_done && + STRING (conflict_set_I_l_1).contains (word->best_choice-> + 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) && + (!tessedit_test_uniform_wd_spacing || + ((word->best_choice->permuter () == NUMBER_PERM) || + uniformly_spaced (word)))) { + prev_word_done = TRUE; + prev_word_score = word_len; + } + else { + prev_word_done = FALSE; + prev_word_score = 0; + } + + if (fixsp_prefer_joined_1s) { + /* 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->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, prev_char_punct = FALSE; i < word_len; i++) { + current_char_punct = + punct_chars.contains (word->best_choice->string ()[i]); + 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->best_choice-> + string ()[word_len - 1]); + prev_char_1 = + ((word_done + && (word->best_choice->string ()[word_len - 1] == '1')) + || (!word_done + && STRING (conflict_set_I_l_1).contains (word->best_choice-> + string ()[word_len - + 1]))); + } + /* 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 digit_or_numeric_punct(WERD_RES *word, char ch) { + return (isdigit (ch) || + (fixsp_numeric_fix && + (word->best_choice->permuter () == NUMBER_PERM) && + STRING (numeric_punctuation).contains (ch))); +} + + +/************************************************************************* + * 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 = -1; + BOX 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 >= 0) { + gap = box.left () - prev_right; + if (gap < min_gap) + min_gap = gap; + } + prev_right = box.right (); + } + } + if (min_gap < MAX_INT16) { + prev_right = -1; //back to start + word_it.set_to_list (&words); + for (; //cant use cycle pt due to inserted combos at start of list + (prev_right < 0) || !word_it.at_first (); word_it.forward ()) { + word = word_it.data (); + if (!word->part_of_combo) { + box = word->word->bounding_box (); + if (prev_right >= 0) { + 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; + 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 blbs to combo + //old combo no longer needed + delete word_it.extract (); + } + else { + //Cpy current wd to combo + combo->copy_on (word); + word->part_of_combo = TRUE; + } + combo->done = FALSE; + if (combo->outword != NULL) { + delete combo->outword; + delete combo->best_choice; + delete combo->raw_choice; + combo->outword = NULL; + combo->best_choice = NULL; + combo->raw_choice = NULL; + } + } + else + //catch up + prev_word_it = word_it; + } + prev_right = box.right (); + } + } + } + else + words.clear (); //signal termination +} + + +void dump_words(WERD_RES_LIST &perm, INT16 score, INT16 mode, BOOL8 improved) { + WERD_RES_IT word_res_it(&perm); + static STRING initial_str; + + if (debug_fix_space_level > 0) { + if (mode == 1) { + initial_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) { + initial_str += word_res_it.data ()->best_choice->string (); + initial_str += ' '; + } + } + } + + #ifndef SECURE_NAMES + 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->string (). + string (), + (int) word_res_it.data ()->best_choice->permuter ()); + } + tprintf ("\"\n"); + } + else if (improved) { + tprintf ("FIX SPACING \"%s\" => \"", initial_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->string (). + string (), + (int) word_res_it.data ()->best_choice->permuter ()); + } + tprintf ("\"\n"); + } + #endif + } +} + + +/************************************************************************* + * uniformly_spaced() + * Return true if one of the following are true: + * - All inter-char gaps are the same width + * - The largest gap is no larger than twice the mean/median of the others + * - The largest gap is < 64/5 = 13 and all others are <= 0 + * **** REMEMBER - WE'RE NOW WORKING WITH A BLN WERD !!! + *************************************************************************/ +BOOL8 uniformly_spaced( //sensible word + WERD_RES *word) { + PBLOB_IT blob_it; + BOX box; + INT16 prev_right = -MAX_INT16; + INT16 gap; + INT16 max_gap = -MAX_INT16; + INT16 max_gap_count = 0; + STATS gap_stats (0, MAXSPACING); + BOOL8 result; + const ROW *row = word->denorm.row (); + float max_non_space; + float normalised_max_nonspace; + INT16 i = 0; + STRING punct_chars = "\"`',.:;"; + + blob_it.set_to_list (word->outword->blob_list ()); + + for (blob_it.mark_cycle_pt (); !blob_it.cycled_list (); blob_it.forward ()) { + box = blob_it.data ()->bounding_box (); + if ((prev_right > -MAX_INT16) && + (!fixsp_ignore_punct || + (!punct_chars.contains (word->best_choice->string ()[i - 1]) && + !punct_chars.contains (word->best_choice->string ()[i])))) { + gap = box.left () - prev_right; + if (gap < max_gap) + gap_stats.add (gap, 1); + else if (gap == max_gap) + max_gap_count++; + else { + if (max_gap_count > 0) + gap_stats.add (max_gap, max_gap_count); + max_gap = gap; + max_gap_count = 1; + } + } + prev_right = box.right (); + i++; + } + + max_non_space = (row->space () + 3 * row->kern ()) / 4; + normalised_max_nonspace = max_non_space * bln_x_height / row->x_height (); + + result = ((gap_stats.get_total () == 0) || + (max_gap <= normalised_max_nonspace) || + ((gap_stats.get_total () > 2) && + (max_gap <= 2 * gap_stats.median ())) || + ((gap_stats.get_total () <= 2) && + (max_gap <= 2 * gap_stats.mean ()))); + #ifndef SECURE_NAMES + if ((debug_fix_space_level > 1)) { + if (result) + tprintf + ("ACCEPT SPACING FOR: \"%s\" norm_maxnon = %f max=%d maxcount=%d total=%d mean=%f median=%f\n", + word->best_choice->string ().string (), normalised_max_nonspace, + max_gap, max_gap_count, gap_stats.get_total (), gap_stats.mean (), + gap_stats.median ()); + else + tprintf + ("REJECT SPACING FOR: \"%s\" norm_maxnon = %f max=%d maxcount=%d total=%d mean=%f median=%f\n", + word->best_choice->string ().string (), normalised_max_nonspace, + max_gap, max_gap_count, gap_stats.get_total (), gap_stats.mean (), + gap_stats.median ()); + } + #endif + + return result; +} + + +BOOL8 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 DONT REJECT IF THE WERD IS AMBIGUOUS - FOR SPACING WE DONT + 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->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; +} + + +/************************************************************************* + * fix_sp_fp_word() + * Test the current word to see if it can be split by deleting noise blobs. If + * so, do the buisiness. + * Return with the iterator pointing to the same place if the word is unchanged, + * or the last of the replacement words. + *************************************************************************/ +void fix_sp_fp_word(WERD_RES_IT &word_res_it, ROW *row) { + 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 (!fixsp_check_for_fp_noise_space || + 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; + + #ifndef SECURE_NAMES + if (debug_fix_space_level > 1) { + tprintf ("FP fixspace working on \"%s\"\n", + word_res->best_choice->string ().string ()); + } + #endif + gblob_sort_list ((PBLOB_LIST *) word_res->word->rej_cblob_list (), FALSE); + sub_word_list_it.add_after_stay_put (word_res_it.extract ()); + fix_noisy_space_list(sub_word_list, row); + 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 fix_noisy_space_list(WERD_RES_LIST &best_perm, ROW *row) { + 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; + WERD_RES *new_word_res; + INT16 current_score; + BOOL8 improved = FALSE; + + //default score + best_score = fp_eval_word_spacing (best_perm); + + dump_words (best_perm, best_score, 1, improved); + + new_word_res = new WERD_RES; + old_word_res = best_perm_it.data (); + //Kludge to force deep copy + old_word_res->combination = TRUE; + *new_word_res = *old_word_res; //deep copy + //Undo kludge + old_word_res->combination = FALSE; + //Undo kludge + new_word_res->combination = FALSE; + current_perm_it.add_to_end (new_word_res); + + break_noisiest_blob_word(current_perm); + + while ((best_score != PERFECT_WERDS) && !current_perm.empty ()) { + match_current_words(current_perm, row); + 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); + 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 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 blb of noisiest wd + int blob_index; //of wds noisiest blb + float noise_score; //of wds noisiest blb + 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 blb + + 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 ()); + } + + worst_word_it.add_before_then_move (new WERD_RES (new_word)); + + word_res->done = FALSE; + if (word_res->outword != NULL) { + delete word_res->outword; + delete word_res->best_choice; + delete word_res->raw_choice; + word_res->outword = NULL; + word_res->best_choice = NULL; + word_res->raw_choice = NULL; + } +} + + +INT16 worst_noise_blob(WERD_RES *word_res, float *worst_noise_score) { + PBLOB_IT blob_it; + INT16 blob_count; + 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 = bln_x_height * fixsp_small_outlines_size; + float non_noise_limit = bln_x_height * 0.8; + + blob_it.set_to_list (word_res->outword->blob_list ()); + //normalised + blob_count = blob_it.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->string ().string ()); + #endif + + for (i = 0; i < blob_count; i++, blob_it.forward ()) { + if (word_res->reject_map[i].accepted ()) + noise_score[i] = non_noise_limit; + else + noise_score[i] = blob_noise_score (blob_it.data ()); + + 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 blob_noise_score(PBLOB *blob) { + OUTLINE_IT outline_it; + BOX box; //BB of outline + INT16 outline_count = 0; + INT16 max_dimension; + INT16 largest_outline_dimension = 0; + + outline_it.set_to_list (blob->out_list ()); + for (outline_it.mark_cycle_pt (); + !outline_it.cycled_list (); outline_it.forward ()) { + outline_count++; + box = outline_it.data ()->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 (fixsp_noise_score_fixing) { + if (outline_count > 5) + //penalise LOTS of blobs + largest_outline_dimension *= 2; + + box = blob->bounding_box (); + + if ((box.bottom () > bln_baseline_offset * 4) || + (box.top () < bln_baseline_offset / 2)) + //Lax blob is if high or low + largest_outline_dimension /= 2; + } + return largest_outline_dimension; +} + + +void fixspace_dbg(WERD_RES *word) { + BOX box = word->word->bounding_box (); + BOOL8 show_map_detail = FALSE; + INT16 i; + + box.print (); + #ifndef SECURE_NAMES + tprintf (" \"%s\" ", word->best_choice->string ().string ()); + tprintf ("Blob count: %d (word); %d/%d (outword)\n", + word->word->gblob_list ()->length (), + word->outword->gblob_list ()->length (), + word->outword->rej_blob_list ()->length ()); + word->reject_map.print (debug_fp); + tprintf ("\n"); + if (show_map_detail) { + tprintf ("\"%s\"\n", word->best_choice->string ().string ()); + for (i = 0; word->best_choice->string ()[i] != '\0'; i++) { + tprintf ("**** \"%c\" ****\n", word->best_choice->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"); + #endif +} + + +/************************************************************************* + * 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 + *************************************************************************/ + +INT16 fp_eval_word_spacing(WERD_RES_LIST &word_res_list) { + WERD_RES_IT word_it(&word_res_list); + WERD_RES *word; + PBLOB_IT blob_it; + INT16 word_length; + INT16 score = 0; + INT16 i; + const char *chs; + float small_limit = bln_x_height * fixsp_small_outlines_size; + + if (!fixsp_fp_eval) + return (eval_word_spacing (word_res_list)); + + for (word_it.mark_cycle_pt (); !word_it.cycled_list (); word_it.forward ()) { + word = word_it.data (); + word_length = word->reject_map.length (); + chs = word->best_choice->string ().string (); + 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 (chs) > 0)) { + blob_it.set_to_list (word->outword->blob_list ()); + for (i = 0; i < word_length; i++, blob_it.forward ()) { + if ((chs[i] == ' ') || + (blob_noise_score (blob_it.data ()) < small_limit)) + score -= 1; //penalise possibly erroneous non-space + + else if (word->reject_map[i].accepted ()) + score++; + } + } + } + if (score < 0) + score = 0; + return score; +} diff --git a/ccmain/fixspace.h b/ccmain/fixspace.h new file mode 100644 index 0000000000..75a2892c1b --- /dev/null +++ b/ccmain/fixspace.h @@ -0,0 +1,72 @@ +/****************************************************************** + * 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 "varable.h" +#include "ocrclass.h" +#include "notdll.h" + +extern BOOL_VAR_H (fixsp_check_for_fp_noise_space, TRUE, +"Try turning noise to space in fixed pitch"); +extern BOOL_VAR_H (fixsp_fp_eval, TRUE, "Use alternate evaluation for fp"); +extern BOOL_VAR_H (fixsp_noise_score_fixing, TRUE, "More sophisticated?"); +extern INT_VAR_H (fixsp_non_noise_limit, 1, +"How many non-noise blbs either side?"); +extern double_VAR_H (fixsp_small_outlines_size, 0.28, +"Small if lt xht x this"); +extern BOOL_VAR_H (fixsp_ignore_punct, TRUE, "In uniform spacing calc"); +extern BOOL_VAR_H (fixsp_numeric_fix, TRUE, "Try to deal with numeric punct"); +extern BOOL_VAR_H (fixsp_prefer_joined_1s, TRUE, "Arbitrary boost"); +extern BOOL_VAR_H (tessedit_test_uniform_wd_spacing, FALSE, +"Limit context word spacing"); +extern BOOL_VAR_H (tessedit_prefer_joined_punct, FALSE, +"Reward punctation joins"); +extern INT_VAR_H (fixsp_done_mode, 1, "What constitues done for spacing"); +extern INT_VAR_H (debug_fix_space_level, 0, "Contextual fixspace debug"); +extern STRING_VAR_H (numeric_punctuation, ".,", +"Punct. chs expected WITHIN numbers"); +void fix_fuzzy_spaces( //find fuzzy words + volatile ETEXT_DESC *monitor, //progress monitor + INT32 word_count, //count of words in doc + PAGE_RES *page_res); +void fix_fuzzy_space_list( //space explorer + WERD_RES_LIST &best_perm, + ROW *row); +void initialise_search(WERD_RES_LIST &src_list, WERD_RES_LIST &new_list); +void match_current_words(WERD_RES_LIST &words, ROW *row); +INT16 eval_word_spacing(WERD_RES_LIST &word_res_list); +BOOL8 digit_or_numeric_punct(WERD_RES *word, char ch); +void transform_to_next_perm(WERD_RES_LIST &words); +void dump_words(WERD_RES_LIST &perm, INT16 score, INT16 mode, BOOL8 improved); +BOOL8 uniformly_spaced( //sensible word + WERD_RES *word); +BOOL8 fixspace_thinks_word_done(WERD_RES *word); +void fix_sp_fp_word(WERD_RES_IT &word_res_it, ROW *row); +void fix_noisy_space_list(WERD_RES_LIST &best_perm, ROW *row); +void break_noisiest_blob_word(WERD_RES_LIST &words); +INT16 worst_noise_blob(WERD_RES *word_res, float *worst_noise_score); +float blob_noise_score(PBLOB *blob); +void fixspace_dbg(WERD_RES *word); +INT16 fp_eval_word_spacing(WERD_RES_LIST &word_res_list); +#endif diff --git a/ccmain/fixxht.cpp b/ccmain/fixxht.cpp new file mode 100644 index 0000000000..f1339dabbf --- /dev/null +++ b/ccmain/fixxht.cpp @@ -0,0 +1,790 @@ +/********************************************************************** + * 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 "mfcpch.h" +#include +#include +#include "varable.h" +#include "tessvars.h" +#include "control.h" +#include "reject.h" +#include "fixxht.h" +#include "secname.h" + +#define EXTERN + +EXTERN double_VAR (x_ht_fraction_of_caps_ht, 0.7, +"Fract of cps ht est of xht"); +EXTERN double_VAR (x_ht_variation, 0.35, +"Err band as fract of caps/xht dist"); +EXTERN double_VAR (x_ht_sub_variation, 0.5, +"Err band as fract of caps/xht dist"); +EXTERN BOOL_VAR (rej_trial_ambigs, TRUE, +"reject x-ht ambigs when under trial"); +EXTERN BOOL_VAR (x_ht_conservative_ambigs, FALSE, +"Dont rely on ambigs + maxht"); +EXTERN BOOL_VAR (x_ht_check_est, TRUE, "Cross check estimates"); +EXTERN BOOL_VAR (x_ht_case_flip, FALSE, "Flip or reject suspect case"); +EXTERN BOOL_VAR (x_ht_include_dodgy_blobs, TRUE, +"Include blobs with possible noise?"); +EXTERN BOOL_VAR (x_ht_limit_flip_trials, TRUE, +"Dont do trial flips when ambigs are close to xht?"); +EXTERN BOOL_VAR (rej_use_check_block_occ, TRUE, +"Analyse rejection behaviour"); + +EXTERN STRING_VAR (chs_non_ambig_caps_ht, +"!#$%&()/12346789?ABDEFGHIKLNQRT[]\\bdfhkl", +"Reliable ascenders"); +EXTERN STRING_VAR (chs_x_ht, "acegmnopqrsuvwxyz", "X height chars"); +EXTERN STRING_VAR (chs_non_ambig_x_ht, "aenqr", "reliable X height chars"); +EXTERN STRING_VAR (chs_ambig_caps_x, "cCmMoO05sSuUvVwWxXzZ", +"X ht or caps ht chars"); +EXTERN STRING_VAR (chs_bl_ambig_caps_x, "pPyY", " Caps or descender ambigs"); + +/* The following arent used in this module but are used in applybox.c */ +EXTERN STRING_VAR (chs_caps_ht, +"!#$%&()/0123456789?ABCDEFGHIJKLMNOPQRSTUVWXYZ[]\\bdfhkl{|}", +"Ascender chars"); +EXTERN STRING_VAR (chs_desc, "gjpqy", "Descender chars"); +EXTERN STRING_VAR (chs_non_ambig_bl, +"!#$%&01246789?ABCDEFGHIKLMNORSTUVWXYZabcdehiklmnorstuvwxz", +"Reliable baseline chars"); +EXTERN STRING_VAR (chs_odd_top, "ijt", "Chars with funny ascender region"); +EXTERN STRING_VAR (chs_odd_bot, "()35JQ[]\\/{}|", "Chars with funny base"); + +/* The following arent used but are defined for completeness */ +EXTERN STRING_VAR (chs_bl, +"!#$%&()/01246789?ABCDEFGHIJKLMNOPRSTUVWXYZ[]\\abcdefhiklmnorstuvwxz{}", +"Baseline chars"); +EXTERN STRING_VAR (chs_non_ambig_desc, "gq", "Reliable descender chars"); + +/************************************************************************* + * re_estimate_x_ht() + * + * Walk the blobs in the word together with the text string and reject map. + * NOTE: All evaluation is done on the baseline normalised word. This is so that + * the BOX class can be used (integer). The reasons for this are: + * a) We must use the outword - ie the Tess result + * b) The outword is always converted to integer representation as that is how + * Tess works + * c) We would like to use the BOX class, cos its there - this is integer + * precision. + * d) If we de-normed the outword we would get rounding errors and would find + * that integers are too imprecise (x-height around 15 pixels instead of a + * scale of 128 in bln form. + * CONVINCED? + * + * A) Try to re-estimatate x-ht and caps ht from confirmed pts in word. + * + * FOR each non reject blob + * IF char is baseline posn ambiguous + * Remove ambiguity by comparing its posn with respect to baseline. + * IF char is a confirmed x-ht char + * Add x-ht posn to confirmed_x_ht pts for word + * IF char is a confirmed caps-ht char + * Add blob_ht to caps ht pts for word + * + * IF Std Dev of caps hts < 2 (AND # samples > 0) + * Use mean as caps ht estimate (Dont use median as we can expect a + * fair variation between the heights of the NON_AMBIG_CAPS_HT_CHS) + * IF Std Dev of caps hts >= 2 (AND # samples > 0) + * Suspect small caps font. + * Look for 2 clusters, each with Std Dev < 2. + * IF 2 clusters found + * Pick the smaller median as the caps ht estimate of the smallcaps. + * + * IF failed to estimate a caps ht + * Use the median caps ht if there is one, + * ELSE use the caps ht estimate of the previous word. NO!!! + * + * + * IF there are confirmed x-height chars + * Estimate confirmed x-height as the median value + * ELSE IF there is a confirmed caps ht + * Estimate confirmed x-height as a fraction of confirmed caps ht value + * ELSE + * Use the value for the previous word or the row value if this is the + * first word in the block. NO!!! + * + * B) Add in case ambiguous blobs based on confirmed x-ht/caps ht, changing case + * as necessary. Reestimate caps ht and x-ht as in A, using the extended + * clusters. + * + * C) If word contains rejects, and x-ht estimate significantly differs from + * original estimate, return TRUE so that the word can be rematched + *************************************************************************/ + +void re_estimate_x_ht( //improve for 1 word + WERD_RES *word_res, //word to do + float *trial_x_ht //new match value + ) { + PBLOB_IT blob_it; + INT16 blob_ht_above_baseline; + + const char *word_str; + INT16 i; + + STATS all_blobs_ht (0, 300); //every blob in word + STATS x_ht (0, 300); //confirmed pts in wd + STATS caps_ht (0, 300); //confirmed pts in wd + STATS case_ambig (0, 300); //lower case ambigs + + INT16 rej_blobs_count = 0; + INT16 rej_blobs_max_height = 0; + INT32 rej_blobs_max_area = 0; + float x_ht_ok_variation; + float max_blob_ht; + float marginally_above_x_ht; + + BOX blob_box; //blob bounding box + float est_x_ht = 0.0; //word estimate + float est_caps_ht = 0.0; //word estimate + //based on hard data? + BOOL8 est_caps_ht_certain = FALSE; + BOOL8 est_x_ht_certain = FALSE;//based on hard data? + BOOL8 trial = FALSE; //Sepeculative values? + BOOL8 no_comment = FALSE; //No change in xht + float ambig_lc_x_est; + float ambig_uc_caps_est; + INT16 x_ht_ambigs = 0; + INT16 caps_ht_ambigs = 0; + + /* Calculate default variation of blob x_ht from bln x_ht for bln word */ + x_ht_ok_variation = + (bln_x_height / x_ht_fraction_of_caps_ht - bln_x_height) * x_ht_variation; + + word_str = word_res->best_choice->string ().string (); + /* + Cycle blobs, allocating to one of the stats sets when possible. + */ + blob_it.set_to_list (word_res->outword->blob_list ()); + for (blob_it.mark_cycle_pt (), i = 0; + !blob_it.cycled_list (); blob_it.forward (), i++) { + if (!dodgy_blob (blob_it.data ())) { + blob_box = blob_it.data ()->bounding_box (); + blob_ht_above_baseline = blob_box.top () - bln_baseline_offset; + all_blobs_ht.add (blob_ht_above_baseline, 1); + + if (word_res->reject_map[i].rejected ()) { + rej_blobs_count++; + if (blob_box.height () > rej_blobs_max_height) + rej_blobs_max_height = blob_box.height (); + if (blob_box.area () > rej_blobs_max_area) + rej_blobs_max_area = blob_box.area (); + } + else { + if (STRING (chs_non_ambig_x_ht).contains (word_str[i])) + x_ht.add (blob_ht_above_baseline, 1); + + if (STRING (chs_non_ambig_caps_ht).contains (word_str[i])) + caps_ht.add (blob_ht_above_baseline, 1); + + if (STRING (chs_ambig_caps_x).contains (word_str[i])) { + case_ambig.add (blob_ht_above_baseline, 1); + if (STRING (chs_x_ht).contains (word_str[i])) + x_ht_ambigs++; + else + caps_ht_ambigs++; + } + + if (STRING (chs_bl_ambig_caps_x).contains (word_str[i])) { + if (STRING (chs_x_ht).contains (word_str[i])) { + /* confirm x_height provided > 15% total height below baseline */ + if ((bln_baseline_offset - blob_box.bottom ()) / + (float) blob_box.height () > 0.15) + x_ht.add (blob_ht_above_baseline, 1); + } + else { + /* confirm caps_height provided < 5% total height below baseline */ + if ((bln_baseline_offset - blob_box.bottom ()) / + (float) blob_box.height () < 0.05) + caps_ht.add (blob_ht_above_baseline, 1); + } + } + } + } + } + est_caps_ht = estimate_from_stats (caps_ht); + est_x_ht = estimate_from_stats (x_ht); + est_ambigs(word_res, case_ambig, &ambig_lc_x_est, &ambig_uc_caps_est); + max_blob_ht = all_blobs_ht.ile (0.9999); + + #ifndef SECURE_NAMES + if (debug_x_ht_level >= 20) { + tprintf ("Mode20:A: %s ", word_str); + word_res->reject_map.print (debug_fp); + tprintf (" XHT:%f CAP:%f MAX:%f AMBIG X:%f CAP:%f\n", + est_x_ht, est_caps_ht, max_blob_ht, + ambig_lc_x_est, ambig_uc_caps_est); + } + #endif + if (!x_ht_conservative_ambigs && + (ambig_lc_x_est > 0) && + (ambig_lc_x_est == ambig_uc_caps_est) && + (max_blob_ht > ambig_lc_x_est + x_ht_ok_variation)) { + //may be zero but believe xht + ambig_uc_caps_est = est_caps_ht; + #ifndef SECURE_NAMES + if (debug_x_ht_level >= 20) + tprintf ("Mode20:B: Fiddle ambig_uc_caps_est to %f\n", + ambig_lc_x_est); + #endif + } + + /* Now make some estimates */ + + if ((est_x_ht > 0) || + (est_caps_ht > 0) || + ((ambig_lc_x_est > 0) && (ambig_lc_x_est != ambig_uc_caps_est))) { + /* There is some sensible data to go on so make the most of it. */ + if (debug_x_ht_level >= 20) + tprintf ("Mode20:C: Sensible Data\n", ambig_lc_x_est); + if (est_x_ht > 0) { + est_x_ht_certain = TRUE; + if (est_caps_ht == 0) { + if ((ambig_uc_caps_est > ambig_lc_x_est) && + (ambig_uc_caps_est > est_x_ht + x_ht_ok_variation)) + est_caps_ht = ambig_uc_caps_est; + else + est_caps_ht = est_x_ht / x_ht_fraction_of_caps_ht; + } + if (case_ambig.get_total () > 0) + improve_estimate(word_res, est_x_ht, est_caps_ht, x_ht, caps_ht); + est_caps_ht_certain = caps_ht.get_total () > 0; + #ifndef SECURE_NAMES + if (debug_x_ht_level >= 20) + tprintf ("Mode20:D: Est from xht XHT:%f CAP:%f\n", + est_x_ht, est_caps_ht); + #endif + } + else if (est_caps_ht > 0) { + est_caps_ht_certain = TRUE; + if ((ambig_lc_x_est > 0) && + (ambig_lc_x_est < est_caps_ht - x_ht_ok_variation)) + est_x_ht = ambig_lc_x_est; + else + est_x_ht = est_caps_ht * x_ht_fraction_of_caps_ht; + if (ambig_lc_x_est + ambig_uc_caps_est > 0) + improve_estimate(word_res, est_x_ht, est_caps_ht, x_ht, caps_ht); + est_x_ht_certain = x_ht.get_total () > 0; + #ifndef SECURE_NAMES + if (debug_x_ht_level >= 20) + tprintf ("Mode20:E: Est from caps XHT:%f CAP:%f\n", + est_x_ht, est_caps_ht); + #endif + } + else { + /* Do something based on case ambig chars alone - we have guessed that the + ambigs are lower case. */ + est_x_ht = ambig_lc_x_est; + est_x_ht_certain = TRUE; + if (ambig_uc_caps_est > ambig_lc_x_est) { + est_caps_ht = ambig_uc_caps_est; + est_caps_ht_certain = TRUE; + } + else + est_caps_ht = est_x_ht / x_ht_fraction_of_caps_ht; + + #ifndef SECURE_NAMES + if (debug_x_ht_level >= 20) + tprintf ("Mode20:F: Est from ambigs XHT:%f CAP:%f\n", + est_x_ht, est_caps_ht); + #endif + } + /* Check for sane interpretation of evidence: + Try shifting caps ht if min certain caps ht is not significantly greater + than the estimated x ht or the max certain x ht is not significantly less + than the estimated caps ht. */ + if (x_ht_check_est) { + if ((caps_ht.get_total () > 0) && + (est_x_ht + x_ht_ok_variation >= caps_ht.ile (0.0001))) { + trial = TRUE; + est_caps_ht = est_x_ht; + est_x_ht = x_ht_fraction_of_caps_ht * est_caps_ht; + + #ifndef SECURE_NAMES + if (debug_x_ht_level >= 20) + tprintf ("Mode20:G: Trial XHT:%f CAP:%f\n", + est_x_ht, est_caps_ht); + #endif + } + else if ((x_ht.get_total () > 0) && + (est_caps_ht - x_ht_ok_variation <= x_ht.ile (0.9999))) { + trial = TRUE; + est_x_ht = est_caps_ht; + est_caps_ht = est_x_ht / x_ht_fraction_of_caps_ht; + #ifndef SECURE_NAMES + if (debug_x_ht_level >= 20) + tprintf ("Mode20:H: Trial XHT:%f CAP:%f\n", + est_x_ht, est_caps_ht); + #endif + } + } + } + + else { + /* There is no sensible data so we're in the dark. */ + + marginally_above_x_ht = bln_x_height + + x_ht_ok_variation * x_ht_sub_variation; + /* + If there are no rejects, or the only rejects have a narrow height, or have + a small area compared to a normal char, then estimate the x-height as the + original one. (I.e dont fiddle about if the only rejects look like + punctuation) - we use max height as mean or median will be too low if + there are only two blobs - Eg "F." + */ + + if (debug_x_ht_level >= 20) + tprintf ("Mode20:I: In the dark\n"); + + if ((rej_blobs_count == 0) || + (rej_blobs_max_height < 0.3 * max_blob_ht) || + (rej_blobs_max_area < 0.3 * max_blob_ht * max_blob_ht)) { + no_comment = TRUE; + if (debug_x_ht_level >= 20) + tprintf ("Mode20:J: No comment due to no rejects\n"); + } + else if (x_ht_limit_flip_trials && + ((max_blob_ht < marginally_above_x_ht) || + ((ambig_lc_x_est > 0) && + (ambig_lc_x_est == ambig_uc_caps_est) && + (ambig_lc_x_est < marginally_above_x_ht)))) { + no_comment = TRUE; + if (debug_x_ht_level >= 20) + tprintf ("Mode20:K: No comment as close to xht %f < %f\n", + ambig_lc_x_est, marginally_above_x_ht); + } + else if (x_ht_conservative_ambigs && (ambig_uc_caps_est > 0)) { + trial = TRUE; + est_caps_ht = ambig_lc_x_est; + est_x_ht = x_ht_fraction_of_caps_ht * est_caps_ht; + + #ifndef SECURE_NAMES + if (debug_x_ht_level >= 20) + tprintf ("Mode20:L: Trial XHT:%f CAP:%f\n", + est_x_ht, est_caps_ht); + #endif + } + /* + If the top of the word is nowhere near where we expect ascenders to be + (less than half the x_ht -> caps_ht distance) - suspect an all caps word + at the x-ht. Estimate x-ht accordingly - but only as a TRIAL! + NOTE we do NOT check location of baseline. Commas can descend as much as + real descenders so we would need to do something to make sure that any + disqualifying descenders were not at the end. + */ + else { + if (max_blob_ht < + (bln_x_height + bln_x_height / x_ht_fraction_of_caps_ht) / 2.0) { + trial = TRUE; + est_x_ht = x_ht_fraction_of_caps_ht * max_blob_ht; + est_caps_ht = max_blob_ht; + + #ifndef SECURE_NAMES + if (debug_x_ht_level >= 20) + tprintf ("Mode20:M: Trial XHT:%f CAP:%f\n", + est_x_ht, est_caps_ht); + #endif + } + else { + no_comment = TRUE; + if (debug_x_ht_level >= 20) + tprintf ("Mode20:N: No comment as nothing else matched\n"); + } + } + } + + /* Sanity check - reject word if fails */ + + if (!no_comment && + ((est_x_ht > 2 * bln_x_height) || + (est_x_ht / word_res->denorm.scale () <= min_sane_x_ht_pixels) || + (est_caps_ht <= est_x_ht) || (est_caps_ht >= 2.5 * est_x_ht))) { + no_comment = TRUE; + if (!trial && rej_use_xht) { + if (debug_x_ht_level >= 2) { + tprintf ("Sanity check rejecting %s ", word_str); + word_res->reject_map.print (debug_fp); + tprintf ("\n"); + } + word_res->reject_map.rej_word_xht_fixup (); + + } + if (debug_x_ht_level >= 20) + tprintf ("Mode20:O: No comment as nothing else matched\n"); + } + + if (no_comment || trial) { + word_res->x_height = bln_x_height / word_res->denorm.scale (); + word_res->guessed_x_ht = TRUE; + word_res->caps_height = (bln_x_height / x_ht_fraction_of_caps_ht) / + word_res->denorm.scale (); + word_res->guessed_caps_ht = TRUE; + /* + Reject ambigs in the current word if we are uncertain and: + there are rejects OR + there is only one char which is an ambig OR + there is conflict between the case of the ambigs even though there is + no height separation Eg "Ms" recognised from "MS" + */ + if (rej_trial_ambigs && + ((word_res->reject_map.reject_count () > 0) || + (word_res->reject_map.length () == 1) || + ((x_ht_ambigs > 0) && (caps_ht_ambigs > 0)))) { + #ifndef SECURE_NAMES + if (debug_x_ht_level >= 2) { + tprintf ("TRIAL Rej Ambigs %s ", word_str); + word_res->reject_map.print (debug_fp); + } + #endif + reject_ambigs(word_res); + if (debug_x_ht_level >= 2) { + tprintf (" "); + word_res->reject_map.print (debug_fp); + tprintf ("\n"); + } + } + } + else { + word_res->x_height = est_x_ht / word_res->denorm.scale (); + word_res->guessed_x_ht = !est_x_ht_certain; + word_res->caps_height = est_caps_ht / word_res->denorm.scale (); + word_res->guessed_caps_ht = !est_caps_ht_certain; + } + + if (!no_comment && (fabs (est_x_ht - bln_x_height) > x_ht_ok_variation)) + *trial_x_ht = est_x_ht / word_res->denorm.scale (); + else + *trial_x_ht = 0.0; + + #ifndef SECURE_NAMES + if (((*trial_x_ht > 0) && (debug_x_ht_level >= 3)) || + (debug_x_ht_level >= 5)) { + tprintf ("%s ", word_str); + word_res->reject_map.print (debug_fp); + tprintf + (" X:%0.2f Cps:%0.2f Mxht:%0.2f RJ MxHt:%d MxAr:%d Rematch:%c\n", + est_x_ht, est_caps_ht, max_blob_ht, rej_blobs_max_height, + rej_blobs_max_area, *trial_x_ht > 0 ? '*' : ' '); + } + #endif + +} + + +/************************************************************************* + * check_block_occ() + * Checks word for coarse block occupancy, rejecting more chars and flipping + * case of case ambiguous chars as required. + *************************************************************************/ + +void check_block_occ(WERD_RES *word_res) { + PBLOB_IT blob_it; + STRING new_string; + REJMAP new_map = word_res->reject_map; + WERD_CHOICE *new_choice; + + const char *word_str = word_res->best_choice->string ().string (); + INT16 i; + INT16 reject_count = 0; + char confirmed_char; + float x_ht; + float caps_ht; + + if (word_res->x_height > 0) + x_ht = word_res->x_height * word_res->denorm.scale (); + else + x_ht = bln_x_height; + + if (word_res->caps_height > 0) + caps_ht = word_res->caps_height * word_res->denorm.scale (); + else + caps_ht = x_ht / x_ht_fraction_of_caps_ht; + + blob_it.set_to_list (word_res->outword->blob_list ()); + + for (blob_it.mark_cycle_pt (), i = 0; + !blob_it.cycled_list (); blob_it.forward (), i++) { + new_string += word_str[i]; //default copy + if (word_res->reject_map[i].accepted ()) { + confirmed_char = check_blob_occ (word_str[i], + blob_it.data ()->bounding_box (). + top () - bln_baseline_offset, x_ht, + caps_ht); + + if (confirmed_char == '\0') { + if (rej_use_check_block_occ) { + new_map[i].setrej_xht_fixup (); + reject_count++; + } + } + else + new_string[i] = confirmed_char; + } + } + if ((reject_count > 0) || (new_string != word_str)) { + if (debug_x_ht_level >= 2) { + tprintf ("Shape Verification: %s ", word_str); + word_res->reject_map.print (debug_fp); + tprintf (" -> %s ", new_string.string ()); + new_map.print (debug_fp); + tprintf ("\n"); + } + new_choice = new WERD_CHOICE (new_string.string (), + word_res->best_choice->rating (), + word_res->best_choice->certainty (), + word_res->best_choice->permuter ()); + delete word_res->best_choice; + word_res->best_choice = new_choice; + word_res->reject_map = new_map; + } +} + + +/************************************************************************* + * check_blob_occ() + * + * Checks blob for position relative to position above baseline + * Returns 0 for reject, or (possibly case shifted) confirmed char + *************************************************************************/ + +char check_blob_occ(char proposed_char, + INT16 blob_ht_above_baseline, + float x_ht, + float caps_ht) { + BOOL8 blob_definite_x_ht; + BOOL8 blob_definite_caps_ht; + float acceptable_variation; + + acceptable_variation = (caps_ht - x_ht) * x_ht_variation; + /* ??? REJECT if expected descender and nothing significantly below BL */ + + /* ??? REJECT if expected ascender and nothing significantly above x-ht */ + + /* + IF AMBIG_CAPS_X_CHS + IF blob is definitely an ascender ( > xht + xht err )AND + char is an x-ht char + THEN + flip case + IF blob is defintiely an x-ht ( <= xht + xht err ) AND + char is an ascender char + THEN + flip case + */ + blob_definite_x_ht = blob_ht_above_baseline <= x_ht + acceptable_variation; + blob_definite_caps_ht = blob_ht_above_baseline >= + caps_ht - acceptable_variation; + + if (STRING (chs_ambig_caps_x).contains (proposed_char)) { + if ((!blob_definite_x_ht && !blob_definite_caps_ht) || + (proposed_char == '0' && !blob_definite_caps_ht) || + (proposed_char == 'o' && !blob_definite_x_ht)) + return '\0'; + + else if (blob_definite_caps_ht && + STRING (chs_x_ht).contains (proposed_char)) { + if (x_ht_case_flip) + //flip to upper case + return (char) toupper (proposed_char); + else + return '\0'; + } + + else if (blob_definite_x_ht && + !STRING (chs_x_ht).contains (proposed_char)) { + if (x_ht_case_flip) + //flip to lower case + return (char) tolower (proposed_char); + else + return '\0'; + } + } + else + if ((STRING (chs_non_ambig_x_ht).contains (proposed_char) + && !blob_definite_x_ht) + || (STRING (chs_non_ambig_caps_ht).contains (proposed_char) + && !blob_definite_caps_ht)) + return '\0'; + return proposed_char; +} + + +float estimate_from_stats(STATS &stats) { + if (stats.get_total () <= 0) + return 0.0; + else if (stats.get_total () >= 3) + return stats.ile (0.5); //median + else + return stats.mean (); +} + + +void improve_estimate(WERD_RES *word_res, + float &est_x_ht, + float &est_caps_ht, + STATS &x_ht, + STATS &caps_ht) { + PBLOB_IT blob_it; + INT16 blob_ht_above_baseline; + + const char *word_str; + INT16 i; + BOX blob_box; //blob bounding box + char confirmed_char; + float new_val; + + /* IMPROVE estimates here - if good estimates, and case ambig chars, + rescan blobs to fix case ambig blobs, re-estimate hts ??? maybe always do + it after deciding x-height + */ + + blob_it.set_to_list (word_res->outword->blob_list ()); + word_str = word_res->best_choice->string ().string (); + for (blob_it.mark_cycle_pt (), i = 0; + !blob_it.cycled_list (); blob_it.forward (), i++) { + if ((STRING (chs_ambig_caps_x).contains (word_str[i])) && + (!dodgy_blob (blob_it.data ()))) { + blob_box = blob_it.data ()->bounding_box (); + blob_ht_above_baseline = blob_box.top () - bln_baseline_offset; + confirmed_char = check_blob_occ (word_str[i], + blob_ht_above_baseline, + est_x_ht, est_caps_ht); + if (confirmed_char != '\0') + if (STRING (chs_x_ht).contains (confirmed_char)) + x_ht.add (blob_ht_above_baseline, 1); + else + caps_ht.add (blob_ht_above_baseline, 1); + } + } + new_val = estimate_from_stats (x_ht); + if (new_val > 0) + est_x_ht = new_val; + new_val = estimate_from_stats (caps_ht); + if (new_val > 0) + est_caps_ht = new_val; +} + + +void reject_ambigs( //rej any accepted xht ambig chars + WERD_RES *word) { + const char *word_str; + int i = 0; + + word_str = word->best_choice->string ().string (); + while (*word_str != '\0') { + if (STRING (chs_ambig_caps_x).contains (*word_str)) + word->reject_map[i].setrej_xht_fixup (); + word_str++; + i++; + } +} + + +void est_ambigs( //xht ambig ht stats + WERD_RES *word_res, + STATS &stats, + float *ambig_lc_x_est, //xht est + float *ambig_uc_caps_est //caps est + ) { + float x_ht_ok_variation; + STATS short_ambigs (0, 300); + STATS tall_ambigs (0, 300); + PBLOB_IT blob_it; + BOX blob_box; //blob bounding box + INT16 blob_ht_above_baseline; + + const char *word_str; + INT16 i; + float min; //min ambig ch ht + float max; //max ambig ch ht + float short_limit; // for lower case + float tall_limit; // for upper case + + x_ht_ok_variation = + (bln_x_height / x_ht_fraction_of_caps_ht - bln_x_height) * x_ht_variation; + + if (stats.get_total () == 0) { + *ambig_lc_x_est = 0; + *ambig_uc_caps_est = 0; + } + else { + min = stats.ile (0.0); + max = stats.ile (0.99999); + if ((max - min) < x_ht_ok_variation) { + *ambig_lc_x_est = *ambig_uc_caps_est = stats.mean (); + //close enough + } + else { + /* Try reclustering into lower and upper case chars */ + short_limit = min + (max - min) * x_ht_variation; + tall_limit = max - (max - min) * x_ht_variation; + word_str = word_res->best_choice->string ().string (); + blob_it.set_to_list (word_res->outword->blob_list ()); + for (blob_it.mark_cycle_pt (), i = 0; + !blob_it.cycled_list (); blob_it.forward (), i++) { + if (word_res->reject_map[i].accepted () && + STRING (chs_ambig_caps_x).contains (word_str[i]) && + (!dodgy_blob (blob_it.data ()))) { + blob_box = blob_it.data ()->bounding_box (); + blob_ht_above_baseline = + blob_box.top () - bln_baseline_offset; + if (blob_ht_above_baseline <= short_limit) + short_ambigs.add (blob_ht_above_baseline, 1); + else if (blob_ht_above_baseline >= tall_limit) + tall_ambigs.add (blob_ht_above_baseline, 1); + } + } + *ambig_lc_x_est = short_ambigs.mean (); + *ambig_uc_caps_est = tall_ambigs.mean (); + /* Cop out if we havent got sensible clusters. */ + if (*ambig_uc_caps_est - *ambig_lc_x_est <= x_ht_ok_variation) + *ambig_lc_x_est = *ambig_uc_caps_est = stats.mean (); + //close enough + } + } +} + + +/************************************************************************* + * dodgy_blob() + * Returns true if the blob has more than one outline, one above the other. + * These are dodgy as the top blob could be noise, causing the bounding box xht + * to be misleading + *************************************************************************/ + +BOOL8 dodgy_blob(PBLOB *blob) { + OUTLINE_IT outline_it = blob->out_list (); + INT16 highest_bottom = -MAX_INT16; + INT16 lowest_top = MAX_INT16; + BOX outline_box; + + if (x_ht_include_dodgy_blobs) + return FALSE; //no blob is ever dodgy + for (outline_it.mark_cycle_pt (); + !outline_it.cycled_list (); outline_it.forward ()) { + outline_box = outline_it.data ()->bounding_box (); + if (lowest_top > outline_box.top ()) + lowest_top = outline_box.top (); + if (highest_bottom < outline_box.bottom ()) + highest_bottom = outline_box.bottom (); + } + return highest_bottom >= lowest_top; +} diff --git a/ccmain/fixxht.h b/ccmain/fixxht.h new file mode 100644 index 0000000000..95ac5ec5d4 --- /dev/null +++ b/ccmain/fixxht.h @@ -0,0 +1,92 @@ +/********************************************************************** + * File: fixxht.h (Formerly fixxht.h) + * 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. + * + **********************************************************************/ + +#ifndef FIXXHT_H +#define FIXXHT_H + +#include "varable.h" +#include "statistc.h" +#include "pageres.h" +#include "notdll.h" + +extern double_VAR_H (x_ht_fraction_of_caps_ht, 0.7, +"Fract of cps ht est of xht"); +extern double_VAR_H (x_ht_variation, 0.35, +"Err band as fract of caps/xht dist"); +extern double_VAR_H (x_ht_sub_variation, 0.5, +"Err band as fract of caps/xht dist"); +extern BOOL_VAR_H (rej_trial_ambigs, TRUE, +"reject x-ht ambigs when under trial"); +extern BOOL_VAR_H (x_ht_conservative_ambigs, FALSE, +"Dont rely on ambigs + maxht"); +extern BOOL_VAR_H (x_ht_check_est, TRUE, "Cross check estimates"); +extern BOOL_VAR_H (x_ht_case_flip, FALSE, "Flip or reject suspect case"); +extern BOOL_VAR_H (x_ht_include_dodgy_blobs, TRUE, +"Include blobs with possible noise?"); +extern BOOL_VAR_H (x_ht_limit_flip_trials, TRUE, +"Dont do trial flips when ambigs are close to xht?"); +extern BOOL_VAR_H (rej_use_check_block_occ, TRUE, +"Analyse rejection behaviour"); +extern STRING_VAR_H (chs_non_ambig_caps_ht, +"!#$%&()/12346789?ABDEFGHIKLNQRT[]\\bdfhkl", +"Reliable ascenders"); +extern STRING_VAR_H (chs_x_ht, "acegmnopqrsuvwxyz", "X height chars"); +extern STRING_VAR_H (chs_non_ambig_x_ht, "aenqr", "reliable X height chars"); +extern STRING_VAR_H (chs_ambig_caps_x, "cCmMoO05sSuUvVwWxXzZ", +"X ht or caps ht chars"); +extern STRING_VAR_H (chs_bl_ambig_caps_x, "pPyY", +" Caps or descender ambigs"); +extern STRING_VAR_H (chs_caps_ht, +"!#$%&()/0123456789?ABCDEFGHIJKLMNOPQRSTUVWXYZ[]\\bdfhkl{|}", +"Ascender chars"); +extern STRING_VAR_H (chs_desc, "gjpqy", "Descender chars"); +extern STRING_VAR_H (chs_non_ambig_bl, +"!#$%&01246789?ABCDEFGHIKLMNORSTUVWXYZabcdehiklmnorstuvwxz", +"Reliable baseline chars"); +extern STRING_VAR_H (chs_odd_top, "ijt", "Chars with funny ascender region"); +extern STRING_VAR_H (chs_odd_bot, "()35JQ[]\\/{}|", "Chars with funny base"); +extern STRING_VAR_H (chs_bl, +"!#$%&()/01246789?ABCDEFGHIJKLMNOPRSTUVWXYZ[]\\abcdefhiklmnorstuvwxz{}", +"Baseline chars"); +extern STRING_VAR_H (chs_non_ambig_desc, "gq", "Reliable descender chars"); +void re_estimate_x_ht( //improve for 1 word + WERD_RES *word_res, //word to do + float *trial_x_ht //new match value + ); +void check_block_occ(WERD_RES *word_res); +char check_blob_occ(char proposed_char, + INT16 blob_ht_above_baseline, + float x_ht, + float caps_ht); +float estimate_from_stats(STATS &stats); +void improve_estimate(WERD_RES *word_res, + float &est_x_ht, + float &est_caps_ht, + STATS &x_ht, + STATS &caps_ht); +void reject_ambigs( //rej any accepted xht ambig chars + WERD_RES *word); + //xht ambig ht stats +void est_ambigs(WERD_RES *word_res, + STATS &stats, + float *ambig_lc_x_est, //xht est + float *ambig_uc_caps_est //caps est + ); +BOOL8 dodgy_blob(PBLOB *blob); +#endif diff --git a/ccmain/imgscale.cpp b/ccmain/imgscale.cpp new file mode 100644 index 0000000000..d2d256c583 --- /dev/null +++ b/ccmain/imgscale.cpp @@ -0,0 +1,154 @@ +/********************************************************************** + * File: imgscale.cpp (Formerly dyn_prog.c) + * Description: Dynamic programming for smart scaling of images. + * Author: Phil Cheatle + * Created: Wed Nov 18 16:12:03 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. + * + **********************************************************************/ + +/************************************************************************* + * This is really Sheelagh's code that I've hacked into a more usable form. + * It is used by scaleim.c All I did to it was to change "factor" from int to + * float. + *************************************************************************/ +/************************************************************************ + * This version uses the result of the previous row to influence the + * current row's calculation. + ************************************************************************/ + +#include "mfcpch.h" +#include +#include +#include "errcode.h" + +#define f(xc, yc) ((xc - factor*yc)*(xc - factor*yc)) + +#define g(oldyc, yc, oldxc, xc) (factor*factor*(oldyc - yc)*(oldyc - yc)/(abs(oldxc - xc) + 1)) + +void +dyn_exit (const char s[]) { + fprintf (stderr, "%s", s); + err_exit(); +} + + +void dyn_prog( //The clever bit + int n, + int *x, + int *y, + int ymax, + int *oldx, + int *oldy, + int oldn, + float factor) { + int i, z, j, matchflag; + int **ymin; + float **F, fz; + + /* F[i][z] gives minimum over y <= z */ + + F = (float **) calloc (n, sizeof (float *)); + ymin = (int **) calloc (n, sizeof (int *)); + if ((F == NULL) || (ymin == NULL)) + dyn_exit ("Error in calloc\n"); + + for (i = 0; i < n; i++) { + F[i] = (float *) calloc (ymax - n + i + 1, sizeof (float)); + ymin[i] = (int *) calloc (ymax - n + i + 1, sizeof (int)); + if ((F[i] == NULL) || (ymin[i] == NULL)) + dyn_exit ("Error in calloc\n"); + } + + F[0][0] = f (x[0], 0); + /* find nearest transition of same sign (white to black) */ + j = 0; + while ((j < oldn) && (oldx[j] < x[0])) + j += 2; + if (j >= oldn) + j -= 2; + else if ((j - 2 >= 0) && ((x[0] - oldx[j - 2]) < (oldx[j] - x[0]))) + j -= 2; + if (abs (oldx[j] - x[0]) < factor) { + matchflag = 1; + F[0][0] += g (oldy[j], 0, oldx[j], x[0]); + } + else + matchflag = 0; + ymin[0][0] = 0; + + for (z = 1; z < ymax - n + 1; z++) { + fz = f (x[0], z); + /* add penalty for deviating from previous row if necessary */ + + if (matchflag) + fz += g (oldy[j], z, oldx[j], x[0]); + if (fz < F[0][z - 1]) { + F[0][z] = fz; + ymin[0][z] = z; + } + else { + F[0][z] = F[0][z - 1]; + ymin[0][z] = ymin[0][z - 1]; + } + } + + for (i = 1; i < n; i++) { + F[i][i] = f (x[i], i) + F[i - 1][i - 1]; + /* add penalty for deviating from previous row if necessary */ + if (j > 0) + j--; + else + j++; + while ((j < oldn) && (oldx[j] < x[i])) + j += 2; + if (j >= oldn) + j -= 2; + else if ((j - 2 >= 0) && ((x[i] - oldx[j - 2]) < (oldx[j] - x[i]))) + j -= 2; + if (abs (oldx[j] - x[i]) < factor) { + matchflag = 1; + F[i][i] += g (oldy[j], i, oldx[j], x[i]); + } + else + matchflag = 0; + ymin[i][i] = i; + for (z = i + 1; z < ymax - n + i + 1; z++) { + fz = f (x[i], z) + F[i - 1][z - 1]; + /* add penalty for deviating from previous row if necessary */ + if (matchflag) + fz += g (oldy[j], z, oldx[j], x[i]); + if (fz < F[i][z - 1]) { + F[i][z] = fz; + ymin[i][z] = z; + } + else { + F[i][z] = F[i][z - 1]; + ymin[i][z] = ymin[i][z - 1]; + } + } + } + + y[n - 1] = ymin[n - 1][ymax - 1]; + for (i = n - 2; i >= 0; i--) + y[i] = ymin[i][y[i + 1] - 1]; + + for (i = 0; i < n; i++) { + free (F[i]); + free (ymin[i]); + } + free(F); + free(ymin); + + return; +} diff --git a/ccmain/imgscale.h b/ccmain/imgscale.h new file mode 100644 index 0000000000..335c0b0148 --- /dev/null +++ b/ccmain/imgscale.h @@ -0,0 +1,32 @@ +/********************************************************************** + * File: imgscale.h (Formerly dyn_prog.h) + * Description: Dynamic programming for smart scaling of images. + * Author: Phil Cheatle + * Created: Wed Nov 18 16:12:03 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 IMGSCALE_H +#define IMGSCALE_H + +void dyn_prog( //The clever bit + int n, + int *x, + int *y, + int ymax, + int *oldx, + int *oldy, + int oldn, + float factor); +#endif diff --git a/ccmain/matmatch.cpp b/ccmain/matmatch.cpp new file mode 100644 index 0000000000..1b13b9285a --- /dev/null +++ b/ccmain/matmatch.cpp @@ -0,0 +1,404 @@ +/********************************************************************** + * File: matmatch.cpp (Formerly matrix_match.c) + * Description: matrix matching routines for Tessedit + * Author: Chris Newton + * Created: Wed Nov 24 15:57:41 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 "mfcpch.h" +#include +#include +#include +#include +#ifdef __UNIX__ +#include +#endif +#include "tessvars.h" +#include "stderr.h" +#include "img.h" +#include "evnts.h" +#include "showim.h" +#include "hosthplb.h" +#include "grphics.h" +#include "evnts.h" +#include "adaptions.h" +#include "matmatch.h" +#include "secname.h" + +#define EXTERN + +EXTERN BOOL_VAR (tessedit_display_mm, FALSE, "Display matrix matches"); +EXTERN BOOL_VAR (tessedit_mm_debug, FALSE, +"Print debug information for matrix matcher"); +EXTERN INT_VAR (tessedit_mm_prototype_min_size, 3, +"Smallest number of samples in a cluster for a prototype to be used"); + +// Colours for displaying the match +#define BB_COLOUR 0 +#define BW_COLOUR 1 +#define WB_COLOUR 3 +#define UB_COLOUR 5 +#define BU_COLOUR 7 +#define UU_COLOUR 9 +#define WU_COLOUR 11 +#define UW_COLOUR 13 +#define WW_COLOUR 15 + +#define BINIM_BLACK 0 +#define BINIM_WHITE 1 + +float matrix_match( // returns match score + IMAGE *image1, + IMAGE *image2) { + ASSERT_HOST (image1->get_bpp () == 1 && image2->get_bpp () == 1); + + if (image1->get_xsize () >= image2->get_xsize ()) + return match1 (image1, image2); + else + return match1 (image2, image1); +} + + +float match1( /* returns match score */ + IMAGE *image_w, + IMAGE *image_n) { + INT32 x_offset; + INT32 y_offset; + INT32 x_size = image_w->get_xsize (); + INT32 y_size; + INT32 x_size2 = image_n->get_xsize (); + INT32 y_size2; + IMAGE match_image; + IMAGELINE imline_w; + IMAGELINE imline_n; + IMAGELINE match_imline; + INT32 x; + INT32 y; + float sum = 0.0; + + x_offset = (image_w->get_xsize () - image_n->get_xsize ()) / 2; + + ASSERT_HOST (x_offset >= 0); + match_imline.init (x_size); + + sum = 0; + + if (image_w->get_ysize () < image_n->get_ysize ()) { + y_size = image_n->get_ysize (); + y_size2 = image_w->get_ysize (); + y_offset = (y_size - y_size2) / 2; + + if (tessedit_display_mm && !tessedit_mm_use_prototypes) + tprintf ("I1 (%d, %d), I2 (%d, %d), MI (%d, %d)\n", x_size, + image_w->get_ysize (), x_size2, image_n->get_ysize (), + x_size, y_size); + + match_image.create (x_size, y_size, 4); + + for (y = 0; y < y_offset; y++) { + image_n->fast_get_line (0, y, x_size2, &imline_n); + for (x = 0; x < x_size2; x++) { + if (imline_n.pixels[x] == BINIM_BLACK) { + sum += -1; + match_imline.pixels[x] = UB_COLOUR; + } + else { + match_imline.pixels[x] = UW_COLOUR; + } + } + match_image.fast_put_line (x_offset, y, x_size2, &match_imline); + } + + for (y = y_offset + y_size2; y < y_size; y++) { + image_n->fast_get_line (0, y, x_size2, &imline_n); + for (x = 0; x < x_size2; x++) { + if (imline_n.pixels[x] == BINIM_BLACK) { + sum += -1.0; + match_imline.pixels[x] = UB_COLOUR; + } + else { + match_imline.pixels[x] = UW_COLOUR; + } + } + match_image.fast_put_line (x_offset, y, x_size2, &match_imline); + } + + for (y = y_offset; y < y_offset + y_size2; y++) { + image_w->fast_get_line (0, y - y_offset, x_size, &imline_w); + image_n->fast_get_line (0, y, x_size2, &imline_n); + for (x = 0; x < x_offset; x++) { + if (imline_w.pixels[x] == BINIM_BLACK) { + sum += -1.0; + match_imline.pixels[x] = BU_COLOUR; + } + else { + match_imline.pixels[x] = WU_COLOUR; + } + } + + for (x = x_offset + x_size2; x < x_size; x++) { + if (imline_w.pixels[x] == BINIM_BLACK) { + sum += -1.0; + match_imline.pixels[x] = BU_COLOUR; + } + else { + match_imline.pixels[x] = WU_COLOUR; + } + } + + for (x = x_offset; x < x_offset + x_size2; x++) { + if (imline_n.pixels[x - x_offset] == imline_w.pixels[x]) { + sum += 1.0; + if (imline_w.pixels[x] == BINIM_BLACK) + match_imline.pixels[x] = BB_COLOUR; + else + match_imline.pixels[x] = WW_COLOUR; + } + else { + sum += -1.0; + if (imline_w.pixels[x] == BINIM_BLACK) + match_imline.pixels[x] = BW_COLOUR; + else + match_imline.pixels[x] = WB_COLOUR; + } + } + + match_image.fast_put_line (0, y, x_size, &match_imline); + } + } + else { + y_size = image_w->get_ysize (); + y_size2 = image_n->get_ysize (); + y_offset = (y_size - y_size2) / 2; + + if (tessedit_display_mm && !tessedit_mm_use_prototypes) + tprintf ("I1 (%d, %d), I2 (%d, %d), MI (%d, %d)\n", x_size, + image_w->get_ysize (), x_size2, image_n->get_ysize (), + x_size, y_size); + + match_image.create (x_size, y_size, 4); + + for (y = 0; y < y_offset; y++) { + image_w->fast_get_line (0, y, x_size, &imline_w); + for (x = 0; x < x_size; x++) { + if (imline_w.pixels[x] == BINIM_BLACK) { + sum += -1; + match_imline.pixels[x] = BU_COLOUR; + } + else { + match_imline.pixels[x] = WU_COLOUR; + } + } + match_image.fast_put_line (0, y, x_size, &match_imline); + } + + for (y = y_offset + y_size2; y < y_size; y++) { + image_w->fast_get_line (0, y, x_size, &imline_w); + for (x = 0; x < x_size; x++) { + if (imline_w.pixels[x] == BINIM_BLACK) { + sum += -1; + match_imline.pixels[x] = BU_COLOUR; + } + else { + match_imline.pixels[x] = WU_COLOUR; + } + } + match_image.fast_put_line (0, y, x_size, &match_imline); + } + + for (y = y_offset; y < y_offset + y_size2; y++) { + image_w->fast_get_line (0, y, x_size, &imline_w); + image_n->fast_get_line (0, y - y_offset, x_size2, &imline_n); + for (x = 0; x < x_offset; x++) { + if (imline_w.pixels[x] == BINIM_BLACK) { + sum += -1.0; + match_imline.pixels[x] = BU_COLOUR; + } + else { + match_imline.pixels[x] = WU_COLOUR; + } + } + + for (x = x_offset + x_size2; x < x_size; x++) { + if (imline_w.pixels[x] == BINIM_BLACK) { + sum += -1.0; + match_imline.pixels[x] = BU_COLOUR; + } + else { + match_imline.pixels[x] = WU_COLOUR; + } + } + + for (x = x_offset; x < x_offset + x_size2; x++) { + if (imline_n.pixels[x - x_offset] == imline_w.pixels[x]) { + sum += 1.0; + if (imline_w.pixels[x] == BINIM_BLACK) + match_imline.pixels[x] = BB_COLOUR; + else + match_imline.pixels[x] = WW_COLOUR; + } + else { + sum += -1.0; + if (imline_w.pixels[x] == BINIM_BLACK) + match_imline.pixels[x] = BW_COLOUR; + else + match_imline.pixels[x] = WB_COLOUR; + } + } + + match_image.fast_put_line (0, y, x_size, &match_imline); + } + } + +#ifndef GRAPHICS_DISABLED + if (tessedit_display_mm && !tessedit_mm_use_prototypes) { + tprintf ("Match score %f\n", 1.0 - sum / (x_size * y_size)); + display_images(image_w, image_n, &match_image); + } +#endif + + if (tessedit_mm_debug) + tprintf ("Match score %f\n", 1.0 - sum / (x_size * y_size)); + + return (1.0 - sum / (x_size * y_size)); +} + + +/************************************************************************* + * display_images() + * + * Show a pair of images, plus the match image + * + *************************************************************************/ + +#ifndef GRAPHICS_DISABLED +void display_images(IMAGE *image_w, IMAGE *image_n, IMAGE *match_image) { + WINDOW w_im_window; + WINDOW n_im_window; + WINDOW match_window; + GRAPHICS_EVENT event; //output event + INT16 i; + + // xmin xmax ymin ymax + w_im_window = create_window ("Image 1", SCROLLINGWIN, 20, 100, 10 * image_w->get_xsize (), 10 * image_w->get_ysize (), 0, image_w->get_xsize (), 0, image_w->get_ysize (), + TRUE, FALSE, FALSE, TRUE); // down event & key only + + clear_view_surface(w_im_window); + show_sub_image (image_w, + 0, 0, + image_w->get_xsize (), image_w->get_ysize (), + w_im_window, 0, 0); + + line_color_index(w_im_window, RED); + for (i = 1; i < image_w->get_xsize (); i++) { + move2d (w_im_window, i, 0); + draw2d (w_im_window, i, image_w->get_ysize ()); + } + for (i = 1; i < image_w->get_ysize (); i++) { + move2d (w_im_window, 0, i); + draw2d (w_im_window, image_w->get_xsize (), i); + } + + // xmin xmax ymin ymax + n_im_window = create_window ("Image 2", SCROLLINGWIN, 240, 100, 10 * image_n->get_xsize (), 10 * image_n->get_ysize (), 0, image_n->get_xsize (), 0, image_n->get_ysize (), + TRUE, FALSE, FALSE, TRUE); // down event & key only + + clear_view_surface(n_im_window); + show_sub_image (image_n, + 0, 0, + image_n->get_xsize (), image_n->get_ysize (), + n_im_window, 0, 0); + + line_color_index(n_im_window, RED); + for (i = 1; i < image_n->get_xsize (); i++) { + move2d (n_im_window, i, 0); + draw2d (n_im_window, i, image_n->get_ysize ()); + } + for (i = 1; i < image_n->get_ysize (); i++) { + move2d (n_im_window, 0, i); + draw2d (n_im_window, image_n->get_xsize (), i); + } + overlap_picture_ops(TRUE); + + // xmin xmax ymin ymax + match_window = create_window ("Match Result", SCROLLINGWIN, 460, 100, 10 * match_image->get_xsize (), 10 * match_image->get_ysize (), 0, match_image->get_xsize (), 0, match_image->get_ysize (), + TRUE, FALSE, FALSE, TRUE); // down event & key only + + clear_view_surface(match_window); + show_sub_image (match_image, + 0, 0, + match_image->get_xsize (), match_image->get_ysize (), + match_window, 0, 0); + + line_color_index(match_window, RED); + for (i = 1; i < match_image->get_xsize (); i++) { + move2d (match_window, i, 0); + draw2d (match_window, i, match_image->get_ysize ()); + } + for (i = 1; i < match_image->get_ysize (); i++) { + move2d (match_window, 0, i); + draw2d (match_window, match_image->get_xsize (), i); + } + overlap_picture_ops(TRUE); + + await_event(match_window, TRUE, ANY_EVENT, &event); + destroy_window(w_im_window); + destroy_window(n_im_window); + destroy_window(match_window); +} + + +/************************************************************************* + * display_image() + * + * Show a single image + * + *************************************************************************/ + +WINDOW display_image(IMAGE *image, + const char *title, + INT32 x, + INT32 y, + BOOL8 wait) { + WINDOW im_window; + INT16 i; + GRAPHICS_EVENT event; //output event + + // xmin xmax ymin ymax + im_window = create_window (title, SCROLLINGWIN, x, y, 10 * image->get_xsize (), 10 * image->get_ysize (), 0, image->get_xsize (), 0, image->get_ysize (), + TRUE, FALSE, FALSE, TRUE); // down event & key only + + clear_view_surface(im_window); + show_sub_image (image, + 0, 0, + image->get_xsize (), image->get_ysize (), im_window, 0, 0); + + line_color_index(im_window, RED); + for (i = 1; i < image->get_xsize (); i++) { + move2d (im_window, i, 0); + draw2d (im_window, i, image->get_ysize ()); + } + for (i = 1; i < image->get_ysize (); i++) { + move2d (im_window, 0, i); + draw2d (im_window, image->get_xsize (), i); + } + overlap_picture_ops(TRUE); + + if (wait) + await_event(im_window, TRUE, ANY_EVENT, &event); + + return im_window; +} +#endif diff --git a/ccmain/matmatch.h b/ccmain/matmatch.h new file mode 100644 index 0000000000..142c516060 --- /dev/null +++ b/ccmain/matmatch.h @@ -0,0 +1,48 @@ +/********************************************************************** + * File: matmatch.h (Formerly matrix_match.h) + * Description: matrix matching routines for Tessedit + * Author: Chris Newton + * Created: Wed Nov 24 15:57:41 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 MATMATCH_H +#define MATMATCH_H + +#include "img.h" +#include "hosthplb.h" +#include "notdll.h" + +#define BINIM_BLACK 0 +#define BINIM_WHITE 1 +#define BAD_MATCH 9999.0 + +extern BOOL_VAR_H (tessedit_display_mm, FALSE, "Display matrix matches"); +extern BOOL_VAR_H (tessedit_mm_debug, FALSE, +"Print debug information for matrix matcher"); +extern INT_VAR_H (tessedit_mm_prototype_min_size, 3, +"Smallest number of samples in a cluster for a prototype to be used"); +float matrix_match( // returns match score + IMAGE *image1, + IMAGE *image2); +float match1( /* returns match score */ + IMAGE *image_w, + IMAGE *image_n); +void display_images(IMAGE *image_w, IMAGE *image_n, IMAGE *match_image); +WINDOW display_image(IMAGE *image, + const char *title, + INT32 x, + INT32 y, + BOOL8 wait); +#endif diff --git a/ccmain/output.cpp b/ccmain/output.cpp new file mode 100644 index 0000000000..b779d98e2d --- /dev/null +++ b/ccmain/output.cpp @@ -0,0 +1,1185 @@ +/****************************************************************** + * 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. + * + **********************************************************************/ + +#include "mfcpch.h" +#include "ocrshell.h" +#include +#include +#ifdef __UNIX__ +#include +#include +#include +#endif +#include "mainblk.h" +#include "tfacep.h" +#include "tessvars.h" +#include "control.h" +#include "secname.h" +#include "reject.h" +#include "docqual.h" +#include "output.h" +#include "bestfirst.h" + +#define EXTERN + +#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 +int NO_BLOCK = 0; //don't output block information +INT16 XOFFSET = 0; //the image can be a part of bigger picture and we want to have the original coordinates +INT16 YOFFSET = 0; + +EXTERN BOOL_EVAR (tessedit_write_block_separators, FALSE, +"Write block separators in output"); +EXTERN BOOL_VAR (tessedit_write_raw_output, FALSE, +"Write raw stuff to name.raw"); +EXTERN BOOL_EVAR (tessedit_write_output, TRUE, "Write text to name.txt"); +EXTERN BOOL_EVAR (tessedit_write_ratings, FALSE, +"Return ratings in IPEOCRAPI data"); +EXTERN BOOL_EVAR (tessedit_write_txt_map, TRUE, +"Write .txt to .etx map file"); +EXTERN BOOL_EVAR (tessedit_write_rep_codes, TRUE, +"Write repetition char code"); +EXTERN BOOL_EVAR (tessedit_write_unlv, FALSE, "Write .unlv output file"); +EXTERN STRING_EVAR (unrecognised_char, "|", +"Output char for unidentified blobs"); +EXTERN INT_EVAR (suspect_level, 99, "Suspect marker level"); +EXTERN INT_VAR (suspect_space_level, 100, +"Min suspect level for rejecting spaces"); +EXTERN INT_VAR (suspect_short_words, 2, +"Dont Suspect dict wds longer than this"); +EXTERN BOOL_VAR (suspect_constrain_1Il, FALSE, +"UNLV keep 1Il chars rejected"); +EXTERN double_VAR (suspect_rating_per_ch, 999.9, +"Dont touch bad rating limit"); +EXTERN double_VAR (suspect_accept_rating, -999.9, "Accept good rating limit"); + +EXTERN BOOL_EVAR (tessedit_minimal_rejection, FALSE, +"Only reject tess failures"); +EXTERN BOOL_VAR (tessedit_zero_rejection, FALSE, "Dont reject ANYTHING"); +EXTERN BOOL_VAR (tessedit_word_for_word, FALSE, +"Make output have exactly one word per WERD"); +EXTERN BOOL_VAR (tessedit_zero_kelvin_rejection, FALSE, +"Dont reject ANYTHING AT ALL"); +EXTERN BOOL_VAR (tessedit_consistent_reps, TRUE, +"Force all rep chars the same"); + +FILE *txt_mapfile = NULL; //reject map +FILE *unlv_file = NULL; //reject map + +/********************************************************************** + * 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 +} + + +void output_pass( //Tess output pass //send to api + PAGE_RES_IT &page_res_it, + BOOL8 write_to_shm) { + BLOCK_RES *block_of_last_word; + INT16 block_id; + BOOL8 force_eol; //During output + BLOCK *nextblock; //block of next word + WERD *nextword; //next word + + if (tessedit_write_txt_map) + txt_mapfile = open_outfile (".map"); + if (tessedit_write_unlv) + unlv_file = open_outfile (".unlv"); + 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 (tessedit_write_block_separators && + block_of_last_word != page_res_it.block ()) { + block_of_last_word = page_res_it.block (); + if (block_of_last_word->block->text_region () == NULL) { + if (block_of_last_word->block->poly_block () == NULL) + block_id = 1; + else + block_id = + ((WEIRD_BLOCK *) block_of_last_word->block->poly_block ())-> + id_no(); + } + else + block_id = block_of_last_word->block->text_region ()->id_no (); + if (!NO_BLOCK) + fprintf (textfile, "|^~tr%d\n", block_id); + fprintf (txt_mapfile, "|^~tr%d\n", block_id); + } + + 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, + write_to_shm); + page_res_it.forward (); + } + if (write_to_shm) + ocr_send_text(FALSE); + if (tessedit_write_block_separators) { + if (!NO_BLOCK) + fprintf (textfile, "|^~tr\n"); + fprintf (txt_mapfile, "|^~tr\n"); + } + if (tessedit_write_txt_map) { + fprintf (txt_mapfile, "\n"); //because txt gets one + #ifdef __UNIX__ + fsync (fileno (txt_mapfile)); + #endif + fclose(txt_mapfile); + } +} + + +/************************************************************************* + * 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 write_results( //output a word + PAGE_RES_IT &page_res_it, //full info + char newline_type, //type of newline + BOOL8 force_eol, //override tilde crunch? + BOOL8 write_to_shm //send to api + ) { + //word to do + WERD_RES *word = page_res_it.word (); + WERD_CHOICE *ep_choice; //ep format + STRING repetition_code; + const STRING *wordstr; + const char *text; + int i; + char unrecognised = STRING (unrecognised_char)[0]; + char ep_chars[32]; //Only for unlv_tilde_crunch + int ep_chars_index = 0; + char txt_chs[32]; //Only for unlv_tilde_crunch + char map_chs[32]; //Only for unlv_tilde_crunch + int txt_index = 0; + static BOOL8 tilde_crunch_written = FALSE; + static BOOL8 last_char_was_newline = TRUE; + static BOOL8 last_char_was_tilde = FALSE; + static BOOL8 empty_block = TRUE; + BOOL8 need_reject = FALSE; + char *ptr; //string ptr + PBLOB_IT blob_it; //blobs + + /* if (word->best_choice->string().length() == 0) + { + tprintf("No output: to output\n"); + } + else if (word->best_choice->string()[0]==' ') + { + tprintf("spaceword to output\n"); + } + else if (word->best_choice->string()[0]=='\0') + { + tprintf("null to output\n"); + }*/ + if (word->unlv_crunch_mode != CR_NONE + && !tessedit_zero_kelvin_rejection && !tessedit_word_for_word) { + 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 preceeding good text */ + txt_chs[txt_index] = ' '; + map_chs[txt_index++] = '1'; + ep_chars[ep_chars_index++] = ' '; + last_char_was_tilde = FALSE; + } + need_reject = TRUE; + } + if ((need_reject && !last_char_was_tilde) || (force_eol && empty_block)) { + /* Write a reject char - mark as rejected unless zero_rejection mode */ + last_char_was_tilde = TRUE; + txt_chs[txt_index] = unrecognised; + if (tessedit_zero_rejection || (suspect_level == 0)) { + map_chs[txt_index++] = '1'; + ep_chars[ep_chars_index++] = unrecognised; + } + else { + map_chs[txt_index++] = '0'; + /* + The ep_choice string is a faked reject to allow newdiff to sync the .etx + with the .txt and .map files. + */ + ep_chars[ep_chars_index++] = CTRL_INSET; + //escape code + //dummy reject + ep_chars[ep_chars_index++] = 1; + //dummy reject + ep_chars[ep_chars_index++] = 1; + //type + ep_chars[ep_chars_index++] = 2; + //dummy reject + ep_chars[ep_chars_index++] = 1; + //dummy reject + ep_chars[ep_chars_index++] = 1; + } + tilde_crunch_written = TRUE; + last_char_was_newline = FALSE; + empty_block = FALSE; + } + + if ((word->word->flag (W_EOL) && !last_char_was_newline) || force_eol) { + /* Add a new line output */ + txt_chs[txt_index] = '\n'; + map_chs[txt_index++] = '\n'; + //end line + ep_chars[ep_chars_index++] = newline_type; + + //Cos of the real newline + tilde_crunch_written = FALSE; + last_char_was_newline = TRUE; + last_char_was_tilde = FALSE; + } + txt_chs[txt_index] = '\0'; + map_chs[txt_index] = '\0'; + //xiaofan + if (tessedit_write_output && !NO_BLOCK) + fprintf (textfile, "%s", txt_chs); + + if (tessedit_write_unlv) + fprintf (unlv_file, "%s", txt_chs); + + if (tessedit_write_txt_map) + fprintf (txt_mapfile, "%s", map_chs); + + //terminate string + ep_chars[ep_chars_index] = '\0'; + word->ep_choice = new WERD_CHOICE (ep_chars, 0, 0, NO_PERM); + + if (force_eol) + empty_block = TRUE; + return; + } + + /* NORMAL PROCESSING of non tilde crunched words */ + + tilde_crunch_written = FALSE; + if (newline_type) + last_char_was_newline = TRUE; + else + last_char_was_newline = FALSE; + empty_block = force_eol; //About to write a real word + + if (unlv_tilde_crunching && + last_char_was_tilde && + (word->word->space () == 0) && + !(word->word->flag (W_REP_CHAR) && tessedit_write_rep_codes) && + (word->best_choice->string ()[0] == ' ')) { + /* Prevent adjacent tilde across words - we know that adjacent tildes within + words have been removed */ + ptr = (char *) word->best_choice->string ().string (); + strcpy (ptr, ptr + 1); //shuffle up + word->reject_map.remove_pos (0); + blob_it = word->outword->blob_list (); + delete blob_it.extract (); //get rid of reject blob + } + if (newline_type || + (word->word->flag (W_REP_CHAR) && tessedit_write_rep_codes)) + last_char_was_tilde = FALSE; + else { + if (word->reject_map.length () > 0) { + if (word->best_choice->string ()[word->reject_map.length () - 1] == + ' ') + last_char_was_tilde = TRUE; + else + last_char_was_tilde = FALSE; + } + else if (word->word->space () > 0) + last_char_was_tilde = FALSE; + /* else it is unchanged as there are no output chars */ + } + + ptr = (char *) word->best_choice->string ().string (); + ASSERT_HOST (strlen (ptr) == word->reject_map.length ()); + + if (word->word->flag (W_REP_CHAR) && tessedit_consistent_reps) + ensure_rep_chars_are_consistent(word); + + set_unlv_suspects(word); + check_debug_pt (word, 120); + if (tessedit_rejection_debug) { + tprintf ("Dict word: \"%s\": %d\n", + word->best_choice->string ().string (), + dict_word (word->best_choice->string ().string ())); + } + + if (tessedit_write_unlv) { + write_unlv_text(word); + } + + if (word->word->flag (W_REP_CHAR) && tessedit_write_rep_codes) { + repetition_code = "|^~R"; + repetition_code += get_rep_char (word); + wordstr = &repetition_code; + } + else { + wordstr = &(word->best_choice->string ()); + if (tessedit_zero_rejection) { + /* OVERRIDE ALL REJECTION MECHANISMS - ONLY REJECT TESS FAILURES */ + text = wordstr->string (); + for (i = 0; text[i] != '\0'; 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 */ + text = wordstr->string (); + for (i = 0; text[i] != '\0'; i++) { + if ((text[i] != ' ') && word->reject_map[i].rejected ()) + word->reject_map[i].setrej_minimal_rej_accept (); + } + } + } + + if (write_to_shm) + write_shm_text (word, page_res_it.block ()->block, + page_res_it.row (), *wordstr); + + if (tessedit_write_output) + write_cooked_text (word->word, *wordstr, TRUE, FALSE, textfile); + + if (tessedit_write_raw_output) + write_cooked_text (word->word, word->raw_choice->string (), + TRUE, FALSE, rawfile); + + if (tessedit_write_txt_map) + write_map(txt_mapfile, word); + + ep_choice = make_epaper_choice (word, newline_type); + word->ep_choice = ep_choice; + + character_count += word->best_choice->string ().length (); + word_count++; +} + + +/********************************************************************** + * make_epaper_choice + * + * Construct the epaper text string for a word, using the reject map to + * determine whether each blob should be rejected. + **********************************************************************/ + +WERD_CHOICE *make_epaper_choice( //convert one word + WERD_RES *word, //word to do + char newline_type //type of newline + ) { + INT16 index = 0; //to string + INT16 blobindex; //to word + INT16 prevright = 0; //right of previous blob + INT16 nextleft; //left of next blob + PBLOB *blob; + BOX inset_box; //bounding box + PBLOB_IT blob_it; //blob iterator + char word_string[MAX_PATH]; //converted string + BOOL8 force_total_reject; + char unrecognised = STRING (unrecognised_char)[0]; + + blob_it.set_to_list (word->outword->blob_list ()); + + ASSERT_HOST (word->reject_map.length () == + word->best_choice->string ().length ()); + /* + tprintf( "\"%s\" -> length: %d; blobcount: %d (%d)\n", + word->best_choice->string().string(), + word->best_choice->string().length(), + blob_it.length(), + blob_count( word->outword ) ); + */ + + if (word->best_choice->string ().length () == 0) + force_total_reject = TRUE; + else { + force_total_reject = FALSE; + ASSERT_HOST (blob_it.length () == + word->best_choice->string ().length ()); + } + if (!blob_it.empty ()) { + for (index = 0; index < word->word->space (); index++) + word_string[index] = ' '; //leading blanks + } + /* Why does this generate leading blanks regardless of whether the + word_choice string is empty, when write_cooked_text ony generates leading + blanks when the string is NOT empty???. */ + + if (word->word->flag (W_REP_CHAR) && tessedit_write_rep_codes) { + strcpy (word_string + index, "|^~R"); + index += 4; + word_string[index++] = get_rep_char (word); + } + else { + if (!blob_it.empty ()) + prevright = blob_it.data ()->bounding_box ().left (); + //actually first left + for (blobindex = 0, blob_it.mark_cycle_pt (); + !blob_it.cycled_list (); blobindex++, blob_it.forward ()) { + blob = blob_it.data (); + if (word->reject_map[blobindex].accepted ()) { + if (word->best_choice->string ()[blobindex] == ' ') + //but not rejected!! + word_string[index++] = unrecognised; + else + word_string[index++] = + word->best_choice->string ()[blobindex]; + } + else { // start reject + inset_box = blob->bounding_box (); + /* Extend reject box to include rejected neighbours */ + while (!blob_it.at_last () && + (force_total_reject || + (word->reject_map[blobindex + 1].rejected ()))) { + blobindex++; + blob = blob_it.forward (); + //get total box + inset_box += blob->bounding_box (); + } + if (blob_it.at_last ()) + nextleft = inset_box.right (); + else + nextleft = blob_it.data_relative (1)->bounding_box ().left (); + + // tprintf("Making reject from (%d,%d)->(%d,%d)\n", + // inset_box.left(),inset_box.bottom(), + // inset_box.right(),inset_box.top()); + + index += make_reject (&inset_box, prevright, nextleft, + &word->denorm, &word_string[index]); + } + prevright = blob->bounding_box ().right (); + } + } + if (newline_type) + //end line + word_string[index++] = newline_type; + word_string[index] = '\0'; //terminate string + if (strlen (word_string) != index) { + tprintf ("ASSERT ABOUT TO FAIL: %s, index %d len %d\n", + word_string, index, strlen (word_string)); + } + //don't pass any zeros + ASSERT_HOST (strlen (word_string) == index); + return new WERD_CHOICE (word_string, 0, 0, NO_PERM); +} + + +/********************************************************************** + * make_reject + * + * Add the escape code to the string for the reject. + **********************************************************************/ + +INT16 +make_reject ( //make reject code +BOX * inset_box, //bounding box +INT16 prevright, //previous char +INT16 nextleft, //next char +DENORM * denorm, //de-normalizer +char word_string[] //output string +) { + INT16 index; //to string + INT16 xpos; //start of inset + INT16 ypos; + INT16 width; //size of inset + INT16 height; + INT16 left_offset; //shift form prev char + INT16 right_offset; //shift to next char + INT16 baseline_offset; //shift from baseline + INT16 inset_index = 0; //number of inset + INT16 min_chars; //min width estimate + INT16 max_chars; //max width estimate + float x_centre; //centre of box + + index = 0; + x_centre = (inset_box->left () + inset_box->right ()) / 2.0; + left_offset = + (INT16) (denorm->x (inset_box->left ()) - denorm->x (prevright)); + right_offset = + (INT16) (denorm->x (nextleft) - denorm->x (inset_box->right ())); + xpos = (INT16) floor (denorm->x (inset_box->left ())); + width = (INT16) ceil (denorm->x (inset_box->right ())) - xpos; + ypos = (INT16) floor (denorm->y (inset_box->bottom (), x_centre)); + height = (INT16) ceil (denorm->y (inset_box->top (), x_centre)) - ypos; + baseline_offset = ypos - (INT16) denorm->y (bln_baseline_offset, x_centre); + //escape code + word_string[index++] = CTRL_INSET; + min_chars = (INT16) ceil (0.27 * width / denorm->row ()->x_height ()); + max_chars = (INT16) floor (1.8 * width / denorm->row ()->x_height ()); + /* + Ensure min_chars and max_chars are in the range 0..254. This ensures that + we can add 1 to them to avoid putting \0 in a string, and still not exceed + the max value in a byte. + */ + if (min_chars < 0) + min_chars = 0; + if (min_chars > 254) + min_chars = 254; + if (max_chars < min_chars) + max_chars = min_chars; + if (max_chars > 254) + max_chars = 254; + //min chars + word_string[index++] = min_chars + 1; + //max chars + word_string[index++] = max_chars + 1; + word_string[index++] = 2; //type? + //store index + word_string[index++] = inset_index / 255 + 1; + word_string[index++] = inset_index % 255 + 1; + return index; //size of string +} + + +/********************************************************************** + * 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 + BOX word_box; //bounding + BOX next_box; //next word + BOX 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; +} + + +/********************************************************************** + * write_cooked_text + * + * Write the cooked text (with bold for pass2 and underline for reject) + * to the given file. + **********************************************************************/ + +void write_cooked_text( //write output + WERD *word, //word to do + const STRING &text, //text to write + BOOL8 acceptable, //good stuff + BOOL8 pass2, //done on pass2 + FILE *fp //file to write + ) { + INT16 index; //blank counter + int status; + static int newaline = 1; + static int havespace = 0; + char buff[512]; + const char *wordstr = text.string (); + int i = 0; + char unrecognised = STRING (unrecognised_char)[0]; + static int old_segs = 0; + BOX mybox; + for (i = 0; wordstr[i] != '\0'; i++) { + if (wordstr[i] == ' ') + buff[i] = unrecognised; + else + buff[i] = wordstr[i]; + } + buff[i] = '\0'; + + if (fp == stdout) { + tprintf ("Cooked=%s, %d segs, acceptable=%d", + buff, num_popped - old_segs, acceptable); + old_segs = num_popped; + return; + } + + if (text.length () > 0) { + for (index = 0; index < word->space (); index++) { + status = fprintf (fp, " "); + havespace = 1; + if (status < 0) + WRITEFAILED.error ("write_cooked_text", EXIT, + "Space Errno: %d", errno); + } + if (pass2) { + status = fprintf (fp, BOLD_ON); + if (status < 0) + WRITEFAILED.error ("write_cooked_text", EXIT, + "Bold Errno: %d", errno); + } + if (!acceptable) { + status = fprintf (fp, UNDERLINE_ON); + if (status < 0) + WRITEFAILED.error ("write_cooked_text", EXIT, + "Underline Errno: %d", errno); + } + + //xiaofan + if (NO_BLOCK && word && strlen (buff)) { + mybox = word->bounding_box (); + if (newaline || !havespace) { + fprintf (fp, " "); + newaline = 0; + } + fprintf (fp, "(%d," INT32FORMAT ",%d," INT32FORMAT ")", + XOFFSET + mybox.left (), + YOFFSET + page_image.get_ysize () - mybox.top (), + XOFFSET + mybox.right (), + YOFFSET + page_image.get_ysize () - mybox.bottom ()); + havespace = 0; + } + + status = fprintf (fp, "%s", buff); + if (status < 0) + WRITEFAILED.error ("write_cooked_text", EXIT, + "Word Errno: %d", errno); + if (pass2) { + status = fprintf (fp, BOLD_OFF); + if (status < 0) + WRITEFAILED.error ("write_cooked_text", EXIT, + "Bold off Errno: %d", errno); + } + if (!acceptable) { + status = fprintf (fp, UNDERLINE_OFF); + if (status < 0) + WRITEFAILED.error ("write_cooked_text", EXIT, + "Underline off Errno: %d", errno); + } + } + if (word->flag (W_EOL)) { + status = fprintf (fp, "\n"); + newaline = 1; + if (status < 0) + WRITEFAILED.error ("write_cooked_text", EXIT, + "Newline Errno: %d", errno); + } + status = fflush (fp); + if (status != 0) + WRITEFAILED.error ("write_cooked_text", EXIT, "Fflush Errno: %d", errno); +} + + +/********************************************************************** + * write_shm_text + * + * Write the cooked text to the shared memory for the api. + **********************************************************************/ + +void write_shm_text( //write output + WERD_RES *word, //word to do + BLOCK *block, //block it is from + ROW_RES *row, //row it is from + const STRING &text //text to write + ) { + INT32 index; //char counter + INT32 index2; //char counter + INT32 length; //chars in word + INT32 ptsize; //font size + INT8 blanks; //blanks in word + UINT8 enhancement; //bold etc + UINT8 font; //font index + char unrecognised = STRING (unrecognised_char)[0]; + PBLOB *blob; + BOX blob_box; //bounding box + PBLOB_IT blob_it; //blob iterator + WERD copy_outword; // copy to denorm + UINT32 rating; //of char + BOOL8 lineend; //end of line + + //point size + ptsize = pixels_to_pts ((INT32) (row->row->x_height () + row->row->ascenders () - row->row->descenders ()), 300); + if (word->word->flag (W_BOL) && ocr_char_space () < 128 + && ocr_send_text (TRUE) != OKAY) + return; //release failed + copy_outword = *(word->outword); + copy_outword.baseline_denormalise (&word->denorm); + blob_it.set_to_list (copy_outword.blob_list ()); + length = text.length (); + + if (length > 0) { + blanks = word->word->space (); + if (blanks == 0 && tessedit_word_for_word && !word->word->flag (W_BOL)) + blanks = 1; + for (index = 0; index < length; index++, blob_it.forward ()) { + blob = blob_it.data (); + blob_box = blob->bounding_box (); + + enhancement = 0; + if (word->italic > 0 || word->italic == 0 && row->italic > 0) + enhancement |= EUC_ITALIC; + if (word->bold > 0 || word->bold == 0 && row->bold > 0) + enhancement |= EUC_BOLD; + if (tessedit_write_ratings) + rating = (UINT32) (-word->best_choice->certainty () / 0.035); + else if (tessedit_zero_rejection) + rating = text[index] == ' ' ? 100 : 0; + else + rating = word->reject_map[index].accepted ()? 0 : 100; + if (rating > 255) + rating = 255; + if (word->font1_count > 2) + font = word->font1; + else if (row->font1_count > 8) + font = row->font1; + else + //font index + font = word->word->flag (W_DONT_CHOP) ? 0 : 1; + + lineend = word->word->flag (W_EOL) && index == length - 1; + if (word->word->flag (W_EOL) && tessedit_zero_rejection + && index < length - 1 && text[index + 1] == ' ') { + for (index2 = index + 1; index2 < length && text[index2] == ' '; + index2++); + if (index2 == length) + lineend = TRUE; + } + + if (!tessedit_zero_rejection || text[index] != ' ' + || tessedit_word_for_word) { + //confidence + ocr_append_char (text[index] == ' ' ? unrecognised : text[index], blob_box.left (), blob_box.right (), page_image.get_ysize () - 1 - blob_box.top (), page_image.get_ysize () - 1 - blob_box.bottom (), font, (UINT8) rating, + ptsize, //point size + blanks, enhancement, //enhancement + OCR_CDIR_LEFT_RIGHT, + OCR_LDIR_DOWN_RIGHT, + lineend ? OCR_NL_NEWLINE : OCR_NL_NONE); + blanks = 0; + } + + } + } + else if (tessedit_word_for_word) { + blanks = word->word->space (); + if (blanks == 0 && !word->word->flag (W_BOL)) + blanks = 1; + blob_box = word->word->bounding_box (); + + enhancement = 0; + if (word->italic > 0) + enhancement |= EUC_ITALIC; + if (word->bold > 0) + enhancement |= EUC_BOLD; + rating = 100; + if (word->font1_count > 2) + font = word->font1; + else if (row->font1_count > 8) + font = row->font1; + else + //font index + font = word->word->flag (W_DONT_CHOP) ? 0 : 1; + + lineend = word->word->flag (W_EOL); + + //font index + ocr_append_char (unrecognised, blob_box.left (), blob_box.right (), page_image.get_ysize () - 1 - blob_box.top (), page_image.get_ysize () - 1 - blob_box.bottom (), font, + rating, //confidence + ptsize, //point size + blanks, enhancement, //enhancement + OCR_CDIR_LEFT_RIGHT, + OCR_LDIR_DOWN_RIGHT, + lineend ? OCR_NL_NEWLINE : OCR_NL_NONE); + } +} + + +/********************************************************************** + * write_map + * + * Write a map file of 0's and 1'a which associates characters from the .txt + * file with those in the .etx file. 0 = .txt char was deleted. 1 = .txt char + * is kept. Note that there may be reject regions in the .etx file WITHOUT + * .txt chars being rejected. The map file should be the same length, and + * the same number of lines as the .txt file + * + * The paramaterised input is because I thought I might be able to generate + * multiple map files in a single run. However, it didn't work because + * newdiff needs etx files! + **********************************************************************/ + +void write_map( //output a map file + FILE *mapfile, //mapfile to write to + WERD_RES *word) { + INT16 index; + int status; + STRING mapstr = ""; + + if (word->best_choice->string ().length () > 0) { + for (index = 0; index < word->word->space (); index++) { + if (word->reject_spaces && + (suspect_level >= suspect_space_level) && + !tessedit_minimal_rejection && !tessedit_zero_rejection) + /* Write rejected spaces to .map file ONLY. Newdiff converts these back to + accepted spaces AFTER generating basic space stats but BEFORE using .etx */ + status = fprintf (mapfile, "0"); + else + status = fprintf (mapfile, "1"); + if (status < 0) + WRITEFAILED.error ("write_map", EXIT, "Space Errno: %d", errno); + } + + if ((word->word->flag (W_REP_CHAR) && tessedit_write_rep_codes)) { + for (index = 0; index < 5; index++) + mapstr += '1'; + } + else { + ASSERT_HOST (word->reject_map.length () == + word->best_choice->string ().length ()); + + for (index = 0; index < word->reject_map.length (); index++) { + if (word->reject_map[index].accepted ()) + mapstr += '1'; + else + mapstr += '0'; + } + } + status = fprintf (mapfile, "%s", mapstr.string ()); + if (status < 0) + WRITEFAILED.error ("write_map", EXIT, "Map str Errno: %d", errno); + } + if (word->word->flag (W_EOL)) { + status = fprintf (mapfile, "\n"); + if (status < 0) + WRITEFAILED.error ("write_map", EXIT, "Newline Errno: %d", errno); + } + status = fflush (mapfile); + if (status != 0) + WRITEFAILED.error ("write_map", EXIT, "fflush Errno: %d", errno); +} + + +/************************************************************************* + * open_file() + *************************************************************************/ + +FILE *open_outfile( //open .map & .unlv file + const char *extension) { + STRING file_name; + FILE *outfile; + + file_name = imagebasename + extension; + if (!(outfile = fopen (file_name.string (), "w"))) { + CANTOPENFILE.error ("open_outfile", EXIT, "%s %d", + file_name.string (), errno); + } + return outfile; +} + + +void write_unlv_text(WERD_RES *word) { + const char *wordstr; + + char buff[512]; //string to output + int i = 0; + int j = 0; + char unrecognised = STRING (unrecognised_char)[0]; + int status; + char space_str[3]; + + wordstr = word->best_choice->string ().string (); + + /* DONT need to do anything special for repeated char words - at this stage + the repetition char has been identified and any other chars have been + rejected. + */ + + for (; wordstr[i] != '\0'; i++) { + if ((wordstr[i] == ' ') || + (wordstr[i] == '~') || (wordstr[i] == '^') || (wordstr[i] == '|')) + buff[j++] = unrecognised; + else { + if (word->reject_map[i].rejected ()) + buff[j++] = '^'; //Add suspect marker + buff[j++] = wordstr[i]; + } + } + buff[j] = '\0'; + + if (strlen (wordstr) > 0) { + if (word->reject_spaces && + (suspect_level >= suspect_space_level) && + !tessedit_minimal_rejection && !tessedit_zero_rejection) + strcpy (space_str, "^ "); //Suspect space + else + strcpy (space_str, " "); //Certain space + + for (i = 0; i < word->word->space (); i++) { + status = fprintf (unlv_file, "%s", space_str); + if (status < 0) + WRITEFAILED.error ("write_unlv_text", EXIT, + "Space Errno: %d", errno); + } + + status = fprintf (unlv_file, "%s", buff); + if (status < 0) + WRITEFAILED.error ("write_unlv_text", EXIT, "Word Errno: %d", errno); + } + if (word->word->flag (W_EOL)) { + status = fprintf (unlv_file, "\n"); + if (status < 0) + WRITEFAILED.error ("write_unlv_text", EXIT, + "Newline Errno: %d", errno); + } + status = fflush (unlv_file); + if (status != 0) + WRITEFAILED.error ("write_unlv_text", EXIT, "Fflush Errno: %d", errno); +} + + +/************************************************************************* + * 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() + *************************************************************************/ +char get_rep_char( // what char is repeated? + WERD_RES *word) { + 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->string ()[i]; + else + return STRING (unrecognised_char)[0]; +} + + +void ensure_rep_chars_are_consistent(WERD_RES *word) { + char rep_char = get_rep_char (word); + char *ptr; + + ptr = (char *) word->best_choice->string ().string (); + for (; *ptr != '\0'; ptr++) { + if (*ptr != rep_char) + *ptr = rep_char; + } +} + + +/************************************************************************* + * SUSPECT LEVELS + * + * 0 - dont 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 set_unlv_suspects(WERD_RES *word) { + int len = word->reject_map.length (); + int i; + const char *ptr; + float rating_per_ch; + + ptr = word->best_choice->string ().string (); + + if (suspect_level == 0) { + for (i = 0; i < len; i++) { + if (word->reject_map[i].rejected ()) + word->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 (ptr) && (count_alphas (ptr) > suspect_short_words)) { + /* Unreject alphas in dictionary words */ + for (i = 0; i < len; i++) { + if (word->reject_map[i].rejected () && isalpha (ptr[i])) + word->reject_map[i].setrej_minimal_rej_accept (); + } + } + + rating_per_ch = word->best_choice->rating () / word->reject_map.length (); + + if (rating_per_ch >= suspect_rating_per_ch) + return; //Dont touch bad ratings + + if ((word->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->reject_map[i].rejected () && (ptr[i] != ' ')) + word->reject_map[i].setrej_minimal_rej_accept (); + } + } + + for (i = 0; i < len; i++) { + if (word->reject_map[i].rejected ()) { + if (word->reject_map[i].flag (R_DOC_REJ)) + word->reject_map[i].setrej_minimal_rej_accept (); + if (word->reject_map[i].flag (R_BLOCK_REJ)) + word->reject_map[i].setrej_minimal_rej_accept (); + if (word->reject_map[i].flag (R_ROW_REJ)) + word->reject_map[i].setrej_minimal_rej_accept (); + } + } + + if (suspect_level == 2) + return; + + if (!suspect_constrain_1Il || + (word->reject_map.length () <= suspect_short_words)) { + for (i = 0; i < len; i++) { + if (word->reject_map[i].rejected ()) { + if ((word->reject_map[i].flag (R_1IL_CONFLICT) || + word->reject_map[i].flag (R_POSTNN_1IL))) + word->reject_map[i].setrej_minimal_rej_accept (); + + if (!suspect_constrain_1Il && + word->reject_map[i].flag (R_MM_REJECT)) + word->reject_map[i].setrej_minimal_rej_accept (); + } + } + } + + if ((acceptable_word_string (word->best_choice->string ().string ()) + != AC_UNACCEPTABLE) || + acceptable_number_string (word->best_choice->string ().string ())) { + if (word->reject_map.length () > suspect_short_words) { + for (i = 0; i < len; i++) { + if (word->reject_map[i].rejected () && + (!word->reject_map[i].perm_rejected () || + word->reject_map[i].flag (R_1IL_CONFLICT) || + word->reject_map[i].flag (R_POSTNN_1IL) || + word->reject_map[i].flag (R_MM_REJECT))) { + word->reject_map[i].setrej_minimal_rej_accept (); + } + } + } + } +} + + +INT16 count_alphas( //how many alphas + const char *s) { + int count = 0; + + for (; *s != '\0'; s++) { + if (isalpha (*s)) + count++; + } + return count; +} + + +INT16 count_alphanums( //how many alphanums + const char *s) { + int count = 0; + + for (; *s != '\0'; s++) { + if (isalnum (*s)) + count++; + } + return count; +} + + +BOOL8 acceptable_number_string(const char *s) { + BOOL8 prev_digit = FALSE; + + if (*s == '(') + s++; + + if ((*s == '$') || (*s == '.') || (*s == '+') || (*s == '-')) + s++; + + for (; *s != '\0'; s++) { + if (isdigit (*s)) + prev_digit = TRUE; + else if (prev_digit && ((*s == '.') || (*s == ',') || (*s == '-'))) + prev_digit = FALSE; + else if (prev_digit && + (*(s + 1) == '\0') && ((*s == '%') || (*s == ')'))) + return TRUE; + else if (prev_digit && + (*s == '%') && (*(s + 1) == ')') && (*(s + 2) == '\0')) + return TRUE; + else + return FALSE; + } + return TRUE; +} diff --git a/ccmain/output.h b/ccmain/output.h new file mode 100644 index 0000000000..f644f27b8e --- /dev/null +++ b/ccmain/output.h @@ -0,0 +1,112 @@ +/****************************************************************** + * 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 "varable.h" +//#include "epapconv.h" +#include "pageres.h" +#include "notdll.h" + +extern BOOL_EVAR_H (tessedit_write_block_separators, TRUE, +"Write block separators in output"); +extern BOOL_VAR_H (tessedit_write_raw_output, FALSE, +"Write raw stuff to name.raw"); +extern BOOL_EVAR_H (tessedit_write_output, TRUE, "Write text to name.txt"); +extern BOOL_EVAR_H (tessedit_write_txt_map, TRUE, +"Write .txt to .etx map file"); +extern BOOL_EVAR_H (tessedit_write_rep_codes, TRUE, +"Write repetition char code"); +extern BOOL_EVAR_H (tessedit_write_unlv, FALSE, "Write .unlv output file"); +extern STRING_EVAR_H (unrecognised_char, "|", +"Output char for unidentified blobs"); +extern INT_EVAR_H (suspect_level, 99, "Suspect marker level"); +extern INT_VAR_H (suspect_space_level, 100, +"Min suspect level for rejecting spaces"); +extern INT_VAR_H (suspect_short_words, 2, +"Dont Suspect dict wds longer than this"); +extern BOOL_VAR_H (suspect_constrain_1Il, FALSE, +"UNLV keep 1Il chars rejected"); +extern double_VAR_H (suspect_rating_per_ch, 999.9, +"Dont touch bad rating limit"); +extern double_VAR_H (suspect_accept_rating, -999.9, +"Accept good rating limit"); +extern BOOL_EVAR_H (tessedit_minimal_rejection, FALSE, +"Only reject tess failures"); +extern BOOL_VAR_H (tessedit_zero_rejection, FALSE, "Dont reject ANYTHING"); +extern BOOL_VAR_H (tessedit_word_for_word, FALSE, +"Make output have exactly one word per WERD"); +extern BOOL_VAR_H (tessedit_consistent_reps, TRUE, +"Force all rep chars the same"); +void output_pass( //Tess output pass //send to api + PAGE_RES_IT &page_res_it, + BOOL8 write_to_shm); +void write_results( //output a word + PAGE_RES_IT &page_res_it, //full info + char newline_type, //type of newline + BOOL8 force_eol, //override tilde crunch? + BOOL8 write_to_shm //send to api + ); +WERD_CHOICE *make_epaper_choice( //convert one word + WERD_RES *word, //word to do + char newline_type //type of newline + ); +INT16 make_reject ( //make reject code +BOX * inset_box, //bounding box +INT16 prevright, //previous char +INT16 nextleft, //next char +DENORM * denorm, //de-normalizer +char word_string[] //output string +); +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 + ); +void write_cooked_text( //write output + WERD *word, //word to do + const STRING &text, //text to write + BOOL8 acceptable, //good stuff + BOOL8 pass2, //done on pass2 + FILE *fp //file to write + ); +void write_shm_text( //write output + WERD_RES *word, //word to do + BLOCK *block, //block it is from + ROW_RES *row, //row it is from + const STRING &text //text to write + ); +void write_map( //output a map file + FILE *mapfile, //mapfile to write to + WERD_RES *word); +FILE *open_outfile( //open .map & .unlv file + const char *extension); +void write_unlv_text(WERD_RES *word); +char get_rep_char( // what char is repeated? + WERD_RES *word); +void ensure_rep_chars_are_consistent(WERD_RES *word); +void set_unlv_suspects(WERD_RES *word); +INT16 count_alphas( //how many alphas + const char *s); +INT16 count_alphanums( //how many alphanums + const char *s); +BOOL8 acceptable_number_string(const char *s); +#endif diff --git a/ccmain/paircmp.cpp b/ccmain/paircmp.cpp new file mode 100644 index 0000000000..a9443b26ef --- /dev/null +++ b/ccmain/paircmp.cpp @@ -0,0 +1,107 @@ +/********************************************************************** + * File: paircmp.cpp (Formerly paircmp.c) + * Description: Code to compare two blobs using the adaptive matcher + * Author: Ray Smith + * Created: Wed Apr 21 09:31:02 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 "mfcpch.h" +#include "blobcmp.h" +#include "tfacep.h" +#include "paircmp.h" + +#define EXTERN + +/********************************************************************** + * compare_blob_pairs + * + * A blob processor to compare pairs of selected blobs. + **********************************************************************/ + +BOOL8 compare_blob_pairs( //blob processor + BLOCK *, + ROW *row, //row it came from + WERD *, + PBLOB *blob //blob to compare + ) { + static ROW *prev_row = NULL; //other in pair + static PBLOB *prev_blob = NULL; + float rating; //from matcher + + if (prev_row == NULL || prev_blob == NULL) { + prev_row = row; + prev_blob = blob; + } + else { + rating = compare_blobs (prev_blob, prev_row, blob, row); + tprintf ("Rating=%g\n", rating); + prev_row = NULL; + prev_blob = NULL; + } + return TRUE; +} + + +/********************************************************************** + * compare_blobs + * + * Compare 2 blobs and return the rating. + **********************************************************************/ + +float compare_blobs( //match 2 blobs + PBLOB *blob1, //first blob + ROW *row1, //row it came from + PBLOB *blob2, //other blob + ROW *row2) { + PBLOB *bn_blob1; //baseline norm + PBLOB *bn_blob2; + DENORM denorm1, denorm2; + float rating; //match result + + bn_blob1 = blob1->baseline_normalise (row1, &denorm1); + bn_blob2 = blob2->baseline_normalise (row2, &denorm2); + rating = compare_bln_blobs (bn_blob1, &denorm1, bn_blob2, &denorm2); + delete bn_blob1; + delete bn_blob2; + return rating; +} + + +/********************************************************************** + * compare_bln_blobs + * + * Compare 2 baseline normalised blobs and return the rating. + **********************************************************************/ + +float compare_bln_blobs( //match 2 blobs + PBLOB *blob1, //first blob + DENORM *denorm1, + PBLOB *blob2, //other blob + DENORM *denorm2) { + TBLOB *tblob1; //tessblobs + TBLOB *tblob2; + TEXTROW tessrow1, tessrow2; //tess rows + float rating; //match result + + tblob1 = make_tess_blob (blob1, TRUE); + make_tess_row(denorm1, &tessrow1); + tblob2 = make_tess_blob (blob2, TRUE); + make_tess_row(denorm2, &tessrow2); + rating = compare_tess_blobs (tblob1, &tessrow1, tblob2, &tessrow2); + free_blob(tblob1); + free_blob(tblob2); + + return rating; +} diff --git a/ccmain/paircmp.h b/ccmain/paircmp.h new file mode 100644 index 0000000000..772cdd8370 --- /dev/null +++ b/ccmain/paircmp.h @@ -0,0 +1,43 @@ +/********************************************************************** + * File: paircmp.h (Formerly paircmp.h) + * Description: Code to compare two blobs using the adaptive matcher + * Author: Ray Smith + * Created: Wed Apr 21 09:31:02 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 PAIRCMP_H +#define PAIRCMP_H + +#include "ocrblock.h" +#include "varable.h" +#include "notdll.h" + +BOOL8 compare_blob_pairs( //blob processor + BLOCK *, + ROW *row, //row it came from + WERD *, + PBLOB *blob //blob to compare + ); +float compare_blobs( //match 2 blobs + PBLOB *blob1, //first blob + ROW *row1, //row it came from + PBLOB *blob2, //other blob + ROW *row2); +float compare_bln_blobs( //match 2 blobs + PBLOB *blob1, //first blob + DENORM *denorm1, + PBLOB *blob2, //other blob + DENORM *denorm2); +#endif diff --git a/ccmain/reject.cpp b/ccmain/reject.cpp new file mode 100644 index 0000000000..808af8e839 --- /dev/null +++ b/ccmain/reject.cpp @@ -0,0 +1,1655 @@ +/********************************************************************** + * 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. + * + **********************************************************************/ + +#include "mfcpch.h" +#include "tessvars.h" +#ifdef __UNIX__ +#include +#include +#endif +#include "scanutils.h" +#include +#include +//#include "tessbox.h" +#include "memry.h" +#include "reject.h" +#include "tfacep.h" +#include "mainblk.h" +#include "charcut.h" +#include "imgs.h" +#include "scaleimg.h" +#include "control.h" +#include "docqual.h" +#include "secname.h" + +/* #define SECURE_NAMES done in secnames.h when necessary */ + +//extern "C" { +#include "callnet.h" +//} + +#include "notdll.h" + +CLISTIZEH (STRING) CLISTIZE (STRING) +#define EXTERN +EXTERN +INT_VAR (tessedit_reject_mode, 0, "Rejection algorithm"); +EXTERN +INT_VAR (tessedit_ok_mode, 5, "Acceptance decision algorithm"); +EXTERN +BOOL_VAR (tessedit_use_nn, FALSE, ""); +EXTERN +BOOL_VAR (tessedit_rejection_debug, FALSE, "Adaption debug"); +EXTERN +BOOL_VAR (tessedit_rejection_stats, FALSE, "Show NN stats"); +EXTERN +BOOL_VAR (tessedit_flip_0O, TRUE, "Contextual 0O O0 flips"); +EXTERN +double_VAR (tessedit_lower_flip_hyphen, 1.5, +"Aspect ratio dot/hyphen test"); +EXTERN +double_VAR (tessedit_upper_flip_hyphen, 1.8, +"Aspect ratio dot/hyphen test"); + +EXTERN +BOOL_VAR (rej_trust_doc_dawg, FALSE, +"Use DOC dawg in 11l conf. detector"); +EXTERN +BOOL_VAR (rej_1Il_use_dict_word, FALSE, "Use dictword test"); +EXTERN +BOOL_VAR (rej_1Il_trust_permuter_type, TRUE, "Dont double check"); + +EXTERN +BOOL_VAR (one_ell_conflict_default, TRUE, "one_ell_conflict default"); +EXTERN +BOOL_VAR (show_char_clipping, FALSE, "Show clip image window?"); +EXTERN +BOOL_VAR (nn_debug, FALSE, "NN DEBUGGING?"); +EXTERN +BOOL_VAR (nn_reject_debug, FALSE, "NN DEBUG each char?"); +EXTERN +BOOL_VAR (nn_lax, FALSE, "Use 2nd rate matches"); +EXTERN +BOOL_VAR (nn_double_check_dict, FALSE, "Double check"); +EXTERN +BOOL_VAR (nn_conf_double_check_dict, TRUE, +"Double check for confusions"); +EXTERN +BOOL_VAR (nn_conf_1Il, TRUE, "NN use 1Il conflicts"); +EXTERN +BOOL_VAR (nn_conf_Ss, TRUE, "NN use Ss conflicts"); +EXTERN +BOOL_VAR (nn_conf_hyphen, TRUE, "NN hyphen conflicts"); +EXTERN +BOOL_VAR (nn_conf_test_good_qual, FALSE, "NN dodgy 1Il cross check"); +EXTERN +BOOL_VAR (nn_conf_test_dict, TRUE, "NN dodgy 1Il cross check"); +EXTERN +BOOL_VAR (nn_conf_test_sensible, TRUE, "NN dodgy 1Il cross check"); +EXTERN +BOOL_VAR (nn_conf_strict_on_dodgy_chs, TRUE, +"Require stronger NN match"); +EXTERN +double_VAR (nn_dodgy_char_threshold, 0.99, "min accept score"); +EXTERN +INT_VAR (nn_conf_accept_level, 4, "NN accept dodgy 1Il matches? "); +EXTERN +INT_VAR (nn_conf_initial_i_level, 3, +"NN accept initial Ii match level "); + +EXTERN +BOOL_VAR (no_unrej_dubious_chars, TRUE, "Dubious chars next to reject?"); +EXTERN +BOOL_VAR (no_unrej_no_alphanum_wds, TRUE, "Stop unrej of non A/N wds?"); +EXTERN +BOOL_VAR (no_unrej_1Il, FALSE, "Stop unrej of 1Ilchars?"); +EXTERN +BOOL_VAR (rej_use_tess_accepted, TRUE, "Individual rejection control"); +EXTERN +BOOL_VAR (rej_use_tess_blanks, TRUE, "Individual rejection control"); +EXTERN +BOOL_VAR (rej_use_good_perm, TRUE, "Individual rejection control"); +EXTERN +BOOL_VAR (rej_use_sensible_wd, FALSE, "Extend permuter check"); +EXTERN +BOOL_VAR (rej_alphas_in_number_perm, FALSE, "Extend permuter check"); + +EXTERN +double_VAR (rej_whole_of_mostly_reject_word_fract, 0.85, +"if >this fract"); +EXTERN +INT_VAR (rej_mostly_reject_mode, 1, +"0-never, 1-afterNN, 2-after new xht"); +EXTERN +double_VAR (tessed_fullstop_aspect_ratio, 1.2, +"if >this fract then reject"); + +EXTERN +INT_VAR (net_image_width, 40, "NN input image width"); +EXTERN +INT_VAR (net_image_height, 36, "NN input image height"); +EXTERN +INT_VAR (net_image_x_height, 22, "NN input image x_height"); +EXTERN +INT_VAR (tessedit_image_border, 2, "Rej blbs near image edge limit"); + +/* + Net input is assumed to have (net_image_width * net_image_height) input + units of image pixels, followed by 0, 1, or N units representing the + baseline position. 0 implies no baseline information. 1 implies a floating + point value. N implies a "guage" of N units. For any char an initial set + of these are ON, the remainder OFF to indicate the "level" of the + baseline. + + HOWEVER!!! NOTE THAT EACH NEW INPUT LAYER FORMAT EXPECTS TO BE RUN WITH A + DIFFERENT tessed/netmatch/nmatch.c MODULE. - These are classic C modules + generated by aspirin with HARD CODED CONSTANTS +*/ + +EXTERN +INT_VAR (net_bl_nodes, 20, "Number of baseline nodes"); + +EXTERN +double_VAR (nn_reject_threshold, 0.5, "NN min accept score"); +EXTERN +double_VAR (nn_reject_head_and_shoulders, 0.6, "top scores sep factor"); + +/* NOTE - ctoh doesn't handle "=" properly, hence \075 */ +EXTERN +STRING_VAR (ok_single_ch_non_alphanum_wds, "-?\075", +"Allow NN to unrej"); +EXTERN +STRING_VAR (ok_repeated_ch_non_alphanum_wds, "-?*\075", +"Allow NN to unrej"); +EXTERN +STRING_VAR (conflict_set_I_l_1, "Il1[]", "Il1 conflict set"); +EXTERN +STRING_VAR (conflict_set_S_s, "Ss$", "Ss conflict set"); +EXTERN +STRING_VAR (conflict_set_hyphen, "-_~", "hyphen conflict set"); +EXTERN +STRING_VAR (dubious_chars_left_of_reject, "!'+`()-./\\<>;:^_,~\"", +"Unreliable chars"); +EXTERN +STRING_VAR (dubious_chars_right_of_reject, "!'+`()-./\\<>;:^_,~\"", +"Unreliable chars"); + +EXTERN +INT_VAR (min_sane_x_ht_pixels, 8, "Reject any x-ht lt or eq than this"); + +/************************************************************************* + * set_done() + * + * Set the done flag based on the word acceptability criteria + *************************************************************************/ + +void set_done( //set done flag + WERD_RES *word, + INT16 pass) { + /* + 0: Original heuristic used in Tesseract and Ray's prototype Resaljet + */ + if (tessedit_ok_mode == 0) { + /* NOTE - done even if word contains some or all spaces !!! */ + word->done = word->tess_accepted; + } + /* + 1: Reject words containing blanks and on pass 1 reject I/l/1 conflicts + */ + else if (tessedit_ok_mode == 1) { + word->done = word->tess_accepted && + (strchr (word->best_choice->string ().string (), ' ') == NULL); + + if (word->done && (pass == 1) && one_ell_conflict (word, FALSE)) + word->done = FALSE; + } + /* + 2: as 1 + only accept dict words or numerics in pass 1 + */ + else if (tessedit_ok_mode == 2) { + word->done = word->tess_accepted && + (strchr (word->best_choice->string ().string (), ' ') == NULL); + + if (word->done && (pass == 1) && one_ell_conflict (word, FALSE)) + word->done = FALSE; + + if (word->done && + (pass == 1) && + (word->best_choice->permuter () != SYSTEM_DAWG_PERM) && + (word->best_choice->permuter () != FREQ_DAWG_PERM) && + (word->best_choice->permuter () != USER_DAWG_PERM) && + (word->best_choice->permuter () != NUMBER_PERM)) { + #ifndef SECURE_NAMES + if (tessedit_rejection_debug) + tprintf ("\nVETO Tess accepting poor word \"%s\"\n", + word->best_choice->string ().string ()); + #endif + word->done = FALSE; + } + } + /* + 3: as 2 + only accept dict words or numerics in pass 2 as well + */ + else if (tessedit_ok_mode == 3) { + word->done = word->tess_accepted && + (strchr (word->best_choice->string ().string (), ' ') == NULL); + + if (word->done && (pass == 1) && one_ell_conflict (word, FALSE)) + word->done = FALSE; + + if (word->done && + (word->best_choice->permuter () != SYSTEM_DAWG_PERM) && + (word->best_choice->permuter () != FREQ_DAWG_PERM) && + (word->best_choice->permuter () != USER_DAWG_PERM) && + (word->best_choice->permuter () != NUMBER_PERM)) { + #ifndef SECURE_NAMES + if (tessedit_rejection_debug) + tprintf ("\nVETO Tess accepting poor word \"%s\"\n", + word->best_choice->string ().string ()); + #endif + word->done = FALSE; + } + } + /* + 4: as 2 + reject dict ambigs in pass 1 + */ + else if (tessedit_ok_mode == 4) { + word->done = word->tess_accepted && + (strchr (word->best_choice->string ().string (), ' ') == NULL); + + if (word->done && (pass == 1) && one_ell_conflict (word, FALSE)) + word->done = FALSE; + + if (word->done && + (pass == 1) && + ((word->best_choice->permuter () != SYSTEM_DAWG_PERM) && + (word->best_choice->permuter () != FREQ_DAWG_PERM) && + (word->best_choice->permuter () != USER_DAWG_PERM) && + (word->best_choice->permuter () != NUMBER_PERM)) || + (test_ambig_word (word))) { + #ifndef SECURE_NAMES + if (tessedit_rejection_debug) + tprintf ("\nVETO Tess accepting poor word \"%s\"\n", + word->best_choice->string ().string ()); + #endif + word->done = FALSE; + } + } + /* + 5: as 3 + reject dict ambigs in both passes + */ + else if (tessedit_ok_mode == 5) { + word->done = word->tess_accepted && + (strchr (word->best_choice->string ().string (), ' ') == NULL); + + if (word->done && (pass == 1) && one_ell_conflict (word, FALSE)) + word->done = FALSE; + + if (word->done && + ((word->best_choice->permuter () != SYSTEM_DAWG_PERM) && + (word->best_choice->permuter () != FREQ_DAWG_PERM) && + (word->best_choice->permuter () != USER_DAWG_PERM) && + (word->best_choice->permuter () != NUMBER_PERM)) || + (test_ambig_word (word))) { + #ifndef SECURE_NAMES + if (tessedit_rejection_debug) + tprintf ("\nVETO Tess accepting poor word \"%s\"\n", + word->best_choice->string ().string ()); + #endif + word->done = FALSE; + } + } + + else { + tprintf ("BAD tessedit_ok_mode\n"); + err_exit(); + } +} + + +/************************************************************************* + * make_reject_map() + * + * Sets the done flag to indicate whether the resylt is acceptable. + * + * Sets a reject map for the word. + *************************************************************************/ + +void make_reject_map( //make rej map for wd //detailed results + WERD_RES *word, + BLOB_CHOICE_LIST_CLIST *blob_choices, + ROW *row, + INT16 pass //1st or 2nd? + ) { + INT16 i; + + flip_0O(word); + check_debug_pt (word, -1); //For trap only + set_done(word, pass); //Set acceptance + word->reject_map.initialise (word->best_choice->string ().length ()); + reject_blanks(word); + /* + 0: Rays original heuristic - the baseline + */ + if (tessedit_reject_mode == 0) { + if (!word->done) + reject_poor_matches(word, blob_choices); + } + /* + 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 + */ + else if (tessedit_reject_mode == 5) { + if (bln_x_height / word->denorm.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->string ().string (), ' ') != NULL)) + word->reject_map.rej_word_contains_blanks (); + + if (rej_use_good_perm) { + if (((word->best_choice->permuter () == SYSTEM_DAWG_PERM) || + (word->best_choice->permuter () == FREQ_DAWG_PERM) || + (word->best_choice->permuter () == USER_DAWG_PERM)) && + (!rej_use_sensible_wd || + (acceptable_word_string + (word->best_choice->string ().string ()) != + AC_UNACCEPTABLE))) { + //PASSED TEST + } + else if (word->best_choice->permuter () == NUMBER_PERM) { + if (rej_alphas_in_number_perm) { + for (i = 0; word->best_choice->string ()[i] != '\0'; + i++) { + if (word->reject_map[i].accepted () && + isalpha (word->best_choice->string ()[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->string ().string ())); + } + + /* Un-reject any rejected characters if NN permits */ + + if (tessedit_use_nn && (pass == 2) && + word->reject_map.recoverable_rejects ()) + nn_recover_rejects(word, row); + flip_hyphens(word); + check_debug_pt (word, 20); +} + + +void reject_blanks(WERD_RES *word) { + INT16 i; + + for (i = 0; word->best_choice->string ()[i] != '\0'; i++) { + if (word->best_choice->string ()[i] == ' ') + //rej unrecognised blobs + word->reject_map[i].setrej_tess_failure (); + } +} + + +void reject_I_1_L(WERD_RES *word) { + INT16 i; + + for (i = 0; word->best_choice->string ()[i] != '\0'; i++) { + if (STRING (conflict_set_I_l_1). + contains (word->best_choice->string ()[i])) { + //rej 1Il conflict + word->reject_map[i].setrej_1Il_conflict (); + } + } +} + + +void reject_poor_matches( //detailed results + WERD_RES *word, + BLOB_CHOICE_LIST_CLIST *blob_choices) { + float threshold; + INT16 i = 0; + //super iterator + BLOB_CHOICE_LIST_C_IT list_it = blob_choices; + BLOB_CHOICE_IT choice_it; //real iterator + + #ifndef SECURE_NAMES + if (strlen (word->best_choice->string ().string ()) != list_it.length ()) { + tprintf + ("ASSERT FAIL string:\"%s\"; strlen=%d; choices len=%d; blob len=%d\n", + word->best_choice->string ().string (), + strlen (word->best_choice->string ().string ()), list_it.length (), + word->outword->blob_list ()->length ()); + } + #endif + ASSERT_HOST (strlen (word->best_choice->string ().string ()) == + list_it.length ()); + ASSERT_HOST (word->outword->blob_list ()->length () == list_it.length ()); + threshold = compute_reject_threshold (blob_choices); + + for (list_it.mark_cycle_pt (); + !list_it.cycled_list (); list_it.forward (), i++) { + /* NB - only compares the threshold against the TOP choice char in the + choices list for a blob !! - the selected one may be below the threshold */ + choice_it.set_to_list (list_it.data ()); + if ((word->best_choice->string ()[i] == ' ') || + (choice_it.length () == 0)) + //rej unrecognised blobs + word->reject_map[i].setrej_tess_failure (); + else if (choice_it.data ()->certainty () < threshold) + //rej poor score blob + word->reject_map[i].setrej_poor_match (); + } +} + + +/********************************************************************** + * 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( //compute threshold //detailed results + BLOB_CHOICE_LIST_CLIST *blob_choices) { + INT16 index; //to ratings + INT16 blob_count; //no of blobs in word + INT16 ok_blob_count = 0; //non TESS rej blobs in word + float *ratings; //array of confidences + float threshold; //rejection threshold + float bestgap; //biggest gap + float gapstart; //bottom of gap + //super iterator + BLOB_CHOICE_LIST_C_IT list_it = blob_choices; + BLOB_CHOICE_IT choice_it; //real iterator + + blob_count = blob_choices->length (); + ratings = (float *) alloc_mem (blob_count * sizeof (float)); + for (list_it.mark_cycle_pt (), index = 0; + !list_it.cycled_list (); list_it.forward (), index++) { + choice_it.set_to_list (list_it.data ()); + if (choice_it.length () > 0) { + ratings[ok_blob_count] = choice_it.data ()->certainty (); + //get in an array + // tprintf("Rating[%d]=%c %g %g\n", + // index,choice_it.data()->char_class(), + // choice_it.data()->rating(),choice_it.data()->certainty()); + ok_blob_count++; + } + } + ASSERT_HOST (index == blob_count); + qsort (ratings, ok_blob_count, sizeof (float), sort_floats); + //sort them + bestgap = 0; + gapstart = ratings[0] - 1; //all reject if none better + if (ok_blob_count >= 3) { + for (index = 0; index < ok_blob_count - 1; index++) { + if (ratings[index + 1] - ratings[index] > bestgap) { + bestgap = ratings[index + 1] - ratings[index]; + //find biggest + gapstart = ratings[index]; + } + } + } + threshold = gapstart + bestgap / 2; + // tprintf("First=%g, last=%g, gap=%g, threshold=%g\n", + // ratings[0],ratings[index],bestgap,threshold); + + free_mem(ratings); + return threshold; +} + + +/********************************************************************** + * sort_floats + * + * qsort function to sort 2 floats. + **********************************************************************/ + +int sort_floats( //qsort function + const void *arg1, //ptrs to floats + const void *arg2) { + float diff; //difference + + diff = *((float *) arg1) - *((float *) arg2); + if (diff > 0) + return 1; + else if (diff < 0) + return -1; + else + return 0; +} + + +/************************************************************************* + * 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. + *************************************************************************/ + +void reject_edge_blobs(WERD_RES *word) { + BOX word_box = word->word->bounding_box (); + BOX blob_box; + PBLOB_IT blob_it = word->outword->blob_list (); + //blobs + int blobindex = 0; + float centre; + + if ((word_box.left () < tessedit_image_border) || + (word_box.bottom () < tessedit_image_border) || + (word_box.right () + tessedit_image_border > + page_image.get_xsize () - 1) || + (word_box.top () + tessedit_image_border > page_image.get_ysize () - 1)) { + ASSERT_HOST (word->reject_map.length () == blob_it.length ()); + for (blobindex = 0, blob_it.mark_cycle_pt (); + !blob_it.cycled_list (); blobindex++, blob_it.forward ()) { + blob_box = blob_it.data ()->bounding_box (); + centre = (blob_box.left () + blob_box.right ()) / 2.0; + if ((word->denorm.x (blob_box.left ()) < tessedit_image_border) || + (word->denorm.y (blob_box.bottom (), centre) < + tessedit_image_border) || + (word->denorm.x (blob_box.right ()) + tessedit_image_border > + page_image.get_xsize () - 1) || + (word->denorm.y (blob_box.top (), centre) + + tessedit_image_border > page_image.get_ysize () - 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 one_ell_conflict(WERD_RES *word_res, BOOL8 update_map) { + const char *word; + INT16 word_len; //its length + INT16 first_alphanum_idx; + INT16 i; + 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->string ().string (); + word_len = strlen (word); + /* + 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, non_conflict_set_char = FALSE; + (i < word_len) && !non_conflict_set_char; i++) + non_conflict_set_char = isalnum (word[i]) && + !STRING (conflict_set_I_l_1).contains (word[i]); + 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); + 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_idx = first_alphanum_pos (word); + if (word[first_alphanum_idx] == 'I') { + word_res->best_choice->string ()[first_alphanum_idx] = 'l'; + if (safe_dict_word (word) > 0) { + word_res->best_choice->string ()[first_alphanum_idx] = 'I'; + if (update_map) + word_res->reject_map[first_alphanum_idx]. + setrej_1Il_conflict(); + return TRUE; + } + else { + word_res->best_choice->string ()[first_alphanum_idx] = 'I'; + return FALSE; + } + } + + if (word[first_alphanum_idx] == 'l') { + word_res->best_choice->string ()[first_alphanum_idx] = 'I'; + if (safe_dict_word (word) > 0) { + word_res->best_choice->string ()[first_alphanum_idx] = 'l'; + if (update_map) + word_res->reject_map[first_alphanum_idx]. + setrej_1Il_conflict(); + return TRUE; + } + else { + word_res->best_choice->string ()[first_alphanum_idx] = '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_idx = first_alphanum_pos (word); + if (word[first_alphanum_idx] == 'l') { + word_res->best_choice->string ()[first_alphanum_idx] = 'I'; + if (safe_dict_word (word) > 0) + return FALSE; + else + word_res->best_choice->string ()[first_alphanum_idx] = 'l'; + } + else if (word[first_alphanum_idx] == 'I') { + word_res->best_choice->string ()[first_alphanum_idx] = 'l'; + if (safe_dict_word (word) > 0) + return FALSE; + else + word_res->best_choice->string ()[first_alphanum_idx] = '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)) { + allow_1s = (alpha_count (word) == 0) || + (word_res->best_choice->permuter () == NUMBER_PERM); + + conflict = FALSE; + for (i = 0; i < word_len; i++) { + if ((!allow_1s || (word[i] != '1')) && + STRING (conflict_set_I_l_1).contains (word[i])) { + 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); + if ((word_type == AC_LOWER_CASE) || (word_type == AC_INITIAL_CAP)) { + first_alphanum_idx = first_alphanum_pos (word); + if (STRING (conflict_set_I_l_1).contains (word[first_alphanum_idx])) { + if (update_map) + word_res->reject_map[first_alphanum_idx].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 first_alphanum_pos(const char *word) { + INT16 i; + + for (i = 0; word[i] != '\0'; i++) { + if (isalnum (word[i])) + return i; + } + return -1; +} + + +INT16 alpha_count(const char *word) { + INT16 i; + INT16 count = 0; + + for (i = 0; word[i] != '\0'; i++) { + if (isalpha (word[i])) + count++; + } + return count; +} + + +BOOL8 word_contains_non_1_digit(const char *word) { + INT16 i; + + for (i = 0; word[i] != '\0'; i++) { + if (isdigit (word[i]) && word[i] != '1') + return TRUE; + } + return FALSE; +} + + +BOOL8 test_ambig_word( //test for ambiguity + WERD_RES *word) { + BOOL8 ambig = FALSE; + + if ((word->best_choice->permuter () == SYSTEM_DAWG_PERM) || + (word->best_choice->permuter () == FREQ_DAWG_PERM) || + (word->best_choice->permuter () == USER_DAWG_PERM)) { + ambig = !NoDangerousAmbig(word->best_choice->string().string(), NULL); + } + return ambig; +} + + +/************************************************************************* + * ambig_word() + * + * This is a recursive routine which tests the dictionary for all combinations + * of conflict set alternatives for characters in a given word. + *************************************************************************/ + +BOOL8 ambig_word( //original word + const char *start_word, + char *temp_word, //alterable copy + INT16 test_char_pos //idx to char to alter + ) { + const char *ambigs; //Ambiguities for char + + if (*(temp_word + test_char_pos) == '\0') { + if (safe_dict_word (temp_word)) { + if (strcmp (start_word, temp_word) == 0) + return FALSE; + else + return TRUE; + } + else + return FALSE; + } + else { + ambigs = char_ambiguities (*(temp_word + test_char_pos)); + if (ambigs == NULL) + return ambig_word (start_word, temp_word, test_char_pos + 1); + else { + while (*ambigs != '\0') { + *(temp_word + test_char_pos) = *ambigs++; + //test next ambiguity + if (ambig_word (start_word, temp_word, test_char_pos + 1)) + return TRUE; + } + return FALSE; + } + } +} + + +/************************************************************************* + * char_ambiguities() + * + * Return a pointer to a string containing the full conflict set of characters + * which includes the specified character, if there is one. If the specified + * character is not a member of a conflict set, return NULL. + * (NOTE that a character is assumed to be a member of only ONE conflict set.) + *************************************************************************/ + +const char *char_ambiguities(char c) { + static STRING_CLIST conflict_sets; + static BOOL8 read_conflict_sets = FALSE; + STRING_C_IT cs_it(&conflict_sets); + const char *cs; + STRING cs_file_name; + FILE *cs_file; + char buff[1024]; + + if (!read_conflict_sets) { + cs_file_name = datadir + "confsets"; + if (!(cs_file = fopen (cs_file_name.string (), "r"))) { + CANTOPENFILE.error ("char_ambiguities", EXIT, "%s %d", + cs_file_name.string (), errno); + } + while (fscanf (cs_file, "%s", buff) == 1) { + cs_it.add_after_then_move (new STRING (buff)); + } + read_conflict_sets = TRUE; + cs_it.move_to_first (); + if (tessedit_rejection_debug) { + for (cs_it.mark_cycle_pt (); + !cs_it.cycled_list (); cs_it.forward ()) { + tprintf ("\"%s\"\n", cs_it.data ()->string ()); + } + } + } + + cs_it.move_to_first (); + for (cs_it.mark_cycle_pt (); !cs_it.cycled_list (); cs_it.forward ()) { + cs = cs_it.data ()->string (); + if (strchr (cs, c) != NULL) + return cs; + } + return NULL; +} + +#ifndef EMBEDDED +void test_ambigs(const char *word) { + char orig_word[80]; + char temp_word[80]; + + if (strlen (word) > 80) + tprintf ("Ridiculously long word \"%s\"\n", word); + else { + strcpy(orig_word, word); + while (strlen (orig_word) > 0) { + strcpy(temp_word, orig_word); + + #ifndef SECURE_NAMES + if (ambig_word (orig_word, temp_word, 0)) + tprintf ("Ambiguity \"%s\" -> \"%s\"\n", orig_word, temp_word); + else + tprintf ("NO Ambiguities for \"%s\"\n", orig_word); + tprintf ("Next Word > "); + #endif + scanf ("%s", orig_word); + } + } +} +#endif + +/************************************************************************* + * nn_recover_rejects() + * Generate the nn_reject_map - a copy of the current reject map, but dont + * reject previously rejected chars if the NN matcher agrees with the best + * choice. + *************************************************************************/ + +void nn_recover_rejects(WERD_RES *word, ROW *row) { + //copy for debug + REJMAP old_map = word->reject_map; + /* + NOTE THAT THIS IS RELATIVELY INEFFICIENT AS THE WHOLE OF THE WERD IS + MATCHED BY THE NN MATCHER. IF COULD EASILY BE RESTRICTED TO JUST THE + REJECT CHARACTERS (Though initial use is when words are total rejects + anyway). + */ + + set_global_subsubloc_code(SUBSUBLOC_NN); + nn_match_word(word, row); + + if (no_unrej_1Il) + dont_allow_1Il(word); + if (no_unrej_dubious_chars) + dont_allow_dubious_chars(word); + + if (rej_mostly_reject_mode == 1) + reject_mostly_rejects(word); + /* + IF there are no unrejected alphanumerics AND + The word is not an acceptable single non alphanum char word AND + The word is not an acceptable repeated non alphanum char word + THEN Reject whole word + */ + if (no_unrej_no_alphanum_wds && + (count_alphanums (word) < 1) && + !((word->best_choice->string ().length () == 1) && + STRING (ok_single_ch_non_alphanum_wds).contains (word->best_choice-> + string ()[0])) + && !repeated_nonalphanum_wd (word, row)) + + word->reject_map.rej_word_no_alphanums (); + + #ifndef SECURE_NAMES + + if (nn_debug) { + tprintf ("\nTess: \"%s\" MAP ", word->best_choice->string ().string ()); + old_map.print (stdout); + tprintf ("->"); + word->reject_map.print (stdout); + tprintf ("\n"); + } + #endif + set_global_subsubloc_code(SUBSUBLOC_OTHER); +} + + +void nn_match_word( //Match a word + WERD_RES *word, + ROW *row) { + PIXROW_LIST *pixrow_list; + PIXROW_IT pixrow_it; + IMAGELINE *imlines; //lines of the image + BOX pix_box; //box of imlines extent +#ifndef GRAPHICS_DISABLED + WINDOW win = NULL; +#endif + IMAGE clip_image; + IMAGE scaled_image; + float baseline_pos; + INT16 net_image_size; + INT16 clip_image_size; + WERD copy_outword; // copy to denorm + INT16 i; + + const char *word_string; + BOOL8 word_in_dict; //Tess wd in dict + BOOL8 checked_dict_word; //Tess wd definitely in dict + BOOL8 sensible_word; //OK char string + BOOL8 centre; //Not at word end chs + BOOL8 good_quality_word; + INT16 char_quality; + INT16 accepted_char_quality; + + INT16 conf_level; //0:REJECT + //1:DODGY ACCEPT + //2:DICT ACCEPT + //3:CLEAR ACCEPT + INT16 first_alphanum_idx; + + word_string = word->best_choice->string ().string (); + first_alphanum_idx = first_alphanum_pos (word_string); + word_in_dict = ((word->best_choice->permuter () == SYSTEM_DAWG_PERM) || + (word->best_choice->permuter () == FREQ_DAWG_PERM) || + (word->best_choice->permuter () == USER_DAWG_PERM)); + checked_dict_word = word_in_dict && (safe_dict_word (word_string) > 0); + sensible_word = acceptable_word_string (word_string) != AC_UNACCEPTABLE; + + word_char_quality(word, row, &char_quality, &accepted_char_quality); + good_quality_word = word->best_choice->string ().length () == char_quality; + + #ifndef SECURE_NAMES + if (nn_reject_debug) { + tprintf ("Dict: %c Checked Dict: %c Sensible: %c Quality: %c\n", + word_in_dict ? 'T' : 'F', + checked_dict_word ? 'T' : 'F', + sensible_word ? 'T' : 'F', good_quality_word ? 'T' : 'F'); + } + #endif + + if (word->best_choice->string ().length () != + word->outword->blob_list ()->length ()) { + #ifndef SECURE_NAMES + tprintf ("nn_match_word ASSERT FAIL String:\"%s\"; #Blobs=%d\n", + word->best_choice->string ().string (), + word->outword->blob_list ()->length ()); + #endif + err_exit(); + } + + copy_outword = *(word->outword); + copy_outword.baseline_denormalise (&word->denorm); + /* + For each character, generate and match a new image, containing JUST the + character we have clipped, centered in the image, on a white background. + Note that we MUST have a square image so that we can scale it uniformly in + x and y. We base the size on x_height as this can be found fairly reliably. + */ + net_image_size = (net_image_width > net_image_height) ? + net_image_width : net_image_height; + clip_image_size = (INT16) floor (0.5 + + net_image_size * word->x_height / + net_image_x_height); + if ((clip_image_size <= 1) || (net_image_size <= 1)) { + return; + } + + /* + Get the image of the word and the pix positions of each char + */ + char_clip_word(©_outword, page_image, pixrow_list, imlines, pix_box); +#ifndef GRAPHICS_DISABLED + if (show_char_clipping) { + win = display_clip_image (©_outword, page_image, + pixrow_list, pix_box); + } +#endif + pixrow_it.set_to_list (pixrow_list); + pixrow_it.move_to_first (); + for (pixrow_it.mark_cycle_pt (), i = 0; + !pixrow_it.cycled_list (); pixrow_it.forward (), i++) { + if (pixrow_it.data ()-> + bad_box (page_image.get_xsize (), page_image.get_ysize ())) + continue; + clip_image.create (clip_image_size, clip_image_size, 1); + //make bin imge + if (!copy_outword.flag (W_INVERSE)) + invert_image(&clip_image); //white background for black on white + pixrow_it.data ()->char_clip_image (imlines, pix_box, row, + clip_image, baseline_pos); + if (copy_outword.flag (W_INVERSE)) + invert_image(&clip_image); //invert white on black for scaling &NN + scaled_image.create (net_image_size, net_image_size, 1); + scale_image(clip_image, scaled_image); + baseline_pos *= net_image_size / clip_image_size; + //scale with im + centre = !pixrow_it.at_first () && !pixrow_it.at_last (); + + conf_level = nn_match_char (scaled_image, baseline_pos, + word_in_dict, checked_dict_word, + sensible_word, centre, + good_quality_word, word_string[i]); + if (word->reject_map[i].recoverable ()) { + if ((i == first_alphanum_idx) && + ((word_string[i] == 'I') || (word_string[i] == 'i'))) { + if (conf_level >= nn_conf_initial_i_level) + word->reject_map[i].setrej_nn_accept (); + //un-reject char + } + else if (conf_level > 0) + //un-reject char + word->reject_map[i].setrej_nn_accept (); + } +#ifndef GRAPHICS_DISABLED + if (show_char_clipping) + display_images(clip_image, scaled_image); +#endif + clip_image.destroy (); + scaled_image.destroy (); + } + + delete[]imlines; // Free array of imlines + delete pixrow_list; + +#ifndef GRAPHICS_DISABLED + if (show_char_clipping) { + destroy_window(win); + } +#endif +} + + +/************************************************************************* + * nn_match_char() + * Call Neural Net matcher to match a single character, given a scaled, + * square image + *************************************************************************/ + +INT16 nn_match_char( //of character + IMAGE &scaled_image, + float baseline_pos, //rel to scaled_image + BOOL8 dict_word, //part of dict wd? + BOOL8 checked_dict_word, //part of dict wd? + BOOL8 sensible_word, //part acceptable str? + BOOL8 centre, //not at word ends? + BOOL8 good_quality_word, //initial segmentation + char tess_ch //confirm this? + ) { + INT16 conf_level; //0..2 + INT32 row; + INT32 col; + INT32 y_size = scaled_image.get_ysize (); + INT32 start_y = y_size - (y_size - net_image_height) / 2 - 1; + INT32 end_y = start_y - net_image_height + 1; + IMAGELINE imline; + float *input_vector; + float *input_vec_ptr; + char top; + float top_score; + char next; + float next_score; + INT16 input_nodes = (net_image_height * net_image_width) + net_bl_nodes; + INT16 j; + + input_vector = (float *) alloc_mem (input_nodes * sizeof (float)); + input_vec_ptr = input_vector; + + invert_image(&scaled_image); //cos nns work better + for (row = start_y; row >= end_y; row--) { + scaled_image.fast_get_line (0, row, net_image_width, &imline); + for (col = 0; col < net_image_width; col++) + *input_vec_ptr++ = imline.pixels[col]; + } + /* + The bit map presented to the net may be shorter than the image, so shift + the coord to be relative to the bitmap portion. + */ + baseline_pos -= (y_size - net_image_height) / 2.0; + /* + Baseline pos is 0 if below bitmap, 1 if above and in proportion otherwise. + This is represented to the net as a set of bl_nodes, an initial proportion + of which are set to 1.0, indicating the level of the baseline. The + remainder are 0.0 + */ + + if (baseline_pos < 0) + baseline_pos = 0; + else if (baseline_pos >= net_image_height) + baseline_pos = net_image_height + 1; + else + baseline_pos = baseline_pos + 1; + baseline_pos = baseline_pos / (net_image_height + 1); + + if (net_bl_nodes > 0) { + baseline_pos *= 1.7; //Use a wider range + if (net_bl_nodes > 1) { + /* Multi-node baseline representation */ + for (j = 0; j < net_bl_nodes; j++) { + if (baseline_pos > ((float) j / net_bl_nodes)) + *input_vec_ptr++ = 1.0; + else + *input_vec_ptr++ = 0.0; + } + } + else { + /* Single node baseline */ + *input_vec_ptr++ = baseline_pos; + } + } + + callnet(input_vector, &top, &top_score, &next, &next_score); + conf_level = evaluate_net_match (top, top_score, next, next_score, + tess_ch, dict_word, checked_dict_word, + sensible_word, centre, good_quality_word); + #ifndef SECURE_NAMES + if (nn_reject_debug) { + tprintf ("top:\"%c\" %4.2f next:\"%c\" %4.2f TESS:\"%c\" Conf: %d\n", + top, top_score, next, next_score, tess_ch, conf_level); + } + #endif + free_mem(input_vector); + return conf_level; +} + + +INT16 evaluate_net_match(char top, + float top_score, + char next, + float next_score, + char tess_ch, + BOOL8 dict_word, + BOOL8 checked_dict_word, + BOOL8 sensible_word, + BOOL8 centre, + BOOL8 good_quality_word) { + INT16 accept_level; //0 Very clearly matched + //1 Clearly top + //2 Top but poor match + //3 Next & poor top match + //4 Next but good top match + //5 No chance + BOOL8 good_top_choice; + BOOL8 excellent_top_choice; + BOOL8 confusion_match = FALSE; + BOOL8 dodgy_char = !isalnum (tess_ch); + + good_top_choice = (top_score > nn_reject_threshold) && + (nn_reject_head_and_shoulders * top_score > next_score); + + excellent_top_choice = good_top_choice && + (top_score > nn_dodgy_char_threshold); + + if (top == tess_ch) { + if (excellent_top_choice) + accept_level = 0; + else if (good_top_choice) + accept_level = 1; //Top correct and well matched + else + accept_level = 2; //Top correct but poor match + } + else if ((nn_conf_1Il && + STRING (conflict_set_I_l_1).contains (tess_ch) && + STRING (conflict_set_I_l_1).contains (top)) || + (nn_conf_hyphen && + STRING (conflict_set_hyphen).contains (tess_ch) && + STRING (conflict_set_hyphen).contains (top)) || + (nn_conf_Ss && + STRING (conflict_set_S_s).contains (tess_ch) && + STRING (conflict_set_S_s).contains (top))) { + confusion_match = TRUE; + if (good_top_choice) + accept_level = 1; //Good top confusion + else + accept_level = 2; //Poor top confusion + } + else if ((nn_conf_1Il && + STRING (conflict_set_I_l_1).contains (tess_ch) && + STRING (conflict_set_I_l_1).contains (next)) || + (nn_conf_hyphen && + STRING (conflict_set_hyphen).contains (tess_ch) && + STRING (conflict_set_hyphen).contains (next)) || + (nn_conf_Ss && + STRING (conflict_set_S_s).contains (tess_ch) && + STRING (conflict_set_S_s).contains (next))) { + confusion_match = TRUE; + if (!good_top_choice) + accept_level = 3; //Next confusion and top match dodgy + else + accept_level = 4; //Next confusion and good top match + } + else if (next == tess_ch) { + if (!good_top_choice) + accept_level = 3; //Next match and top match dodgy + else + accept_level = 4; //Next match and good top match + } + else + accept_level = 5; + + /* Could allow some match flexibility here sS$ etc */ + + /* Now set confirmation level according to how much we can believe the tess + char. */ + + if ((accept_level == 0) && !confusion_match) + return 3; + + if ((accept_level <= 1) && + (!nn_conf_strict_on_dodgy_chs || !dodgy_char) && !confusion_match) + return 3; + + if ((accept_level == 2) && + !confusion_match && !dodgy_char && + good_quality_word && + dict_word && + (checked_dict_word || !nn_double_check_dict) && sensible_word) + return 2; + + if (confusion_match && + (accept_level <= nn_conf_accept_level) && + (good_quality_word || + (!nn_conf_test_good_qual && + !STRING (conflict_set_I_l_1).contains (tess_ch))) && + (dict_word || !nn_conf_test_dict) && + (checked_dict_word || !nn_conf_double_check_dict) && + (sensible_word || !nn_conf_test_sensible)) + return 1; + + if (!confusion_match && + nn_lax && + (accept_level == 3) && + (good_quality_word || !nn_conf_test_good_qual) && + (dict_word || !nn_conf_test_dict) && + (sensible_word || !nn_conf_test_sensible)) + return 1; + else + return 0; +} + + +/************************************************************************* + * dont_allow_dubious_chars() + * Let Rejects "eat" into adjacent "dubious" chars. I.e those prone to be wrong + * if adjacent to a reject. + *************************************************************************/ +void dont_allow_dubious_chars(WERD_RES *word) { + int i = 0; + int rej_pos; + int word_len = word->reject_map.length (); + + while (i < word_len) { + /* Find next reject */ + + while ((i < word_len) && (word->reject_map[i].accepted ())) + i++; + + if (i < word_len) { + rej_pos = i; + + /* Reject dubious chars to the left */ + i--; + while ((i >= 0) && + STRING (dubious_chars_left_of_reject).contains (word-> + best_choice-> + string () + [i])) { + word->reject_map[i--].setrej_dubious (); + } + + /* Skip adjacent rejects */ + + for (i = rej_pos; + (i < word_len) && (word->reject_map[i].rejected ()); i++); + + /* Reject dubious chars to the right */ + + while ((i < word_len) && + STRING (dubious_chars_right_of_reject).contains (word-> + best_choice-> + string () + [i])) { + word->reject_map[i++].setrej_dubious (); + } + } + } +} + + +/************************************************************************* + * dont_allow_1Il() + * Dont unreject LONE accepted 1Il conflict set chars + *************************************************************************/ +void dont_allow_1Il(WERD_RES *word) { + int i = 0; + int word_len = word->reject_map.length (); + const char *s = word->best_choice->string ().string (); + BOOL8 accepted_1Il = FALSE; + + for (i = 0; i < word_len; i++) { + if (word->reject_map[i].accepted ()) { + if (STRING (conflict_set_I_l_1).contains (s[i])) + accepted_1Il = TRUE; + else { + if (isalnum (s[i])) + return; // >=1 non 1Il ch accepted + } + } + } + if (!accepted_1Il) + return; //Nothing to worry about + + for (i = 0; i < word_len; i++) { + if (STRING (conflict_set_I_l_1).contains (s[i]) && + word->reject_map[i].accepted ()) + word->reject_map[i].setrej_postNN_1Il (); + } +} + + +INT16 count_alphanums( //how many alphanums + WERD_RES *word) { + int count = 0; + int i; + + for (i = 0; i < word->reject_map.length (); i++) { + if ((word->reject_map[i].accepted ()) && + (isalnum (word->best_choice->string ()[i]))) + count++; + } + return count; +} + + +void reject_mostly_rejects( //rej all if most rejectd + 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 repeated_nonalphanum_wd(WERD_RES *word, ROW *row) { + INT16 char_quality; + INT16 accepted_char_quality; + + if (word->best_choice->string ().length () <= 1) + return FALSE; + + if (!STRING (ok_repeated_ch_non_alphanum_wds). + contains (word->best_choice->string ()[0])) + return FALSE; + + if (!repeated_ch_string (word->best_choice->string ().string ())) + return FALSE; + + word_char_quality(word, row, &char_quality, &accepted_char_quality); + + if ((word->best_choice->string ().length () == char_quality) && + (char_quality == accepted_char_quality)) + return TRUE; + else + return FALSE; +} + + +BOOL8 repeated_ch_string(const char *rep_ch_str) { + char c; + + if ((rep_ch_str == NULL) || (*rep_ch_str == '\0')) { + return FALSE; + } + + c = *rep_ch_str; + rep_ch_str++; + while (*rep_ch_str == c) { + rep_ch_str++; + } + if (*rep_ch_str == '\0') + return TRUE; + return FALSE; +} + + +INT16 safe_dict_word(const char *s) { + int dict_word_type; + + dict_word_type = dict_word (s); + if (dict_word_type == DOC_DAWG_PERM) + return 0; + else + return dict_word_type; +} + + +void flip_hyphens(WERD_RES *word) { + char *str = (char *) word->best_choice->string ().string (); + int i = 0; + PBLOB_IT outword_it; + int prev_right = -9999; + int next_left; + BOX out_box; + float aspect_ratio; + + if (tessedit_lower_flip_hyphen <= 1) + return; + + outword_it.set_to_list (word->outword->blob_list ()); + + for (outword_it.mark_cycle_pt (); + !outword_it.cycled_list (); outword_it.forward (), i++) { + out_box = outword_it.data ()->bounding_box (); + if (outword_it.at_last ()) + next_left = 9999; + else + next_left = outword_it.data_relative (1)->bounding_box ().left (); + /* + Dont touch small or touching blobs - it is too dangerous + */ + if ((out_box.width () > 8 * word->denorm.scale ()) && + (out_box.left () > prev_right) && (out_box.right () < next_left)) { + aspect_ratio = out_box.width () / (float) out_box.height (); + if (str[i] == '.') { + if (aspect_ratio >= tessedit_upper_flip_hyphen) { + /* Certain HYPHEN */ + str[i] = '-'; + if (word->reject_map[i].rejected ()) + word->reject_map[i].setrej_hyphen_accept (); + } + if ((aspect_ratio > tessedit_lower_flip_hyphen) && + word->reject_map[i].accepted ()) + //Suspected HYPHEN + word->reject_map[i].setrej_hyphen (); + } + else if (str[i] == '-') { + if ((aspect_ratio >= tessedit_upper_flip_hyphen) && + (word->reject_map[i].rejected ())) + word->reject_map[i].setrej_hyphen_accept (); + //Certain HYPHEN + + if ((aspect_ratio <= tessedit_lower_flip_hyphen) && + (word->reject_map[i].accepted ())) + //Suspected HYPHEN + word->reject_map[i].setrej_hyphen (); + } + } + prev_right = out_box.right (); + } +} + + +void flip_0O(WERD_RES *word) { + char *str = (char *) word->best_choice->string ().string (); + int i; + PBLOB_IT outword_it; + BOX out_box; + + if (!tessedit_flip_0O) + return; + + outword_it.set_to_list (word->outword->blob_list ()); + + for (i = 0, outword_it.mark_cycle_pt (); + !outword_it.cycled_list (); i++, outword_it.forward ()) { + if (isupper (str[i]) || isdigit (str[i])) { + out_box = outword_it.data ()->bounding_box (); + if ((out_box.top () < bln_baseline_offset + bln_x_height) || + (out_box.bottom () > bln_baseline_offset + bln_x_height / 4)) + return; //Beware words with sub/superscripts + } + } + + for (i = 1; str[i] != '\0'; i++, outword_it.forward ()) { + if ((str[i] == '0') || (str[i] == 'O')) { + /* A0A */ + if (non_O_upper (str[i - 1]) && non_O_upper (str[i + 1])) { + str[i] = 'O'; + } + /* A00A */ + if (non_O_upper (str[i - 1]) && + ((str[i + 1] == '0') || (str[i + 1] == 'O')) && + non_O_upper (str[i + 2])) { + str[i] = 'O'; + str[i + 1] = 'O'; + i++; + } + /* AA0 */ + if ((i > 1) && + non_O_upper (str[i - 2]) && + non_O_upper (str[i - 1]) && + !isdigit (str[i + 1]) && + (str[i + 1] != 'l') && (str[i + 1] != 'I')) { + str[i] = 'O'; + } + /* 9O9 */ + if (non_0_digit (str[i - 1]) && non_0_digit (str[i + 1])) { + str[i] = '0'; + } + /* 9OOO */ + if (non_0_digit (str[i - 1]) && + ((str[i + 1] == '0') || (str[i + 1] == 'O')) && + ((str[i + 2] == '0') || (str[i + 2] == 'O'))) { + str[i] = '0'; + str[i + 1] = '0'; + str[i + 2] = '0'; + i += 2; + } + /* 9OO */ + if (non_0_digit (str[i - 1]) && + ((str[i + 1] == '0') || (str[i + 1] == 'O')) && + !isupper (str[i + 2])) { + str[i] = '0'; + str[i + 1] = '0'; + i++; + } + /* 9O */ + if (non_0_digit (str[i - 1]) && !isupper (str[i + 1])) { + str[i] = '0'; + } + /* 9[.,]OOO.. */ + if ((i > 1) && + ((str[i - 1] == '.') || (str[i - 1] == ',')) && + (isdigit (str[i - 2]) || (str[i - 2] == 'O'))) { + if (str[i - 2] == 'O') + str[i - 2] = '0'; + while ((str[i] == 'O') || (str[i] == '0')) { + str[i++] = '0'; + } + i--; + } + } + } +} + + +BOOL8 non_O_upper(char c) { + return isupper (c) && (c != 'O'); +} + + +BOOL8 non_0_digit(char c) { + return isdigit (c) && (c != '0'); +} diff --git a/ccmain/reject.h b/ccmain/reject.h new file mode 100644 index 0000000000..6aeebde914 --- /dev/null +++ b/ccmain/reject.h @@ -0,0 +1,175 @@ +/********************************************************************** + * 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 "varable.h" +#include "pageres.h" +#include "notdll.h" + +extern INT_VAR_H (tessedit_reject_mode, 5, "Rejection algorithm"); +extern INT_VAR_H (tessedit_ok_mode, 5, "Acceptance decision algorithm"); +extern BOOL_VAR_H (tessedit_use_nn, TRUE, ""); +extern BOOL_VAR_H (tessedit_rejection_debug, FALSE, "Adaption debug"); +extern BOOL_VAR_H (tessedit_rejection_stats, FALSE, "Show NN stats"); +extern BOOL_VAR_H (tessedit_flip_0O, TRUE, "Contextual 0O O0 flips"); +extern double_VAR_H (tessedit_lower_flip_hyphen, 1.5, +"Aspect ratio dot/hyphen test"); +extern double_VAR_H (tessedit_upper_flip_hyphen, 1.8, +"Aspect ratio dot/hyphen test"); +extern BOOL_VAR_H (rej_trust_doc_dawg, FALSE, +"Use DOC dawg in 11l conf. detector"); +extern BOOL_VAR_H (rej_1Il_use_dict_word, FALSE, "Use dictword test"); +extern BOOL_VAR_H (rej_1Il_trust_permuter_type, TRUE, "Dont double check"); +extern BOOL_VAR_H (one_ell_conflict_default, TRUE, +"one_ell_conflict default"); +extern BOOL_VAR_H (show_char_clipping, FALSE, "Show clip image window?"); +extern BOOL_VAR_H (nn_debug, FALSE, "NN DEBUGGING?"); +extern BOOL_VAR_H (nn_reject_debug, FALSE, "NN DEBUG each char?"); +extern BOOL_VAR_H (nn_lax, FALSE, "Use 2nd rate matches"); +extern BOOL_VAR_H (nn_double_check_dict, FALSE, "Double check"); +extern BOOL_VAR_H (nn_conf_double_check_dict, TRUE, +"Double check for confusions"); +extern BOOL_VAR_H (nn_conf_1Il, TRUE, "NN use 1Il conflicts"); +extern BOOL_VAR_H (nn_conf_Ss, TRUE, "NN use Ss conflicts"); +extern BOOL_VAR_H (nn_conf_hyphen, TRUE, "NN hyphen conflicts"); +extern BOOL_VAR_H (nn_conf_test_good_qual, FALSE, "NN dodgy 1Il cross check"); +extern BOOL_VAR_H (nn_conf_test_dict, TRUE, "NN dodgy 1Il cross check"); +extern BOOL_VAR_H (nn_conf_test_sensible, TRUE, "NN dodgy 1Il cross check"); +extern BOOL_VAR_H (nn_conf_strict_on_dodgy_chs, TRUE, +"Require stronger NN match"); +extern double_VAR_H (nn_dodgy_char_threshold, 0.99, "min accept score"); +extern INT_VAR_H (nn_conf_accept_level, 4, "NN accept dodgy 1Il matches? "); +extern INT_VAR_H (nn_conf_initial_i_level, 3, +"NN accept initial Ii match level "); +extern BOOL_VAR_H (no_unrej_dubious_chars, TRUE, +"Dubious chars next to reject?"); +extern BOOL_VAR_H (no_unrej_no_alphanum_wds, TRUE, +"Stop unrej of non A/N wds?"); +extern BOOL_VAR_H (no_unrej_1Il, FALSE, "Stop unrej of 1Ilchars?"); +extern BOOL_VAR_H (rej_use_tess_accepted, TRUE, +"Individual rejection control"); +extern BOOL_VAR_H (rej_use_tess_blanks, TRUE, "Individual rejection control"); +extern BOOL_VAR_H (rej_use_good_perm, TRUE, "Individual rejection control"); +extern BOOL_VAR_H (rej_use_sensible_wd, FALSE, "Extend permuter check"); +extern BOOL_VAR_H (rej_alphas_in_number_perm, FALSE, "Extend permuter check"); +extern double_VAR_H (rej_whole_of_mostly_reject_word_fract, 0.85, +"if >this fract"); +extern INT_VAR_H (rej_mostly_reject_mode, 1, +"0-never, 1-afterNN, 2-after new xht"); +extern double_VAR_H (tessed_fullstop_aspect_ratio, 1.2, +"if >this fract then reject"); +extern INT_VAR_H (net_image_width, 40, "NN input image width"); +extern INT_VAR_H (net_image_height, 36, "NN input image height"); +extern INT_VAR_H (net_image_x_height, 22, "NN input image x_height"); +extern INT_VAR_H (tessedit_image_border, 2, "Rej blbs near image edge limit"); +extern INT_VAR_H (net_bl_nodes, 20, "Number of baseline nodes"); +extern double_VAR_H (nn_reject_threshold, 0.5, "NN min accept score"); +extern double_VAR_H (nn_reject_head_and_shoulders, 0.6, +"top scores sep factor"); +extern STRING_VAR_H (ok_single_ch_non_alphanum_wds, "-?\075", +"Allow NN to unrej"); +extern STRING_VAR_H (ok_repeated_ch_non_alphanum_wds, "-?*\075", +"Allow NN to unrej"); +extern STRING_VAR_H (conflict_set_I_l_1, "Il1[]", "Il1 conflict set"); +extern STRING_VAR_H (conflict_set_S_s, "Ss$", "Ss conflict set"); +extern STRING_VAR_H (conflict_set_hyphen, "-_~", "hyphen conflict set"); +extern STRING_VAR_H (dubious_chars_left_of_reject, "!'+`()-./\\<>;:^_,~\"", +"Unreliable chars"); +extern STRING_VAR_H (dubious_chars_right_of_reject, "!'+`()-./\\<>;:^_,~\"", +"Unreliable chars"); +extern INT_VAR_H (min_sane_x_ht_pixels, 8, +"Reject any x-ht lt or eq than this"); +void set_done( //set done flag + WERD_RES *word, + INT16 pass); +void make_reject_map( //make rej map for wd //detailed results + WERD_RES *word, + BLOB_CHOICE_LIST_CLIST *blob_choices, + ROW *row, + INT16 pass //1st or 2nd? + ); +void reject_blanks(WERD_RES *word); +void reject_I_1_L(WERD_RES *word); + //detailed results +void reject_poor_matches(WERD_RES *word, BLOB_CHOICE_LIST_CLIST *blob_choices); +float compute_reject_threshold( //compute threshold //detailed results + BLOB_CHOICE_LIST_CLIST *blob_choices); +int sort_floats( //qsort function + const void *arg1, //ptrs to floats + const void *arg2); +void reject_edge_blobs(WERD_RES *word); +BOOL8 one_ell_conflict(WERD_RES *word_res, BOOL8 update_map); +INT16 first_alphanum_pos(const char *word); +INT16 alpha_count(const char *word); +BOOL8 word_contains_non_1_digit(const char *word); +BOOL8 test_ambig_word( //test for ambiguity + WERD_RES *word); + //original word +BOOL8 ambig_word(const char *start_word, + char *temp_word, //alterable copy + INT16 test_char_pos //idx to char to alter + ); +const char *char_ambiguities(char c); + +#ifndef EMBEDDED +void test_ambigs(const char *word); +#endif + +void nn_recover_rejects(WERD_RES *word, ROW *row); +void nn_match_word( //Match a word + WERD_RES *word, + ROW *row); + //of character +INT16 nn_match_char(IMAGE &scaled_image, + float baseline_pos, //rel to scaled_image + BOOL8 dict_word, //part of dict wd? + BOOL8 checked_dict_word, //part of dict wd? + BOOL8 sensible_word, //part acceptable str? + BOOL8 centre, //not at word ends? + BOOL8 good_quality_word, //initial segmentation + char tess_ch //confirm this? + ); +INT16 evaluate_net_match(char top, + float top_score, + char next, + float next_score, + char tess_ch, + BOOL8 dict_word, + BOOL8 checked_dict_word, + BOOL8 sensible_word, + BOOL8 centre, + BOOL8 good_quality_word); +void dont_allow_dubious_chars(WERD_RES *word); + +void dont_allow_1Il(WERD_RES *word); + +INT16 count_alphanums( //how many alphanums + WERD_RES *word); +void reject_mostly_rejects( //rej all if most rejectd + WERD_RES *word); +BOOL8 repeated_nonalphanum_wd(WERD_RES *word, ROW *row); +BOOL8 repeated_ch_string(const char *rep_ch_str); +INT16 safe_dict_word(const char *s); +void flip_hyphens(WERD_RES *word); +void flip_0O(WERD_RES *word); +BOOL8 non_O_upper(char c); +BOOL8 non_0_digit(char c); +#endif diff --git a/ccmain/scaleimg.cpp b/ccmain/scaleimg.cpp new file mode 100644 index 0000000000..7f02d2759e --- /dev/null +++ b/ccmain/scaleimg.cpp @@ -0,0 +1,366 @@ +/********************************************************************** + * File: scaleimg.cpp (Formerly scaleim.c) + * Description: Smart scaling of images. + * Author: Phil Cheatle + * Created: Wed Nov 18 16:12:03 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. + * + **********************************************************************/ + +/************************************************************************* + * This is really Sheelagh's code that I've hacked into a more usable form. + * You simply call scale_image() passing in source and target images. The target + * image should be empty, but created - in order to define the destination + * size. + *************************************************************************/ + +#include "mfcpch.h" +#include +#include +#include "fileerr.h" +#include "tprintf.h" +#include "grphics.h" +#include "img.h" +//#include "basefile.h" +#include "imgscale.h" +#include "scaleimg.h" + +void scale_image( //scale an image + IMAGE &image, //source image + IMAGE &target_image //target image + ) { + INT32 xsize, ysize, new_xsize, new_ysize; + IMAGELINE line, new_line; + int *hires, *lores, *oldhires, *oldlores; + int i, j, n, oldn, row, col; + int offset = 0; //not used here + float factor; + UINT8 curr_colour, new_colour; + int dummy = -1; + IMAGE image2; //horiz scaled image + + xsize = image.get_xsize (); + ysize = image.get_ysize (); + new_xsize = target_image.get_xsize (); + new_ysize = target_image.get_ysize (); + if (new_ysize > new_xsize) + new_line.init (new_ysize); + else + new_line.init (new_xsize); + + factor = (float) xsize / (float) new_xsize; + + hires = (int *) calloc (xsize, sizeof (int)); + lores = (int *) calloc (new_xsize, sizeof (int)); + oldhires = (int *) calloc (xsize, sizeof (int)); + oldlores = (int *) calloc (new_xsize, sizeof (int)); + if ((hires == NULL) || (lores == NULL) || (oldhires == NULL) + || (oldlores == NULL)) { + fprintf (stderr, "Calloc error in scale_image\n"); + err_exit(); + } + + image2.create (new_xsize, ysize, image.get_bpp ()); + + oldn = 0; + /* do first row separately because hires[col-1] doesn't make sense here */ + image.fast_get_line (0, 0, xsize, &line); + /* each line nominally begins with white */ + curr_colour = 1; + n = 0; + for (i = 0; i < xsize; i++) { + new_colour = *(line.pixels + i); + if (new_colour != curr_colour) { + hires[n] = i; + n++; + curr_colour = new_colour; + } + } + if (offset != 0) + for (i = 0; i < n; i++) + hires[i] += offset; + + if (n > new_xsize) { + tprintf ("Too many transitions (%d) on line 0\n", n); + scale_image_cop_out(image, + target_image, + factor, + hires, + lores, + oldhires, + oldlores); + return; + } + else if (n > 0) + dyn_prog (n, hires, lores, new_xsize, &dummy, &dummy, 0, factor); + else + lores[0] = new_xsize; + + curr_colour = 1; + j = 0; + for (i = 0; i < new_xsize; i++) { + if (lores[j] == i) { + curr_colour = 1 - curr_colour; + j++; + } + *(new_line.pixels + i) = curr_colour; + } + image2.put_line (0, 0, new_xsize, &new_line, 0); + + for (i = 0; i < n; i++) { + oldhires[i] = hires[i]; + oldlores[i] = lores[i]; + } + + for (i = n; i < oldn; i++) { + oldhires[i] = 0; + oldlores[i] = 0; + } + oldn = n; + + for (row = 1; row < ysize; row++) { + image.fast_get_line (0, row, xsize, &line); + /* each line nominally begins with white */ + curr_colour = 1; + n = 0; + for (i = 0; i < xsize; i++) { + new_colour = *(line.pixels + i); + if (new_colour != curr_colour) { + hires[n] = i; + n++; + curr_colour = new_colour; + } + } + for (i = n; i < oldn; i++) { + hires[i] = 0; + lores[i] = 0; + } + if (offset != 0) + for (i = 0; i < n; i++) + hires[i] += offset; + + if (n > new_xsize) { + tprintf ("Too many transitions (%d) on line %d\n", n, row); + scale_image_cop_out(image, + target_image, + factor, + hires, + lores, + oldhires, + oldlores); + return; + } + else if (n > 0) + dyn_prog(n, hires, lores, new_xsize, oldhires, oldlores, oldn, factor); + else + lores[0] = new_xsize; + + curr_colour = 1; + j = 0; + for (i = 0; i < new_xsize; i++) { + if (lores[j] == i) { + curr_colour = 1 - curr_colour; + j++; + } + *(new_line.pixels + i) = curr_colour; + } + image2.put_line (0, row, new_xsize, &new_line, 0); + + for (i = 0; i < n; i++) { + oldhires[i] = hires[i]; + oldlores[i] = lores[i]; + } + for (i = n; i < oldn; i++) { + oldhires[i] = 0; + oldlores[i] = 0; + } + oldn = n; + } + + free(hires); + free(lores); + free(oldhires); + free(oldlores); + + /* NOW DO THE VERTICAL SCALING from image2 to target_image*/ + + xsize = new_xsize; + factor = (float) ysize / (float) new_ysize; + offset = 0; + + hires = (int *) calloc (ysize, sizeof (int)); + lores = (int *) calloc (new_ysize, sizeof (int)); + oldhires = (int *) calloc (ysize, sizeof (int)); + oldlores = (int *) calloc (new_ysize, sizeof (int)); + if ((hires == NULL) || (lores == NULL) || (oldhires == NULL) + || (oldlores == NULL)) { + fprintf (stderr, "Calloc error in scale_image (vert)\n"); + err_exit(); + } + + oldn = 0; + /* do first col separately because hires[col-1] doesn't make sense here */ + image2.get_column (0, 0, ysize, &line, 0); + /* each line nominally begins with white */ + curr_colour = 1; + n = 0; + for (i = 0; i < ysize; i++) { + new_colour = *(line.pixels + i); + if (new_colour != curr_colour) { + hires[n] = i; + n++; + curr_colour = new_colour; + } + } + + if (offset != 0) + for (i = 0; i < n; i++) + hires[i] += offset; + + if (n > new_ysize) { + tprintf ("Too many transitions (%d) on column 0\n", n); + scale_image_cop_out(image, + target_image, + factor, + hires, + lores, + oldhires, + oldlores); + return; + } + else if (n > 0) + dyn_prog (n, hires, lores, new_ysize, &dummy, &dummy, 0, factor); + else + lores[0] = new_ysize; + + curr_colour = 1; + j = 0; + for (i = 0; i < new_ysize; i++) { + if (lores[j] == i) { + curr_colour = 1 - curr_colour; + j++; + } + *(new_line.pixels + i) = curr_colour; + } + target_image.put_column (0, 0, new_ysize, &new_line, 0); + + for (i = 0; i < n; i++) { + oldhires[i] = hires[i]; + oldlores[i] = lores[i]; + } + for (i = n; i < oldn; i++) { + oldhires[i] = 0; + oldlores[i] = 0; + } + oldn = n; + + for (col = 1; col < xsize; col++) { + image2.get_column (col, 0, ysize, &line, 0); + /* each line nominally begins with white */ + curr_colour = 1; + n = 0; + for (i = 0; i < ysize; i++) { + new_colour = *(line.pixels + i); + if (new_colour != curr_colour) { + hires[n] = i; + n++; + curr_colour = new_colour; + } + } + for (i = n; i < oldn; i++) { + hires[i] = 0; + lores[i] = 0; + } + + if (offset != 0) + for (i = 0; i < n; i++) + hires[i] += offset; + + if (n > new_ysize) { + tprintf ("Too many transitions (%d) on column %d\n", n, col); + scale_image_cop_out(image, + target_image, + factor, + hires, + lores, + oldhires, + oldlores); + return; + } + else if (n > 0) + dyn_prog(n, hires, lores, new_ysize, oldhires, oldlores, oldn, factor); + else + lores[0] = new_ysize; + + curr_colour = 1; + j = 0; + for (i = 0; i < new_ysize; i++) { + if (lores[j] == i) { + curr_colour = 1 - curr_colour; + j++; + } + *(new_line.pixels + i) = curr_colour; + } + target_image.put_column (col, 0, new_ysize, &new_line, 0); + + for (i = 0; i < n; i++) { + oldhires[i] = hires[i]; + oldlores[i] = lores[i]; + } + for (i = n; i < oldn; i++) { + oldhires[i] = 0; + oldlores[i] = 0; + } + oldn = n; + } + free(hires); + free(lores); + free(oldhires); + free(oldlores); +} + + +/********************************************************************** + * scale_image_cop_out + * + * Cop-out of scale_image by doing it the easy way and free the data. + **********************************************************************/ + +void scale_image_cop_out( //scale an image + IMAGE &image, //source image + IMAGE &target_image, //target image + float factor, //scale factor + int *hires, + int *lores, + int *oldhires, + int *oldlores) { + INT32 xsize, ysize, new_xsize, new_ysize; + + xsize = image.get_xsize (); + ysize = image.get_ysize (); + new_xsize = target_image.get_xsize (); + new_ysize = target_image.get_ysize (); + + if (factor <= 0.5) + reduce_sub_image (&image, 0, 0, xsize, ysize, + &target_image, 0, 0, (INT32) (1.0 / factor), FALSE); + else if (factor >= 2) + enlarge_sub_image (&image, 0, 0, &target_image, + 0, 0, new_xsize, new_ysize, (INT32) factor, FALSE); + else + copy_sub_image (&image, 0, 0, xsize, ysize, &target_image, 0, 0, FALSE); + free(hires); + free(lores); + free(oldhires); + free(oldlores); +} diff --git a/ccmain/scaleimg.h b/ccmain/scaleimg.h new file mode 100644 index 0000000000..4807b09e2c --- /dev/null +++ b/ccmain/scaleimg.h @@ -0,0 +1,35 @@ +/********************************************************************** + * File: scaleimg.h (Formerly scaleim.h) + * Description: Smart scaling of images. + * Author: Phil Cheatle + * Created: Wed Nov 18 16:12:03 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 SCALEIMG_H +#define SCALEIMG_H + +void scale_image( //scale an image + IMAGE &image, //source image + IMAGE &target_image //target image + ); +void scale_image_cop_out( //scale an image + IMAGE &image, //source image + IMAGE &target_image, //target image + float factor, //scale factor + int *hires, + int *lores, + int *oldhires, + int *oldlores); +#endif diff --git a/ccmain/tessbox.cpp b/ccmain/tessbox.cpp new file mode 100644 index 0000000000..99ae8da0c2 --- /dev/null +++ b/ccmain/tessbox.cpp @@ -0,0 +1,370 @@ +/********************************************************************** + * 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. + * + **********************************************************************/ + +#include "mfcpch.h" +#include "tfacep.h" +#include "tfacepp.h" +#include "tessbox.h" +#include "mfoutline.h" + +#define EXTERN + +/********************************************************************** + * tess_segment_pass1 + * + * Segment a word using the pass1 conditions of the tess segmenter. + **********************************************************************/ + +WERD_CHOICE *tess_segment_pass1( //recog one word + WERD *word, //bln word to do + DENORM *denorm, //de-normaliser + POLY_MATCHER matcher, //matcher function + WERD_CHOICE *&raw_choice, //raw result //list of blob lists + BLOB_CHOICE_LIST_CLIST *blob_choices, + WERD *&outword //bln word output + ) { + WERD_CHOICE *result; //return value + int saved_enable_assoc = 0; + int saved_chop_enable = 0; + + if (word->flag (W_DONT_CHOP)) { + saved_enable_assoc = enable_assoc; + saved_chop_enable = chop_enable; + enable_assoc = 0; + chop_enable = 0; + if (word->flag (W_REP_CHAR)) + permute_only_top = 1; + } + set_pass1(); + // tprintf("pass1 chop on=%d, seg=%d, onlytop=%d",chop_enable,enable_assoc,permute_only_top); + result = recog_word (word, denorm, matcher, NULL, NULL, FALSE, + raw_choice, blob_choices, outword); + if (word->flag (W_DONT_CHOP)) { + enable_assoc = saved_enable_assoc; + chop_enable = saved_chop_enable; + permute_only_top = 0; + } + return result; +} + + +/********************************************************************** + * tess_segment_pass2 + * + * Segment a word using the pass2 conditions of the tess segmenter. + **********************************************************************/ + +WERD_CHOICE *tess_segment_pass2( //recog one word + WERD *word, //bln word to do + DENORM *denorm, //de-normaliser + POLY_MATCHER matcher, //matcher function + WERD_CHOICE *&raw_choice, //raw result //list of blob lists + BLOB_CHOICE_LIST_CLIST *blob_choices, + WERD *&outword //bln word output + ) { + WERD_CHOICE *result; //return value + int saved_enable_assoc = 0; + int saved_chop_enable = 0; + + if (word->flag (W_DONT_CHOP)) { + saved_enable_assoc = enable_assoc; + saved_chop_enable = chop_enable; + enable_assoc = 0; + chop_enable = 0; + if (word->flag (W_REP_CHAR)) + permute_only_top = 1; + } + set_pass2(); + result = recog_word (word, denorm, matcher, NULL, NULL, FALSE, + raw_choice, blob_choices, outword); + if (word->flag (W_DONT_CHOP)) { + enable_assoc = saved_enable_assoc; + chop_enable = saved_chop_enable; + permute_only_top = 0; + } + return result; +} + + +/********************************************************************** + * correct_segment_pass2 + * + * Segment a word correctly using the pass2 conditions of the tess segmenter. + * Then call the tester with all the correctly segmented blobs. + * If the correct segmentation cannot be found, the tester is called + * with the segmentation found by tess and all the correct flags set to + * false and all strings are NULL. + **********************************************************************/ + +WERD_CHOICE *correct_segment_pass2( //recog one word + WERD *word, //bln word to do + DENORM *denorm, //de-normaliser + POLY_MATCHER matcher, //matcher function + POLY_TESTER tester, //tester function + WERD_CHOICE *&raw_choice, //raw result //list of blob lists + BLOB_CHOICE_LIST_CLIST *blob_choices, + WERD *&outword //bln word output + ) { + set_pass2(); + return recog_word (word, denorm, matcher, NULL, tester, TRUE, + raw_choice, blob_choices, outword); +} + + +/********************************************************************** + * test_segment_pass2 + * + * Segment a word correctly using the pass2 conditions of the tess segmenter. + * Then call the tester on all words used by tess in its search. + * Do this only on words where the correct segmentation could be found. + **********************************************************************/ + +WERD_CHOICE *test_segment_pass2( //recog one word + WERD *word, //bln word to do + DENORM *denorm, //de-normaliser + POLY_MATCHER matcher, //matcher function + POLY_TESTER tester, //tester function + WERD_CHOICE *&raw_choice, //raw result //list of blob lists + BLOB_CHOICE_LIST_CLIST *blob_choices, + WERD *&outword //bln word output + ) { + set_pass2(); + return recog_word (word, denorm, matcher, tester, NULL, TRUE, + raw_choice, blob_choices, outword); +} + + +/********************************************************************** + * tess_acceptable_word + * + * Return true if the word is regarded as "good enough". + **********************************************************************/ + +BOOL8 tess_acceptable_word( //test acceptability + WERD_CHOICE *word_choice, //after context + WERD_CHOICE *raw_choice //before context + ) { + A_CHOICE choice; //after context + A_CHOICE tess_raw; //before + + choice.rating = word_choice->rating (); + choice.certainty = word_choice->certainty (); + choice.string = (char *) word_choice->string ().string (); + tess_raw.rating = raw_choice->rating (); + tess_raw.certainty = raw_choice->certainty (); + tess_raw.string = (char *) raw_choice->string ().string (); + //call tess + return AcceptableResult (&choice, &tess_raw); +} + + +/********************************************************************** + * tess_adaptable_word + * + * Return true if the word is regarded as "good enough". + **********************************************************************/ + +BOOL8 tess_adaptable_word( //test adaptability + WERD *word, //word to test + WERD_CHOICE *word_choice, //after context + WERD_CHOICE *raw_choice //before context + ) { + TWERD *tessword; //converted word + INT32 result; //answer + + tessword = make_tess_word (word, NULL); + result = AdaptableWord (tessword, word_choice->string ().string (), + raw_choice->string ().string ()); + delete_word(tessword); + return result != 0; +} + + +/********************************************************************** + * tess_cn_matcher + * + * Match a blob using the Tess Char Normalized (non-adaptive) matcher + * only. + **********************************************************************/ + +void tess_cn_matcher( //call tess + PBLOB *pblob, //previous blob + PBLOB *blob, //blob to match + PBLOB *nblob, //next blob + WERD *word, //word it came from + DENORM *denorm, //de-normaliser + BLOB_CHOICE_LIST &ratings //list of results + ) { + LIST result; //tess output + TBLOB *tessblob; //converted blob + TEXTROW tessrow; //dummy row + + tess_cn_matching = TRUE; //turn it on + tess_bn_matching = FALSE; + //convert blob + tessblob = make_tess_blob (blob, TRUE); + //make dummy row + make_tess_row(denorm, &tessrow); + //classify + result = AdaptiveClassifier (tessblob, NULL, &tessrow); + free_blob(tessblob); + //make our format + convert_choice_list(result, ratings); +} + + +/********************************************************************** + * tess_bn_matcher + * + * Match a blob using the Tess Baseline Normalized (adaptive) matcher + * only. + **********************************************************************/ + +void tess_bn_matcher( //call tess + PBLOB *pblob, //previous blob + PBLOB *blob, //blob to match + PBLOB *nblob, //next blob + WERD *word, //word it came from + DENORM *denorm, //de-normaliser + BLOB_CHOICE_LIST &ratings //list of results + ) { + LIST result; //tess output + TBLOB *tessblob; //converted blob + TEXTROW tessrow; //dummy row + + tess_bn_matching = TRUE; //turn it on + tess_cn_matching = FALSE; + //convert blob + tessblob = make_tess_blob (blob, TRUE); + //make dummy row + make_tess_row(denorm, &tessrow); + //classify + result = AdaptiveClassifier (tessblob, NULL, &tessrow); + free_blob(tessblob); + //make our format + convert_choice_list(result, ratings); +} + + +/********************************************************************** + * tess_default_matcher + * + * Match a blob using the default functionality of the Tess matcher. + **********************************************************************/ + +void tess_default_matcher( //call tess + PBLOB *pblob, //previous blob + PBLOB *blob, //blob to match + PBLOB *nblob, //next blob + WERD *word, //word it came from + DENORM *denorm, //de-normaliser + BLOB_CHOICE_LIST &ratings //list of results + ) { + LIST result; //tess output + TBLOB *tessblob; //converted blob + TEXTROW tessrow; //dummy row + + tess_bn_matching = FALSE; //turn it off + tess_cn_matching = FALSE; + //convert blob + tessblob = make_tess_blob (blob, TRUE); + //make dummy row + make_tess_row(denorm, &tessrow); + //classify + result = AdaptiveClassifier (tessblob, NULL, &tessrow); + free_blob(tessblob); + //make our format + convert_choice_list(result, ratings); +} + + +/********************************************************************** + * tess_training_tester + * + * Matcher tester function which actually trains tess. + **********************************************************************/ + +void tess_training_tester( //call tess + PBLOB *blob, //blob to match + DENORM *denorm, //de-normaliser + BOOL8 correct, //ly segmented + char *text, //correct text + INT32 count, //chars in text + BLOB_CHOICE_LIST *ratings //list of results + ) { + TBLOB *tessblob; //converted blob + TEXTROW tessrow; //dummy row + + if (correct) { + NormMethod = character; //Force char norm spc 30/11/93 + tess_bn_matching = FALSE; //turn it off + tess_cn_matching = FALSE; + //convert blob + tessblob = make_tess_blob (blob, TRUE); + //make dummy row + make_tess_row(denorm, &tessrow); + //learn it + LearnBlob(tessblob, &tessrow, text, count); + free_blob(tessblob); + } +} + + +/********************************************************************** + * tess_adapter + * + * Adapt to the word using the Tesseract mechanism. + **********************************************************************/ + +void tess_adapter( //adapt to word + WERD *word, //bln word + DENORM *denorm, //de-normalise + const char *string, //string for word + const char *raw_string, //before context + const char *rejmap //reject map + ) { + TWERD *tessword; //converted word + static TEXTROW tessrow; //dummy row + + //make dummy row + make_tess_row(denorm, &tessrow); + //make a word + tessword = make_tess_word (word, &tessrow); + AdaptToWord(tessword, &tessrow, string, raw_string, rejmap); + //adapt to it + delete_word(tessword); //free it +} + + +/********************************************************************** + * tess_add_doc_word + * + * Add the given word to the document dictionary + **********************************************************************/ + +void tess_add_doc_word( //test acceptability + WERD_CHOICE *word_choice //after context + ) { + A_CHOICE choice; //after context + + choice.rating = word_choice->rating (); + choice.certainty = word_choice->certainty (); + choice.string = (char *) word_choice->string ().string (); + add_document_word(&choice); +} diff --git a/ccmain/tessbox.h b/ccmain/tessbox.h new file mode 100644 index 0000000000..fd8dd45271 --- /dev/null +++ b/ccmain/tessbox.h @@ -0,0 +1,110 @@ +/********************************************************************** + * 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 "notdll.h" + +WERD_CHOICE *tess_segment_pass1( //recog one word + WERD *word, //bln word to do + DENORM *denorm, //de-normaliser + POLY_MATCHER matcher, //matcher function + WERD_CHOICE *&raw_choice, //raw result //list of blob lists + BLOB_CHOICE_LIST_CLIST *blob_choices, + WERD *&outword //bln word output + ); +WERD_CHOICE *tess_segment_pass2( //recog one word + WERD *word, //bln word to do + DENORM *denorm, //de-normaliser + POLY_MATCHER matcher, //matcher function + WERD_CHOICE *&raw_choice, //raw result //list of blob lists + BLOB_CHOICE_LIST_CLIST *blob_choices, + WERD *&outword //bln word output + ); + //recog one word +WERD_CHOICE *correct_segment_pass2(WERD *word, //bln word to do + DENORM *denorm, //de-normaliser + POLY_MATCHER matcher, //matcher function + POLY_TESTER tester, //tester function + WERD_CHOICE *&raw_choice, //raw result //list of blob lists + BLOB_CHOICE_LIST_CLIST *blob_choices, + WERD *&outword //bln word output + ); +WERD_CHOICE *test_segment_pass2( //recog one word + WERD *word, //bln word to do + DENORM *denorm, //de-normaliser + POLY_MATCHER matcher, //matcher function + POLY_TESTER tester, //tester function + WERD_CHOICE *&raw_choice, //raw result //list of blob lists + BLOB_CHOICE_LIST_CLIST *blob_choices, + WERD *&outword //bln word output + ); +BOOL8 tess_acceptable_word( //test acceptability + WERD_CHOICE *word_choice, //after context + WERD_CHOICE *raw_choice //before context + ); +BOOL8 tess_adaptable_word( //test adaptability + WERD *word, //word to test + WERD_CHOICE *word_choice, //after context + WERD_CHOICE *raw_choice //before context + ); +void tess_cn_matcher( //call tess + PBLOB *pblob, //previous blob + PBLOB *blob, //blob to match + PBLOB *nblob, //next blob + WERD *word, //word it came from + DENORM *denorm, //de-normaliser + BLOB_CHOICE_LIST &ratings //list of results + ); +void tess_bn_matcher( //call tess + PBLOB *pblob, //previous blob + PBLOB *blob, //blob to match + PBLOB *nblob, //next blob + WERD *word, //word it came from + DENORM *denorm, //de-normaliser + BLOB_CHOICE_LIST &ratings //list of results + ); +void tess_default_matcher( //call tess + PBLOB *pblob, //previous blob + PBLOB *blob, //blob to match + PBLOB *nblob, //next blob + WERD *word, //word it came from + DENORM *denorm, //de-normaliser + BLOB_CHOICE_LIST &ratings //list of results + ); +void tess_training_tester( //call tess + PBLOB *blob, //blob to match + DENORM *denorm, //de-normaliser + BOOL8 correct, //ly segmented + char *text, //correct text + INT32 count, //chars in text + BLOB_CHOICE_LIST *ratings //list of results + ); +void tess_adapter( //adapt to word + WERD *word, //bln word + DENORM *denorm, //de-normalise + const char *string, //string for word + const char *raw_string, //before context + const char *rejmap); +void tess_add_doc_word( //test acceptability + WERD_CHOICE *word_choice //after context + ); +#endif diff --git a/ccmain/tessedit.cpp b/ccmain/tessedit.cpp new file mode 100644 index 0000000000..4d6f2d0622 --- /dev/null +++ b/ccmain/tessedit.cpp @@ -0,0 +1,321 @@ +/********************************************************************** + * 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 "mfcpch.h" +//#include +//#include +//#include +//#include +#include "tfacep.h" //must be before main.h +//#include "fileerr.h" +#include "stderr.h" +#include "basedir.h" +#include "tessvars.h" +//#include "debgwin.h" +//#include "epapdest.h" +#include "control.h" +#include "imgs.h" +#include "reject.h" +#include "pageres.h" +//#include "gpapdest.h" +#include "mainblk.h" +#include "nwmain.h" +#include "pgedit.h" +#include "ocrshell.h" +#include "tprintf.h" +//#include "ipeerr.h" +//#include "restart.h" +#include "tessedit.h" +//#include "fontfind.h" +#include "permute.h" +#include "permdawg.h" +#include "permnum.h" +#include "stopper.h" +#include "adaptmatch.h" +#include "intmatcher.h" +#include "chop.h" +#include "globals.h" + +//extern "C" { +#include "callnet.h" //phils nn stuff +//} +#include "notdll.h" //phils nn stuff + +#define VARDIR "configs/" /*variables files */ + //config under api +#define API_CONFIG "configs/api_config" +#define EXTERN + +EXTERN BOOL_EVAR (tessedit_write_vars, FALSE, "Write all vars to file"); +EXTERN BOOL_VAR (tessedit_tweaking_tess_vars, FALSE, +"Fiddle tess config values"); + +EXTERN INT_VAR (tweak_ReliableConfigThreshold, 2, "Tess VAR"); + +EXTERN double_VAR (tweak_garbage, 1.5, "Tess VAR"); +EXTERN double_VAR (tweak_ok_word, 1.25, "Tess VAR"); +EXTERN double_VAR (tweak_good_word, 1.1, "Tess VAR"); +EXTERN double_VAR (tweak_freq_word, 1.0, "Tess VAR"); +EXTERN double_VAR (tweak_ok_number, 1.4, "Tess VAR"); +EXTERN double_VAR (tweak_good_number, 1.1, "Tess VAR"); +EXTERN double_VAR (tweak_non_word, 1.25, "Tess VAR"); +EXTERN double_VAR (tweak_CertaintyPerChar, -0.5, "Tess VAR"); +EXTERN double_VAR (tweak_NonDictCertainty, -2.5, "Tess VAR"); +EXTERN double_VAR (tweak_RejectCertaintyOffset, 1.0, "Tess VAR"); +EXTERN double_VAR (tweak_GoodAdaptiveMatch, 0.125, "Tess VAR"); +EXTERN double_VAR (tweak_GreatAdaptiveMatch, 0.10, "Tess VAR"); +EXTERN INT_VAR (tweak_AdaptProtoThresh, 230, "Tess VAR"); +EXTERN INT_VAR (tweak_AdaptFeatureThresh, 230, "Tess VAR"); +EXTERN INT_VAR (tweak_min_outline_points, 6, "Tess VAR"); +EXTERN INT_VAR (tweak_min_outline_area, 2000, "Tess VAR"); +EXTERN double_VAR (tweak_good_split, 50.0, "Tess VAR"); +EXTERN double_VAR (tweak_ok_split, 100.0, "Tess VAR"); + +extern INT16 XOFFSET; +extern INT16 YOFFSET; +extern int NO_BLOCK; + + //progress monitor +ETEXT_DESC *global_monitor = NULL; + +int init_tesseract(const char *arg0, + const char *textbase, + const char *configfile, + int configc, + const char *const *configv) { + FILE *var_file; + static char c_path[MAX_PATH]; //path for c code + + // Set the basename, compute the data directory and read C++ configs. + main_setup(arg0, textbase, configc, configv); + debug_window_on.set_value (FALSE); + + if (tessedit_write_vars) { + var_file = fopen ("edited.cfg", "w"); + if (var_file != NULL) { + print_variables(var_file); + fclose(var_file); + } + } + strcpy (c_path, datadir.string ()); + c_path[strlen (c_path) - strlen (m_data_sub_dir.string ())] = '\0'; + demodir = c_path; + start_recog(configfile, textbase); + + ReliableConfigThreshold = tweak_ReliableConfigThreshold; + + set_tess_tweak_vars(); + + if (tessedit_use_nn) //phils nn stuff + init_net(); + return 0; //Normal exit +} + +void end_tesseract() { + end_recog(); +} + +#ifdef _TIFFIO_ +void read_tiff_image(TIFF* tif, IMAGE* image) { + tdata_t buf; + uint32 image_width, image_height; + uint16 photometric; + short bpp; + TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &image_width); + TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &image_height); + TIFFGetField(tif, TIFFTAG_BITSPERSAMPLE, &bpp); + TIFFGetField(tif, TIFFTAG_PHOTOMETRIC, &photometric); + // Tesseract's internal representation is 0-is-black, + // so if the photometric is 1 (min is black) then high-valued pixels + // are 1 (white), otherwise they are 0 (black). + UINT8 high_value = photometric == 1; + image->create(image_width, image_height, bpp); + IMAGELINE line; + line.init(image_width); + + buf = _TIFFmalloc(TIFFScanlineSize(tif)); + int bytes_per_line = (image_width*bpp + 7)/8; + UINT8* dest_buf = image->get_buffer(); + // This will go badly wrong with one of the more exotic tiff formats, + // but the majority will work OK. + for (int y = 0; y < image_height; ++y) { + TIFFReadScanline(tif, buf, y); + memcpy(dest_buf, buf, bytes_per_line); + dest_buf += bytes_per_line; + } + if (high_value == 0) + invert_image(image); + _TIFFfree(buf); +} +#endif + +/* Define command type identifiers */ + +enum CMD_EVENTS +{ + ACTION_1_CMD_EVENT, + RECOG_WERDS, + RECOG_PSEUDO, + ACTION_2_CMD_EVENT +}; + +/********************************************************************** + * extend_menu() + * + * Function called by pgeditor to let you extend the command menu. + * Items can be added to the "MODES" and "OTHER" menus. The modes_id_base + * and other_id_base parameters are required to offset your command event ids + * from those of pgeditor, and to let the pgeditor which commands are mode + * changes and which are unmoded commands. (Sorry if you think these offsets + * are a bit kludgy, the alternative would be to duplicate all the menu + * constructor modes within pgeditor so that the offsets could be hidden.) + * + * Items for the "MODES" menu may only be simple menu items (just a name and + * id). Items for the "OTHER" menu can be editable parameters or boolean + * toggles. Refer to menu.h to see how to build different types. + **********************************************************************/ + +void extend_menu( //handle for "MODES" + RADIO_MENU *modes_menu, + INT16 modes_id_base, //mode cmd ids offset + NON_RADIO_MENU *other_menu, //handle for "OTHER" + INT16 other_id_base //mode cmd ids offset + ) { + /* Example new mode */ + + modes_menu->add_child (new RADIO_MENU_LEAF ("Recog Words", + modes_id_base + RECOG_WERDS)); + modes_menu->add_child (new RADIO_MENU_LEAF ("Recog Blobs", + modes_id_base + RECOG_PSEUDO)); + + /* Example toggle + + other_menu->add_child( + new TOGGLE_MENU_LEAF( "Action 2", //Display string + other_id_base + ACTION_2_CMD_EVENT, //offset command id + FALSE ) ); //Initial value + + Example text parm (commented out) + + other_menu->add_child( + new VARIABLE_MENU_LEAF( "Parm change", //Display string + other_id_base + ACTION_3_CMD_EVENT, //offset command id + "default value" ) ); //default value string + */ +} + + +/********************************************************************** + * extend_moded_commands() + * + * Function called by pgeditor when the user is in one of the extended modes + * defined by extend_menu() and the user has selected an area in the image + * window. + **********************************************************************/ + +void extend_moded_commands( //current mode + INT32 mode, + BOX selection_box //area selected + ) { + char msg[MAX_CHARS + 1]; + + switch (mode) { + case RECOG_WERDS: + command_window->msg ("Recogging selected words"); + + /* This is how to apply a "word processor" function to each selected word */ + + process_selected_words(current_block_list, + selection_box, + &recog_interactive); + break; + case RECOG_PSEUDO: + command_window->msg ("Recogging selected blobs"); + + /* This is how to apply a "word processor" function to each selected word */ + + recog_pseudo_word(current_block_list, selection_box); + break; + default: + sprintf (msg, "Unexpected extended mode " INT32FORMAT, mode); + command_window->msg (msg); + } +} + + +/********************************************************************** + * extend_unmoded_commands() + * + * Function called by pgeditor when the user has selected one of the unmoded + * extended menu options. + **********************************************************************/ + +void extend_unmoded_commands( //current mode + INT32 cmd_event, + char *new_value //changed value if any + ) { + char msg[MAX_CHARS + 1]; + + switch (cmd_event) { + case ACTION_2_CMD_EVENT: //a toggle event + if (new_value[0] == 'T') + //Display message + command_window->msg ("Extended Action 2 ON!!"); + else + command_window->msg ("Extended Action 2 OFF!!"); + break; + default: + sprintf (msg, "Unrecognised extended command " INT32FORMAT " (%s)", + cmd_event, new_value); + command_window->msg (msg); + break; + } +} + + +/************************************************************************* + * set_tess_tweak_vars() + * Set TESS vars from the tweek value - This is only really of use during search + * of the space of tess configs - othertimes the default values are set + * + *************************************************************************/ +void set_tess_tweak_vars() { + if (tessedit_tweaking_tess_vars) { + garbage = tweak_garbage; + ok_word = tweak_ok_word; + good_word = tweak_good_word; + freq_word = tweak_freq_word; + ok_number = tweak_ok_number; + good_number = tweak_good_number; + non_word = tweak_non_word; + CertaintyPerChar = tweak_CertaintyPerChar; + NonDictCertainty = tweak_NonDictCertainty; + RejectCertaintyOffset = tweak_RejectCertaintyOffset; + GoodAdaptiveMatch = tweak_GoodAdaptiveMatch; + GreatAdaptiveMatch = tweak_GreatAdaptiveMatch; + AdaptProtoThresh = tweak_AdaptProtoThresh; + AdaptFeatureThresh = tweak_AdaptFeatureThresh; + min_outline_points = tweak_min_outline_points; + min_outline_area = tweak_min_outline_area; + good_split = tweak_good_split; + ok_split = tweak_ok_split; + } + // if (expiry_day * 24 * 60 * 60 < time(NULL)) + // err_exit(); +} diff --git a/ccmain/tessedit.h b/ccmain/tessedit.h new file mode 100644 index 0000000000..e1f6299ede --- /dev/null +++ b/ccmain/tessedit.h @@ -0,0 +1,67 @@ +/********************************************************************** + * 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 "tessclas.h" +#include "ocrclass.h" +#include "pgedit.h" +#include "notdll.h" + +// Includes libtiff if HAVE_LIBTIFF is defined +#ifdef HAVE_LIBTIFF +#ifdef GOOGLE3 +#include "third_party/tiff/tiffio.h" +#else +#include "tiffio.h" +#endif +#endif + + //progress monitor +extern ETEXT_DESC *global_monitor; + +int init_tesseract(const char *arg0, + const char *textbase, + const char *configfile, + int configc, + const char *const *configv); +void recognize_page(STRING& image_name); +void end_tesseract(); + +#ifdef _TIFFIO_ +void read_tiff_image(TIFF* tif, IMAGE* image); +#endif + +//handle for "MODES" +void extend_menu(RADIO_MENU *modes_menu, + INT16 modes_id_base, //mode cmd ids offset + NON_RADIO_MENU *other_menu, //handle for "OTHER" + INT16 other_id_base //mode cmd ids offset + ); + //current mode +void extend_moded_commands(INT32 mode, + BOX selection_box //area selected + ); + //current mode +void extend_unmoded_commands(INT32 cmd_event, + char *new_value //changed value if any + ); +void set_tess_tweak_vars(); +#endif diff --git a/ccmain/tessembedded.h b/ccmain/tessembedded.h new file mode 100644 index 0000000000..f3d44e689e --- /dev/null +++ b/ccmain/tessembedded.h @@ -0,0 +1,38 @@ +/********************************************************************** + * File: tessembedded.h + * Description: Access to initialization functions in embedded environment + * Author: Marius Renn + * Created: Sun Oct 21 + * + * (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 TESSEMBEDDED_H +#define TESSEMBEDDED_H + +#include "ocrblock.h" +#include "varable.h" +#include "notdll.h" + +int init_tessembedded(const char *arg0, + const char *textbase, + const char *configfile, + int configc, + const char *const *configv); + +void tessembedded_read_file(STRING &name, + BLOCK_LIST *blocks); + +void end_tessembedded(); + +#endif diff --git a/ccmain/tesseractmain.cpp b/ccmain/tesseractmain.cpp new file mode 100644 index 0000000000..8d8b22395c --- /dev/null +++ b/ccmain/tesseractmain.cpp @@ -0,0 +1,311 @@ +/********************************************************************** + * 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 "mfcpch.h" +#include "applybox.h" +#include "control.h" +#include "tessvars.h" +#include "tessedit.h" +#include "baseapi.h" +#include "pageres.h" +#include "imgs.h" +#include "varabled.h" +#include "tprintf.h" +#include "tesseractmain.h" +#include "stderr.h" +#include "notdll.h" +#include "mainblk.h" +#include "globals.h" +#include "tfacep.h" +#include "callnet.h" + +#define VARDIR "configs/" /*variables files */ + //config under api +#define API_CONFIG "configs/api_config" +#define EXTERN + +EXTERN BOOL_VAR (tessedit_read_image, TRUE, "Ensure the image is read"); +EXTERN BOOL_VAR (tessedit_write_images, FALSE, +"Capture the image from the IPE"); +EXTERN BOOL_VAR (tessedit_debug_to_screen, FALSE, "Dont use debug file"); + +extern INT16 XOFFSET; +extern INT16 YOFFSET; +extern int NO_BLOCK; + +const ERRCODE USAGE = "Usage"; +char szAppName[] = "Tessedit"; //app name + +/********************************************************************** + * main() + * + **********************************************************************/ + +#ifndef GRAPHICS_DISABLED +int main(int argc, char **argv) { + STRING outfile; //output file + + if (argc < 3) { + USAGE.error (argv[0], EXIT, + "%s imagename outputbase [configfile [[+|-]varfile]...]\n", argv[0]); + } + + if (argc == 3) + TessBaseAPI::Init(argv[0], argv[1], NULL, false, 0, argv + 2); + else + TessBaseAPI::Init(argv[0], argv[1], argv[3], false, argc - 4, argv + 4); + + tprintf ("Tesseract Open Source OCR Engine\n"); + + IMAGE image; +#ifdef _TIFFIO_ + TIFF* tif = TIFFOpen(argv[1], "r"); + if (tif) { + read_tiff_image(tif, &image); + TIFFClose(tif); + } else { + READFAILED.error (argv[0], EXIT, argv[1]); + } +#else + if (image.read_header(argv[1]) < 0) + READFAILED.error (argv[0], EXIT, argv[1]); + if (image.read(image.get_ysize ()) < 0) { + MEMORY_OUT.error(argv[0], EXIT, "Read of image %s", + argv[1]); + } +#endif + int bytes_per_line = check_legal_image_size(image.get_xsize(), + image.get_ysize(), + image.get_bpp()); + char* text = TessBaseAPI::TesseractRect(image.get_buffer(), image.get_bpp()/8, + bytes_per_line, 0, 0, + image.get_xsize(), image.get_ysize()); + outfile = argv[2]; + outfile += ".txt"; + FILE* fp = fopen(outfile.string(), "w"); + if (fp != NULL) { + fwrite(text, 1, strlen(text), fp); + fclose(fp); + } + delete [] text; + TessBaseAPI::End(); + + return 0; //Normal exit +} +#else + +int main(int argc, char **argv) { + UINT16 lang; //language + STRING pagefile; //input file + + if (argc < 4) { + USAGE.error (argv[0], EXIT, + "%s imagename outputbase configfile [[+|-]varfile]...\n", argv[0]); + } + + time_t t_start = time(NULL); + + init_tessembedded (argv[0], argv[2], argv[3], argc - 4, argv + 4); + + tprintf ("Tesseract Open Source OCR Engine (graphics disabled)\n"); + + if (tessedit_read_image) { +#ifdef _TIFFIO_ + TIFF* tif = TIFFOpen(argv[1], "r"); + if (tif) { + read_tiff_image(tif); + TIFFClose(tif); + } else + READFAILED.error (argv[0], EXIT, argv[1]); + +#else + if (page_image.read_header (argv[1]) < 0) + READFAILED.error (argv[0], EXIT, argv[1]); + if (page_image.read (page_image.get_ysize ()) < 0) { + MEMORY_OUT.error (argv[0], EXIT, "Read of image %s", + argv[1]); + } +#endif + } + + pagefile = argv[1]; + + BLOCK_LIST current_block_list; + tessembedded_read_file(pagefile, ¤t_block_list); + tprintf ("Done reading files.\n"); + + PAGE_RES page_res(¤t_block_list); + + recog_all_words(&page_res, NULL); + + current_block_list.clear(); + ResetAdaptiveClassifier(); + + time_t t_end = time(NULL); + double secs = difftime(t_end, t_start); + tprintf ("Done. Number of seconds: %d\n", (int)secs); + return 0; //Normal exit +} + +#endif + +int initialized = 0; + +#ifdef __MSW32__ +/********************************************************************** + * WinMain + * + * Main function for a windows program. + **********************************************************************/ + +int WINAPI WinMain( //main for windows //command line + HINSTANCE hInstance, + HINSTANCE hPrevInstance, + LPSTR lpszCmdLine, + int nCmdShow) { + WNDCLASS wc; + HWND hwnd; + MSG msg; + + char **argv; + char *argsin[2]; + int argc; + int exit_code; + + wc.style = CS_NOCLOSE | CS_OWNDC; + wc.lpfnWndProc = (WNDPROC) WndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = hInstance; + wc.hIcon = NULL; //LoadIcon (NULL, IDI_APPLICATION); + wc.hCursor = NULL; //LoadCursor (NULL, IDC_ARROW); + wc.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1); + wc.lpszMenuName = NULL; + wc.lpszClassName = szAppName; + + RegisterClass(&wc); + + hwnd = CreateWindow (szAppName, szAppName, + WS_OVERLAPPEDWINDOW | WS_DISABLED, + CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, + CW_USEDEFAULT, HWND_DESKTOP, NULL, hInstance, NULL); + + argsin[0] = strdup (szAppName); + argsin[1] = strdup (lpszCmdLine); + /*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); + + // ShowWindow (hwnd, nCmdShow); + // UpdateWindow (hwnd); + + if (initialized) { + exit_code = main (argc, argv); + free (argsin[0]); + free (argsin[1]); + free(argv); + return exit_code; + } + while (GetMessage (&msg, NULL, 0, 0)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + if (initialized) { + exit_code = main (argc, argv); + break; + } + else + exit_code = msg.wParam; + } + free (argsin[0]); + free (argsin[1]); + free(argv); + return exit_code; +} + + +/********************************************************************** + * WndProc + * + * Function to respond to messages. + **********************************************************************/ + +LONG WINAPI WndProc( //message handler + HWND hwnd, //window with message + UINT msg, //message typ + WPARAM wParam, + LPARAM lParam) { + HDC hdc; + + if (msg == WM_CREATE) { + // + // Create a rendering context. + // + hdc = GetDC (hwnd); + ReleaseDC(hwnd, hdc); + initialized = 1; + return 0; + } + return DefWindowProc (hwnd, msg, wParam, lParam); +} + + +/********************************************************************** + * parse_args + * + * Turn a list of args into a new list of args with each separate + * whitespace spaced string being an arg. + **********************************************************************/ + +int +parse_args ( /*refine arg list */ +int argc, /*no of input args */ +char *argv[], /*input args */ +char *arglist[] /*output args */ +) { + int argcount; /*converted argc */ + char *testchar; /*char in option string */ + int arg; /*current argument */ + + argcount = 0; /*no of options */ + for (arg = 0; arg < argc; arg++) { + testchar = argv[arg]; /*start of arg */ + do { + while (*testchar + && (*testchar == ' ' || *testchar == '\n' + || *testchar == '\t')) + testchar++; /*skip white space */ + if (*testchar) { + /*new arg */ + arglist[argcount++] = testchar; + /*skip to white space */ + for (testchar++; *testchar && *testchar != ' ' && *testchar != '\n' && *testchar != '\t'; testchar++); + if (*testchar) + *testchar++ = '\0'; /*turn to separate args */ + } + } + while (*testchar); + } + return argcount; /*new number of args */ +} +#endif diff --git a/ccmain/tesseractmain.h b/ccmain/tesseractmain.h new file mode 100644 index 0000000000..0c2a9a19e6 --- /dev/null +++ b/ccmain/tesseractmain.h @@ -0,0 +1,58 @@ +/********************************************************************** + * 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 TESSERACTMAIN_H +#define TESSERACTMAIN_H + +#include "varable.h" +#include "tessclas.h" +#include "notdll.h" +#include "tessembedded.h" + +extern BOOL_VAR_H (tessedit_read_image, TRUE, "Ensure the image is read"); +INT32 api_main( //run from api + const char *arg0, //program name + UINT16 lang //language + ); +INT16 setup_info( //setup dummy engine info + UINT16 lang, //user language + const char *name, //of engine + const char *version //of engine + ); +INT16 read_image( //read dummy image info + IMAGE *im_out //output image + ); +#ifdef __MSW32__ +int WINAPI WinMain( //main for windows //command line + HINSTANCE hInstance, + HINSTANCE hPrevInstance, + LPSTR lpszCmdLine, + int nCmdShow); +LONG WINAPI WndProc( //message handler + HWND hwnd, //window with message + UINT msg, //message typ + WPARAM wParam, + LPARAM lParam); +int parse_args ( /*refine arg list */ +int argc, /*no of input args */ +char *argv[], /*input args */ +char *arglist[] /*output args */ +); +#endif +#endif diff --git a/ccmain/tessvars.cpp b/ccmain/tessvars.cpp new file mode 100644 index 0000000000..1ec1c7b0a0 --- /dev/null +++ b/ccmain/tessvars.cpp @@ -0,0 +1,38 @@ +/********************************************************************** + * 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 "mfcpch.h" +#include "tessvars.h" + +#define EXTERN + +EXTERN INT_VAR (tessedit_adapt_kludge, 0, +"Use acceptable result or dangambigs"); +EXTERN BOOL_VAR (interactive_mode, FALSE, "Run interactively?"); +EXTERN BOOL_VAR (edit_variables, FALSE, "Variables Editor Window?"); +// xiaofan EXTERN STRING_VAR(file_type,".bl","Filename extension"); +EXTERN STRING_VAR (file_type, ".tif", "Filename extension"); +INT_VAR (testedit_match_debug, 0, "Integer match debug ctrl"); +EXTERN INT_VAR (tessedit_dangambigs_chop, FALSE, +"Use DangAmbigs to direct chop"); +EXTERN INT_VAR (tessedit_dangambigs_assoc, FALSE, +"Use DangAmbigs to direct assoc"); + +EXTERN IMAGE page_image; //image of page +EXTERN FILE *debug_fp; //write debug stuff here diff --git a/ccmain/tessvars.h b/ccmain/tessvars.h new file mode 100644 index 0000000000..3e65fdbe89 --- /dev/null +++ b/ccmain/tessvars.h @@ -0,0 +1,48 @@ +/********************************************************************** + * 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 "varable.h" +#include "img.h" +#include "tordmain.h" +#include "notdll.h" + +extern INT_VAR_H (tessedit_adapt_kludge, 0, +"Use acceptable result or dangambigs"); +extern BOOL_VAR_H (interactive_mode, FALSE, "Run interactively?"); +extern BOOL_VAR_H (edit_variables, FALSE, "Variables Editor Window?"); +//xiaofan extern STRING_VAR_H(file_type,".bl","Filename extension"); +extern STRING_VAR_H (file_type, ".tif", "Filename extension"); +extern INT_VAR_H (tessedit_truncate_wordchoice_log, 10, +"Max words to keep in list"); +extern INT_VAR_H (testedit_match_debug, 0, "Integer match debug ctrl"); +extern INT_VAR_H (tessedit_truncate_chopper, 1, +"Shorten chopper seam search"); +extern INT_VAR_H (tessedit_fix_sideways_chops, 1, +"Fix sideways chop problem"); +extern INT_VAR_H (tessedit_dangambigs_chop, FALSE, +"Use DangAmbigs to direct chop"); +extern INT_VAR_H (tessedit_dangambigs_assoc, FALSE, +"Use DangAmbigs to direct assoc"); + +extern IMAGE page_image; //image of page +extern FILE *debug_fp; //write debug stuff here +#endif diff --git a/ccmain/tfacep.h b/ccmain/tfacep.h new file mode 100644 index 0000000000..6bcdd2ff1a --- /dev/null +++ b/ccmain/tfacep.h @@ -0,0 +1,121 @@ +/********************************************************************** + * File: tfacep.h (Formerly tfacep.h) + * Description: Declarations of C functions and C owned data. + * Author: Ray Smith + * Created: Mon Apr 27 12:51:28 BST 1992 + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef TFACEP_H +#define TFACEP_H + +#include "hosthplb.h" +#include "tessclas.h" +#include "tessarray.h" +#include "tstruct.h" +#include "notdll.h" +#include "choices.h" +#include "oldlist.h" +#include "hyphen.h" +#include "tface.h" +#include "permute.h" +#include "adaptmatch.h" +#include "blobclass.h" +#include "stopper.h" +#include "associate.h" +#include "chop.h" +#include "expandblob.h" +#include "tordvars.h" +#include "metrics.h" +#include "tface.h" +#include "badwords.h" +#include "structures.h" + +#define BLOB_MATCHING_ON +typedef void (*TESS_TESTER) (TBLOB *, BOOL8, char *, INT32, LIST); +typedef LIST (*TESS_MATCHER) (TBLOB *, TBLOB *, TBLOB *, void *, TEXTROW *); + +extern "C" +{ + /* + int start_recog( //Real main in C + int argc, + char *argv[]); + void program_editup2( //afterforking part + int argc, + char** argv); + + int end_recog( //Real main in C + int argc, + char *argv[]); + void set_interactive_pass(); + void set_pass1(); + void set_pass2(); + //ARRAY cc_recog(TWERD*,TESS_CHOICE*,TESS_CHOICE*,TESS_TESTER, + // TESS_TESTER);*/ + //void wo_learn_blob(TBLOB*,TEXTROW*,char*,INT32); + //LIST AdaptiveClassifier(TBLOB*,TBLOB*,TEXTROW*); + //void LearnBlob(TBLOB*,TEXTROW*,char*,INT32); + //TWERD *newword(); + //TBLOB *newblob(); + //TESSLINE *newoutline(); + //EDGEPT *newedgept(); + //void oldedgept(EDGEPT*); + //void destroy_nodes(void*,void (*)(void*)); + //TESS_LIST *append_choice(TESS_LIST*,char*,double,double,char); + //void fix_quotes (char*); + //void record_certainty(double,int); + //int AcceptableResult(A_CHOICE*,A_CHOICE*); + //int AdaptableWord(TWERD*,const char*,const char*); + //void delete_word(TWERD*); + //void free_blob(TBLOB*); + //void add_document_word(A_CHOICE*); + //void AdaptToWord(TWERD*,TEXTROW*,const char*,const char*,const char*); + //void SaveBadWord(const char*,double); + //void free_choice(TESS_CHOICE*); + //TWERD *newword(); + //TBLOB *newblob(); + //void free_blob( //free a blob + // TBLOB *blob); //blob to free + + //int dict_word( const char* ); + + //extern int tess_cn_matching; + //extern int tess_bn_matching; + //extern int last_word_on_line; + extern TEXTROW normalized_row; + //extern TESS_MATCHER blob_matchers[]; + //extern FILE *rawfile; + //extern FILE *textfile; + //extern int character_count; + //extern int word_count; + //extern int enable_assoc; + //extern int chop_enable; + //extern int permute_only_top; + extern int display_ratings; + +}; + +#if 0 +#define strsave(s) \ + ((s) ? \ + ((char*) strcpy ((char*)alloc_string (strlen(s)+1), s)) : \ + (NULL)) +#endif + +#define BOLD_ON "&dB(s3B" +#define BOLD_OFF "&d@(s0B" +#define UNDERLINE_ON "&dD" +#define UNDERLINE_OFF "&d@" +#endif diff --git a/ccmain/tfacepp.cpp b/ccmain/tfacepp.cpp new file mode 100644 index 0000000000..b43d8bdd39 --- /dev/null +++ b/ccmain/tfacepp.cpp @@ -0,0 +1,411 @@ +/********************************************************************** + * 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. + * + **********************************************************************/ + +#include "mfcpch.h" +#ifdef __UNIX__ +#include +#endif +#include "errcode.h" +#include "tessarray.h" +//#include "fxtop.h" +#include "werd.h" +#include "tfacep.h" +#include "tstruct.h" +#include "tfacepp.h" +#include "tessvars.h" +#include "reject.h" + +#define EXTERN + +EXTERN BOOL_VAR (tessedit_override_permuter, TRUE, "According to dict_word"); + +static POLY_MATCHER tess_matcher;//current matcher +static POLY_TESTER tess_tester; //current tester +static POLY_TESTER tess_trainer; //current trainer +static DENORM *tess_denorm; //current denorm +static WERD *tess_word; //current word + +#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. + **********************************************************************/ +WERD_CHOICE *recog_word( //recog one owrd + WERD *word, //word to do + DENORM *denorm, //de-normaliser + POLY_MATCHER matcher, //matcher function + POLY_TESTER tester, //tester function + POLY_TESTER trainer, //trainer function + BOOL8 testing, //true if answer driven + WERD_CHOICE *&raw_choice, //raw result //list of blob lists + BLOB_CHOICE_LIST_CLIST *blob_choices, + WERD *&outword //bln word output + ) { + WERD_CHOICE *word_choice; + UINT8 perm_type; + UINT8 real_dict_perm_type; + + if (word->blob_list ()->empty ()) { + word_choice = new WERD_CHOICE ("", 10.0f, -1.0f, TOP_CHOICE_PERM); + raw_choice = new WERD_CHOICE ("", 10.0f, -1.0f, TOP_CHOICE_PERM); + outword = word->poly_copy (denorm->row ()->x_height ()); + } + else + word_choice = recog_word_recursive (word, denorm, matcher, tester, + trainer, testing, raw_choice, + blob_choices, outword); + if ((word_choice->string ().length () != + outword->blob_list ()->length ()) || + (word_choice->string ().length () != blob_choices->length ())) { + tprintf + ("recog_word ASSERT FAIL String:\"%s\"; Strlen=%d; #Blobs=%d; #Choices=%d\n", + word_choice->string ().string (), word_choice->string ().length (), + outword->blob_list ()->length (), blob_choices->length ()); + } + ASSERT_HOST (word_choice->string ().length () == + outword->blob_list ()->length ()); + ASSERT_HOST (word_choice->string ().length () == blob_choices->length ()); + + /* Copy any reject blobs into the outword */ + outword->rej_blob_list ()->deep_copy (word->rej_blob_list ()); + + if (tessedit_override_permuter) { + /* Override the permuter type if a straight dictionary check disagrees. */ + perm_type = word_choice->permuter (); + if ((perm_type != SYSTEM_DAWG_PERM) && + (perm_type != FREQ_DAWG_PERM) && (perm_type != USER_DAWG_PERM)) { + real_dict_perm_type = dict_word (word_choice->string ().string ()); + 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_choice->string ().string ()) > 0)) + word_choice->set_permuter (real_dict_perm_type); + //Use dict perm + } + if (tessedit_rejection_debug && perm_type != word_choice->permuter ()) { + tprintf ("Permuter Type Flipped from %d to %d\n", + perm_type, word_choice->permuter ()); + } + } + assert ((word_choice == NULL) == (raw_choice == NULL)); + return word_choice; +} + + +/********************************************************************** + * recog_word_recursive + * + * Convert the word to tess form and pass it to the tess segmenter. + * Convert the output back to editor form. + **********************************************************************/ + +WERD_CHOICE *recog_word_recursive( //recog one owrd + WERD *word, //word to do + DENORM *denorm, //de-normaliser + POLY_MATCHER matcher, //matcher function + POLY_TESTER tester, //tester function + POLY_TESTER trainer, //trainer function + BOOL8 testing, //true if answer driven + WERD_CHOICE *&raw_choice, //raw result //list of blob lists + BLOB_CHOICE_LIST_CLIST *blob_choices, + WERD *&outword //bln word output + ) { + INT32 initial_blob_choice_len; + INT32 word_length; //no of blobs + STRING word_string; //converted from tess + ARRAY tess_ratings; //tess results + A_CHOICE tess_choice; //best word + A_CHOICE tess_raw; //raw result + TWERD *tessword; //tess format + BLOB_CHOICE_LIST *choice_list; //fake list + //iterator + BLOB_CHOICE_LIST_C_IT choice_it; + + tess_matcher = matcher; //install matcher + tess_tester = testing ? tester : NULL; + tess_trainer = testing ? trainer : NULL; + tess_denorm = denorm; + tess_word = word; + // blob_matchers[1]=call_matcher; + if (word->blob_list ()->length () > MAX_UNDIVIDED_LENGTH) { + return split_and_recog_word (word, denorm, matcher, tester, trainer, + testing, raw_choice, blob_choices, + outword); + } + else { + if (word->flag (W_EOL)) + last_word_on_line = TRUE; + else + last_word_on_line = FALSE; + initial_blob_choice_len = blob_choices->length (); + tessword = make_tess_word (word, NULL); + tess_ratings = cc_recog (tessword, &tess_choice, &tess_raw, + testing + && tester != NULL /* ? call_tester : NULL */ , + testing + && trainer != + NULL /* ? call_train_tester : NULL */ ); + //convert word + outword = make_ed_word (tessword, word); + if (outword == NULL) { + outword = word->poly_copy (denorm->row ()->x_height ()); + } + delete_word(tessword); //get rid of it + //no of blobs + word_length = outword->blob_list ()->length (); + //convert all ratings + convert_choice_lists(tess_ratings, blob_choices); + //copy string + word_string = tess_raw.string; + while (word_string.length () < word_length) + word_string += " "; //pad with blanks + raw_choice = new WERD_CHOICE (word_string.string (), + tess_raw.rating, tess_raw.certainty, + tess_raw.permuter); + word_string = tess_choice.string; + if (word_string.length () > word_length) { + tprintf ("recog_word: Discarded long string \"%s\"\n", + word_string.string ()); + word_string = NULL; //should never happen + } + if (blob_choices->length () - initial_blob_choice_len != word_length) { + word_string = NULL; //force rejection + tprintf ("recog_word: Choices list len:%d; blob lists len:%d\n", + blob_choices->length (), word_length); + //list of lists + choice_it.set_to_list (blob_choices); + while (blob_choices->length () - initial_blob_choice_len < + word_length) { + //get fake one + choice_list = new BLOB_CHOICE_LIST; + //add to list + choice_it.add_to_end (choice_list); + tprintf ("recog_word: Added dummy choice list\n"); + } + while (blob_choices->length () - initial_blob_choice_len > + word_length) { + choice_it.move_to_last (); + //should never happen + delete choice_it.extract (); + tprintf ("recog_word: Deleted choice list\n"); + } + } + while (word_string.length () < word_length) + word_string += " "; //pad with blanks + + assert (raw_choice != NULL); + if (tess_choice.string) + strfree(tess_choice.string); + if (tess_raw.string) + strfree(tess_raw.string); + return new WERD_CHOICE (word_string.string (), + tess_choice.rating, tess_choice.certainty, + tess_choice.permuter); + } +} + + +/********************************************************************** + * split_and_recog_word + * + * Convert the word to tess form and pass it to the tess segmenter. + * Convert the output back to editor form. + **********************************************************************/ + +WERD_CHOICE *split_and_recog_word( //recog one owrd + WERD *word, //word to do + DENORM *denorm, //de-normaliser + POLY_MATCHER matcher, //matcher function + POLY_TESTER tester, //tester function + POLY_TESTER trainer, //trainer function + BOOL8 testing, //true if answer driven + WERD_CHOICE *&raw_choice, //raw result //list of blob lists + BLOB_CHOICE_LIST_CLIST *blob_choices, + WERD *&outword //bln word output + ) { + // INT32 outword1_len; + // INT32 outword2_len; + WERD *first_word; //poly copy of word + WERD *second_word; //fabricated word + WERD *outword2; //2nd output word + PBLOB *blob; + WERD_CHOICE *result; //resturn value + WERD_CHOICE *result2; //output of 2nd word + WERD_CHOICE *raw_choice2; //raw version of 2nd + float gap; //blob gap + float bestgap; //biggest gap + PBLOB_LIST new_blobs; //list of gathered blobs + PBLOB_IT blob_it; + //iterator + PBLOB_IT new_blob_it = &new_blobs; + + first_word = word->poly_copy (denorm->row ()->x_height ()); + blob_it.set_to_list (first_word->blob_list ()); + bestgap = -MAX_INT32; + while (!blob_it.at_last ()) { + blob = blob_it.data (); + //gap to next + gap = blob_it.data_relative (1)->bounding_box ().left () - blob->bounding_box ().right (); + blob_it.forward (); + if (gap > bestgap) { + bestgap = gap; //find biggest + new_blob_it = blob_it; //save position + } + } + //take 2nd half + new_blobs.assign_to_sublist (&new_blob_it, &blob_it); + //make it a word + second_word = new WERD (&new_blobs, 1, NULL); + ASSERT_HOST (word->blob_list ()->length () == + first_word->blob_list ()->length () + + second_word->blob_list ()->length ()); + + result = recog_word_recursive (first_word, denorm, matcher, + tester, trainer, testing, raw_choice, + blob_choices, outword); + delete first_word; //done that one + result2 = recog_word_recursive (second_word, denorm, matcher, + tester, trainer, testing, raw_choice2, + blob_choices, outword2); + delete second_word; //done that too + *result += *result2; //combine ratings + delete result2; + *raw_choice += *raw_choice2; + delete raw_choice2; //finished with it + // outword1_len= outword->blob_list()->length(); + // outword2_len= outword2->blob_list()->length(); + outword->join_on (outword2); //join words + delete outword2; + // if ( outword->blob_list()->length() != outword1_len + outword2_len ) + // tprintf( "Split&Recog: part1len=%d; part2len=%d; combinedlen=%d\n", + // outword1_len, outword2_len, outword->blob_list()->length() ); + // ASSERT_HOST( outword->blob_list()->length() == outword1_len + outword2_len ); + return result; +} + + +/********************************************************************** + * call_matcher + * + * Called from Tess with a blob in tess form. + * Convert the blob to editor form. + * Call the matcher setup by the segmenter in tess_matcher. + * Convert the output choices back to tess form. + **********************************************************************/ + +LIST call_matcher( //call a matcher + TBLOB *ptblob, //previous + TBLOB *tessblob, //blob to match + TBLOB *ntblob, //next + void *, //unused parameter + TEXTROW * //always null anyway + ) { + PBLOB *pblob; //converted blob + PBLOB *blob; //converted blob + PBLOB *nblob; //converted blob + LIST result; //tess output + BLOB_CHOICE *choice; //current choice + char string[2]; //char converted + BLOB_CHOICE_LIST ratings; //matcher result + BLOB_CHOICE_IT it; //iterator + + blob = make_ed_blob (tessblob);//convert blob + if (blob == NULL) + return NULL; //can't do it + pblob = ptblob != NULL ? make_ed_blob (ptblob) : NULL; + nblob = ntblob != NULL ? make_ed_blob (ntblob) : NULL; + (*tess_matcher) (pblob, blob, nblob, tess_word, tess_denorm, ratings); + //match it + delete blob; //don't need that now + if (pblob != NULL) + delete pblob; + if (nblob != NULL) + delete nblob; + it.set_to_list (&ratings); //get list + result = NULL; + string[1] = '\0'; + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + choice = it.data (); + string[0] = choice->char_class (); + result = append_choice (result, string, + choice->rating (), choice->certainty (), + choice->config ()); + } + return result; //converted list +} + + +/********************************************************************** + * call_tester + * + * Called from Tess with a blob in tess form. + * Convert the blob to editor form. + * Call the tester setup by the segmenter in tess_tester. + **********************************************************************/ + +void call_tester( //call a tester + TBLOB *tessblob, //blob to test + BOOL8 correct_blob, //true if good + char *text, //source text + INT32 count, //chars in text + LIST result //output of matcher + ) { + PBLOB *blob; //converted blob + BLOB_CHOICE_LIST ratings; //matcher result + + blob = make_ed_blob (tessblob);//convert blob + if (blob == NULL) + return; + //make it right type + convert_choice_list(result, ratings); + if (tess_tester != NULL) + (*tess_tester) (blob, tess_denorm, correct_blob, text, count, &ratings); + delete blob; //don't need that now +} + + +/********************************************************************** + * call_train_tester + * + * Called from Tess with a blob in tess form. + * Convert the blob to editor form. + * Call the trainer setup by the segmenter in tess_trainer. + **********************************************************************/ + +void call_train_tester( //call a tester + TBLOB *tessblob, //blob to test + BOOL8 correct_blob, //true if good + char *text, //source text + INT32 count, //chars in text + LIST result //output of matcher + ) { + PBLOB *blob; //converted blob + BLOB_CHOICE_LIST ratings; //matcher result + + blob = make_ed_blob (tessblob);//convert blob + if (blob == NULL) + return; + //make it right type + convert_choice_list(result, ratings); + if (tess_trainer != NULL) + (*tess_trainer) (blob, tess_denorm, correct_blob, text, count, &ratings); + delete blob; //don't need that now +} diff --git a/ccmain/tfacepp.h b/ccmain/tfacepp.h new file mode 100644 index 0000000000..20c9c21906 --- /dev/null +++ b/ccmain/tfacepp.h @@ -0,0 +1,85 @@ +/********************************************************************** + * File: tfacepp.h (Formerly tface++.h) + * Description: C++ side of the C/C++ Tess/Editor interface. + * Author: Ray Smith + * Created: Thu Apr 23 15:39:23 BST 1992 + * + * (C) Copyright 1992, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +#ifndef TFACEPP_H +#define TFACEPP_H + +#include "varable.h" +#include "tstruct.h" +#include "ratngs.h" +#include "tessclas.h" +#include "notdll.h" + +extern BOOL_VAR_H (tessedit_override_permuter, TRUE, +"According to dict_word"); +WERD_CHOICE *recog_word( //recog one owrd + WERD *word, //word to do + DENORM *denorm, //de-normaliser + POLY_MATCHER matcher, //matcher function + POLY_TESTER tester, //tester function + POLY_TESTER trainer, //trainer function + BOOL8 testing, //true if answer driven + WERD_CHOICE *&raw_choice, //raw result //list of blob lists + BLOB_CHOICE_LIST_CLIST *blob_choices, + WERD *&outword //bln word output + ); + //recog one owrd +WERD_CHOICE *recog_word_recursive(WERD *word, //word to do + DENORM *denorm, //de-normaliser + POLY_MATCHER matcher, //matcher function + POLY_TESTER tester, //tester function + POLY_TESTER trainer, //trainer function + BOOL8 testing, //true if answer driven + WERD_CHOICE *&raw_choice, //raw result //list of blob lists + BLOB_CHOICE_LIST_CLIST *blob_choices, + WERD *&outword //bln word output + ); + //recog one owrd +WERD_CHOICE *split_and_recog_word(WERD *word, //word to do + DENORM *denorm, //de-normaliser + POLY_MATCHER matcher, //matcher function + POLY_TESTER tester, //tester function + POLY_TESTER trainer, //trainer function + BOOL8 testing, //true if answer driven + WERD_CHOICE *&raw_choice, //raw result //list of blob lists + BLOB_CHOICE_LIST_CLIST *blob_choices, + WERD *&outword //bln word output + ); +LIST call_matcher( //call a matcher + TBLOB *ptblob, //previous + TBLOB *tessblob, //blob to match + TBLOB *ntblob, //next + void *, //unused parameter + TEXTROW * //always null anyway + ); +void call_tester( //call a tester + TBLOB *tessblob, //blob to test + BOOL8 correct_blob, //true if good + char *text, //source text + INT32 count, //chars in text + LIST result //output of matcher + ); +void call_train_tester( //call a tester + TBLOB *tessblob, //blob to test + BOOL8 correct_blob, //true if good + char *text, //source text + INT32 count, //chars in text + LIST result //output of matcher + ); +#endif diff --git a/ccmain/tstruct.cpp b/ccmain/tstruct.cpp new file mode 100644 index 0000000000..6d2ebb5bb9 --- /dev/null +++ b/ccmain/tstruct.cpp @@ -0,0 +1,511 @@ +/********************************************************************** + * File: tstruct.cpp (Formerly tstruct.c) + * Description: Code to manipulate the structures of the C++/C interface. + * Author: Ray Smith + * Created: Thu Apr 23 15:49: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. + * + **********************************************************************/ + +#include "mfcpch.h" +#include "tfacep.h" +#include "tstruct.h" +//#include "structures.h" + +static ERRCODE BADFRAGMENTS = "Couldn't find matching fragment ends"; + +ELISTIZE (FRAGMENT) +//extern /*"C"*/ oldoutline(TESSLINE*); +/********************************************************************** + * FRAGMENT::FRAGMENT + * + * Constructor for fragments. + **********************************************************************/ +FRAGMENT::FRAGMENT ( //constructor +EDGEPT * head_pt, //start point +EDGEPT * tail_pt //end point +):head (head_pt->pos.x, head_pt->pos.y), tail (tail_pt->pos.x, +tail_pt->pos.y) { + headpt = head_pt; //save ptrs + tailpt = tail_pt; +} + + +/********************************************************************** + * make_ed_word + * + * Make an editor format word from the tess style word. + **********************************************************************/ + +WERD *make_ed_word( //construct word + TWERD *tessword, //word to convert + WERD *clone //clone this one + ) { + WERD *word; //converted word + TBLOB *tblob; //current blob + PBLOB *blob; //new blob + PBLOB_LIST blobs; //list of blobs + PBLOB_IT blob_it = &blobs; //iterator + + for (tblob = tessword->blobs; tblob != NULL; tblob = tblob->next) { + blob = make_ed_blob (tblob); + if (blob != NULL) + blob_it.add_after_then_move (blob); + } + if (!blobs.empty ()) + word = new WERD (&blobs, clone); + else + word = NULL; + return word; +} + + +/********************************************************************** + * make_ed_blob + * + * Make an editor format blob from the tess style blob. + **********************************************************************/ + +PBLOB *make_ed_blob( //construct blob + TBLOB *tessblob //blob to convert + ) { + TESSLINE *tessol; //tess outline + FRAGMENT_LIST fragments; //list of fragments + OUTLINE *outline; //current outline + OUTLINE_LIST out_list; //list of outlines + OUTLINE_IT out_it = &out_list; //iterator + + for (tessol = tessblob->outlines; tessol != NULL; tessol = tessol->next) { + //stick in list + register_outline(tessol, &fragments); + } + while (!fragments.empty ()) { + outline = make_ed_outline (&fragments); + if (outline != NULL) + out_it.add_after_then_move (outline); + } + if (out_it.empty()) + return NULL; //couldn't do it + return new PBLOB (&out_list); //turn to blob +} + + +/********************************************************************** + * make_ed_outline + * + * Make an editor format outline from the list of fragments. + **********************************************************************/ + +OUTLINE *make_ed_outline( //constructoutline + FRAGMENT_LIST *list //list of fragments + ) { + FRAGMENT *fragment; //current fragment + EDGEPT *edgept; //current point + ICOORD headpos; //coords of head + ICOORD tailpos; //coords of tail + FCOORD pos; //coords of edgept + FCOORD vec; //empty + POLYPT *polypt; //current point + POLYPT_LIST poly_list; //list of point + POLYPT_IT poly_it = &poly_list;//iterator + FRAGMENT_IT fragment_it = list;//fragment + + headpos = fragment_it.data ()->head; + do { + fragment = fragment_it.data (); + edgept = fragment->headpt; //start of segment + do { + pos = FCOORD (edgept->pos.x, edgept->pos.y); + vec = FCOORD (edgept->vec.x, edgept->vec.y); + polypt = new POLYPT (pos, vec); + //add to list + poly_it.add_after_then_move (polypt); + edgept = edgept->next; + } + while (edgept != fragment->tailpt); + tailpos = ICOORD (edgept->pos.x, edgept->pos.y); + //get rid of it + delete fragment_it.extract (); + if (tailpos != headpos) { + if (fragment_it.empty ()) { + // tprintf("Bad tailpos (%d,%d), Head=(%d,%d), no fragments.\n", + // fragment->head.x(),fragment->head.y(), + // headpos.x(),headpos.y()); + return NULL; + } + fragment_it.forward (); + //find next segment + for (fragment_it.mark_cycle_pt (); !fragment_it.cycled_list () && fragment_it.data ()->head != tailpos; + fragment_it.forward ()); + if (fragment_it.data ()->head != tailpos) { + // tprintf("Bad tailpos (%d,%d), Fragments are:\n", + // tailpos.x(),tailpos.y()); + for (fragment_it.mark_cycle_pt (); + !fragment_it.cycled_list (); fragment_it.forward ()) { + fragment = fragment_it.extract (); + // tprintf("Head=(%d,%d), tail=(%d,%d)\n", + // fragment->head.x(),fragment->head.y(), + // fragment->tail.x(),fragment->tail.y()); + delete fragment; + } + return NULL; //can't do it + // BADFRAGMENTS.error("make_ed_blob",ABORT,NULL); + } + } + } + while (tailpos != headpos); + return new OUTLINE (&poly_it); //turn to outline +} + + +/********************************************************************** + * register_outline + * + * Add the fragments in the given outline to the list + **********************************************************************/ + +void register_outline( //add fragments + TESSLINE *outline, //tess format + FRAGMENT_LIST *list //list to add to + ) { + EDGEPT *startpt; //start of outline + EDGEPT *headpt; //start of fragment + EDGEPT *tailpt; //end of fragment + FRAGMENT *fragment; //new fragment + FRAGMENT_IT it = list; //iterator + + startpt = outline->loop; + do { + startpt = startpt->next; + if (startpt == NULL) + return; //illegal! + } + while (startpt->flags[0] == 0 && startpt != outline->loop); + headpt = startpt; + do + startpt = startpt->next; + while (startpt->flags[0] != 0 && startpt != headpt); + if (startpt->flags[0] != 0) + return; //all hidden! + + headpt = startpt; + do { + tailpt = headpt; + do + tailpt = tailpt->next; + while (tailpt->flags[0] == 0 && tailpt != startpt); + fragment = new FRAGMENT (headpt, tailpt); + it.add_after_then_move (fragment); + while (tailpt->flags[0] != 0) + tailpt = tailpt->next; + headpt = tailpt; + } + while (tailpt != startpt); +} + + +/********************************************************************** + * convert_choice_lists + * + * Convert the ARRAY of TESS_LIST of TESS_CHOICEs into a BLOB_CHOICE_LIST. + **********************************************************************/ + +void convert_choice_lists( //convert lists + ARRAY tessarray, //list from tess + BLOB_CHOICE_LIST_CLIST *ratings //list of results + ) { + INT32 length; //elements in array + INT32 index; //index to array + LIST result; //tess output + //iterator + BLOB_CHOICE_LIST_C_IT it = ratings; + BLOB_CHOICE_LIST *choice; //created choice + + if (tessarray != NULL) { + length = array_count (tessarray); + for (index = 0; index < length; index++) { + result = (LIST) array_value (tessarray, index); + //make one + choice = new BLOB_CHOICE_LIST; + //convert blob choices + convert_choice_list(result, *choice); + //add to super list + it.add_after_then_move (choice); + } + free_mem(tessarray); //lists already freed + } +} + + +/********************************************************************** + * convert_choice_list + * + * Convert the LIST of TESS_CHOICEs into a BLOB_CHOICE_LIST. + **********************************************************************/ + +void convert_choice_list( //convert lists + LIST list, //list from tess + BLOB_CHOICE_LIST &ratings //list of results + ) { + LIST result; //tess output + BLOB_CHOICE_IT it = &ratings; //iterator + BLOB_CHOICE *choice; //created choice + A_CHOICE *tesschoice; //choice to convert + + for (result = list; result != NULL; result = result->next) { + //traverse list + tesschoice = (A_CHOICE *) result->node; + //make one + choice = new BLOB_CHOICE (tesschoice->string[0], tesschoice->rating, tesschoice->certainty, tesschoice->config); + it.add_after_then_move (choice); + } + destroy_nodes (list, (void (*)(void *)) free_choice); + //get rid of it +} + + +/********************************************************************** + * make_tess_row + * + * Make a fake row structure to pass to the tesseract matchers. + **********************************************************************/ + +void make_tess_row( //make fake row + DENORM *denorm, //row info + TEXTROW *tessrow //output row + ) { + tessrow->baseline.segments = 1; + tessrow->baseline.xstarts[0] = -32767; + tessrow->baseline.xstarts[1] = 32767; + tessrow->baseline.quads[0].a = 0; + tessrow->baseline.quads[0].b = 0; + tessrow->baseline.quads[0].c = bln_baseline_offset; + tessrow->xheight.segments = 1; + tessrow->xheight.xstarts[0] = -32767; + tessrow->xheight.xstarts[1] = 32767; + tessrow->xheight.quads[0].a = 0; + tessrow->xheight.quads[0].b = 0; + tessrow->xheight.quads[0].c = bln_x_height + bln_baseline_offset; + tessrow->lineheight = bln_x_height; + tessrow->ascrise = denorm->row ()->ascenders () * denorm->scale (); + tessrow->descdrop = denorm->row ()->descenders () * denorm->scale (); +} + + +/********************************************************************** + * make_tess_word + * + * Convert the word to Tess format. + **********************************************************************/ + +TWERD *make_tess_word( //convert owrd + WERD *word, //word to do + TEXTROW *row //fake row + ) { + TWERD *tessword; //tess format + + tessword = newword (); //use old allocator + tessword->row = row; //give them something + //copy string + tessword->correct = strsave (word->text ()); + tessword->guess = NULL; + tessword->blobs = make_tess_blobs (word->blob_list ()); + tessword->blanks = 1; + tessword->blobcount = word->blob_list ()->length (); + tessword->next = NULL; + return tessword; +} + + +/********************************************************************** + * make_tess_blobs + * + * Make Tess style blobs from a list of BLOBs. + **********************************************************************/ + +TBLOB *make_tess_blobs( //make tess blobs + PBLOB_LIST *bloblist //list to convert + ) { + PBLOB_IT it = bloblist; //iterator + PBLOB *blob; //current blob + TBLOB *head; //output list + TBLOB *tail; //end of list + TBLOB *tessblob; + + head = NULL; + tail = NULL; + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + blob = it.data (); + tessblob = make_tess_blob (blob, TRUE); + if (head) + tail->next = tessblob; + else + head = tessblob; + tail = tessblob; + } + return head; +} + + +/********************************************************************** + * make_tess_blob + * + * Make a single Tess style blob + **********************************************************************/ + +TBLOB *make_tess_blob( //make tess blob + PBLOB *blob, //blob to convert + BOOL8 flatten //flatten outline structure + ) { + INT32 index; + TBLOB *tessblob; + + tessblob = newblob (); + tessblob->outlines = (struct olinestruct *) + make_tess_outlines (blob->out_list (), flatten); + for (index = 0; index < TBLOBFLAGS; index++) + tessblob->flags[index] = 0; //!! + tessblob->correct = 0; + tessblob->guess = 0; + for (index = 0; index < MAX_WO_CLASSES; index++) { + tessblob->classes[index] = 0; + tessblob->values[index] = 0; + } + tessblob->next = NULL; + return tessblob; +} + + +/********************************************************************** + * make_tess_outlines + * + * Make Tess style outlines from a list of OUTLINEs. + **********************************************************************/ + +TESSLINE *make_tess_outlines( //make tess outlines + OUTLINE_LIST *outlinelist, //list to convert + BOOL8 flatten //flatten outline structure + ) { + OUTLINE_IT it = outlinelist; //iterator + OUTLINE *outline; //current outline + TESSLINE *head; //output list + TESSLINE *tail; //end of list + TESSLINE *tessoutline; + + head = NULL; + tail = NULL; + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + outline = it.data (); + tessoutline = newoutline (); + tessoutline->compactloop = NULL; + tessoutline->loop = make_tess_edgepts (outline->polypts (), + tessoutline->topleft, + tessoutline->botright); + if (tessoutline->loop == NULL) { + oldoutline(tessoutline); + continue; + } + tessoutline->start = tessoutline->loop->pos; + tessoutline->node = NULL; + tessoutline->next = NULL; + tessoutline->child = NULL; + if (!outline->child ()->empty ()) { + if (flatten) + tessoutline->next = (struct olinestruct *) + make_tess_outlines (outline->child (), flatten); + else { + tessoutline->next = NULL; + tessoutline->child = (struct olinestruct *) + make_tess_outlines (outline->child (), flatten); + } + } + else + tessoutline->next = NULL; + if (head) + tail->next = tessoutline; + else + head = tessoutline; + while (tessoutline->next != NULL) + tessoutline = tessoutline->next; + tail = tessoutline; + } + return head; +} + + +/********************************************************************** + * make_tess_edgepts + * + * Make Tess style edgepts from a list of POLYPTs. + **********************************************************************/ + +EDGEPT *make_tess_edgepts( //make tess edgepts + POLYPT_LIST *edgeptlist, //list to convert + TPOINT &tl, //bounding box + TPOINT &br) { + INT32 index; + POLYPT_IT it = edgeptlist; //iterator + POLYPT *edgept; //current edgept + EDGEPT *head; //output list + EDGEPT *tail; //end of list + EDGEPT *tessedgept; + + head = NULL; + tail = NULL; + tl.x = MAX_INT16; + tl.y = -MAX_INT16; + br.x = -MAX_INT16; + br.y = MAX_INT16; + for (it.mark_cycle_pt (); !it.cycled_list ();) { + edgept = it.data (); + tessedgept = newedgept (); + tessedgept->pos.x = (INT16) edgept->pos.x (); + tessedgept->pos.y = (INT16) edgept->pos.y (); + if (tessedgept->pos.x < tl.x) + tl.x = tessedgept->pos.x; + if (tessedgept->pos.x > br.x) + br.x = tessedgept->pos.x; + if (tessedgept->pos.y > tl.y) + tl.y = tessedgept->pos.y; + if (tessedgept->pos.y < br.y) + br.y = tessedgept->pos.y; + if (head != NULL && tessedgept->pos.x == tail->pos.x + && tessedgept->pos.y == tail->pos.y) { + oldedgept(tessedgept); + } + else { + for (index = 0; index < EDGEPTFLAGS; index++) + tessedgept->flags[index] = 0; + if (head != NULL) { + tail->vec.x = tessedgept->pos.x - tail->pos.x; + tail->vec.y = tessedgept->pos.y - tail->pos.y; + tessedgept->prev = tail; + } + tessedgept->next = head; + if (head) + tail->next = tessedgept; + else + head = tessedgept; + tail = tessedgept; + } + it.forward (); + } + head->prev = tail; + tail->vec.x = head->pos.x - tail->pos.x; + tail->vec.y = head->pos.y - tail->pos.y; + if (head == tail) { + oldedgept(head); + return NULL; //empty + } + return head; +} diff --git a/ccmain/tstruct.h b/ccmain/tstruct.h new file mode 100644 index 0000000000..1a4ae7796c --- /dev/null +++ b/ccmain/tstruct.h @@ -0,0 +1,108 @@ +/********************************************************************** + * File: tstruct.h (Formerly tstruct.h) + * Description: Code to manipulate the structures of the C++/C interface. + * Author: Ray Smith + * Created: Thu Apr 23 15:49: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. + * + **********************************************************************/ + +#ifndef TSTRUCT_H +#define TSTRUCT_H + +#include "tessarray.h" +#include "werd.h" +#include "tessclas.h" +#include "ratngs.h" +#include "notdll.h" +#include "oldlist.h" + +/* +struct TESS_LIST +{ + TESS_LIST *node; //data + TESS_LIST *next; //next in list +}; + +struct TESS_CHOICE +{ + float rating; //scaled + float certainty; //absolute + char permuter; //which permuter code + INT8 config; //which config + char* string; //really can! +}; +*/ +class FRAGMENT:public ELIST_LINK +{ + public: + FRAGMENT() { //constructor + } + FRAGMENT(EDGEPT *head_pt, //start + EDGEPT *tail_pt); //end + + ICOORD head; //coords of start + ICOORD tail; //coords of end + EDGEPT *headpt; //start point + EDGEPT *tailpt; //end point + + NEWDELETE2 (FRAGMENT) +}; + +ELISTIZEH (FRAGMENT) +WERD *make_ed_word( //construct word + TWERD *tessword, //word to convert + WERD *clone //clone this one + ); +PBLOB *make_ed_blob( //construct blob + TBLOB *tessblob //blob to convert + ); +OUTLINE *make_ed_outline( //constructoutline + FRAGMENT_LIST *list //list of fragments + ); +void register_outline( //add fragments + TESSLINE *outline, //tess format + FRAGMENT_LIST *list //list to add to + ); +void convert_choice_lists( //convert lists + ARRAY tessarray, //list from tess + BLOB_CHOICE_LIST_CLIST *ratings //list of results + ); +void convert_choice_list( //convert lists + LIST list, //list from tess + BLOB_CHOICE_LIST &ratings //list of results + ); +void make_tess_row( //make fake row + DENORM *denorm, //row info + TEXTROW *tessrow //output row + ); +TWERD *make_tess_word( //convert owrd + WERD *word, //word to do + TEXTROW *row //fake row + ); +TBLOB *make_tess_blobs( //make tess blobs + PBLOB_LIST *bloblist //list to convert + ); +TBLOB *make_tess_blob( //make tess blob + PBLOB *blob, //blob to convert + BOOL8 flatten //flatten outline structure + ); +TESSLINE *make_tess_outlines( //make tess outlines + OUTLINE_LIST *outlinelist, //list to convert + BOOL8 flatten //flatten outline structure + ); +EDGEPT *make_tess_edgepts( //make tess edgepts + POLYPT_LIST *edgeptlist, //list to convert + TPOINT &tl, //bounding box + TPOINT &br); +#endif diff --git a/ccmain/werdit.cpp b/ccmain/werdit.cpp new file mode 100644 index 0000000000..299809e061 --- /dev/null +++ b/ccmain/werdit.cpp @@ -0,0 +1,193 @@ +/********************************************************************** + * 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 "mfcpch.h" +#include "werdit.h" + +#define EXTERN + +//EXTERN BOOL_VAR(wordit_linearc,FALSE,"Pass poly of linearc to Tess"); + +/********************************************************************** + * WERDIT::start_page + * + * Get ready to iterate over the page by setting the iterators. + **********************************************************************/ + +void WERDIT::start_page( //set iterators + BLOCK_LIST *block_list //blocks to check + ) { + block_it.set_to_list (block_list); + block_it.mark_cycle_pt (); + do { + while (block_it.data ()->row_list ()->empty () + && !block_it.cycled_list ()) { + block_it.forward (); + } + if (!block_it.data ()->row_list ()->empty ()) { + row_it.set_to_list (block_it.data ()->row_list ()); + row_it.mark_cycle_pt (); + while (row_it.data ()->word_list ()->empty () + && !row_it.cycled_list ()) { + row_it.forward (); + } + if (!row_it.data ()->word_list ()->empty ()) { + word_it.set_to_list (row_it.data ()->word_list ()); + word_it.mark_cycle_pt (); + } + } + } + while (!block_it.cycled_list () && row_it.data ()->word_list ()->empty ()); +} + + +/********************************************************************** + * WERDIT::forward + * + * Give the next word on the page, or NULL if none left. + * This code assumes all rows to be non-empty, but blocks are allowed + * to be empty as eventually we will have non-text blocks. + * The output is always a copy and needs to be deleted by somebody. + **********************************************************************/ + +WERD *WERDIT::forward() { //use iterators + WERD *word; //actual word + // WERD *larc_word; //linearc copy + WERD *result; //output word + ROW *row; //row of word + + if (word_it.cycled_list ()) { + return NULL; //finished page + } + else { + word = word_it.data (); + row = row_it.data (); + word_it.forward (); + if (word_it.cycled_list ()) { + row_it.forward (); //finished row + if (row_it.cycled_list ()) { + do { + block_it.forward (); //finished block + if (!block_it.cycled_list ()) { + row_it.set_to_list (block_it.data ()->row_list ()); + row_it.mark_cycle_pt (); + } + } + //find non-empty block + while (!block_it.cycled_list () + && row_it.cycled_list ()); + } + if (!row_it.cycled_list ()) { + word_it.set_to_list (row_it.data ()->word_list ()); + word_it.mark_cycle_pt (); + } + } + + // if (wordit_linearc && !word->flag(W_POLYGON)) + // { + // larc_word=word->larc_copy(row->x_height()); + // result=larc_word->poly_copy(row->x_height()); + // delete larc_word; + // } + // else + result = word->poly_copy (row->x_height ()); + return result; + } +} + + +/********************************************************************** + * make_pseudo_word + * + * Make all the blobs inside a selection into a single word. + * The word is always a copy and needs to be deleted. + **********************************************************************/ + +WERD *make_pseudo_word( //make fake word + BLOCK_LIST *block_list, //blocks to check //block of selection + BOX &selection_box, + BLOCK *&pseudo_block, + ROW *&pseudo_row //row of selection + ) { + BLOCK_IT block_it(block_list); + BLOCK *block; + ROW_IT row_it; + ROW *row; + WERD_IT word_it; + WERD *word; + PBLOB_IT blob_it; + PBLOB *blob; + PBLOB_LIST new_blobs; //list of gathered blobs + //iterator + PBLOB_IT new_blob_it = &new_blobs; + WERD *pseudo_word; //fabricated word + WERD *poly_word; //poly copy of word + // WERD *larc_word; //linearc copy + + for (block_it.mark_cycle_pt (); + !block_it.cycled_list (); block_it.forward ()) { + block = block_it.data (); + if (block->bounding_box ().overlap (selection_box)) { + pseudo_block = block; + row_it.set_to_list (block->row_list ()); + for (row_it.mark_cycle_pt (); + !row_it.cycled_list (); row_it.forward ()) { + row = row_it.data (); + if (row->bounding_box ().overlap (selection_box)) { + word_it.set_to_list (row->word_list ()); + for (word_it.mark_cycle_pt (); + !word_it.cycled_list (); word_it.forward ()) { + word = word_it.data (); + if (word->bounding_box ().overlap (selection_box)) { + // if (wordit_linearc && !word->flag(W_POLYGON)) + // { + // larc_word=word->larc_copy(row->x_height()); + // poly_word=larc_word->poly_copy(row->x_height()); + // delete larc_word; + // } + // else + poly_word = word->poly_copy (row->x_height ()); + blob_it.set_to_list (poly_word->blob_list ()); + for (blob_it.mark_cycle_pt (); + !blob_it.cycled_list (); blob_it.forward ()) { + blob = blob_it.data (); + if (blob->bounding_box (). + overlap (selection_box)) { + new_blob_it.add_after_then_move (blob_it. + extract + ()); + //steal off list + pseudo_row = row; + } + } + delete poly_word; //get rid of it + } + } + } + } + } + } + if (!new_blobs.empty ()) { + //make new word + pseudo_word = new WERD (&new_blobs, 1, NULL); + } + else + pseudo_word = NULL; + return pseudo_word; +} diff --git a/ccmain/werdit.h b/ccmain/werdit.h new file mode 100644 index 0000000000..0c6050bb8a --- /dev/null +++ b/ccmain/werdit.h @@ -0,0 +1,67 @@ +/********************************************************************** + * 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 "varable.h" +#include "ocrblock.h" +#include "notdll.h" + +class WERDIT +{ + public: + WERDIT() { + } //empty contructor + WERDIT( //empty contructor + BLOCK_LIST *blocklist) { //blocks on page + start_page(blocklist); //ready to scan + } + + void start_page( //get ready + BLOCK_LIST *blocklist); //blocks on page + + WERD *forward(); //get next word + WERD *next_word() { //get next word + return word_it.data (); //already at next + } + ROW *row() { //get current row + return word_it.cycled_list ()? NULL : row_it.data (); + } + ROW *next_row() { //get next row + return row_it.data_relative (1); + } + BLOCK *block() { //get current block + return block_it.data (); + } + + private: + BLOCK_IT block_it; //iterators + ROW_IT row_it; + WERD_IT word_it; +}; + +//extern BOOL_VAR_H(wordit_linearc,FALSE,"Pass poly of linearc to Tess"); +WERD *make_pseudo_word( //make fake word + BLOCK_LIST *block_list, //blocks to check //block of selection + BOX &selection_box, + BLOCK *&pseudo_block, + ROW *&pseudo_row //row of selection + ); +#endif diff --git a/ccstruct/Makefile.am b/ccstruct/Makefile.am new file mode 100644 index 0000000000..50fdaddcd7 --- /dev/null +++ b/ccstruct/Makefile.am @@ -0,0 +1,25 @@ +SUBDIRS = +AM_CPPFLAGS = \ + -I$(top_srcdir)/ccutil -I$(top_srcdir)/cutil \ + -I$(top_srcdir)/image -I$(top_srcdir)/viewer + +EXTRA_DIST = \ + blckerr.h blobbox.h blobs.h blread.h coutln.h crakedge.h \ + genblob.h hpddef.h hpdsizes.h ipoints.h labls.h linlsq.h \ + lmedsq.h mod128.h normalis.h ocrblock.h ocrrow.h pageblk.h \ + pageres.h pdblock.h pdclass.h points.h polyaprx.h polyblk.h \ + polyblob.h polyvert.h poutline.h quadlsq.h quadratc.h \ + quspline.h ratngs.h rect.h rejctmap.h rwpoly.h statistc.h \ + stepblob.h txtregn.h vecfuncs.h werd.h + +noinst_LIBRARIES = libtesseract_ccstruct.a +libtesseract_ccstruct_a_SOURCES = \ + blobbox.cpp blobs.cpp blread.cpp callcpp.cpp \ + coutln.cpp genblob.cpp labls.cpp linlsq.cpp \ + lmedsq.cpp mod128.cpp normalis.cpp ocrblock.cpp \ + ocrrow.cpp pageblk.cpp pageres.cpp pdblock.cpp \ + points.cpp polyaprx.cpp polyblk.cpp polyblob.cpp \ + polyvert.cpp poutline.cpp quadlsq.cpp quadratc.cpp \ + quspline.cpp ratngs.cpp rect.cpp rejctmap.cpp \ + rwpoly.cpp statistc.cpp stepblob.cpp txtregn.cpp \ + vecfuncs.cpp werd.cpp diff --git a/ccstruct/Makefile.in b/ccstruct/Makefile.in new file mode 100644 index 0000000000..0259918444 --- /dev/null +++ b/ccstruct/Makefile.in @@ -0,0 +1,587 @@ +# Makefile.in generated by automake 1.9.6 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005 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@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = .. +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +INSTALL = @INSTALL@ +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@ +subdir = ccstruct +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \ + $(top_srcdir)/config/ac_define_versionlevel.m4 \ + $(top_srcdir)/config/acinclude_custom.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(SHELL) $(top_srcdir)/config/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config_auto.h +CONFIG_CLEAN_FILES = +LIBRARIES = $(noinst_LIBRARIES) +AR = ar +ARFLAGS = cru +libtesseract_ccstruct_a_AR = $(AR) $(ARFLAGS) +libtesseract_ccstruct_a_LIBADD = +am_libtesseract_ccstruct_a_OBJECTS = blobbox.$(OBJEXT) blobs.$(OBJEXT) \ + blread.$(OBJEXT) callcpp.$(OBJEXT) coutln.$(OBJEXT) \ + genblob.$(OBJEXT) labls.$(OBJEXT) linlsq.$(OBJEXT) \ + lmedsq.$(OBJEXT) mod128.$(OBJEXT) normalis.$(OBJEXT) \ + ocrblock.$(OBJEXT) ocrrow.$(OBJEXT) pageblk.$(OBJEXT) \ + pageres.$(OBJEXT) pdblock.$(OBJEXT) points.$(OBJEXT) \ + polyaprx.$(OBJEXT) polyblk.$(OBJEXT) polyblob.$(OBJEXT) \ + polyvert.$(OBJEXT) poutline.$(OBJEXT) quadlsq.$(OBJEXT) \ + quadratc.$(OBJEXT) quspline.$(OBJEXT) ratngs.$(OBJEXT) \ + rect.$(OBJEXT) rejctmap.$(OBJEXT) rwpoly.$(OBJEXT) \ + statistc.$(OBJEXT) stepblob.$(OBJEXT) txtregn.$(OBJEXT) \ + vecfuncs.$(OBJEXT) werd.$(OBJEXT) +libtesseract_ccstruct_a_OBJECTS = \ + $(am_libtesseract_ccstruct_a_OBJECTS) +DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/config/depcomp +am__depfiles_maybe = depfiles +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +CXXLD = $(CXX) +CXXLINK = $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) \ + -o $@ +SOURCES = $(libtesseract_ccstruct_a_SOURCES) +DIST_SOURCES = $(libtesseract_ccstruct_a_SOURCES) +RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \ + html-recursive info-recursive install-data-recursive \ + install-exec-recursive install-info-recursive \ + install-recursive installcheck-recursive installdirs-recursive \ + pdf-recursive ps-recursive uninstall-info-recursive \ + uninstall-recursive +ETAGS = etags +CTAGS = ctags +DIST_SUBDIRS = $(SUBDIRS) +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMDEP_FALSE = @AMDEP_FALSE@ +AMDEP_TRUE = @AMDEP_TRUE@ +AMTAR = @AMTAR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CXXRPOFLAGS = @CXXRPOFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +GNUWIN32_DIR = @GNUWIN32_DIR@ +HAVE_GNUWIN32_FALSE = @HAVE_GNUWIN32_FALSE@ +HAVE_GNUWIN32_TRUE = @HAVE_GNUWIN32_TRUE@ +HAVE_LIBTIFF_FALSE = @HAVE_LIBTIFF_FALSE@ +HAVE_LIBTIFF_TRUE = @HAVE_LIBTIFF_TRUE@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTIFF_CFLAGS = @LIBTIFF_CFLAGS@ +LIBTIFF_LIBS = @LIBTIFF_LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAINTAINER_MODE_FALSE = @MAINTAINER_MODE_FALSE@ +MAINTAINER_MODE_TRUE = @MAINTAINER_MODE_TRUE@ +MAKEINFO = @MAKEINFO@ +OBJEXT = @OBJEXT@ +OPTS = @OPTS@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_DATE = @PACKAGE_DATE@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PACKAGE_YEAR = @PACKAGE_YEAR@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +RPO_NO = @RPO_NO@ +RPO_YES = @RPO_YES@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +USING_CL_FALSE = @USING_CL_FALSE@ +USING_CL_TRUE = @USING_CL_TRUE@ +VERSION = @VERSION@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_RANLIB = @ac_ct_RANLIB@ +ac_ct_STRIP = @ac_ct_STRIP@ +am__fastdepCC_FALSE = @am__fastdepCC_FALSE@ +am__fastdepCC_TRUE = @am__fastdepCC_TRUE@ +am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@ +am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@ +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@ +datadir = @datadir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +SUBDIRS = +AM_CPPFLAGS = \ + -I$(top_srcdir)/ccutil -I$(top_srcdir)/cutil \ + -I$(top_srcdir)/image -I$(top_srcdir)/viewer + +EXTRA_DIST = \ + blckerr.h blobbox.h blobs.h blread.h coutln.h crakedge.h \ + genblob.h hpddef.h hpdsizes.h ipoints.h labls.h linlsq.h \ + lmedsq.h mod128.h normalis.h ocrblock.h ocrrow.h pageblk.h \ + pageres.h pdblock.h pdclass.h points.h polyaprx.h polyblk.h \ + polyblob.h polyvert.h poutline.h quadlsq.h quadratc.h \ + quspline.h ratngs.h rect.h rejctmap.h rwpoly.h statistc.h \ + stepblob.h txtregn.h vecfuncs.h werd.h + +noinst_LIBRARIES = libtesseract_ccstruct.a +libtesseract_ccstruct_a_SOURCES = \ + blobbox.cpp blobs.cpp blread.cpp callcpp.cpp \ + coutln.cpp genblob.cpp labls.cpp linlsq.cpp \ + lmedsq.cpp mod128.cpp normalis.cpp ocrblock.cpp \ + ocrrow.cpp pageblk.cpp pageres.cpp pdblock.cpp \ + points.cpp polyaprx.cpp polyblk.cpp polyblob.cpp \ + polyvert.cpp poutline.cpp quadlsq.cpp quadratc.cpp \ + quspline.cpp ratngs.cpp rect.cpp rejctmap.cpp \ + rwpoly.cpp statistc.cpp stepblob.cpp txtregn.cpp \ + vecfuncs.cpp werd.cpp + +all: all-recursive + +.SUFFIXES: +.SUFFIXES: .cpp .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 \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu ccstruct/Makefile'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu 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 + +clean-noinstLIBRARIES: + -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) +libtesseract_ccstruct.a: $(libtesseract_ccstruct_a_OBJECTS) $(libtesseract_ccstruct_a_DEPENDENCIES) + -rm -f libtesseract_ccstruct.a + $(libtesseract_ccstruct_a_AR) libtesseract_ccstruct.a $(libtesseract_ccstruct_a_OBJECTS) $(libtesseract_ccstruct_a_LIBADD) + $(RANLIB) libtesseract_ccstruct.a + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/blobbox.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/blobs.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/blread.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/callcpp.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/coutln.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/genblob.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/labls.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/linlsq.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lmedsq.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod128.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/normalis.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ocrblock.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ocrrow.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pageblk.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pageres.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pdblock.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/points.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/polyaprx.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/polyblk.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/polyblob.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/polyvert.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/poutline.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/quadlsq.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/quadratc.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/quspline.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ratngs.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rect.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rejctmap.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rwpoly.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/statistc.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stepblob.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/txtregn.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vecfuncs.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/werd.Po@am__quote@ + +.cpp.o: +@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ $< + +.cpp.obj: +@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` +uninstall-info-am: + +# 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. +$(RECURSIVE_TARGETS): + @failcom='exit 1'; \ + for f in x $$MAKEFLAGS; do \ + case $$f in \ + *=* | --[!k]*);; \ + *k*) failcom='fail=yes';; \ + esac; \ + done; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + list='$(SUBDIRS)'; 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; \ + (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" + +mostlyclean-recursive clean-recursive distclean-recursive \ +maintainer-clean-recursive: + @failcom='exit 1'; \ + for f in x $$MAKEFLAGS; do \ + case $$f in \ + *=* | --[!k]*);; \ + *k*) failcom='fail=yes';; \ + esac; \ + done; \ + dot_seen=no; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + rev=''; for subdir in $$list; do \ + if test "$$subdir" = "."; then :; else \ + rev="$$subdir $$rev"; \ + fi; \ + done; \ + rev="$$rev ."; \ + target=`echo $@ | sed s/-recursive//`; \ + for subdir in $$rev; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done && test -z "$$fail" +tags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \ + done +ctags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \ + done + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + 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 || \ + tags="$$tags $$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique; \ + fi +ctags: CTAGS +CTAGS: ctags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(CTAGS_ARGS)$$tags$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +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)'; for file in $$list; do \ + case $$file in \ + $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \ + $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \ + esac; \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + dir="/$$dir"; \ + $(mkdir_p) "$(distdir)$$dir"; \ + else \ + dir=''; \ + fi; \ + if test -d $$d/$$file; then \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$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 \ + test -d "$(distdir)/$$subdir" \ + || $(mkdir_p) "$(distdir)/$$subdir" \ + || exit 1; \ + distdir=`$(am__cd) $(distdir) && pwd`; \ + top_distdir=`$(am__cd) $(top_distdir) && pwd`; \ + (cd $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$top_distdir" \ + distdir="$$distdir/$$subdir" \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-recursive +all-am: Makefile $(LIBRARIES) +installdirs: installdirs-recursive +installdirs-am: +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: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_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-recursive + +clean-am: clean-generic clean-noinstLIBRARIES mostlyclean-am + +distclean: distclean-recursive + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +info: info-recursive + +info-am: + +install-data-am: + +install-exec-am: + +install-info: install-info-recursive + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-compile mostlyclean-generic + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: uninstall-info-am + +uninstall-info: uninstall-info-recursive + +.PHONY: $(RECURSIVE_TARGETS) CTAGS GTAGS all all-am check check-am \ + clean clean-generic clean-noinstLIBRARIES clean-recursive \ + ctags ctags-recursive distclean distclean-compile \ + distclean-generic distclean-recursive distclean-tags distdir \ + dvi dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-exec install-exec-am \ + install-info install-info-am install-man install-strip \ + installcheck installcheck-am installdirs installdirs-am \ + maintainer-clean maintainer-clean-generic \ + maintainer-clean-recursive mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-recursive pdf pdf-am ps ps-am \ + tags tags-recursive uninstall uninstall-am uninstall-info-am + +# 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: diff --git a/ccstruct/blckerr.h b/ccstruct/blckerr.h new file mode 100644 index 0000000000..e306163983 --- /dev/null +++ b/ccstruct/blckerr.h @@ -0,0 +1,29 @@ +/********************************************************************** + * 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 diff --git a/ccstruct/blobbox.cpp b/ccstruct/blobbox.cpp new file mode 100644 index 0000000000..6e25ba0f07 --- /dev/null +++ b/ccstruct/blobbox.cpp @@ -0,0 +1,778 @@ +/********************************************************************** + * 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 "mfcpch.h" +#include "blobbox.h" + +#define PROJECTION_MARGIN 10 //arbitrary +#define EXTERN + +EXTERN double_VAR (textord_error_weight, 3, +"Weighting for error in believability"); +EXTERN BOOL_VAR (pitsync_projection_fix, TRUE, +"Fix bug in projection profile"); + +ELISTIZE (BLOBNBOX) ELIST2IZE (TO_ROW) ELISTIZE (TO_BLOCK) +/********************************************************************** + * 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 + nextblob->joined = TRUE; +} + + +/********************************************************************** + * 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 && (blob_ptr != NULL || 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 (); + if (blob->blob_ptr != NULL) + find_blob_limits (blob->blob_ptr, rightx - blobwidth, rightx, + rotation, test_ymin, test_ymax); + else + find_cblob_vlimits (blob->cblob_ptr, rightx - blobwidth, + rightx, + /*rotation, */ test_ymin, test_ymax); + blob_it.forward (); + if (test_ymin < ymin) + ymin = test_ymin; + if (test_ymax > ymax) + ymax = test_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 = BOX (bl, tr); //change box + else { + newblob = new BLOBNBOX; + //box is all it has + newblob->box = BOX (bl, tr); + //stay on current + end_it->add_after_stay_put (newblob); + } + } + } + } +} + + +/********************************************************************** + * find_blob_limits + * + * Scan the outlines of the blob to locate the y min and max + * between the given x limits. + **********************************************************************/ + +void find_blob_limits( //get y limits + PBLOB *blob, //blob to search + float leftx, //x limits + float rightx, + FCOORD rotation, //for landscape + float &ymin, //output y limits + float &ymax) { + float testy; //y intercept + FCOORD pos; //rotated + FCOORD vec; + POLYPT *polypt; //current point + //outlines + OUTLINE_IT out_it = blob->out_list (); + POLYPT_IT poly_it; //outline pts + + ymin = (float) MAX_INT32; + ymax = (float) -MAX_INT32; + for (out_it.mark_cycle_pt (); !out_it.cycled_list (); out_it.forward ()) { + //get points + poly_it.set_to_list (out_it.data ()->polypts ()); + for (poly_it.mark_cycle_pt (); !poly_it.cycled_list (); + poly_it.forward ()) { + polypt = poly_it.data (); + pos = polypt->pos; + pos.rotate (rotation); + vec = polypt->vec; + vec.rotate (rotation); + if (pos.x () < leftx && pos.x () + vec.x () > leftx + || pos.x () > leftx && pos.x () + vec.x () < leftx) { + testy = pos.y () + vec.y () * (leftx - pos.x ()) / vec.x (); + //intercept of boundary + if (testy < ymin) + ymin = testy; + if (testy > ymax) + ymax = testy; + } + if (pos.x () >= leftx && pos.x () <= rightx) { + if (pos.y () > ymax) + ymax = pos.y (); + if (pos.y () < ymin) + ymin = pos.y (); + } + if (pos.x () > rightx && pos.x () + vec.x () < rightx + || pos.x () < rightx && pos.x () + vec.x () > rightx) { + testy = pos.y () + vec.y () * (rightx - pos.x ()) / vec.x (); + //intercept of boundary + if (testy < ymin) + ymin = testy; + if (testy > ymax) + ymax = testy; + } + } + } +} + + +/********************************************************************** + * 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) { + if (pos.y () > ymax) + ymax = pos.y (); + if (pos.y () < ymin) + ymin = pos.y (); + } + 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) { + if (pos.y () > ymax) + ymax = pos.y (); + if (pos.y () < ymin) + ymin = pos.y (); + } + 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) { + if (pos.x () > xmax) + xmax = pos.x (); + if (pos.x () < xmin) + xmin = pos.x (); + } + vec = outline->step (stepindex); + pos += vec; //move to next + } + } +} + + +/********************************************************************** + * rotate_blob + * + * Poly copy the blob and rotate the copy by the given vector. + **********************************************************************/ + +PBLOB *rotate_blob( //get y limits + PBLOB *blob, //blob to search + FCOORD rotation //vector to rotate by + ) { + PBLOB *copy; //copy of blob + POLYPT *polypt; //current point + OUTLINE_IT out_it; + POLYPT_IT poly_it; //outline pts + + copy = new PBLOB; + *copy = *blob; //deep copy + out_it.set_to_list (copy->out_list ()); + for (out_it.mark_cycle_pt (); !out_it.cycled_list (); out_it.forward ()) { + //get points + poly_it.set_to_list (out_it.data ()->polypts ()); + for (poly_it.mark_cycle_pt (); !poly_it.cycled_list (); + poly_it.forward ()) { + polypt = poly_it.data (); + //rotate it + polypt->pos.rotate (rotation); + polypt->vec.rotate (rotation); + } + out_it.data ()->compute_bb (); + } + return copy; +} + + +/********************************************************************** + * rotate_cblob + * + * Poly copy the blob and rotate the copy by the given vector. + **********************************************************************/ + +PBLOB *rotate_cblob( //rotate it + C_BLOB *blob, //blob to search + float xheight, //for poly approx + FCOORD rotation //for landscape + ) { + PBLOB *copy; //copy of blob + POLYPT *polypt; //current point + OUTLINE_IT out_it; + POLYPT_IT poly_it; //outline pts + + copy = new PBLOB (blob, xheight); + out_it.set_to_list (copy->out_list ()); + for (out_it.mark_cycle_pt (); !out_it.cycled_list (); out_it.forward ()) { + //get points + poly_it.set_to_list (out_it.data ()->polypts ()); + for (poly_it.mark_cycle_pt (); !poly_it.cycled_list (); + poly_it.forward ()) { + polypt = poly_it.data (); + //rotate it + polypt->pos.rotate (rotation); + polypt->vec.rotate (rotation); + } + out_it.data ()->compute_bb (); + } + return copy; +} + + +/********************************************************************** + * 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. + **********************************************************************/ + +BOX box_next( //get bounding box + BLOBNBOX_IT *it //iterator to blobds + ) { + BLOBNBOX *blob; //current blob + BOX result; //total box + + blob = it->data (); + result = blob->bounding_box (); + do { + it->forward (); + blob = it->data (); + if (blob->blob () == NULL && blob->cblob () == NULL) + //was pre-chopped + result += blob->bounding_box (); + } + //until next real blob + while (blob->blob () == NULL && 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. + **********************************************************************/ + +BOX box_next_pre_chopped( //get bounding box + BLOBNBOX_IT *it //iterator to blobds + ) { + BLOBNBOX *blob; //current blob + BOX 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 +):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; + } +} + + +/********************************************************************** + * 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 + BOX row_box; //bound of row + BLOBNBOX *blob; //current blob + BOX 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->blob () != NULL) + vertical_blob_projection (blob->blob (), &projection); + else if (blob->cblob () != NULL) + vertical_cblob_projection (blob->cblob (), &projection); + } +} + + +/********************************************************************** + * vertical_blob_projection + * + * Compute the vertical projection of a blob from its outlines + * and add to the given STATS. + **********************************************************************/ + +void vertical_blob_projection( //project outlines + PBLOB *blob, //blob to project + STATS *stats //output + ) { + //outlines of blob + OUTLINE_IT out_it = blob->out_list (); + + for (out_it.mark_cycle_pt (); !out_it.cycled_list (); out_it.forward ()) { + vertical_outline_projection (out_it.data (), stats); + } +} + + +/********************************************************************** + * vertical_outline_projection + * + * Compute the vertical projection of a outline from its outlines + * and add to the given STATS. + **********************************************************************/ + +void vertical_outline_projection( //project outlines + OUTLINE *outline, //outline to project + STATS *stats //output + ) { + POLYPT *polypt; //current point + INT32 xcoord; //current pixel coord + float end_x; //end of vec + POLYPT_IT poly_it = outline->polypts (); + OUTLINE_IT out_it = outline->child (); + float ymean; //amount to add + float width; //amount of x + + for (poly_it.mark_cycle_pt (); !poly_it.cycled_list (); poly_it.forward ()) { + polypt = poly_it.data (); + end_x = polypt->pos.x () + polypt->vec.x (); + if (polypt->vec.x () > 0) { + for (xcoord = (INT32) floor (polypt->pos.x ()); + xcoord < end_x; xcoord++) { + if (polypt->pos.x () < xcoord) { + width = (float) xcoord; + ymean = + polypt->vec.y () * (xcoord - + polypt->pos.x ()) / polypt->vec.x () + + polypt->pos.y (); + } + else { + width = polypt->pos.x (); + ymean = polypt->pos.y (); + } + if (end_x > xcoord + 1) { + width -= xcoord + 1; + ymean += + polypt->vec.y () * (xcoord + 1 - + polypt->pos.x ()) / polypt->vec.x () + + polypt->pos.y (); + } + else { + width -= end_x; + ymean += polypt->pos.y () + polypt->vec.y (); + } + ymean = ymean * width / 2; + stats->add (xcoord, (INT32) floor (ymean + 0.5)); + } + } + else if (polypt->vec.x () < 0) { + for (xcoord = (INT32) floor (end_x); + xcoord < polypt->pos.x (); xcoord++) { + if (polypt->pos.x () > xcoord + 1) { + width = xcoord + 1.0f; + ymean = + polypt->vec.y () * (xcoord + 1 - + polypt->pos.x ()) / polypt->vec.x () + + polypt->pos.y (); + } + else { + width = polypt->pos.x (); + ymean = polypt->pos.y (); + } + if (end_x < xcoord) { + width -= xcoord; + ymean += + polypt->vec.y () * (xcoord - + polypt->pos.x ()) / polypt->vec.x () + + polypt->pos.y (); + } + else { + width -= end_x; + ymean += polypt->pos.y () + polypt->vec.y (); + } + ymean = ymean * width / 2; + stats->add (xcoord, (INT32) floor (ymean + 0.5)); + } + } + } + + for (out_it.mark_cycle_pt (); !out_it.cycled_list (); out_it.forward ()) { + vertical_outline_projection (out_it.data (), stats); + } +} + + +/********************************************************************** + * 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) { + if (pitsync_projection_fix) + stats->add (pos.x (), -pos.y ()); + else + stats->add (pos.x (), pos.y ()); + } + else if (step.x () < 0) { + if (pitsync_projection_fix) + stats->add (pos.x () - 1, pos.y ()); + else + 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 + ) { + 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->blob() != NULL) + delete box->blob(); + if (box->cblob() != NULL) + delete box->cblob(); + } +} + +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); +} + diff --git a/ccstruct/blobbox.h b/ccstruct/blobbox.h new file mode 100644 index 0000000000..7934c34edc --- /dev/null +++ b/ccstruct/blobbox.h @@ -0,0 +1,381 @@ +/********************************************************************** + * 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 "varable.h" +#include "clst.h" +#include "elst2.h" +#include "werd.h" +#include "ocrblock.h" +#include "statistc.h" + +extern double_VAR_H (textord_error_weight, 3, +"Weighting for error in believability"); + +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 +}; + +class BLOBNBOX; +ELISTIZEH (BLOBNBOX) +class BLOBNBOX:public ELIST_LINK +{ + public: + BLOBNBOX() { //empty + blob_ptr = NULL; + cblob_ptr = NULL; + joined = FALSE; + reduced = FALSE; + area = 0; + } + BLOBNBOX( //constructor + PBLOB *srcblob) { + blob_ptr = srcblob; + cblob_ptr = NULL; + box = srcblob->bounding_box (); + joined = FALSE; + reduced = FALSE; + area = (int) srcblob->area (); + } + BLOBNBOX( //constructor + C_BLOB *srcblob) { + blob_ptr = NULL; + cblob_ptr = srcblob; + box = srcblob->bounding_box (); + joined = FALSE; + reduced = FALSE; + area = (int) srcblob->area (); + } + + //get bounding box + const BOX &bounding_box() const { + return box; + } + //get bounding box + const BOX &reduced_box() const { + return red_box; + } + void set_reduced_box( //set other box + BOX new_box) { + red_box = new_box; + reduced = TRUE; + } + INT32 enclosed_area() const { //get area + return area; + } + + void rotate_box( //just box + FCOORD vec) { + box.rotate (vec); + } + + BOOL8 joined_to_prev() const { //access function + return joined != 0; + } + BOOL8 red_box_set() const { //access function + return reduced != 0; + } + void merge( //merge with next + BLOBNBOX *nextblob); + 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 + + PBLOB *blob() { //access function + return blob_ptr; + } + C_BLOB *cblob() { //access function + return cblob_ptr; + } + +#ifndef GRAPHICS_DISABLED + void plot( //draw one + WINDOW window, //window to draw in + COLOUR blob_colour, //for outer bits + COLOUR child_colour) { //for holes + if (blob_ptr != NULL) + blob_ptr->plot (window, blob_colour, child_colour); + if (cblob_ptr != NULL) + cblob_ptr->plot (window, blob_colour, child_colour); + } +#endif + + NEWDELETE2 (BLOBNBOX) private: + int area:30; //enclosed area + int joined:1; //joined to prev + int reduced:1; //reduced box set + BOX box; //bounding box + BOX red_box; //bounding box + PBLOB *blob_ptr; //poly blob + C_BLOB *cblob_ptr; //edgestep blob +}; + +class TO_ROW:public ELIST2_LINK +{ + public: + TO_ROW() { + } //empty + TO_ROW( //constructor + BLOBNBOX *blob, //from first blob + float top, //of row //target height + float bottom, + float row_size); + + 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 () - textord_error_weight * 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 + + //true when dead + NEWDELETE2 (TO_ROW) 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 + float ascrise; //ascenders + float descdrop; //descenders + 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: + 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 +}; + +ELIST2IZEH (TO_ROW) +class TO_BLOCK:public ELIST_LINK +{ + public: + TO_BLOCK() { + } //empty + TO_BLOCK( //constructor + BLOCK *src_block); //real block + ~TO_BLOCK(); + + TO_ROW_LIST *get_rows() { //access function + return &row_list; + } + + 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 (); + printf ("Row range (%g,%g), para_c=%g, blobcount=" INT32FORMAT + "\n", row->min_y (), row->max_y (), row->parallel_c (), + row->blob_list ()->length ()); + } + } + + 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 + 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 + + NEWDELETE2 (TO_BLOCK) 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_blob_limits( //get y limits + PBLOB *blob, //blob to search + float leftx, //x limits + float rightx, + FCOORD rotation, //for landscape + float &ymin, //output y limits + float &ymax); +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); +PBLOB *rotate_blob( //get y limits + PBLOB *blob, //blob to search + FCOORD rotation //vector to rotate by + ); +PBLOB *rotate_cblob( //rotate it + C_BLOB *blob, //blob to search + float xheight, //for poly approx + FCOORD rotation //for landscape + ); +C_BLOB *crotate_cblob( //rotate it + C_BLOB *blob, //blob to search + FCOORD rotation //for landscape + ); +BOX box_next( //get bounding box + BLOBNBOX_IT *it //iterator to blobds + ); +BOX box_next_pre_chopped( //get bounding box + BLOBNBOX_IT *it //iterator to blobds + ); +void vertical_blob_projection( //project outlines + PBLOB *blob, //blob to project + STATS *stats //output + ); + //project outlines +void vertical_outline_projection(OUTLINE *outline, //outline to project + STATS *stats //output + ); +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 + ); +#endif diff --git a/ccstruct/blobs.cpp b/ccstruct/blobs.cpp new file mode 100644 index 0000000000..73fd09e11b --- /dev/null +++ b/ccstruct/blobs.cpp @@ -0,0 +1,247 @@ +/* -*-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 "mfcpch.h" +#include "blobs.h" +#include "cutil.h" +#include "emalloc.h" +#include "structures.h" + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +/********************************************************************** + * blob_origin + * + * Compute the origin of a compound blob, define to be the centre + * of the bounding box. + **********************************************************************/ +void blob_origin(TBLOB *blob, /*blob to compute on */ + TPOINT *origin) { /*return value */ + TPOINT topleft; /*bounding box */ + TPOINT botright; + + /*find bounding box */ + blob_bounding_box(blob, &topleft, &botright); + /*centre of box */ + origin->x = (topleft.x + botright.x) / 2; + origin->y = (topleft.y + botright.y) / 2; +} + + +/********************************************************************** + * blob_bounding_box + * + * Compute the bounding_box of a compound blob, define to be the + * max coordinate value of the bounding boxes of all the top-level + * outlines in the box. + **********************************************************************/ +void blob_bounding_box(TBLOB *blob, /*blob to compute on */ + register TPOINT *topleft, /*bounding box */ + register TPOINT *botright) { + register TESSLINE *outline; /*current outline */ + + if (blob == NULL || blob->outlines == NULL) { + topleft->x = topleft->y = 0; + *botright = *topleft; /*default value */ + } + else { + outline = blob->outlines; + *topleft = outline->topleft; + *botright = outline->botright; + for (outline = outline->next; outline != NULL; outline = outline->next) { + if (outline->topleft.x < topleft->x) + /*find extremes */ + topleft->x = outline->topleft.x; + if (outline->botright.x > botright->x) + /*find extremes */ + botright->x = outline->botright.x; + if (outline->topleft.y > topleft->y) + /*find extremes */ + topleft->y = outline->topleft.y; + if (outline->botright.y < botright->y) + /*find extremes */ + botright->y = outline->botright.y; + } + } +} + + +/********************************************************************** + * blobs_bounding_box + * + * Return the smallest extreme point that contain this word. + **********************************************************************/ +void blobs_bounding_box(TBLOB *blobs, TPOINT *topleft, TPOINT *botright) { + TPOINT tl; + TPOINT br; + TBLOB *blob; + /* Start with first blob */ + blob_bounding_box(blobs, topleft, botright); + + iterate_blobs(blob, blobs) { + blob_bounding_box(blob, &tl, &br); + + if (tl.x < topleft->x) + topleft->x = tl.x; + if (tl.y > topleft->y) + topleft->y = tl.y; + if (br.x > botright->x) + botright->x = br.x; + if (br.y < botright->y) + botright->y = br.y; + } +} + + +/********************************************************************** + * blobs_origin + * + * Compute the origin of a compound blob, define to be the centre + * of the bounding box. + **********************************************************************/ +void blobs_origin(TBLOB *blobs, /*blob to compute on */ + TPOINT *origin) { /*return value */ + TPOINT topleft; /*bounding box */ + TPOINT botright; + + /*find bounding box */ + blobs_bounding_box(blobs, &topleft, &botright); + /*center of box */ + origin->x = (topleft.x + botright.x) / 2; + origin->y = (topleft.y + botright.y) / 2; +} + + +/********************************************************************** + * blobs_widths + * + * Compute the widths of a list of blobs. Return an array of the widths + * and gaps. + **********************************************************************/ +WIDTH_RECORD *blobs_widths(TBLOB *blobs) { /*blob to compute on */ + WIDTH_RECORD *width_record; + TPOINT topleft; /*bounding box */ + TPOINT botright; + TBLOB *blob; /*blob to compute on */ + int i = 0; + int blob_end; + int num_blobs = count_blobs (blobs); + + /* Get memory */ + width_record = (WIDTH_RECORD *) memalloc (sizeof (int) * num_blobs * 2); + width_record->num_chars = num_blobs; + + blob_bounding_box(blobs, &topleft, &botright); + width_record->widths[i++] = botright.x - topleft.x; + /* First width */ + blob_end = botright.x; + + iterate_blobs (blob, blobs->next) { + blob_bounding_box(blob, &topleft, &botright); + width_record->widths[i++] = topleft.x - blob_end; + width_record->widths[i++] = botright.x - topleft.x; + blob_end = botright.x; + } + return (width_record); +} + + +/********************************************************************** + * count_blobs + * + * Return a count of the number of blobs attached to this one. + **********************************************************************/ +int count_blobs(TBLOB *blobs) { + TBLOB *b; + int x = 0; + + iterate_blobs (b, blobs) x++; + return (x); +} + + +/********************************************************************** + * delete_word + * + * Reclaim the memory taken by this word structure and all of its + * lower level structures. + **********************************************************************/ +void delete_word(TWERD *word) { + TBLOB *blob; + TBLOB *nextblob; + TESSLINE *outline; + TESSLINE *nextoutline; + TESSLINE *child; + TESSLINE *nextchild; + + for (blob = word->blobs; blob; blob = nextblob) { + nextblob = blob->next; + + for (outline = blob->outlines; outline; outline = nextoutline) { + nextoutline = outline->next; + + delete_edgepts (outline->loop); + + for (child = outline->child; child; child = nextchild) { + nextchild = child->next; + + delete_edgepts (child->loop); + + oldoutline(child); + } + oldoutline(outline); + } + oldblob(blob); + } + if (word->correct != NULL) + strfree (word->correct); /* Reclaim memory */ + oldword(word); +} + + +/********************************************************************** + * delete_edgepts + * + * Delete a list of EDGEPT structures. + **********************************************************************/ +void delete_edgepts(register EDGEPT *edgepts) { + register EDGEPT *this_edge; + register EDGEPT *next_edge; + + if (edgepts == NULL) + return; + + this_edge = edgepts; + do { + next_edge = this_edge->next; + oldedgept(this_edge); + this_edge = next_edge; + } + while (this_edge != edgepts); +} diff --git a/ccstruct/blobs.h b/ccstruct/blobs.h new file mode 100644 index 0000000000..16c64b423a --- /dev/null +++ b/ccstruct/blobs.h @@ -0,0 +1,119 @@ +/* -*-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 "vecfuncs.h" +#include "tessclas.h" + +/*---------------------------------------------------------------------- + T y p e s +----------------------------------------------------------------------*/ +typedef struct +{ /* Widths of pieces */ + int num_chars; + int widths[1]; +} WIDTH_RECORD; + +/*---------------------------------------------------------------------- + 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 +----------------------------------------------------------------------*/ +void blob_origin(TBLOB *blob, /*blob to compute on */ + TPOINT *origin); /*return value */ + + /*blob to compute on */ +void blob_bounding_box(TBLOB *blob, + register TPOINT *topleft, /*bounding box */ + register TPOINT *botright); + +void blobs_bounding_box(TBLOB *blobs, TPOINT *topleft, TPOINT *botright); + +void blobs_origin(TBLOB *blobs, /*blob to compute on */ + TPOINT *origin); /*return value */ + + /*blob to compute on */ +WIDTH_RECORD *blobs_widths(TBLOB *blobs); + +int count_blobs(TBLOB *blobs); + +void delete_word(TWERD *word); + +void delete_edgepts(register EDGEPT *edgepts); + +/* +#if defined(__STDC__) || defined(__cplusplus) +# define _ARGS(s) s +#else +# define _ARGS(s) () +#endif*/ + +/* blobs.c +void blob_origin + _ARGS((BLOB *blob, + TPOINT *origin)); + +void blob_bounding_box + _ARGS((BLOB *blob, + TPOINT *topleft, + TPOINT *botright)); + +void blobs_bounding_box + _ARGS((BLOB *blobs, + TPOINT *topleft, + TPOINT *botright)); + +void blobs_origin + _ARGS((BLOB *blobs, + TPOINT *origin)); + +WIDTH_RECORD *blobs_widths + _ARGS((BLOB *blobs)); + +int count_blobs + _ARGS((BLOB *blobs)); + +void delete_word + _ARGS((TWERD *word)); + +void delete_edgepts + _ARGS((EDGEPT *edgepts)); +#undef _ARGS +*/ +#endif diff --git a/ccstruct/blread.cpp b/ccstruct/blread.cpp new file mode 100644 index 0000000000..93a3412a12 --- /dev/null +++ b/ccstruct/blread.cpp @@ -0,0 +1,537 @@ +/********************************************************************** + * 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 "mfcpch.h" +#include +#ifdef __UNIX__ +#include +#endif +#include "scanutils.h" +#include "fileerr.h" +#include "imgtiff.h" +#include "pdclass.h" +#include "rwpoly.h" +#include "blread.h" + +#define PD_EXT ".pd" +#define VEC_EXT ".vec" //accupage file +#define HPD_EXT ".bl" //hand pd file + //unlv zone file +#define UNLV_EXT ".uzn" +#define BLOCK_EXPANSION 8 //boundary expansion +#define EXTERN + +EXTERN BOOL_EVAR (ignore_weird_blocks, TRUE, "Don't read weird blocks"); + +static BOX convert_vec_block( //make non-rect block + VEC_ENTRY *entries, //vectors + UINT16 entry_count, //no of entries + INT32 ysize, //image size + ICOORDELT_IT *left_it, //block sides + ICOORDELT_IT *right_it); + +/********************************************************************** + * BLOCK::read_pd_file + * + * Read a whole pd file to make a list of blocks, or use the whole page. + **********************************************************************/ + +BOOL8 read_pd_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 + INT32 block_count; //no of blocks + INT32 junk_count; //no of junks to read + INT32 junks[4]; //junk elements + INT32 vertex_count; //boundary vertices + INT32 xcoord; //current coords + INT32 ycoord; + INT32 prevx; //previous coords + INT32 prevy; + BLOCK_IT block_it = blocks; //block iterator + ICOORDELT_LIST dummy; //for constructor + ICOORDELT_IT left_it = &dummy; //iterator + ICOORDELT_IT right_it = &dummy;//iterator + + if (read_hpd_file (name, xsize, ysize, blocks)) + return TRUE; //succeeded + if (read_vec_file (name, xsize, ysize, blocks)) + return TRUE; //succeeded + if (read_unlv_file (name, xsize, ysize, blocks)) + return TRUE; //succeeded + name += PD_EXT; //add extension + if ((pdfp = fopen (name.string (), "r")) == NULL) { + //make rect block + block = new BLOCK (name.string (), TRUE, 0, 0, 0, 0, xsize, ysize); + block_it.add_to_end (block); //on end of list + return FALSE; //didn't read one + } + else { + if (fread (&block_count, sizeof (block_count), 1, pdfp) != 1) + READFAILED.error ("read_pd_file", EXIT, "Block count"); + tprintf ("%d blocks in .pd file.\n", block_count); + while (block_count > 0) { + if (fread (&junk_count, sizeof (junk_count), 1, pdfp) != 1) + READFAILED.error ("read_pd_file", EXIT, "Junk count"); + if (fread (&vertex_count, sizeof (vertex_count), 1, pdfp) != 1) + READFAILED.error ("read_pd_file", EXIT, "Vertex count"); + block = new BLOCK; //make a block + //on end of list + block_it.add_to_end (block); + left_it.set_to_list (&block->leftside); + right_it.set_to_list (&block->rightside); + + //read a pair + get_pd_vertex (pdfp, xsize, ysize, &block->box, xcoord, ycoord); + vertex_count -= 2; //count read ones + prevx = xcoord; + do { + if (xcoord == prevx) { + if (!right_it.empty ()) { + if (right_it.data ()->x () <= xcoord + BLOCK_EXPANSION) + right_it.data ()->set_y (right_it.data ()->y () + + BLOCK_EXPANSION); + else + right_it.data ()->set_y (right_it.data ()->y () - + BLOCK_EXPANSION); + } + right_it. + add_before_then_move (new + ICOORDELT (xcoord + BLOCK_EXPANSION, + ycoord)); + } + prevx = xcoord; //remember previous + prevy = ycoord; + get_pd_vertex (pdfp, xsize, ysize, &block->box, xcoord, ycoord); + vertex_count -= 2; //count read ones + } + while (ycoord <= prevy); + right_it.data ()->set_y (right_it.data ()->y () - BLOCK_EXPANSION); + + //start of left + left_it.add_to_end (new ICOORDELT (prevx - BLOCK_EXPANSION, prevy - BLOCK_EXPANSION)); + + do { + prevx = xcoord; //remember previous + get_pd_vertex (pdfp, xsize, ysize, &block->box, xcoord, ycoord); + vertex_count -= 2; + if (xcoord != prevx && vertex_count > 0) { + if (xcoord > prevx) + left_it. + add_to_end (new + ICOORDELT (xcoord - BLOCK_EXPANSION, + ycoord + BLOCK_EXPANSION)); + else + left_it. + add_to_end (new + ICOORDELT (xcoord - BLOCK_EXPANSION, + ycoord - BLOCK_EXPANSION)); + } + else if (vertex_count == 0) + left_it.add_to_end (new ICOORDELT (prevx - BLOCK_EXPANSION, + ycoord + BLOCK_EXPANSION)); + } + while (vertex_count > 0); //until all read + + while (junk_count > 0) { + if (fread (junks, sizeof (INT32), 4, pdfp) != 4) + READFAILED.error ("read_pd_file", EXIT, "Junk coords"); + junk_count--; + } + block_count--; //count read blocks + } + } + fclose(pdfp); + return TRUE; //read one +} + + +/********************************************************************** + * get_pd_vertex + * + * Read a pair of coords, invert the y and clip to image limits. + * Also update the bounding box. + * + * Read a whole pd file to make a list of blocks, or use the whole page. + **********************************************************************/ + +void get_pd_vertex( //get new vertex + FILE *pdfp, //file to read + INT32 xsize, //image size + INT32 ysize, //image size + BOX *box, //bounding box + INT32 &xcoord, //output coords + INT32 &ycoord) { + BOX new_coord; //expansion box + + //get new coords + if (fread (&xcoord, sizeof (xcoord), 1, pdfp) != 1) + READFAILED.error ("read_pd_file", EXIT, "Xcoord"); + if (fread (&ycoord, sizeof (ycoord), 1, pdfp) != 1) + READFAILED.error ("read_pd_file", EXIT, "Xcoord"); + ycoord = ysize - ycoord; //invert y + if (xcoord < BLOCK_EXPANSION) + xcoord = BLOCK_EXPANSION; //clip to limits + if (xcoord > xsize - BLOCK_EXPANSION) + xcoord = xsize - BLOCK_EXPANSION; + if (ycoord < BLOCK_EXPANSION) + ycoord = BLOCK_EXPANSION; + if (ycoord > ysize - BLOCK_EXPANSION) + ycoord = ysize - BLOCK_EXPANSION; + + new_coord = + BOX (ICOORD (xcoord - BLOCK_EXPANSION, ycoord - BLOCK_EXPANSION), + ICOORD (xcoord + BLOCK_EXPANSION, ycoord + BLOCK_EXPANSION)); + (*box) += new_coord; +} + + +/********************************************************************** + * BLOCK::read_hpd_file + * + * Read a whole hpd file to make a list of blocks. + * Return FALSE if the .vec fiel cannot be found + **********************************************************************/ + +BOOL8 read_hpd_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 + PAGE_BLOCK_LIST *page_blocks; + INT32 block_no; //no of blocks + BLOCK_IT block_it = blocks; //block iterator + + name += HPD_EXT; //add extension + if ((pdfp = fopen (name.string (), "r")) == NULL) { + return FALSE; //can't find it + } + fclose(pdfp); + page_blocks = read_poly_blocks (name.string ()); + block_no = 0; + scan_hpd_blocks (name.string (), page_blocks, block_no, &block_it); + tprintf ("Text region count=%d\n", block_no); + return TRUE; //read one +} + + +/********************************************************************** + * BLOCK::scan_hpd_blocks + * + * Read a whole hpd file to make a list of blocks. + * Return FALSE if the .vec fiel cannot be found + **********************************************************************/ + +void scan_hpd_blocks( //print list of sides + const char *name, //block label + PAGE_BLOCK_LIST *page_blocks, //head of full pag + INT32 &block_no, //no of blocks + BLOCK_IT *block_it //block iterator + ) { + BLOCK *block; //current block + //page blocks + PAGE_BLOCK_IT pb_it = page_blocks; + PAGE_BLOCK *current_block; + TEXT_REGION_IT tr_it; + TEXT_BLOCK *tb; + TEXT_REGION *tr; + BOX *block_box; //from text region + + for (pb_it.mark_cycle_pt (); !pb_it.cycled_list (); pb_it.forward ()) { + current_block = pb_it.data (); + if (current_block->type () == PB_TEXT) { + tb = (TEXT_BLOCK *) current_block; + if (!tb->regions ()->empty ()) { + tr_it.set_to_list (tb->regions ()); + for (tr_it.mark_cycle_pt (); + !tr_it.cycled_list (); tr_it.forward ()) { + block_no++; + tr = tr_it.data (); + block_box = tr->bounding_box (); + block = new BLOCK (name, TRUE, 0, 0, + block_box->left (), block_box->bottom (), + block_box->right (), block_box->top ()); + block->hand_block = tr; + block->hand_poly = tr; + block_it->add_after_then_move (block); + } + } + } + else if (current_block->type () == PB_WEIRD + && !ignore_weird_blocks + && ((WEIRD_BLOCK *) current_block)->id_no () > 0) { + block_no++; + block_box = current_block->bounding_box (); + block = new BLOCK (name, TRUE, 0, 0, + block_box->left (), block_box->bottom (), + block_box->right (), block_box->top ()); + block->hand_block = NULL; + block->hand_poly = current_block; + block_it->add_after_then_move (block); + } + if (!current_block->child ()->empty ()) + scan_hpd_blocks (name, current_block->child (), block_no, block_it); + } +} + + + + + +/********************************************************************** + * BLOCK::read_vec_file + * + * Read a whole vec file to make a list of blocks. + * Return FALSE if the .vec fiel cannot be found + **********************************************************************/ + +BOOL8 read_vec_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 + INT32 block_no; //no of blocks + INT32 block_index; //current blocks + INT32 vector_count; //total vectors + VEC_HEADER header; //file header + BLOCK_HEADER *vec_blocks; //blocks from file + VEC_ENTRY *vec_entries; //vectors from file + BLOCK_IT block_it = blocks; //block iterator + ICOORDELT_IT left_it; //iterators + ICOORDELT_IT right_it; + + name += VEC_EXT; //add extension + if ((pdfp = fopen (name.string (), "r")) == NULL) { + return FALSE; //can't find it + } + if (fread (&header, sizeof (header), 1, pdfp) != 1) + READFAILED.error ("read_vec_file", EXIT, "Header"); + //from intel + header.filesize = reverse32 (header.filesize); + header.bytesize = reverse16 (header.bytesize); + header.arraysize = reverse16 (header.arraysize); + header.width = reverse16 (header.width); + header.height = reverse16 (header.height); + header.res = reverse16 (header.res); + header.bpp = reverse16 (header.bpp); + tprintf ("%d blocks in %s file:", header.arraysize, VEC_EXT); + vector_count = header.filesize - header.arraysize * sizeof (BLOCK_HEADER); + vector_count /= sizeof (VEC_ENTRY); + vec_blocks = + (BLOCK_HEADER *) alloc_mem (header.arraysize * sizeof (BLOCK_HEADER)); + vec_entries = (VEC_ENTRY *) alloc_mem (vector_count * sizeof (VEC_ENTRY)); + xsize = header.width; //real image size + ysize = header.height; + if (fread (vec_blocks, sizeof (BLOCK_HEADER), header.arraysize, pdfp) + != static_cast(header.arraysize)) + READFAILED.error ("read_vec_file", EXIT, "Blocks"); + if (fread (vec_entries, sizeof (VEC_ENTRY), vector_count, pdfp) + != static_cast(vector_count)) + READFAILED.error ("read_vec_file", EXIT, "Vectors"); + for (block_index = 0; block_index < header.arraysize; block_index++) { + vec_blocks[block_index].offset = + reverse16 (vec_blocks[block_index].offset); + vec_blocks[block_index].order = + reverse16 (vec_blocks[block_index].order); + vec_blocks[block_index].entries = + reverse16 (vec_blocks[block_index].entries); + vec_blocks[block_index].charsize = + reverse16 (vec_blocks[block_index].charsize); + } + for (block_index = 0; block_index < vector_count; block_index++) { + vec_entries[block_index].start = + ICOORD (reverse16 (vec_entries[block_index].start.x ()), + reverse16 (vec_entries[block_index].start.y ())); + vec_entries[block_index].end = + ICOORD (reverse16 (vec_entries[block_index].end.x ()), + reverse16 (vec_entries[block_index].end.y ())); + } + for (block_no = 1; block_no <= header.arraysize; block_no++) { + for (block_index = 0; block_index < header.arraysize; block_index++) { + if (vec_blocks[block_index].order == block_no + && vec_blocks[block_index].valid) { + block = new BLOCK; + left_it.set_to_list (&block->leftside); + right_it.set_to_list (&block->rightside); + block->box = + convert_vec_block (&vec_entries + [vec_blocks[block_index].offset], + vec_blocks[block_index].entries, ysize, + &left_it, &right_it); + block->set_xheight (vec_blocks[block_index].charsize); + //on end of list + block_it.add_to_end (block); + // tprintf("Block at (%d,%d)->(%d,%d) has index %d and order %d\n", + // block->box.left(), + // block->box.bottom(), + // block->box.right(), + // block->box.top(), + // block_index,vec_blocks[block_index].order); + } + } + } + free_mem(vec_blocks); + free_mem(vec_entries); + tprintf ("%d valid\n", block_it.length ()); + fclose(pdfp); + return TRUE; //read one +} + + +/********************************************************************** + * BLOCK::convert_vec_block + * + * Read a whole vec file to make a list of blocks. + * Return FALSE if the .vec fiel cannot be found + **********************************************************************/ + +static BOX convert_vec_block( //make non-rect block + VEC_ENTRY *entries, //vectors + UINT16 entry_count, //no of entries + INT32 ysize, //image size + ICOORDELT_IT *left_it, //block sides + ICOORDELT_IT *right_it) { + BOX block_box; //bounding box + BOX vec_box; //box of vec + ICOORD box_point; //expanded coord + ICOORD shift_vec; //for box expansion + ICOORD prev_pt; //previous coord + ICOORD end_pt; //end of vector + INT32 vertex_index; //boundary vertices + + for (vertex_index = 0; vertex_index < entry_count; vertex_index++) { + entries[vertex_index].start = ICOORD (entries[vertex_index].start.x (), + ysize - 1 - + entries[vertex_index].start.y ()); + entries[vertex_index].end = + ICOORD (entries[vertex_index].end.x (), + ysize - 1 - entries[vertex_index].end.y ()); + vec_box = BOX (entries[vertex_index].start, entries[vertex_index].end); + block_box += vec_box; //find total bounds + } + + for (vertex_index = 0; vertex_index < entry_count + && (entries[vertex_index].start.y () != block_box.bottom () + || entries[vertex_index].end.y () != block_box.bottom ()); + vertex_index++); + ASSERT_HOST (vertex_index < entry_count); + prev_pt = entries[vertex_index].start; + end_pt = entries[vertex_index].end; + do { + for (vertex_index = 0; vertex_index < entry_count + && entries[vertex_index].start != end_pt; vertex_index++); + //found start of vertical + ASSERT_HOST (vertex_index < entry_count); + box_point = entries[vertex_index].start; + if (box_point.x () <= prev_pt.x ()) + shift_vec = ICOORD (-BLOCK_EXPANSION, -BLOCK_EXPANSION); + else + shift_vec = ICOORD (-BLOCK_EXPANSION, BLOCK_EXPANSION); + left_it->add_to_end (new ICOORDELT (box_point + shift_vec)); + prev_pt = box_point; + for (vertex_index = 0; vertex_index < entry_count + && entries[vertex_index].start != end_pt; vertex_index++); + //found horizontal + ASSERT_HOST (vertex_index < entry_count); + end_pt = entries[vertex_index].end; + } + while (end_pt.y () < block_box.top ()); + shift_vec = ICOORD (-BLOCK_EXPANSION, BLOCK_EXPANSION); + left_it->add_to_end (new ICOORDELT (end_pt + shift_vec)); + + for (vertex_index = 0; vertex_index < entry_count + && (entries[vertex_index].start.y () != block_box.top () + || entries[vertex_index].end.y () != block_box.top ()); + vertex_index++); + ASSERT_HOST (vertex_index < entry_count); + prev_pt = entries[vertex_index].start; + end_pt = entries[vertex_index].end; + do { + for (vertex_index = 0; vertex_index < entry_count + && entries[vertex_index].start != end_pt; vertex_index++); + //found start of vertical + ASSERT_HOST (vertex_index < entry_count); + box_point = entries[vertex_index].start; + if (box_point.x () < prev_pt.x ()) + shift_vec = ICOORD (BLOCK_EXPANSION, -BLOCK_EXPANSION); + else + shift_vec = ICOORD (BLOCK_EXPANSION, BLOCK_EXPANSION); + right_it->add_before_then_move (new ICOORDELT (box_point + shift_vec)); + prev_pt = box_point; + for (vertex_index = 0; vertex_index < entry_count + && entries[vertex_index].start != end_pt; vertex_index++); + //found horizontal + ASSERT_HOST (vertex_index < entry_count); + end_pt = entries[vertex_index].end; + } + while (end_pt.y () > block_box.bottom ()); + shift_vec = ICOORD (BLOCK_EXPANSION, -BLOCK_EXPANSION); + right_it->add_before_then_move (new ICOORDELT (end_pt + shift_vec)); + + shift_vec = ICOORD (BLOCK_EXPANSION, BLOCK_EXPANSION); + box_point = block_box.botleft () - shift_vec; + end_pt = block_box.topright () + shift_vec; + return BOX (box_point, end_pt); +} + + +/********************************************************************** + * read_unlv_file + * + * Read a whole unlv zone file to make a list of blocks. + **********************************************************************/ + +BOOL8 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 (), "r")) == NULL) { + return FALSE; //didn't read one + } + else { + while (fscanf (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 - 1 - y - height), (INT16) (x + width), (INT16) (ysize - 1 - y)); + //on end of list + block_it.add_to_end (block); + } + fclose(pdfp); + } + return true; +} diff --git a/ccstruct/blread.h b/ccstruct/blread.h new file mode 100644 index 0000000000..bbd03239cc --- /dev/null +++ b/ccstruct/blread.h @@ -0,0 +1,63 @@ +/********************************************************************** + * 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 "varable.h" +#include "ocrblock.h" + +BOOL8 read_pd_file( //print list of sides + STRING name, //basename of file + INT32 xsize, //image size + INT32 ysize, //image size + BLOCK_LIST *blocks //output list + ); +void get_pd_vertex( //get new vertex + FILE *pdfp, //file to read + INT32 xsize, //image size + INT32 ysize, //image size + BOX *box, //bounding box + INT32 &xcoord, //output coords + INT32 &ycoord); +BOOL8 read_hpd_file( //print list of sides + STRING name, //basename of file + INT32 xsize, //image size + INT32 ysize, //image size + BLOCK_LIST *blocks //output list + ); +void scan_hpd_blocks( //print list of sides + const char *name, //block label + PAGE_BLOCK_LIST *page_blocks, //head of full pag + INT32 &block_no, //no of blocks + BLOCK_IT *block_it //block iterator + ); +BOOL8 read_vec_file( //print list of sides + STRING name, //basename of file + INT32 xsize, //image size + INT32 ysize, //image size + BLOCK_LIST *blocks //output list + ); +BOOL8 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 + ); +#endif diff --git a/ccstruct/callcpp.cpp b/ccstruct/callcpp.cpp new file mode 100644 index 0000000000..af67d75c14 --- /dev/null +++ b/ccstruct/callcpp.cpp @@ -0,0 +1,270 @@ +/********************************************************************** + * File: callcpp.cpp + * 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. + * + **********************************************************************/ + +#include "mfcpch.h" +#include "errcode.h" +#ifdef __UNIX__ +#include +#include +#endif +#include +#include "memry.h" +#include "grphics.h" +#include "evnts.h" +#include "varable.h" +#include "callcpp.h" +#include "tprintf.h" +//#include "strace.h" +#include "host.h" + +//extern "C" { + +INT_VAR (tess_cp_mapping0, 0, "Mappings for class pruner distance"); +INT_VAR (tess_cp_mapping1, 1, "Mappings for class pruner distance"); +INT_VAR (tess_cp_mapping2, 2, "Mappings for class pruner distance"); +INT_VAR (tess_cp_mapping3, 3, "Mappings for class pruner distance"); +INT_VAR (stopper_numbers_on, 0, "Allow numbers to be acceptable choices"); +INT_VAR (config_pruner_enabled, 0, "Turn on config pruner"); +INT_VAR (feature_prune_percentile, 0, "Percent of features to use"); +INT_VAR (newcp_ratings_on, 0, "Use new class pruner normalisation"); +INT_VAR (record_matcher_output, 0, "Record detailed matcher info"); +INT_VAR (il1_adaption_test, 0, "Dont adapt to i/I at beginning of word"); +double_VAR (permuter_pending_threshold, 0.0, +"Worst conf for using pending dictionary"); +double_VAR (newcp_duff_rating, 0.30, "Worst rating for calling real matcher"); +double_VAR (newcp_prune_threshold, 1.2, "Ratio of best to prune"); +double_VAR (tessedit_cp_ratio, 0.0, "Ratio from best to prune"); +//Global matcher info from the class pruner. +INT32 cp_classes; +INT32 cp_bestindex; +INT32 cp_bestrating; +INT32 cp_bestconf; +char cp_chars[2]; +INT32 cp_ratings[2]; +INT32 cp_confs[2]; +INT32 cp_maps[4]; +//Global info to control writes of matcher info +INT32 blob_type; //write control +char blob_answer; //correct char +char *word_answer; //correct word +INT32 matcher_pass; //pass in chopper.c +INT32 bits_in_states; //no of bits in states + +#ifndef __UNIX__ +/********************************************************************** + * assert + * + * A version of assert for C on NT. + **********************************************************************/ + +void assert( //recog one owrd + int testing //assert fail if false + ) { + ASSERT_HOST(testing); +} +#endif + +void setup_cp_maps() { + cp_maps[0] = tess_cp_mapping0; + cp_maps[1] = tess_cp_mapping1; + cp_maps[2] = tess_cp_mapping2; + cp_maps[3] = tess_cp_mapping3; +} + + +void trace_stack() { //Trace current stack +} + + +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); +} + + +char *c_alloc_string( //allocate string + INT32 count //no of chars required + ) { + return alloc_string (count); +} + + +void c_free_string( //free a string + char *string //string to free + ) { + free_string(string); +} + + +void *c_alloc_struct( //allocate memory + INT32 count, //no of chars required + const char *name //class name + ) { + return alloc_struct (count, name); +} + + +void c_free_struct( //free a structure + void *deadstruct, //structure to free + INT32 count, //no of bytes + const char *name //class name + ) { + free_struct(deadstruct, count, name); +} + + +void *c_alloc_mem_p( //allocate permanent space + INT32 count //block size to allocate + ) { + return alloc_mem_p (count); +} + + +void *c_alloc_mem( //get some memory + INT32 count //no of bytes to get + ) { + return alloc_mem (count); +} + + +void c_free_mem( //free mem from alloc_mem + void *oldchunk //chunk to free + ) { + free_mem(oldchunk); +} + + +void c_check_mem( //check consistency + const char *string, //context message + INT8 level //level of check + ) { + check_mem(string, level); +} + +#ifndef GRAPHICS_DISABLED +void *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 create_window (name, SCROLLINGWIN, xpos, ypos, xsize, ysize, + xmin, xmax, ymin, ymax, TRUE, FALSE, FALSE, TRUE); +} + + +void c_line_color_index( /*set color */ + void *win, + C_COL index) { + WINDOW window = (WINDOW) win; + + // ASSERT_HOST(index>=0 && index<=48); + if (index < 0 || index > 48) + index = (C_COL) 1; + window->Line_color_index ((COLOUR) index); +} + + +void c_move( /*move pen */ + void *win, + double x, + double y) { + WINDOW window = (WINDOW) win; + + window->Move2d (x, y); +} + + +void c_draw( /*move pen */ + void *win, + double x, + double y) { + WINDOW window = (WINDOW) win; + + window->Draw2d (x, y); +} + + +void c_make_current( /*move pen */ + void *win) { + WINDOW window = (WINDOW) win; + + window->Make_picture_current (); +} + + +void c_clear_window( /*move pen */ + void *win) { + WINDOW window = (WINDOW) win; + + window->Clear_view_surface (); +} + + +char window_wait( /*move pen */ + void *win) { + WINDOW window = (WINDOW) win; + GRAPHICS_EVENT event; + + await_event(window, TRUE, ANY_EVENT, &event); + if (event.type == KEYPRESS_EVENT) + return event.key; + else + return '\0'; +} +#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; +} + + +//}; diff --git a/ccstruct/coutln.cpp b/ccstruct/coutln.cpp new file mode 100644 index 0000000000..6579eeb58e --- /dev/null +++ b/ccstruct/coutln.cpp @@ -0,0 +1,604 @@ +/********************************************************************** + * 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 "mfcpch.h" +#include +#ifdef __UNIX__ +#include +#endif +#include "coutln.h" + +ELISTIZE_S (C_OUTLINE) +ICOORD C_OUTLINE::step_coords[4] = { + ICOORD (-1, 0), ICOORD (0, -1), ICOORD (1, 0), ICOORD (0, 1) +}; + +/********************************************************************** + * C_OUTLINE::C_OUTLINE + * + * Constructor to build a C_OUTLINE from a CRACKEDGE LOOP. + **********************************************************************/ + +C_OUTLINE::C_OUTLINE ( +//constructor +CRACKEDGE * startpt, //outline to convert +ICOORD bot_left, //bounding box +ICOORD top_right, INT16 length //length of loop +):box (bot_left, top_right), start (startpt->pos) { + INT16 stepindex; //index to step + CRACKEDGE *edgept; //current point + + stepcount = length; //no of steps + //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; + } +} + + +/********************************************************************** + * 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) { + INT8 dirdiff; //direction difference + DIR128 prevdir; //previous direction + DIR128 dir; //current direction + DIR128 lastdir; //dir of last step + BOX new_box; //easy bounding + INT16 stepindex; //index to step + INT16 srcindex; //source steps + ICOORD pos; //current position + + pos = startpt; + stepcount = length; //no of steps + //get memory + steps = (UINT8 *) alloc_mem (step_mem()); + memset(steps, 0, step_mem()); + + lastdir = new_steps[length - 1]; + prevdir = lastdir; + for (stepindex = 0, srcindex = 0; srcindex < length; + stepindex++, srcindex++) { + new_box = BOX (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); +} + +/********************************************************************** + * C_OUTLINE::C_OUTLINE + * + * Constructor to build a C_OUTLINE from a rotation of a C_OUTLINE. + **********************************************************************/ + +C_OUTLINE::C_OUTLINE( //constructor + C_OUTLINE *srcline, //outline to + FCOORD rotation //rotate + ) { + BOX 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; + //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 = BOX (start, start); + destindex = 0; + for (stepindex = 0; stepindex < srcline->stepcount; stepindex++) { + pos += srcline->step (stepindex); + destpos = pos; + destpos.rotate (rotation); + if (destpos.x () != prevpos.x () || destpos.y () != prevpos.y ()) { + dir = DIR128 (FCOORD (destpos - prevpos)); + dir += 64; //turn to step style + new_step = dir.get_dir (); + if (new_step & 31) { + set_step(destindex++, dir + round1); + if (destindex < 2 + || (dirdiff = + step_dir (destindex - 1) - step_dir (destindex - 2)) != + -64 && dirdiff != 64) + set_step(destindex++, dir + round2); + else { + set_step(destindex - 1, dir + round2); + set_step(destindex++, dir + round1); + } + } + else { + set_step(destindex++, dir); + if (destindex >= 2 + && + ((dirdiff = + step_dir (destindex - 1) - step_dir (destindex - 2)) == + -64 || dirdiff == 64)) + destindex -= 2; // Forget u turn + } + prevpos = destpos; + new_box = BOX (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; + } + stepcount = destindex; + destpos = start; + for (stepindex = 0; stepindex < stepcount; stepindex++) { + destpos += step (stepindex); + } + ASSERT_HOST (destpos.x () == start.x () && destpos.y () == start.y ()); +} + + +/********************************************************************** + * C_OUTLINE::area + * + * Compute the area of the outline. + **********************************************************************/ + +INT32 C_OUTLINE::area() { //winding number + 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 + C_OUTLINE_IT it = child (); + + 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; +} + + +/********************************************************************** + * C_OUTLINE::outer_area + * + * Compute the area of the outline. + **********************************************************************/ + +INT32 C_OUTLINE::outer_area() { //winding number + 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 (); + 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; +} + + +/********************************************************************** + * C_OUTLINE::count_transitions + * + * Compute the number of x and y maxes and mins in the outline. + **********************************************************************/ + +INT32 C_OUTLINE::count_transitions( //winding number + INT32 threshold //on size + ) { + 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; +} + + +/********************************************************************** + * C_OUTLINE::operator< + * + * Return TRUE if the left operand is inside the right one. + **********************************************************************/ + +BOOL8 +C_OUTLINE::operator< ( //winding number +const C_OUTLINE & other //other outline +) 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 + + 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; +} + + +/********************************************************************** + * C_OUTLINE::winding_number + * + * Return the winding number of the outline around the given point. + **********************************************************************/ + +INT16 C_OUTLINE::winding_number( //winding number + ICOORD point //point to wind around + ) 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 + + 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 +} + + +/********************************************************************** + * 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); + } +} + + +/********************************************************************** + * C_OUTLINE::move + * + * Move C_OUTLINE by vector + **********************************************************************/ + +void C_OUTLINE::move( // reposition OUTLINE + const ICOORD vec // by vector + ) { + 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 +} + + +/********************************************************************** + * C_OUTLINE::plot + * + * Draw the outline in the given colour. + **********************************************************************/ + +#ifndef GRAPHICS_DISABLED +void C_OUTLINE::plot( //draw it + WINDOW window, //window to draw in + COLOUR colour //colour to draw in + ) const { + INT16 stepindex; //index to cstep + ICOORD pos; //current position + DIR128 stepdir; //direction of step + DIR128 oldstepdir; //previous stepdir + + pos = start; //current position + line_color_index(window, colour); + move2d (window, pos.x (), pos.y ()); + stepindex = 0; + stepdir = step_dir (0); //get direction + while (stepindex < stepcount) { + do { + pos += step (stepindex); //step to next + stepindex++; //count steps + oldstepdir = stepdir; + //new direction + stepdir = step_dir (stepindex); + } + while (stepindex < stepcount + && oldstepdir.get_dir () == stepdir.get_dir ()); + //merge straight lines + draw2d (window, pos.x (), pos.y ()); + } +} +#endif + + +/********************************************************************** + * C_OUTLINE::operator= + * + * Assignment - deep copy data + **********************************************************************/ + + //assignment +C_OUTLINE & C_OUTLINE::operator= ( +const C_OUTLINE & source //from this +) { + 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); + return *this; +} diff --git a/ccstruct/coutln.h b/ccstruct/coutln.h new file mode 100644 index 0000000000..1f35d4aa51 --- /dev/null +++ b/ccstruct/coutln.h @@ -0,0 +1,176 @@ +/********************************************************************** + * 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 "grphics.h" +#include "crakedge.h" +#include "mod128.h" +#include "bits16.h" +#include "rect.h" +#include "blckerr.h" + +#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 +}; + +class DLLSYM C_OUTLINE; //forward declaration + +ELISTIZEH_S (C_OUTLINE) +class DLLSYM C_OUTLINE:public ELIST_LINK +{ + public: + C_OUTLINE() { //empty constructor + steps = 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 + ~C_OUTLINE () { //destructor + if (steps != NULL) + free_mem(steps); + steps = NULL; + } + + 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 BOX &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 + } + + //get start position + const ICOORD &start_pos() const { + return start; + } + INT32 pathlength() const { //get path length + return stepcount; + } + // Return step at a given index as a DIR128. + DIR128 step_dir(INT16 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(INT16 index) const { //index of step + return step_coords[(steps[index/4] >> (index%4 * 2)) & STEP_MASK]; + } + + INT32 area(); //return area + INT32 outer_area(); //return area + 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 + + void plot( //draw one + WINDOW window, //window to draw in + COLOUR colour) const; //colour to draw it + + void prep_serialise() { //set ptrs to counts + children.prep_serialise (); + } + + void dump( //write external bits + FILE *f) { + //stepcount = # bytes + serialise_bytes (f, (void *) steps, step_mem()); + children.dump (f); + } + + void de_dump( //read external bits + FILE *f) { + steps = (UINT8 *) de_serialise_bytes (f, step_mem()); + children.de_dump (f); + } + + //assignment + make_serialise (C_OUTLINE) C_OUTLINE & operator= ( + const C_OUTLINE & source); //from this + + private: + int step_mem() const { return (stepcount+3) / 4; } + + BOX box; //boudning box + ICOORD start; //start coord + UINT8 *steps; //step array + INT16 stepcount; //no of steps + BITS16 flags; //flags about outline + C_OUTLINE_LIST children; //child elements + static ICOORD step_coords[4]; +}; +#endif diff --git a/ccstruct/crakedge.h b/ccstruct/crakedge.h new file mode 100644 index 0000000000..5dd6917c54 --- /dev/null +++ b/ccstruct/crakedge.h @@ -0,0 +1,39 @@ +/********************************************************************** + * 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: + ICOORD pos; /*position of crack */ + INT8 stepx; //edge step + INT8 stepy; + INT8 stepdir; //chaincode + CRACKEDGE *prev; /*previous point */ + CRACKEDGE *next; /*next point */ + + NEWDELETE2 (CRACKEDGE) CRACKEDGE () { + } //empty constructor +}; +#endif diff --git a/ccstruct/genblob.cpp b/ccstruct/genblob.cpp new file mode 100644 index 0000000000..bd461b95e2 --- /dev/null +++ b/ccstruct/genblob.cpp @@ -0,0 +1,133 @@ +/********************************************************************** + * 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 "mfcpch.h" +#include "stepblob.h" +#include "polyblob.h" +#include "genblob.h" + +/********************************************************************** + * blob_comparator() + * + * Blob comparator used to sort a blob list so that blobs are in increasing + * order of left edge. + **********************************************************************/ + +int blob_comparator( //sort blobs + const void *blob1p, //ptr to ptr to blob1 + const void *blob2p //ptr to ptr to blob2 + ) { + PBLOB *blob1 = *(PBLOB **) blob1p; + PBLOB *blob2 = *(PBLOB **) blob2p; + + return blob1->bounding_box ().left () - blob2->bounding_box ().left (); +} + + +/********************************************************************** + * 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 (); +} + + +/********************************************************************** + * gblob_bounding_box() + * + * Return the bounding box of a generic blob. + **********************************************************************/ + +BOX gblob_bounding_box( //Get bounding box + PBLOB *blob, //generic blob + BOOL8 polygonal //is blob polygonal? + ) { + if (polygonal) + return blob->bounding_box (); + else + return ((C_BLOB *) blob)->bounding_box (); +} + + +/********************************************************************** + * gblob_sort_list() + * + * Sort a generic blob list into order of bounding box left edge + **********************************************************************/ + +void gblob_sort_list( //Sort a gblob list + PBLOB_LIST *blob_list, //generic blob list + BOOL8 polygonal //is list polygonal? + ) { + PBLOB_IT b_it; + C_BLOB_IT c_it; + + if (polygonal) { + b_it.set_to_list (blob_list); + b_it.sort (blob_comparator); + } + else { + c_it.set_to_list ((C_BLOB_LIST *) blob_list); + c_it.sort (c_blob_comparator); + } +} + + +/********************************************************************** + * gblob_out_list() + * + * Return the generic outline list of a generic blob. + **********************************************************************/ + +OUTLINE_LIST *gblob_out_list( //Get outline list + PBLOB *blob, //generic blob + BOOL8 polygonal //is blob polygonal? + ) { + if (polygonal) + return blob->out_list (); + else + return (OUTLINE_LIST *) ((C_BLOB *) blob)->out_list (); +} + + +/********************************************************************** + * goutline_bounding_box() + * + * Return the bounding box of a generic outline. + **********************************************************************/ + +BOX goutline_bounding_box( //Get bounding box + OUTLINE *outline, //generic outline + BOOL8 polygonal //is outline polygonal? + ) { + if (polygonal) + return outline->bounding_box (); + else + return ((C_OUTLINE *) outline)->bounding_box (); +} diff --git a/ccstruct/genblob.h b/ccstruct/genblob.h new file mode 100644 index 0000000000..eaf17ef92f --- /dev/null +++ b/ccstruct/genblob.h @@ -0,0 +1,52 @@ +/********************************************************************** + * 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 + +#include "polyblob.h" +#include "hosthplb.h" +#include "rect.h" +#include "notdll.h" + +int blob_comparator( //sort blobs + const void *blob1p, //ptr to ptr to blob1 + const void *blob2p //ptr to ptr to blob2 + ); +int c_blob_comparator( //sort blobs + const void *blob1p, //ptr to ptr to blob1 + const void *blob2p //ptr to ptr to blob2 + ); +BOX gblob_bounding_box( //Get bounding box + PBLOB *blob, //generic blob + BOOL8 polygonal //is blob polygonal? + ); +void gblob_sort_list( //Sort a gblob list + PBLOB_LIST *blob_list, //generic blob list + BOOL8 polygonal //is list polygonal? + ); +OUTLINE_LIST *gblob_out_list( //Get outline list + PBLOB *blob, //generic blob + BOOL8 polygonal //is blob polygonal? + ); +BOX goutline_bounding_box( //Get bounding box + OUTLINE *outline, //generic outline + BOOL8 polygonal //is outline polygonal? + ); +#endif diff --git a/ccstruct/hpddef.h b/ccstruct/hpddef.h new file mode 100644 index 0000000000..bb90c4283c --- /dev/null +++ b/ccstruct/hpddef.h @@ -0,0 +1,39 @@ +/********************************************************************** + * File: hpddef.h + * Description: Defines for dll symbols for handpd.dll. + * Author: Ray Smith + * Created: Tue Apr 30 17:15:01 MDT 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. + * + **********************************************************************/ + +//This file does NOT use the usual single inclusion code as it +//is necessary to allow it to be executed every time it is included. +//#ifndef HPDDEF_H +//#define HPDDEF_H + +#undef DLLSYM +#ifndef __IPEDLL +# define DLLSYM +#else +# ifdef __BUILDING_HANDPD__ +# define DLLSYM DLLEXPORT +# else +# define DLLSYM DLLIMPORT +# endif +#endif +#if defined(__CFM68K__) && !defined(__USING_STATIC_LIBS__) +# pragma import on +#endif + +//#endif diff --git a/ccstruct/hpdsizes.h b/ccstruct/hpdsizes.h new file mode 100644 index 0000000000..2670e21b07 --- /dev/null +++ b/ccstruct/hpdsizes.h @@ -0,0 +1,8 @@ +#ifndef HPDSIZES_H +#define HPDSIZES_H + +#define NUM_TEXT_ATTR 10 +#define NUM_BLOCK_ATTR 7 +#define MAXLENGTH 128 +#define NUM_BACKGROUNDS 8 +#endif diff --git a/ccstruct/ipoints.h b/ccstruct/ipoints.h new file mode 100644 index 0000000000..fe62d12978 --- /dev/null +++ b/ccstruct/ipoints.h @@ -0,0 +1,479 @@ +/********************************************************************** + * 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; +} +#endif diff --git a/ccstruct/labls.cpp b/ccstruct/labls.cpp new file mode 100644 index 0000000000..7e9d48691d --- /dev/null +++ b/ccstruct/labls.cpp @@ -0,0 +1,188 @@ +/********************************************************************** + * File: labls.c (Formerly labels.c) + * Description: Attribute definition tables + * 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 "mfcpch.h" +#include "hpdsizes.h" +#include "labls.h" + +/****************************************************************************** + * TEXT REGIONS + *****************************************************************************/ +DLLSYM INT32 tn[NUM_TEXT_ATTR] = { + 3, //T_HORIZONTAL + 4, //T_TEXT + 2, //T_SERIF + 2, //T_PROPORTIONAL + 2, //T_NORMAL + 2, //T_UPRIGHT + 2, //T_SOLID + 3, //T_BLACK + 2, //T_NOTUNDER + 2, //T_NOTDROP +}; + +DLLSYM char tlabel[NUM_TEXT_ATTR][4][MAXLENGTH] = { { + //T_HORIZONTAL + "Horizontal", + "Vertical", + "Skew", + "" + }, + { //T_TEXT + "Text", + "Table", + "Form", + "Mixed" + }, + { //T_SERIF + "Serif", + "Sans-serif", + "", + "" + }, + { //T_PROPORTIONAL + "Proportional", + "Fixed pitch", + "", + "" + }, + { //T_NORMAL + "Normal", + "Bold", + "", + "" + }, + { //T_UPRIGHT + "Upright", + "Italic", + "", + "" + }, + { //T_SOLID + "Solid", + "Outline", + "", + "" + }, + { //T_BLACK + "Black", + "White", + "Coloured", + "" + }, + { //T_NOTUNDER + "Not underlined", + "Underlined", + "", + "" + }, + { //T_NOTDROP + "Not drop caps", + "Drop Caps", + "", + "" + } +}; + +DLLSYM INT32 bn[NUM_BLOCK_ATTR] = { + 4, //G_MONOCHROME + 2, //I_MONOCHROME + 2, //I_SMOOTH + 3, //R_SINGLE + 3, //R_BLACK + 3, //S_BLACK + 2 //W_TEXT +}; + +DLLSYM INT32 tvar[NUM_TEXT_ATTR]; +DLLSYM INT32 bvar[NUM_BLOCK_ATTR]; +DLLSYM char blabel[NUM_BLOCK_ATTR][4][MAXLENGTH] = { { + //G_MONOCHROME + + /**************************************************************************** + * GRAPHICS + ***************************************************************************/ + "Monochrome ", + "Two colour ", + "Spot colour", + "Multicolour" + }, + + /**************************************************************************** + * IMAGE + ***************************************************************************/ + { //I_MONOCHROME + "Monochrome ", + "Colour ", + "", + "" + }, + { //I_SMOOTH + "Smooth ", + "Grainy ", + "", + "" + }, + + /**************************************************************************** + * RULES + ***************************************************************************/ + { //R_SINGLE + "Single ", + "Double ", + "Multiple", + "" + }, + { //R_BLACK + "Black ", + "White ", + "Coloured", + "" + }, + + /**************************************************************************** + * SCRIBBLE + ***************************************************************************/ + { //S_BLACK + "Black ", + "White ", + "Coloured", + "" + }, + /**************************************************************************** + * WEIRD + ***************************************************************************/ + { //W_TEXT + "No text ", + "Contains text", + "", + "" + } +}; + +DLLSYM char backlabel[NUM_BACKGROUNDS][MAXLENGTH] = { + "White", //B_WHITE + "Black", //B_BLACK + "Coloured", //B_COLOURED + "Textured", //B_TEXTURED + "Patterned", //B_PATTERNED + "Gradient fill", //B_GRADIENTFILL + "Image", //B_IMAGE + "Text" //B_TEXT +}; diff --git a/ccstruct/labls.h b/ccstruct/labls.h new file mode 100644 index 0000000000..d55b9d3e0b --- /dev/null +++ b/ccstruct/labls.h @@ -0,0 +1,38 @@ +/********************************************************************** + * File: labls.h (Formerly labels.h) + * Description: Attribute definition tables + * 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 LABLS_H +#define LABLS_H + +#include "host.h" +#include "hpdsizes.h" + +#include "hpddef.h" //must be last (handpd.dll) + +extern DLLSYM INT32 tn[NUM_TEXT_ATTR]; + +extern DLLSYM char tlabel[NUM_TEXT_ATTR][4][MAXLENGTH]; + +extern DLLSYM INT32 bn[NUM_BLOCK_ATTR]; + +extern DLLSYM INT32 tvar[NUM_TEXT_ATTR]; +extern DLLSYM INT32 bvar[NUM_BLOCK_ATTR]; +extern DLLSYM char blabel[NUM_BLOCK_ATTR][4][MAXLENGTH]; + +extern DLLSYM char backlabel[NUM_BACKGROUNDS][MAXLENGTH]; +#endif diff --git a/ccstruct/linlsq.cpp b/ccstruct/linlsq.cpp new file mode 100644 index 0000000000..ad620909bd --- /dev/null +++ b/ccstruct/linlsq.cpp @@ -0,0 +1,249 @@ +/********************************************************************** + * 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 "mfcpch.h" +#include +#include +#include "errcode.h" +#include "linlsq.h" + +#ifndef __UNIX__ +#define M_PI 3.14159265359 +#endif + +const ERRCODE EMPTY_LLSQ = "Can't delete from an empty LLSQ"; + +#define EXTERN + +EXTERN double_VAR (pdlsq_posdir_ratio, 4e-6, "Mult of dir to cf pos"); +EXTERN double_VAR (pdlsq_threshold_angleavg, 0.1666666, +"Frac of pi for simple fit"); + +/********************************************************************** + * LLSQ::clear + * + * Function to initialize a LLSQ. + **********************************************************************/ + +void LLSQ::clear() { //initialize + n = 0; //no elements + sigx = 0; //update accumulators + sigy = 0; + sigxx = 0; + sigxy = 0; + sigyy = 0; +} + + +/********************************************************************** + * LLSQ::add + * + * Add an element to the accumulator. + **********************************************************************/ + +void LLSQ::add( //add an element + double x, //xcoord + double y //ycoord + ) { + n++; //count elements + sigx += x; //update accumulators + sigy += y; + sigxx += x * x; + sigxy += x * y; + sigyy += y * y; +} + + +/********************************************************************** + * LLSQ::remove + * + * Delete an element from the acculuator. + **********************************************************************/ + +void LLSQ::remove( //delete an element + double x, //xcoord + double y //ycoord + ) { + if (n <= 0) + //illegal + EMPTY_LLSQ.error ("LLSQ::remove", ABORT, NULL); + n--; //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() { //get gradient + if (n > 1) + return (sigxy - sigx * sigy / n) / (sigxx - sigx * sigx / n); + else + return 0; //too little +} + + +/********************************************************************** + * LLSQ::c + * + * Return the constant of the line fit. + **********************************************************************/ + +double LLSQ::c( //get constant + double m //gradient to fit with + ) { + if (n > 0) + return (sigy - m * sigx) / n; + else + return 0; //too little +} + + +/********************************************************************** + * LLSQ::rms + * + * Return the rms error of the fit. + **********************************************************************/ + +double LLSQ::rms( //get error + double m, //gradient to fit with + double c //constant to fit with + ) { + double error; //total error + + if (n > 0) { + error = + sigyy + m * (m * sigxx + 2 * (c * sigx - sigxy)) + c * (n * c - + 2 * sigy); + if (error >= 0) + error = sqrt (error / n); //sqrt of mean + else + error = 0; + } + else + error = 0; //too little + return error; +} + + +/********************************************************************** + * LLSQ::spearman + * + * Return the spearman correlation coefficient. + **********************************************************************/ + +double LLSQ::spearman() { //get error + double error; //total error + + if (n > 1) { + error = (sigxx - sigx * sigx / n) * (sigyy - sigy * sigy / n); + if (error > 0) { + error = (sigxy - sigx * sigy / n) / sqrt (error); + } + else + error = 1; + } + else + error = 1; //too little + return error; +} + + +/********************************************************************** + * PDLSQ::fit + * + * Return all the parameters of the fit to pos/dir. + * The return value is the rms error. + **********************************************************************/ + +float PDLSQ::fit( //get fit + DIR128 &ang, //output angle + float &sin_ang, //r,theta parameterisation + float &cos_ang, + float &r) { + double a, b; //itermediates + double angle; //resulting angle + double avg_angle; //simple average + double error; //total error + double sinx, cosx; //return values + + if (pos.n > 0) { + a = pos.sigxy - pos.sigx * pos.sigy / pos.n + + pdlsq_posdir_ratio * dir.sigxy; + b = + pos.sigxx - pos.sigyy + (pos.sigy * pos.sigy - + pos.sigx * pos.sigx) / pos.n + + pdlsq_posdir_ratio * (dir.sigxx - dir.sigyy); + if (dir.sigy != 0 || dir.sigx != 0) + avg_angle = atan2 (dir.sigy, dir.sigx); + else + avg_angle = 0; + if ((a != 0 || b != 0) && pos.n > 1) + angle = atan2 (2 * a, b) / 2; + else + angle = avg_angle; + error = avg_angle - angle; + if (error > M_PI / 2) { + error -= M_PI; + angle += M_PI; + } + if (error < -M_PI / 2) { + error += M_PI; + angle -= M_PI; + } + if (error > M_PI * pdlsq_threshold_angleavg + || error < -M_PI * pdlsq_threshold_angleavg) + angle = avg_angle; //go simple + //convert direction + ang = (INT16) (angle * MODULUS / (2 * M_PI)); + sinx = sin (angle); + cosx = cos (angle); + r = (sinx * pos.sigx - cosx * pos.sigy) / pos.n; + // tprintf("x=%g, y=%g, xx=%g, xy=%g, yy=%g, a=%g, b=%g, ang=%g, r=%g\n", + // pos.sigx,pos.sigy,pos.sigxx,pos.sigxy,pos.sigyy, + // a,b,angle,r); + error = dir.sigxx * sinx * sinx + dir.sigyy * cosx * cosx + - 2 * dir.sigxy * sinx * cosx; + error *= pdlsq_posdir_ratio; + error += sinx * sinx * pos.sigxx + cosx * cosx * pos.sigyy + - 2 * sinx * cosx * pos.sigxy + - 2 * r * (sinx * pos.sigx - cosx * pos.sigy) + r * r * pos.n; + if (error >= 0) + //rms value + error = sqrt (error / pos.n); + else + error = 0; //-0 + sin_ang = sinx; + cos_ang = cosx; + } + else { + sin_ang = 0.0f; + cos_ang = 0.0f; + ang = 0; + error = 0; //too little + } + return error; +} diff --git a/ccstruct/linlsq.h b/ccstruct/linlsq.h new file mode 100644 index 0000000000..3360935f14 --- /dev/null +++ b/ccstruct/linlsq.h @@ -0,0 +1,102 @@ +/********************************************************************** + * 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 LINLSQ_H +#define LINLSQ_H + +#include "points.h" +#include "mod128.h" +#include "varable.h" + +class LLSQ +{ + friend class PDLSQ; //pos & direction + + public: + LLSQ() { //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; + } + + double m(); //get gradient + double c( //get constant + double m); //gradient + double rms( //get error + double m, //gradient + double c); //constant + double spearman(); //get error + + private: + INT32 n; //no of elements + 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 +}; + +class PDLSQ +{ + public: + PDLSQ() { //constructor + clear(); //set to zeros + } + void clear() { //initialize + pos.clear (); //clear both + dir.clear (); + } + + void add( //add element + const ICOORD &addpos, //position of pt + const ICOORD &adddir) { //dir of pt + pos.add (addpos.x (), addpos.y ()); + dir.add (adddir.x (), adddir.y ()); + } + void remove( //remove element + const ICOORD &removepos, //position of pt + const ICOORD &removedir) { //dir of pt + pos.remove (removepos.x (), removepos.y ()); + dir.remove (removedir.x (), removedir.y ()); + } + INT32 count() { //no of elements + return pos.count (); + } + + float fit( //get fit parameters + DIR128 &ang, //output angle + float &sin_ang, //output components + float &cos_ang, + float &r); + + private: + LLSQ pos; //position + LLSQ dir; //directions +}; +extern double_VAR_H (pdlsq_posdir_ratio, 0.4e-6, "Mult of dir to cf pos"); +#endif diff --git a/ccstruct/lmedsq.cpp b/ccstruct/lmedsq.cpp new file mode 100644 index 0000000000..cc62fe17b9 --- /dev/null +++ b/ccstruct/lmedsq.cpp @@ -0,0 +1,453 @@ +/********************************************************************** + * File: lmedsq.cpp (Formerly lms.c) + * Description: Code for the LMS class. + * Author: Ray Smith + * Created: Fri Aug 7 09:30:53 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 "mfcpch.h" +#include +#include "statistc.h" +#include "memry.h" +#include "statistc.h" +#include "lmedsq.h" + +#define EXTERN + +EXTERN INT_VAR (lms_line_trials, 12, "Number of linew fits to do"); +#define SEED1 0x1234 //default seeds +#define SEED2 0x5678 +#define SEED3 0x9abc +#define LMS_MAX_FAILURES 3 + +#ifndef __UNIX__ +UINT32 nrand48( //get random number + UINT16 *seeds //seeds to use + ) { + static UINT32 seed = 0; //only seed + + if (seed == 0) { + seed = seeds[0] ^ (seeds[1] << 8) ^ (seeds[2] << 16); + srand(seed); + } + //make 32 bit one + return rand () | (rand () << 16); +} +#endif + +/********************************************************************** + * LMS::LMS + * + * Construct a LMS class, given the max no of samples to be given + **********************************************************************/ + +LMS::LMS ( //constructor +INT32 size //samplesize +):samplesize (size) { + samplecount = 0; + a = 0; + m = 0.0f; + c = 0.0f; + samples = (FCOORD *) alloc_mem (size * sizeof (FCOORD)); + errors = (float *) alloc_mem (size * sizeof (float)); + line_error = 0.0f; + fitted = FALSE; +} + + +/********************************************************************** + * LMS::~LMS + * + * Destruct a LMS class. + **********************************************************************/ + +LMS::~LMS ( //constructor +) { + free_mem(samples); + free_mem(errors); +} + + +/********************************************************************** + * LMS::clear + * + * Clear samples from array. + **********************************************************************/ + +void LMS::clear() { //clear sample + samplecount = 0; + fitted = FALSE; +} + + +/********************************************************************** + * LMS::add + * + * Add another sample. More than the constructed number will be ignored. + **********************************************************************/ + +void LMS::add( //add sample + FCOORD sample //sample coords + ) { + if (samplecount < samplesize) + //save it + samples[samplecount++] = sample; + fitted = FALSE; +} + + +/********************************************************************** + * LMS::fit + * + * Fit a line to the given sample points. + **********************************************************************/ + +void LMS::fit( //fit sample + float &out_m, //output line + float &out_c) { + INT32 index; //of median + INT32 trials; //no of medians + float test_m, test_c; //candidate line + float test_error; //error of test line + + switch (samplecount) { + case 0: + m = 0.0f; //no info + c = 0.0f; + line_error = 0.0f; + break; + + case 1: + m = 0.0f; + c = samples[0].y (); //horiz thru pt + line_error = 0.0f; + break; + + case 2: + if (samples[0].x () != samples[1].x ()) { + m = (samples[1].y () - samples[0].y ()) + / (samples[1].x () - samples[0].x ()); + c = samples[0].y () - m * samples[0].x (); + } + else { + m = 0.0f; + c = (samples[0].y () + samples[1].y ()) / 2; + } + line_error = 0.0f; + break; + + default: + pick_line(m, c); //use pts at random + compute_errors(m, c); //from given line + index = choose_nth_item (samplecount / 2, errors, samplecount); + line_error = errors[index]; + for (trials = 1; trials < lms_line_trials; trials++) { + //random again + pick_line(test_m, test_c); + compute_errors(test_m, test_c); + index = choose_nth_item (samplecount / 2, errors, samplecount); + test_error = errors[index]; + if (test_error < line_error) { + //find least median + line_error = test_error; + m = test_m; + c = test_c; + } + } + } + fitted = TRUE; + out_m = m; + out_c = c; + a = 0; +} + + +/********************************************************************** + * LMS::fit_quadratic + * + * Fit a quadratic to the given sample points. + **********************************************************************/ + +void LMS::fit_quadratic( //fit sample + float outlier_threshold, //min outlier size + double &out_a, //x squared + float &out_b, //output line + float &out_c) { + INT32 trials; //no of medians + double test_a; + float test_b, test_c; //candidate line + float test_error; //error of test line + + if (samplecount < 3) { + out_a = 0; + fit(out_b, out_c); + return; + } + pick_quadratic(a, m, c); + line_error = compute_quadratic_errors (outlier_threshold, a, m, c); + for (trials = 1; trials < lms_line_trials * 2; trials++) { + pick_quadratic(test_a, test_b, test_c); + test_error = compute_quadratic_errors (outlier_threshold, + test_a, test_b, test_c); + if (test_error < line_error) { + line_error = test_error; //find least median + a = test_a; + m = test_b; + c = test_c; + } + } + fitted = TRUE; + out_a = a; + out_b = m; + out_c = c; +} + + +/********************************************************************** + * LMS::constrained_fit + * + * Fit a line to the given sample points. + * The line must have the given gradient. + **********************************************************************/ + +void LMS::constrained_fit( //fit sample + float fixed_m, //forced gradient + float &out_c) { + INT32 index; //of median + INT32 trials; //no of medians + float test_c; //candidate line + static UINT16 seeds[3] = { SEED1, SEED2, SEED3 }; + //for nrand + float test_error; //error of test line + + m = fixed_m; + switch (samplecount) { + case 0: + c = 0.0f; + line_error = 0.0f; + break; + + case 1: + //horiz thru pt + c = samples[0].y () - m * samples[0].x (); + line_error = 0.0f; + break; + + case 2: + c = (samples[0].y () + samples[1].y () + - m * (samples[0].x () + samples[1].x ())) / 2; + line_error = m * samples[0].x () + c - samples[0].y (); + line_error *= line_error; + break; + + default: + index = (INT32) nrand48 (seeds) % samplecount; + //compute line + c = samples[index].y () - m * samples[index].x (); + compute_errors(m, c); //from given line + index = choose_nth_item (samplecount / 2, errors, samplecount); + line_error = errors[index]; + for (trials = 1; trials < lms_line_trials; trials++) { + index = (INT32) nrand48 (seeds) % samplecount; + test_c = samples[index].y () - m * samples[index].x (); + //compute line + compute_errors(m, test_c); + index = choose_nth_item (samplecount / 2, errors, samplecount); + test_error = errors[index]; + if (test_error < line_error) { + //find least median + line_error = test_error; + c = test_c; + } + } + } + fitted = TRUE; + out_c = c; + a = 0; +} + + +/********************************************************************** + * LMS::pick_line + * + * Fit a line to a random pair of sample points. + **********************************************************************/ + +void LMS::pick_line( //fit sample + float &line_m, //output gradient + float &line_c) { + INT16 trial_count; //no of attempts + static UINT16 seeds[3] = { SEED1, SEED2, SEED3 }; + //for nrand + INT32 index1; //picked point + INT32 index2; //picked point + + trial_count = 0; + do { + index1 = (INT32) nrand48 (seeds) % samplecount; + index2 = (INT32) nrand48 (seeds) % samplecount; + line_m = samples[index2].x () - samples[index1].x (); + trial_count++; + } + while (line_m == 0 && trial_count < LMS_MAX_FAILURES); + if (line_m == 0) { + line_c = (samples[index2].y () + samples[index1].y ()) / 2; + } + else { + line_m = (samples[index2].y () - samples[index1].y ()) / line_m; + line_c = samples[index1].y () - samples[index1].x () * line_m; + } +} + + +/********************************************************************** + * LMS::pick_quadratic + * + * Fit a quadratic to a random triplet of sample points. + **********************************************************************/ + +void LMS::pick_quadratic( //fit sample + double &line_a, //x suaread + float &line_m, //output gradient + float &line_c) { + INT16 trial_count; //no of attempts + static UINT16 seeds[3] = { SEED1, SEED2, SEED3 }; + //for nrand + INT32 index1; //picked point + INT32 index2; //picked point + INT32 index3; + FCOORD x1x2; //vector + FCOORD x1x3; + FCOORD x3x2; + double bottom; //of a + + trial_count = 0; + do { + if (trial_count >= LMS_MAX_FAILURES - 1) { + index1 = 0; + index2 = samplecount / 2; + index3 = samplecount - 1; + } + else { + index1 = (INT32) nrand48 (seeds) % samplecount; + index2 = (INT32) nrand48 (seeds) % samplecount; + index3 = (INT32) nrand48 (seeds) % samplecount; + } + x1x2 = samples[index2] - samples[index1]; + x1x3 = samples[index3] - samples[index1]; + x3x2 = samples[index2] - samples[index3]; + bottom = x1x2.x () * x1x3.x () * x3x2.x (); + trial_count++; + } + while (bottom == 0 && trial_count < LMS_MAX_FAILURES); + if (bottom == 0) { + line_a = 0; + pick_line(line_m, line_c); + } + else { + line_a = x1x3 * x1x2 / bottom; + line_m = x1x2.y () - line_a * x1x2.x () + * (samples[index2].x () + samples[index1].x ()); + line_m /= x1x2.x (); + line_c = samples[index1].y () - samples[index1].x () + * (samples[index1].x () * line_a + line_m); + } +} + + +/********************************************************************** + * LMS::compute_errors + * + * Compute the squared error from all the points. + **********************************************************************/ + +void LMS::compute_errors( //fit sample + float line_m, //input gradient + float line_c) { + INT32 index; //picked point + + for (index = 0; index < samplecount; index++) { + errors[index] = + line_m * samples[index].x () + line_c - samples[index].y (); + errors[index] *= errors[index]; + } +} + + +/********************************************************************** + * LMS::compute_quadratic_errors + * + * Compute the squared error from all the points. + **********************************************************************/ + +float LMS::compute_quadratic_errors( //fit sample + float outlier_threshold, //min outlier + double line_a, + float line_m, //input gradient + float line_c) { + INT32 outlier_count; //total outliers + INT32 index; //picked point + INT32 error_count; //no in total + double total_error; //summed squares + + total_error = 0; + outlier_count = 0; + error_count = 0; + for (index = 0; index < samplecount; index++) { + errors[error_count] = line_c + samples[index].x () + * (line_m + samples[index].x () * line_a) - samples[index].y (); + errors[error_count] *= errors[error_count]; + if (errors[error_count] > outlier_threshold) { + outlier_count++; + errors[samplecount - outlier_count] = errors[error_count]; + } + else { + total_error += errors[error_count++]; + } + } + if (outlier_count * 3 < error_count) + return total_error / error_count; + else { + index = choose_nth_item (outlier_count / 2, + errors + samplecount - outlier_count, + outlier_count); + //median outlier + return errors[samplecount - outlier_count + index]; + } +} + + +/********************************************************************** + * LMS::plot + * + * Plot the fitted line of a LMS. + **********************************************************************/ + +#ifndef GRAPHICS_DISABLED +void LMS::plot( //plot fit + WINDOW win, //window + COLOUR colour //colour to draw in + ) { + if (fitted) { + line_color_index(win, colour); + move2d (win, samples[0].x (), + c + samples[0].x () * (m + samples[0].x () * a)); + draw2d (win, samples[samplecount - 1].x (), + c + samples[samplecount - 1].x () * (m + + samples[samplecount - + 1].x () * a)); + } +} +#endif diff --git a/ccstruct/lmedsq.h b/ccstruct/lmedsq.h new file mode 100644 index 0000000000..39294b0cc0 --- /dev/null +++ b/ccstruct/lmedsq.h @@ -0,0 +1,84 @@ +/********************************************************************** + * File: lmedsq.h (Formerly lms.h) + * Description: Code for the LMS class. + * Author: Ray Smith + * Created: Fri Aug 7 09:30:53 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 LMEDSQ_H +#define LMEDSQ_H + +#include "points.h" +#include "varable.h" +#include "grphics.h" +#include "notdll.h" + +class LMS +{ + public: + LMS( //constructor + INT32 size); //no of samples + ~LMS (); //destructor + void clear(); //clear samples + void add( //add sample + FCOORD sample); //sample coords + void fit( //generate fit + float &m, //output line + float &c); + void constrained_fit( //fixed gradient + float fixed_m, //forced gradient + float &out_c); //output line + void fit_quadratic( //easy quadratic + float outlier_threshold, //min outlier + double &a, //x squared + float &b, //x + float &c); //constant + void plot( //plot fit + WINDOW win, //window + COLOUR colour); //colour to draw in + float error() { //get error + return fitted ? line_error : -1; + } + + private: + + void pick_line( //random choice + float &m, //output line + float &c); + void pick_quadratic( //random choice + double &a, //output curve + float &b, + float &c); + void compute_errors( //find errors + float m, //from line + float c); + //find errors + float compute_quadratic_errors(float outlier_threshold, //min outlier + double a, //from curve + float m, + float c); + + BOOL8 fitted; //line parts valid + INT32 samplesize; //max samples + INT32 samplecount; //current sample size + FCOORD *samples; //array of samples + float *errors; //error distances + double a; //x squared + float m; //line gradient + float c; + float line_error; //error of fit +}; +extern INT_VAR_H (lms_line_trials, 12, "Number of linew fits to do"); +#endif diff --git a/ccstruct/mod128.cpp b/ccstruct/mod128.cpp new file mode 100644 index 0000000000..65c18c5cc6 --- /dev/null +++ b/ccstruct/mod128.cpp @@ -0,0 +1,100 @@ +/********************************************************************** + * 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 "mfcpch.h" //precompiled headers +#include "mod128.h" + +static 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 +}; + +static 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. + **********************************************************************/ + +ICOORD DIR128::vector() const { //convert to vector + return dirtab[dir]; //easy really +} diff --git a/ccstruct/mod128.h b/ccstruct/mod128.h new file mode 100644 index 0000000000..38937319ff --- /dev/null +++ b/ccstruct/mod128.h @@ -0,0 +1,85 @@ +/********************************************************************** + * 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 diff --git a/ccstruct/normalis.cpp b/ccstruct/normalis.cpp new file mode 100644 index 0000000000..d9fbdef07b --- /dev/null +++ b/ccstruct/normalis.cpp @@ -0,0 +1,176 @@ +/********************************************************************** + * 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 "mfcpch.h" +#include "werd.h" +#include "normalis.h" + +/********************************************************************** + * DENORM::binary_search_segment + * + * Find the segment to use for the given x. + **********************************************************************/ + +const DENORM_SEG *DENORM::binary_search_segment(float src_x) const { + int bottom, top, middle; //binary search + + bottom = 0; + top = segments; + do { + middle = (bottom + top) / 2; + if (segs[middle].xstart > src_x) + top = middle; + else + bottom = middle; + } + while (top - bottom > 1); + return &segs[bottom]; +} + +/********************************************************************** + * DENORM::scale_at_x + * + * Return scaling at a given (normalized) x coord. + **********************************************************************/ + +float DENORM::scale_at_x(float src_x) const { // In normalized coords. + if (segments != 0) { + const DENORM_SEG* seg = binary_search_segment(src_x); + if (seg->scale_factor > 0.0) + return seg->scale_factor; + } + return scale_factor; +} + +/********************************************************************** + * DENORM::yshift_at_x + * + * Return yshift at a given (normalized) x coord. + **********************************************************************/ + +float DENORM::yshift_at_x(float src_x) const { // In normalized coords. + if (segments != 0) { + const DENORM_SEG* seg = binary_search_segment(src_x); + if (seg->ycoord == -MAX_INT32) { + if (base_is_row) + return source_row->base_line(x(src_x)/scale_at_x(src_x) + x_centre); + else + return m * x(src_x) + c; + } else { + return seg->ycoord; + } + } + return source_row->base_line (x(src_x)/scale_at_x(src_x) + x_centre); +} + +/********************************************************************** + * DENORM::x + * + * Denormalise an x coordinate. + **********************************************************************/ + +float DENORM::x( //convert x coord + float src_x //coord to convert + ) const { + return src_x / scale_at_x(src_x) + x_centre; +} + + +/********************************************************************** + * DENORM::y + * + * Denormalise a y coordinate. + **********************************************************************/ + +float DENORM::y( //convert y coord + float src_y, //coord to convert + float src_centre //x location for base + ) const { + return (src_y - bln_baseline_offset) / scale_at_x(src_centre) + + yshift_at_x(src_centre); +} + + +DENORM::DENORM(float x, //from same pieces + float scaling, + double line_m, //default line + double line_c, + INT16 seg_count, //no of segments + DENORM_SEG *seg_pts, //actual segments + BOOL8 using_row, //as baseline + ROW *src) { + x_centre = x; //just copy + scale_factor = scaling; + source_row = src; + if (seg_count > 0) { + segs = new DENORM_SEG[seg_count]; + for (segments = 0; segments < seg_count; segments++) { + // It is possible, if infrequent that the segments may be out of order. + // since we are searching with a binary search, keep them in order. + if (segments == 0 || segs[segments - 1].xstart <= + seg_pts[segments].xstart) { + segs[segments] = seg_pts[segments]; + } else { + int i; + for (i = 0; i < segments + && segs[segments - 1 - i].xstart > seg_pts[segments].xstart; + ++i) { + segs[segments - i ] = segs[segments - 1 - i]; + } + segs[segments - i] = seg_pts[segments]; + } + } + } + else { + segments = 0; + segs = NULL; + } + base_is_row = using_row; + m = line_m; + c = line_c; +} + + +DENORM::DENORM(const DENORM &src) { + segments = 0; + segs = NULL; + *this = src; +} + + +DENORM & DENORM::operator= (const DENORM & src) { + x_centre = src.x_centre; + scale_factor = src.scale_factor; + source_row = src.source_row; + if (segments > 0) + delete[]segs; + if (src.segments > 0) { + segs = new DENORM_SEG[src.segments]; + for (segments = 0; segments < src.segments; segments++) + segs[segments] = src.segs[segments]; + } + else { + segments = 0; + segs = NULL; + } + base_is_row = src.base_is_row; + m = src.m; + c = src.c; + return *this; +} diff --git a/ccstruct/normalis.h b/ccstruct/normalis.h new file mode 100644 index 0000000000..a0d2812eb0 --- /dev/null +++ b/ccstruct/normalis.h @@ -0,0 +1,108 @@ +/********************************************************************** + * 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 + +class ROW; //forward decl + +class DENORM_SEG +{ + public: + DENORM_SEG() { + } //empty + + INT32 xstart; //start of segment + INT32 ycoord; //y at segment + float scale_factor; //for this segment +}; + +class DENORM +{ + public: + DENORM() { //constructor + source_row = NULL; + x_centre = 0.0f; + scale_factor = 1.0f; + segments = 0; + segs = NULL; + base_is_row = TRUE; + m = c = 0; + } + DENORM( //constructor + float x, //from same pieces + float scaling, + ROW *src) { + x_centre = x; //just copy + scale_factor = scaling; + source_row = src; + segments = 0; + segs = NULL; + base_is_row = TRUE; + m = c = 0; + } + DENORM( //constructor + float x, //from same pieces + float scaling, + double line_m, //default line //no of segments + double line_c, + INT16 seg_count, + DENORM_SEG *seg_pts, //actual segments + BOOL8 using_row, //as baseline + ROW *src); + DENORM(const DENORM &); + DENORM & operator= (const DENORM &); + ~DENORM () { + if (segments > 0) + delete[]segs; + } + + float origin() const { //get x centre + return x_centre; + } + float scale() const { //get scale + return scale_factor; + } + ROW *row() const { //get row + return source_row; + } + float x( //convert an xcoord + float src_x) const; + float y( //convert a ycoord + float src_y, //coord to convert + float src_centre) const; //normed x centre + float scale_at_x( // Return scaling at this coord. + float src_x) const; + float yshift_at_x( // Return yshift at this coord. + float src_x) const; + + private: + const DENORM_SEG *binary_search_segment(float src_x) const; + + BOOL8 base_is_row; //using row baseline? + INT16 segments; //no of segments + double c, m; //baseline + float x_centre; //middle of word + float scale_factor; //scaling + ROW *source_row; //row it came from + DENORM_SEG *segs; //array of segments +}; +#endif diff --git a/ccstruct/ocrblock.cpp b/ccstruct/ocrblock.cpp new file mode 100644 index 0000000000..103df51306 --- /dev/null +++ b/ccstruct/ocrblock.cpp @@ -0,0 +1,368 @@ +/********************************************************************** + * 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 "mfcpch.h" +#include +#include "blckerr.h" +#include "ocrblock.h" +#include "tprintf.h" + +#define BLOCK_LABEL_HEIGHT 150 //char height of block id + +ELISTIZE_S (BLOCK) +/********************************************************************** + * BLOCK::BLOCK + * + * Constructor for a simple rectangular block. + **********************************************************************/ +BLOCK::BLOCK ( //rectangular 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) { //box(ICOORD(xmin,ymin),ICOORD(xmax,ymax)) + //boundaries + ICOORDELT_IT left_it = &leftside; + ICOORDELT_IT right_it = &rightside; + + proportional = prop; + kerning = kern; + spacing = space; + font_class = -1; //not assigned + hand_block = NULL; + 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)); +} + + +/********************************************************************** + * BLOCK::set_sides + * + * Sets left and right vertex lists + **********************************************************************/ + +//void BLOCK::set_sides( //set vertex lists +//ICOORDELT_LIST *left, //left vertices +//ICOORDELT_LIST *right //right vertices +//) +//{ +// ICOORDELT_IT left_it= &leftside; //boundaries +// 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); +//} + +/********************************************************************** + * BLOCK::contains + * + * Return TRUE if the given point is within the block. + **********************************************************************/ + +//BOOL8 BLOCK::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()) +// { +// it.bounding_box(bleft,tright); //get rectangle +// if (pt.x()>=bleft.x() && pt.x()<=tright.x() //inside rect +// && pt.y()>=bleft.y() && pt.y()<=tright.y()) +// return TRUE; //is inside +// } +// return FALSE; //not inside +//} + +/********************************************************************** + * BLOCK::move + * + * Reposition block + **********************************************************************/ + +//void BLOCK::move( // reposition block +//const ICOORD vec // by vector +//) +//{ +// ROW_IT row_it( &rows ); +// ICOORDELT_IT it( &leftside ); + +// for( row_it.mark_cycle_pt(); !row_it.cycled_list(); row_it.forward() ) +// row_it.data()->move( vec ); + +// 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 ); +//} + +/********************************************************************** + * 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::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 = BOX (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::plot + * + * Plot the outline of a block in the given colour. + **********************************************************************/ + +//void BLOCK::plot( //draw outline +//WINDOW window, //window to draw in +//INT32 serial, //serial number +//COLOUR colour //colour to draw in +//) +//{ +// ICOORD startpt; //start of outline +// ICOORD endpt; //end of outline +// ICOORD prevpt; //previous point +// ICOORDELT_IT it= &leftside; //iterator +// char number[32]; //block id + +// line_color_index(window,colour); //set the colour +// text_color_index(window,colour); +// character_height(window,(float)BLOCK_LABEL_HEIGHT); +// text_font_index(window,6); + +// if (!leftside.empty()) +// { +// startpt= *(it.data()); //bottom left corner +//// fprintf(stderr,"Block %d bottom left is (%d,%d)\n", +//// serial,startpt.x(),startpt.y()); +// sprintf(number,"%d",serial); +// text2d(window,startpt.x(),startpt.y(),number,0,FALSE); + +// move2d(window,startpt.x(),startpt.y()); +// do +// { +// prevpt= *(it.data()); //previous point +// it.forward(); //move to next point +// draw2d(window,prevpt.x(),it.data()->y()); //draw round corner +// draw2d(window,it.data()->x(),it.data()->y()); +// } +// while (!it.at_last()); //until end of list +// endpt= *(it.data()); //end point + +// move2d(window,startpt.x(),startpt.y()); //other side of boundary +// it.set_to_list(&rightside); +// prevpt=startpt; +// for (it.mark_cycle_pt();!it.cycled_list();it.forward()) +// { +// draw2d(window,prevpt.x(),it.data()->y()); //draw round corner +// draw2d(window,it.data()->x(),it.data()->y()); +// prevpt= *(it.data()); //previous point +// } +// draw2d(window,endpt.x(),endpt.y()); //close boundary +// if (hand_block!=NULL) +// hand_block->plot(window,colour,serial); +// } +//} + +/********************************************************************** + * BLOCK::show + * + * Show the image corresponding to a block as its set of rectangles. + **********************************************************************/ + +//void BLOCK::show( //show image block +//IMAGE *image, //image to show +//WINDOW window //window to show in +//) +//{ +// BLOCK_RECT_IT it=this; //rectangle iterator +// ICOORD bleft,tright; //corners of rectangle + +// for (it.start_block();!it.cycled_rects();it.forward()) +// { +// it.bounding_box(bleft,tright); //get rectangle +//// fprintf(stderr,"Drawing a block with a bottom left of (%d,%d)\n", +//// bleft.x(),bleft.y()); +// show_sub_image(image,bleft.x(),bleft.y(), +// tright.x()-bleft.x(),tright.y()-bleft.y(), +// window,bleft.x(),bleft.y()); //show it +// } +//} + +/********************************************************************** + * 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 (); + // if ( !leftside.empty() ) + // leftside.clear(); + // if ( !rightside.empty() ) + // rightside.clear(); + // leftside.deep_copy( &source.leftside ); + // rightside.deep_copy( &source.rightside ); + // box=source.box; + return *this; +} diff --git a/ccstruct/ocrblock.h b/ccstruct/ocrblock.h new file mode 100644 index 0000000000..e8ae377200 --- /dev/null +++ b/ccstruct/ocrblock.h @@ -0,0 +1,228 @@ +/********************************************************************** + * 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 "img.h" +#include "ocrrow.h" +#include "pageblk.h" +#include "pdblock.h" + +class BLOCK; //forward decl + +ELISTIZEH_S (BLOCK) +class BLOCK:public ELIST_LINK, public PDBLK +//page block +{ + friend class BLOCK_RECT_IT; //block iterator + + //block label + friend void scan_hpd_blocks(const char *name, + PAGE_BLOCK_LIST *page_blocks, //head of full pag + INT32 &block_no, //no of blocks + BLOCK_IT *block_it); + friend BOOL8 read_vec_file( //read uscan output + STRING name, //basename of file + INT32 xsize, //page size //output list + INT32 ysize, + BLOCK_LIST *blocks); + friend BOOL8 read_pd_file( //read uscan output + STRING name, //basename of file + INT32 xsize, //page size //output list + INT32 ysize, + BLOCK_LIST *blocks); + + public: + BLOCK() { //empty constructor + hand_block = NULL; + hand_poly = NULL; + } + BLOCK( //simple constructor + 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); + + // void set_sides( //set vertex lists + // ICOORDELT_LIST *left, //list of left vertices + // ICOORDELT_LIST *right); //list of right vertices + + ~BLOCK () { //destructor + } + + void set_stats( //set space size etc. + BOOL8 prop, //proportional + INT16 kern, //inter char size + INT16 space, //inter word size + INT16 ch_pitch) { //pitch if fixed + proportional = prop; + kerning = (INT8) kern; + spacing = space; + pitch = ch_pitch; + } + void set_xheight( //set char size + INT32 height) { + xheight = height; + } + void set_font_class( //set font class + INT16 font) { + font_class = font; + } + // TEXT_REGION* text_region() + // { + // return hand_block; + // } + // POLY_BLOCK* poly_block() + // { + // return hand_poly; + // } + BOOL8 prop() const { //return proportional + return proportional; + } + INT32 fixed_pitch() const { //return pitch + return pitch; + } + INT16 kern() const { //return kerning + return kerning; + } + INT16 font() const { //return font class + return font_class; + } + INT16 space() const { //return spacing + return spacing; + } + const char *name() const { //return filename + return filename.string (); + } + INT32 x_height() const { //return xheight + return xheight; + } + ROW_LIST *row_list() { //get rows + return &rows; + } + C_BLOB_LIST *blob_list() { //get blobs + return &c_blobs; + } + C_BLOB_LIST *reject_blobs() { + return &rej_blobs; + } + // void bounding_box( //get box + // ICOORD& bottom_left, //bottom left + // ICOORD& top_right) const //topright + // { + // bottom_left=box.botleft(); + // top_right=box.topright(); + // } + // const BOX& bounding_box() const //get real box + // { + // return box; + // } + + // BOOL8 contains( //is pt inside block + // ICOORD pt); + + // void move( // reposition block + // const ICOORD vec); // by vector + + void sort_rows(); //decreasing y order + + void compress(); //shrink white space + + void check_pitch(); //check proportional + + void compress( //shrink white space + const ICOORD vec); //and move by vector + + void print( //print summary/table + FILE *fp, //file to print on + BOOL8 dump); //dump whole table + + // void plot( //draw histogram + // WINDOW window, //window to draw in + // INT32 serial, //serial number + // COLOUR colour); //colour to draw in + + // void show( //show image + // IMAGE *image, //image to show + // WINDOW window); //window to show in + + void prep_serialise() { //set ptrs to counts + filename.prep_serialise (); + rows.prep_serialise (); + c_blobs.prep_serialise (); + rej_blobs.prep_serialise (); + leftside.prep_serialise (); + rightside.prep_serialise (); + } + + void dump( //write external bits + FILE *f) { + filename.dump (f); + rows.dump (f); + c_blobs.dump (f); + rej_blobs.dump (f); + leftside.dump (f); + rightside.dump (f); + if (hand_block != NULL) + hand_block->serialise (f); + } + + void de_dump( //read external bits + FILE *f) { + filename.de_dump (f); + rows.de_dump (f); + c_blobs.de_dump (f); + rej_blobs.de_dump (f); + leftside.de_dump (f); + rightside.de_dump (f); + if (hand_block != NULL) + hand_block = TEXT_REGION::de_serialise (f); + } + + //assignment + make_serialise (BLOCK) BLOCK & operator= ( + const BLOCK & source); //from this + + private: + BOOL8 proportional; //proportional + 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 + STRING filename; //name of block + // TEXT_REGION* hand_block; //if it exists + // POLY_BLOCK* hand_poly; //wierd as well + ROW_LIST rows; //rows in block + C_BLOB_LIST c_blobs; //before textord + C_BLOB_LIST rej_blobs; //duff stuff + // ICOORDELT_LIST leftside; //left side vertices + // ICOORDELT_LIST rightside; //right side vertices + // BOX box; //bounding box +}; + +int decreasing_top_order( // + const void *row1, + const void *row2); +#endif diff --git a/ccstruct/ocrrow.cpp b/ccstruct/ocrrow.cpp new file mode 100644 index 0000000000..fd40c6b8aa --- /dev/null +++ b/ccstruct/ocrrow.cpp @@ -0,0 +1,216 @@ +/********************************************************************** + * 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 "mfcpch.h" +#include "ocrrow.h" +#include "blobbox.h" + +ELISTIZE_S (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) { + kerning = kern; //just store stuff + spacing = space; + xheight = x_height; + ascrise = ascenders; + descdrop = descenders; +} + + +/********************************************************************** + * 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 + ) { + kerning = kern; //just store stuff + spacing = space; + xheight = to_row->xheight; + ascrise = to_row->ascrise; + descdrop = to_row->descdrop; + baseline = to_row->baseline; +} + + +/********************************************************************** + * 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); +} + + +/********************************************************************** + * ROW::plot + * + * Draw the ROW in the given colour. + **********************************************************************/ + +#ifndef GRAPHICS_DISABLED +void ROW::plot( //draw it + WINDOW window, //window to draw in + COLOUR 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 + } +} +#endif + +/********************************************************************** + * ROW::plot + * + * Draw the ROW in rainbow colours. + **********************************************************************/ + +#ifndef GRAPHICS_DISABLED +void ROW::plot( //draw it + WINDOW 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 + +/********************************************************************** + * ROW::operator= + * + * Assign rows by duplicating the row structure but NOT the WERDLIST + **********************************************************************/ + +ROW & ROW::operator= ( //assignment +const ROW & source //from this +) { + this->ELIST_LINK::operator= (source); + kerning = source.kerning; + spacing = source.spacing; + xheight = source.xheight; + ascrise = source.ascrise; + descdrop = source.descdrop; + if (!words.empty ()) + words.clear (); + baseline = source.baseline; //QSPLINES must do = + bound_box = source.bound_box; + return *this; +} diff --git a/ccstruct/ocrrow.h b/ccstruct/ocrrow.h new file mode 100644 index 0000000000..6c9389300f --- /dev/null +++ b/ccstruct/ocrrow.h @@ -0,0 +1,133 @@ +/********************************************************************** + * 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; + +class ROW:public ELIST_LINK +{ + friend void tweak_row_baseline(ROW *); + 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; + } + INT32 kern() const { //return kerning + return kerning; + } + INT32 space() const { //return spacing + return spacing; + } + float ascenders() const { //return size + return ascrise; + } + float descenders() const { //return size + return descdrop; + } + BOX bounding_box() const { //return bounding box + return bound_box; + } + + void recalc_bounding_box(); //recalculate BB + + void move( // reposition row + const ICOORD vec); // by vector + + void print( //print + FILE *fp); //file to print on + + void plot( //draw one + WINDOW window, //window to draw in + COLOUR colour); //uniform colour + void plot( //draw one + WINDOW window); //in rainbow colours + +#ifndef GRAPHICS_DISABLED + void plot_baseline( //draw the baseline + WINDOW window, //window to draw in + COLOUR colour) { //colour to draw + //draw it + baseline.plot (window, colour); + } +#endif + + void prep_serialise() { //set ptrs to counts + words.prep_serialise (); + baseline.prep_serialise (); + } + + void dump( //write external bits + FILE *f) { + words.dump (f); + baseline.dump (f); + } + + void de_dump( //read external bits + FILE *f) { + words.de_dump (f); + baseline.de_dump (f); + } + + //assignment + make_serialise (ROW) ROW & operator= ( + const ROW & source); //from this + + private: + INT32 kerning; //inter char gap + INT32 spacing; //inter word gap + BOX bound_box; //bounding box + float xheight; //height of line + float ascrise; //size of ascenders + float descdrop; //-size of descenders + WERD_LIST words; //words + QSPLINE baseline; //baseline spline +}; + +ELISTIZEH_S (ROW) +#endif diff --git a/ccstruct/pageblk.cpp b/ccstruct/pageblk.cpp new file mode 100644 index 0000000000..c3325ea3d0 --- /dev/null +++ b/ccstruct/pageblk.cpp @@ -0,0 +1,879 @@ +#include "mfcpch.h" +#include "pageblk.h" +#include +#include +#include +#ifdef __UNIX__ +#include +#else +#include +#endif + +#include "hpddef.h" //must be last (handpd.dll) + +#define G_START 0 +#define I_START 1 +#define R_START 3 +#define S_START 5 + +extern char blabel[NUM_BLOCK_ATTR][4][MAXLENGTH]; +extern char backlabel[NUM_BACKGROUNDS][MAXLENGTH]; + +ELISTIZE_S (PAGE_BLOCK) +void PAGE_BLOCK::pb_delete() { + switch (pb_type) { + case PB_TEXT: + delete ((TEXT_BLOCK *) this); + break; + case PB_GRAPHICS: + delete ((GRAPHICS_BLOCK *) this); + break; + case PB_IMAGE: + delete ((IMAGE_BLOCK *) this); + break; + case PB_RULES: + delete ((RULE_BLOCK *) this); + break; + case PB_SCRIBBLE: + delete ((SCRIBBLE_BLOCK *) this); + break; + case PB_WEIRD: + delete ((WEIRD_BLOCK *) this); + break; + default: + break; + } +} + + +#define QUOTE_IT( parm ) #parm + +void PAGE_BLOCK::serialise(FILE *f) { + + if (fwrite (&pb_type, sizeof (PB_TYPE), 1, f) != 1) + WRITEFAILED.error (QUOTE_IT (PAGE_BLOCK::serialise), ABORT, NULL); + switch (pb_type) { + case PB_TEXT: + ((TEXT_BLOCK *) this)->serialise (f); + break; + case PB_GRAPHICS: + ((GRAPHICS_BLOCK *) this)->serialise (f); + break; + case PB_RULES: + ((RULE_BLOCK *) this)->serialise (f); + break; + case PB_IMAGE: + ((IMAGE_BLOCK *) this)->serialise (f); + break; + case PB_SCRIBBLE: + ((SCRIBBLE_BLOCK *) this)->serialise (f); + break; + case PB_WEIRD: + ((WEIRD_BLOCK *) this)->serialise (f); + break; + default: + break; + } +} + + +PAGE_BLOCK *PAGE_BLOCK::de_serialise(FILE *f) { + PB_TYPE type; + TEXT_BLOCK *tblock; + GRAPHICS_BLOCK *gblock; + RULE_BLOCK *rblock; + IMAGE_BLOCK *iblock; + SCRIBBLE_BLOCK *sblock; + WEIRD_BLOCK *wblock; + + if (fread ((void *) &type, sizeof (PB_TYPE), 1, f) != 1) + WRITEFAILED.error (QUOTE_IT (PAGE_BLOCK::serialise), ABORT, NULL); + switch (type) { + case PB_TEXT: + tblock = (TEXT_BLOCK *) alloc_struct (sizeof (TEXT_BLOCK)); + return tblock->de_serialise (f); + case PB_GRAPHICS: + gblock = (GRAPHICS_BLOCK *) alloc_struct (sizeof (GRAPHICS_BLOCK)); + return gblock->de_serialise (f); + case PB_RULES: + rblock = (RULE_BLOCK *) alloc_struct (sizeof (RULE_BLOCK)); + return rblock->de_serialise (f); + case PB_IMAGE: + iblock = (IMAGE_BLOCK *) alloc_struct (sizeof (IMAGE_BLOCK)); + return iblock->de_serialise (f); + case PB_SCRIBBLE: + sblock = (SCRIBBLE_BLOCK *) alloc_struct (sizeof (SCRIBBLE_BLOCK)); + return sblock->de_serialise (f); + case PB_WEIRD: + wblock = (WEIRD_BLOCK *) alloc_struct (sizeof (SCRIBBLE_BLOCK)); + return wblock->de_serialise (f); + default: + return NULL; + } +} + + +/********************************************************************** + * PAGE_BLOCK::serialise_asc() Convert to ascii file. + * + **********************************************************************/ + +void PAGE_BLOCK::serialise_asc( //convert to ascii + FILE *f //file to use + ) { + serialise_INT32(f, pb_type); + switch (pb_type) { + case PB_TEXT: + ((TEXT_BLOCK *) this)->serialise_asc (f); + break; + case PB_GRAPHICS: + ((GRAPHICS_BLOCK *) this)->serialise_asc (f); + break; + case PB_RULES: + ((RULE_BLOCK *) this)->serialise_asc (f); + break; + case PB_IMAGE: + ((IMAGE_BLOCK *) this)->serialise_asc (f); + break; + case PB_SCRIBBLE: + ((SCRIBBLE_BLOCK *) this)->serialise_asc (f); + break; + case PB_WEIRD: + ((WEIRD_BLOCK *) this)->serialise_asc (f); + break; + default: + break; + } +} + + +/********************************************************************** + * PAGE_BLOCK::internal_serialise_asc() Convert to ascii file. + * + **********************************************************************/ + +void PAGE_BLOCK::internal_serialise_asc( //convert to ascii + FILE *f //file to use + ) { + ((POLY_BLOCK *) this)->serialise_asc (f); + serialise_INT32(f, pb_type); + children.serialise_asc (f); +} + + +/********************************************************************** + * PAGE_BLOCK::de_serialise_asc() Convert from ascii file. + * + **********************************************************************/ + +void PAGE_BLOCK::de_serialise_asc( //convert from ascii + FILE *f //file to use + ) { + PAGE_BLOCK *page_block; //new block for list + INT32 len; /*length to retrive */ + PAGE_BLOCK_IT it; + + ((POLY_BLOCK *) this)->de_serialise_asc (f); + pb_type = (PB_TYPE) de_serialise_INT32 (f); + // children.de_serialise_asc(f); + len = de_serialise_INT32 (f); + it.set_to_list (&children); + for (; len > 0; len--) { + page_block = new_de_serialise_asc (f); + it.add_to_end (page_block); /*put on the list */ + } +} + + +/********************************************************************** + * PAGE_BLOCK::new_de_serialise_asc() Convert from ascii file. + * + **********************************************************************/ + +PAGE_BLOCK *PAGE_BLOCK::new_de_serialise_asc( //convert from ascii + FILE *f //file to use + ) { + PB_TYPE type; + TEXT_BLOCK *tblock; + GRAPHICS_BLOCK *gblock; + RULE_BLOCK *rblock; + IMAGE_BLOCK *iblock; + SCRIBBLE_BLOCK *sblock; + WEIRD_BLOCK *wblock; + + type = (PB_TYPE) de_serialise_INT32 (f); + switch (type) { + case PB_TEXT: + tblock = new TEXT_BLOCK; + tblock->de_serialise_asc (f); + return tblock; + case PB_GRAPHICS: + gblock = new GRAPHICS_BLOCK; + gblock->de_serialise_asc (f); + return gblock; + case PB_RULES: + rblock = new RULE_BLOCK; + rblock->de_serialise_asc (f); + return rblock; + case PB_IMAGE: + iblock = new IMAGE_BLOCK; + iblock->de_serialise_asc (f); + return iblock; + case PB_SCRIBBLE: + sblock = new SCRIBBLE_BLOCK; + sblock->de_serialise_asc (f); + return sblock; + case PB_WEIRD: + wblock = new WEIRD_BLOCK; + wblock->de_serialise_asc (f); + return wblock; + default: + return NULL; + } +} + + +void PAGE_BLOCK::show_attrs(DEBUG_WIN *f) { + PAGE_BLOCK_IT it; + + switch (pb_type) { + case PB_TEXT: + ((TEXT_BLOCK *) this)->show_attrs (f); + break; + case PB_GRAPHICS: + ((GRAPHICS_BLOCK *) this)->show_attrs (f); + break; + case PB_RULES: + ((RULE_BLOCK *) this)->show_attrs (f); + break; + case PB_IMAGE: + ((IMAGE_BLOCK *) this)->show_attrs (f); + break; + case PB_SCRIBBLE: + ((SCRIBBLE_BLOCK *) this)->show_attrs (f); + break; + case PB_WEIRD: + ((WEIRD_BLOCK *) this)->show_attrs (f); + break; + default: + break; + } + + if (!children.empty ()) { + f->dprintf ("containing subblocks\n"); + it.set_to_list (&children); + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) + it.data ()->show_attrs (f); + f->dprintf ("end of subblocks\n"); + } +} + + +PAGE_BLOCK::PAGE_BLOCK (ICOORDELT_LIST * points, PB_TYPE type, PAGE_BLOCK_LIST * child):POLY_BLOCK (points, +POLY_PAGE) { + PAGE_BLOCK_IT + c = &children; + + pb_type = type; + children.clear (); + c.move_to_first (); + c.add_list_before (child); +} + + +PAGE_BLOCK::PAGE_BLOCK (ICOORDELT_LIST * points, PB_TYPE type):POLY_BLOCK (points, +POLY_PAGE) { + pb_type = type; + children.clear (); +} + + +void PAGE_BLOCK::add_a_child(PAGE_BLOCK *newchild) { + PAGE_BLOCK_IT c = &children; + + c.move_to_first (); + c.add_to_end (newchild); +} + + +/********************************************************************** + * PAGE_BLOCK::rotate + * + * Rotate the PAGE_BLOCK and its children + **********************************************************************/ + +void PAGE_BLOCK::rotate( //cos,sin + FCOORD rotation) { + //sub block iterator + PAGE_BLOCK_IT child_it = &children; + PAGE_BLOCK *child; //child block + + for (child_it.mark_cycle_pt (); !child_it.cycled_list (); + child_it.forward ()) { + child = child_it.data (); + child->rotate (rotation); + } + if (pb_type == PB_TEXT) + ((TEXT_BLOCK *) this)->rotate (rotation); + else + POLY_BLOCK::rotate(rotation); +} + + +/********************************************************************** + * PAGE_BLOCK::move + * + * Move the PAGE_BLOCK and its children + **********************************************************************/ + +void PAGE_BLOCK::move(ICOORD shift //amount to move + ) { + //sub block iterator + PAGE_BLOCK_IT child_it = &children; + PAGE_BLOCK *child; //child block + + for (child_it.mark_cycle_pt (); !child_it.cycled_list (); + child_it.forward ()) { + child = child_it.data (); + child->move (shift); + } + if (pb_type == PB_TEXT) + ((TEXT_BLOCK *) this)->move (shift); + else + POLY_BLOCK::move(shift); +} + +#ifndef GRAPHICS_DISABLED +void PAGE_BLOCK::basic_plot(WINDOW window, COLOUR colour) { + PAGE_BLOCK_IT c = &children; + + POLY_BLOCK::plot (window, colour, 0); + + if (!c.empty ()) + for (c.mark_cycle_pt (); !c.cycled_list (); c.forward ()) + c.data ()->plot (window, colour); +} + + +void PAGE_BLOCK::plot(WINDOW window, COLOUR colour) { + TEXT_BLOCK *tblock; + WEIRD_BLOCK *wblock; + + switch (pb_type) { + case PB_TEXT: + basic_plot(window, colour); + tblock = (TEXT_BLOCK *) this; + tblock->plot (window, colour, REGION_COLOUR, SUBREGION_COLOUR); + break; + case PB_WEIRD: + wblock = (WEIRD_BLOCK *) this; + wblock->plot (window, colour); + break; + default: + basic_plot(window, colour); + break; + } +} +#endif + +void show_all_in(PAGE_BLOCK *pblock, POLY_BLOCK *show_area, DEBUG_WIN *f) { + PAGE_BLOCK_IT c; + INT16 i, pnum; + + c.set_to_list (pblock->child ()); + pnum = pblock->child ()->length (); + for (i = 0; i < pnum; i++, c.forward ()) { + if (show_area->contains (c.data ())) + c.data ()->show_attrs (f); + else if (show_area->overlap (c.data ())) + show_all_in (c.data (), show_area, f); + } +} + + +void delete_all_in(PAGE_BLOCK *pblock, POLY_BLOCK *delete_area) { + PAGE_BLOCK_IT c; + INT16 i, pnum; + + c.set_to_list (pblock->child ()); + pnum = pblock->child ()->length (); + for (i = 0; i < pnum; i++, c.forward ()) { + if (delete_area->contains (c.data ())) + c.extract ()->pb_delete (); + else if (delete_area->overlap (c.data ())) + delete_all_in (c.data (), delete_area); + } +} + + +PAGE_BLOCK *smallest_containing(PAGE_BLOCK *pblock, POLY_BLOCK *other) { + PAGE_BLOCK_IT c; + + c.set_to_list (pblock->child ()); + if (c.empty ()) + return (pblock); + + for (c.mark_cycle_pt (); !c.cycled_list (); c.forward ()) + if (c.data ()->contains (other)) + return (smallest_containing (c.data (), other)); + + return (pblock); +} + + +TEXT_BLOCK::TEXT_BLOCK (ICOORDELT_LIST * points, BOOL8 backg[NUM_BACKGROUNDS]):PAGE_BLOCK (points, +PB_TEXT) { + int + i; + + for (i = 0; i < NUM_BACKGROUNDS; i++) + background.set_bit (i, backg[i]); + + text_regions.clear (); +} + + +void +TEXT_BLOCK::set_attrs (BOOL8 backg[NUM_BACKGROUNDS]) { + int i; + + for (i = 0; i < NUM_BACKGROUNDS; i++) + background.set_bit (i, backg[i]); +} + + +void TEXT_BLOCK::add_a_region(TEXT_REGION *newchild) { + TEXT_REGION_IT c; + + c.set_to_list (&text_regions); + + c.move_to_first (); + c.add_to_end (newchild); +} + + +/********************************************************************** + * TEXT_BLOCK::rotate + * + * Rotate the TEXT_BLOCK and its children + **********************************************************************/ + +void TEXT_BLOCK::rotate( //cos,sin + FCOORD rotation) { + //sub block iterator + TEXT_REGION_IT child_it = &text_regions; + TEXT_REGION *child; //child block + + for (child_it.mark_cycle_pt (); !child_it.cycled_list (); + child_it.forward ()) { + child = child_it.data (); + child->rotate (rotation); + } + POLY_BLOCK::rotate(rotation); +} + + +/********************************************************************** + * TEXT_BLOCK::move + * + * Move the TEXT_BLOCK and its children + **********************************************************************/ + +void TEXT_BLOCK::move(ICOORD shift //amount to move + ) { + //sub block iterator + TEXT_REGION_IT child_it = &text_regions; + TEXT_REGION *child; //child block + + for (child_it.mark_cycle_pt (); !child_it.cycled_list (); + child_it.forward ()) { + child = child_it.data (); + child->move (shift); + } + POLY_BLOCK::move(shift); +} + + +/********************************************************************** + * TEXT_BLOCK::serialise_asc() Convert to ascii file. + * + **********************************************************************/ + +void TEXT_BLOCK::serialise_asc( //convert to ascii + FILE *f //file to use + ) { + ((PAGE_BLOCK *) this)->internal_serialise_asc (f); + serialise_INT32 (f, background.val); + text_regions.serialise_asc (f); +} + + +/********************************************************************** + * TEXT_BLOCK::de_serialise_asc() Convert from ascii file. + * + **********************************************************************/ + +void TEXT_BLOCK::de_serialise_asc( //convert from ascii + FILE *f //file to use + ) { + ((PAGE_BLOCK *) this)->de_serialise_asc (f); + background.val = de_serialise_INT32 (f); + text_regions.de_serialise_asc (f); +} + + +#ifndef GRAPHICS_DISABLED +void TEXT_BLOCK::plot(WINDOW window, + COLOUR colour, + COLOUR region_colour, + COLOUR subregion_colour) { + TEXT_REGION_IT t = &text_regions, tc; + + PAGE_BLOCK::basic_plot(window, colour); + + if (!t.empty ()) + for (t.mark_cycle_pt (); !t.cycled_list (); t.forward ()) { + t.data ()->plot (window, region_colour, t.data ()->id_no ()); + tc.set_to_list (t.data ()->regions ()); + if (!tc.empty ()) + for (tc.mark_cycle_pt (); !tc.cycled_list (); tc.forward ()) + tc.data ()->plot (window, subregion_colour, -1); + } +} +#endif + + +void TEXT_BLOCK::show_attrs(DEBUG_WIN *f) { + TEXT_REGION_IT it; + + f->dprintf ("TEXT BLOCK\n"); + print_background(f, background); + if (!text_regions.empty ()) { + f->dprintf ("containing text regions:\n"); + it.set_to_list (&text_regions); + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) + it.data ()->show_attrs (f); + f->dprintf ("end of regions\n"); + } +} + + +DLLSYM void show_all_tr_in(TEXT_BLOCK *tblock, + POLY_BLOCK *show_area, + DEBUG_WIN *f) { + TEXT_REGION_IT t, tc; + INT16 i, tnum, j, ttnum; + + t.set_to_list (tblock->regions ()); + tnum = tblock->regions ()->length (); + for (i = 0; i < tnum; i++, t.forward ()) { + if (show_area->contains (t.data ())) + t.data ()->show_attrs (f); + else if (show_area->overlap (t.data ())) { + tc.set_to_list (t.data ()->regions ()); + ttnum = t.data ()->regions ()->length (); + for (j = 0; j < ttnum; j++, tc.forward ()) + if (show_area->contains (tc.data ())) + tc.data ()->show_attrs (f); + } + } +} + + +void delete_all_tr_in(TEXT_BLOCK *tblock, POLY_BLOCK *delete_area) { + TEXT_REGION_IT t, tc; + INT16 i, tnum, j, ttnum; + + t.set_to_list (tblock->regions ()); + tnum = tblock->regions ()->length (); + for (i = 0; i < tnum; i++, t.forward ()) { + if (delete_area->contains (t.data ())) + delete (t.extract ()); + else if (delete_area->overlap (t.data ())) { + tc.set_to_list (t.data ()->regions ()); + ttnum = t.data ()->regions ()->length (); + for (j = 0; j < ttnum; j++, tc.forward ()) + if (delete_area->contains (tc.data ())) + delete (tc.extract ()); + } + } +} + + +RULE_BLOCK::RULE_BLOCK (ICOORDELT_LIST * points, INT8 sing, INT8 colo):PAGE_BLOCK (points, +PB_RULES) { + multiplicity = sing; + colour = colo; +} + + +void RULE_BLOCK::set_attrs(INT8 sing, INT8 colo) { + multiplicity = sing; + colour = colo; +} + + +void RULE_BLOCK::show_attrs(DEBUG_WIN *f) { + f->dprintf ("RULE BLOCK with attributes %s, %s\n", + blabel[R_START][multiplicity], blabel[R_START + 1][colour]); +} + + +/********************************************************************** + * RULE_BLOCK::serialise_asc() Convert to ascii file. + * + **********************************************************************/ + +void RULE_BLOCK::serialise_asc( //convert to ascii + FILE *f //file to use + ) { + ((PAGE_BLOCK *) this)->internal_serialise_asc (f); + serialise_INT32(f, multiplicity); + serialise_INT32(f, colour); +} + + +/********************************************************************** + * RULE_BLOCK::de_serialise_asc() Convert from ascii file. + * + **********************************************************************/ + +void RULE_BLOCK::de_serialise_asc( //convert from ascii + FILE *f //file to use + ) { + ((PAGE_BLOCK *) this)->de_serialise_asc (f); + multiplicity = de_serialise_INT32 (f); + colour = de_serialise_INT32 (f); +} + + +GRAPHICS_BLOCK::GRAPHICS_BLOCK (ICOORDELT_LIST * points, BOOL8 backg[NUM_BACKGROUNDS], INT8 foreg):PAGE_BLOCK (points, +PB_GRAPHICS) { + int + i; + + for (i = 0; i < NUM_BACKGROUNDS; i++) + background.set_bit (i, backg[i]); + + foreground = foreg; +} + + +void +GRAPHICS_BLOCK::set_attrs (BOOL8 backg[NUM_BACKGROUNDS], INT8 foreg) { + int i; + + for (i = 0; i < NUM_BACKGROUNDS; i++) + background.set_bit (i, backg[i]); + + foreground = foreg; +} + + +void GRAPHICS_BLOCK::show_attrs(DEBUG_WIN *f) { + f->dprintf ("GRAPHICS BLOCK with attribute %s\n", + blabel[G_START][foreground]); + print_background(f, background); +} + + +/********************************************************************** + * GRAPHICS_BLOCK::serialise_asc() Convert to ascii file. + * + **********************************************************************/ + +void GRAPHICS_BLOCK::serialise_asc( //convert to ascii + FILE *f //file to use + ) { + ((PAGE_BLOCK *) this)->internal_serialise_asc (f); + serialise_INT32 (f, background.val); + serialise_INT32(f, foreground); +} + + +/********************************************************************** + * GRAPHICS_BLOCK::de_serialise_asc() Convert from ascii file. + * + **********************************************************************/ + +void GRAPHICS_BLOCK::de_serialise_asc( //convert from ascii + FILE *f //file to use + ) { + ((PAGE_BLOCK *) this)->de_serialise_asc (f); + background.val = de_serialise_INT32 (f); + foreground = de_serialise_INT32 (f); +} + + +IMAGE_BLOCK::IMAGE_BLOCK (ICOORDELT_LIST * points, INT8 colo, INT8 qual):PAGE_BLOCK (points, +PB_IMAGE) { + colour = colo; + quality = qual; +} + + +void IMAGE_BLOCK::set_attrs(INT8 colo, INT8 qual) { + colour = colo; + quality = qual; +} + + +void IMAGE_BLOCK::show_attrs(DEBUG_WIN *f) { + f->dprintf ("IMAGE BLOCK with attributes %s, %s\n", blabel[I_START][colour], + blabel[I_START + 1][quality]); +} + + +/********************************************************************** + * IMAGE_BLOCK::serialise_asc() Convert to ascii file. + * + **********************************************************************/ + +void IMAGE_BLOCK::serialise_asc( //convert to ascii + FILE *f //file to use + ) { + ((PAGE_BLOCK *) this)->internal_serialise_asc (f); + serialise_INT32(f, colour); + serialise_INT32(f, quality); +} + + +/********************************************************************** + * IMAGE_BLOCK::de_serialise_asc() Convert from ascii file. + * + **********************************************************************/ + +void IMAGE_BLOCK::de_serialise_asc( //convert from ascii + FILE *f //file to use + ) { + ((PAGE_BLOCK *) this)->de_serialise_asc (f); + colour = de_serialise_INT32 (f); + quality = de_serialise_INT32 (f); +} + + +SCRIBBLE_BLOCK::SCRIBBLE_BLOCK (ICOORDELT_LIST * points, BOOL8 backg[NUM_BACKGROUNDS], INT8 foreg):PAGE_BLOCK (points, +PB_SCRIBBLE) { + int + i; + + for (i = 0; i < NUM_BACKGROUNDS; i++) + background.set_bit (i, backg[i]); + + foreground = foreg; +} + + +void +SCRIBBLE_BLOCK::set_attrs (BOOL8 backg[NUM_BACKGROUNDS], INT8 foreg) { + int i; + + for (i = 0; i < NUM_BACKGROUNDS; i++) + background.set_bit (i, backg[i]); + + foreground = foreg; +} + + +void SCRIBBLE_BLOCK::show_attrs(DEBUG_WIN *f) { + f->dprintf ("SCRIBBLE BLOCK with attributes %s\n", + blabel[S_START][foreground]); + print_background(f, background); +} + + +/********************************************************************** + * SCRIBBLE_BLOCK::serialise_asc() Convert to ascii file. + * + **********************************************************************/ + +void SCRIBBLE_BLOCK::serialise_asc( //convert to ascii + FILE *f //file to use + ) { + ((PAGE_BLOCK *) this)->internal_serialise_asc (f); + serialise_INT32 (f, background.val); + serialise_INT32(f, foreground); +} + + +/********************************************************************** + * SCRIBBLE_BLOCK::de_serialise_asc() Convert from ascii file. + * + **********************************************************************/ + +void SCRIBBLE_BLOCK::de_serialise_asc( //convert from ascii + FILE *f //file to use + ) { + ((PAGE_BLOCK *) this)->de_serialise_asc (f); + background.val = de_serialise_INT32 (f); + foreground = de_serialise_INT32 (f); +} + + +WEIRD_BLOCK::WEIRD_BLOCK (ICOORDELT_LIST * points, INT32 id_no):PAGE_BLOCK (points, +PB_WEIRD) { + id_number = id_no; +} + + +#ifndef GRAPHICS_DISABLED +void WEIRD_BLOCK::plot(WINDOW window, COLOUR colour) { + PAGE_BLOCK_IT c = this->child (); + + POLY_BLOCK::plot(window, colour, id_number); + + if (!c.empty ()) + for (c.mark_cycle_pt (); !c.cycled_list (); c.forward ()) + c.data ()->plot (window, colour); +} +#endif + + +void WEIRD_BLOCK::set_id(INT32 id_no) { + id_number = id_no; +} + + +void WEIRD_BLOCK::show_attrs(DEBUG_WIN *f) { + f->dprintf ("WEIRD BLOCK with id number %d\n", id_number); +} + + +/********************************************************************** + * WEIRD_BLOCK::serialise_asc() Convert to ascii file. + * + **********************************************************************/ + +void WEIRD_BLOCK::serialise_asc( //convert to ascii + FILE *f //file to use + ) { + ((PAGE_BLOCK *) this)->internal_serialise_asc (f); + serialise_INT32(f, id_number); +} + + +/********************************************************************** + * WEIRD_BLOCK::de_serialise_asc() Convert from ascii file. + * + **********************************************************************/ + +void WEIRD_BLOCK::de_serialise_asc( //convert from ascii + FILE *f //file to use + ) { + ((PAGE_BLOCK *) this)->de_serialise_asc (f); + id_number = de_serialise_INT32 (f); +} + + +void print_background(DEBUG_WIN *f, BITS16 background) { + int i; + + f->dprintf ("Background is \n"); + for (i = 0; i < NUM_BACKGROUNDS; i++) { + if (background.bit (i)) + f->dprintf ("%s, ", backlabel[i]); + } + + f->dprintf ("\n"); + +} diff --git a/ccstruct/pageblk.h b/ccstruct/pageblk.h new file mode 100644 index 0000000000..bc021ef33a --- /dev/null +++ b/ccstruct/pageblk.h @@ -0,0 +1,318 @@ +#ifndef PAGEBLK_C +#define PAGEBLK_C + +#include "elst.h" +#include "txtregn.h" +#include "bits16.h" + +#include "hpddef.h" //must be last (handpd.dll) + +enum PB_TYPE +{ + PB_TEXT, + PB_RULES, + PB_GRAPHICS, + PB_IMAGE, + PB_SCRIBBLE, + PB_WEIRD +}; + +class DLLSYM PAGE_BLOCK; //forward decl +class DLLSYM TEXT_BLOCK; //forward decl +class DLLSYM GRAPHICS_BLOCK; //forward decl +class DLLSYM RULE_BLOCK; //forward decl +class DLLSYM IMAGE_BLOCK; //forward decl +class DLLSYM SCRIBBLE_BLOCK; //forward decl +class DLLSYM WEIRD_BLOCK; //forward decl + +ELISTIZEH_S (PAGE_BLOCK) +class DLLSYM PAGE_BLOCK:public ELIST_LINK, public POLY_BLOCK +//page block +{ + public: + PAGE_BLOCK() { + } //empty constructor + PAGE_BLOCK( //simple constructor + ICOORDELT_LIST *points, + PB_TYPE type, + PAGE_BLOCK_LIST *child); + + PAGE_BLOCK( //simple constructor + ICOORDELT_LIST *points, + PB_TYPE type); + + ~PAGE_BLOCK () { //destructor + } + + void add_a_child(PAGE_BLOCK *newchild); + + PB_TYPE type() { //get type + return pb_type; + } + + PAGE_BLOCK_LIST *child() { //get children + return &children; + } + + void rotate( //rotate it + FCOORD rotation); + void move( //move it + ICOORD shift); //vector + + void basic_plot(WINDOW window, COLOUR colour); + + void plot(WINDOW window, COLOUR colour); + + void show_attrs(DEBUG_WIN *debug); + + NEWDELETE2 (PAGE_BLOCK) void pb_delete (); + + void serialise(FILE *f); + + static PAGE_BLOCK *de_serialise(FILE *f); + + void prep_serialise() { //set ptrs to counts + POLY_BLOCK::prep_serialise(); + children.prep_serialise (); + } + + void dump( //write external bits + FILE *f) { + POLY_BLOCK::dump(f); + children.dump (f); + } + + void de_dump( //read external bits + FILE *f) { + POLY_BLOCK::de_dump(f); + children.de_dump (f); + } + + //note that due to the awful switched nature of the PAGE_BLOCK class, + //a PAGE_BLOCK_LIST cannot be de-serialised by the normal mechanism, since + //each element cannot be de-serialised in place. + //To fix this it is important to use read_poly_blocks or the code therein. + void serialise_asc( //serialise to ascii + FILE *f); + void internal_serialise_asc( //serialise to ascii + FILE *f); + void de_serialise_asc( //serialise from ascii + FILE *f); + //make one from ascii + static PAGE_BLOCK *new_de_serialise_asc(FILE *f); + + private: + PB_TYPE pb_type; + PAGE_BLOCK_LIST children; +}; + +DLLSYM void show_all_in(PAGE_BLOCK *pblock, + POLY_BLOCK *show_area, + DEBUG_WIN *f); + +DLLSYM void delete_all_in(PAGE_BLOCK *pblock, POLY_BLOCK *delete_area); + +DLLSYM PAGE_BLOCK *smallest_containing(PAGE_BLOCK *pblock, POLY_BLOCK *other); + +class DLLSYM TEXT_BLOCK:public PAGE_BLOCK + //text block +{ + public: + TEXT_BLOCK() { + } //empty constructor + TEXT_BLOCK(ICOORDELT_LIST *points); + + TEXT_BLOCK (ICOORDELT_LIST * points, BOOL8 backg[NUM_BACKGROUNDS]); + + //get children + TEXT_REGION_LIST *regions() { + return &text_regions; + } + + INT32 nregions() { + return text_regions.length (); + } + + void add_a_region(TEXT_REGION *newchild); + + void rotate( //rotate it + FCOORD rotation); + void move( //move it + ICOORD shift); //vector + + void plot(WINDOW window, + COLOUR colour, + COLOUR region_colour, + COLOUR subregion_colour); + + void set_attrs (BOOL8 backg[NUM_BACKGROUNDS]); + + void show_attrs(DEBUG_WIN *debug); + + void prep_serialise() { //set ptrs to counts + PAGE_BLOCK::prep_serialise(); + text_regions.prep_serialise (); + } + + void dump( //write external bits + FILE *f) { + PAGE_BLOCK::dump(f); + text_regions.dump (f); + } + + void de_dump( //read external bits + FILE *f) { + PAGE_BLOCK::de_dump(f); + text_regions.de_dump (f); + } + + //serialise to ascii + make_serialise (TEXT_BLOCK) void serialise_asc ( + FILE * f); + void de_serialise_asc( //serialise from ascii + FILE *f); + + private: + BITS16 background; + + TEXT_REGION_LIST text_regions; +}; + +DLLSYM void delete_all_tr_in(TEXT_BLOCK *tblock, POLY_BLOCK *delete_area); + +DLLSYM void show_all_tr_in(TEXT_BLOCK *tblock, + POLY_BLOCK *show_area, + DEBUG_WIN *f); + +class DLLSYM RULE_BLOCK:public PAGE_BLOCK + //rule block +{ + public: + RULE_BLOCK() { + } //empty constructor + RULE_BLOCK(ICOORDELT_LIST *points, INT8 sing, INT8 colo); + + void set_attrs(INT8 sing, INT8 colo); + + void show_attrs(DEBUG_WIN *debug); + + //serialise to ascii + make_serialise (RULE_BLOCK) void serialise_asc ( + FILE * f); + void de_serialise_asc( //serialise from ascii + FILE *f); + + private: + INT8 multiplicity; + INT8 colour; + +}; + +class DLLSYM GRAPHICS_BLOCK:public PAGE_BLOCK + //graphics block +{ + public: + GRAPHICS_BLOCK() { + } //empty constructor + GRAPHICS_BLOCK (ICOORDELT_LIST * points, + BOOL8 backg[NUM_BACKGROUNDS], INT8 foreg); + + void set_attrs (BOOL8 backg[NUM_BACKGROUNDS], INT8 foreg); + + void show_attrs(DEBUG_WIN *debug); + + //serialise to ascii + make_serialise (GRAPHICS_BLOCK) void serialise_asc ( + FILE * f); + void de_serialise_asc( //serialise from ascii + FILE *f); + + private: + BITS16 background; + INT8 foreground; + +}; + +class DLLSYM IMAGE_BLOCK:public PAGE_BLOCK + //image block +{ + public: + IMAGE_BLOCK() { + } //empty constructor + IMAGE_BLOCK(ICOORDELT_LIST *points, INT8 colo, INT8 qual); + + void set_attrs(INT8 colo, INT8 qual); + + void show_attrs(DEBUG_WIN *debug); + + //serialise to ascii + make_serialise (IMAGE_BLOCK) void serialise_asc ( + FILE * f); + void de_serialise_asc( //serialise from ascii + FILE *f); + + private: + INT8 colour; + INT8 quality; + +}; + +class DLLSYM SCRIBBLE_BLOCK:public PAGE_BLOCK + //scribble block +{ + public: + SCRIBBLE_BLOCK() { + } //empty constructor + SCRIBBLE_BLOCK (ICOORDELT_LIST * points, + BOOL8 backg[NUM_BACKGROUNDS], INT8 foreg); + + void set_attrs (BOOL8 backg[NUM_BACKGROUNDS], INT8 foreg); + + void show_attrs(DEBUG_WIN *debug); + + //serialise to ascii + make_serialise (SCRIBBLE_BLOCK) void serialise_asc ( + FILE * f); + void de_serialise_asc( //serialise from ascii + FILE *f); + + private: + BITS16 background; + INT8 foreground; +}; + +class DLLSYM WEIRD_BLOCK:public PAGE_BLOCK + //weird block +{ + public: + WEIRD_BLOCK() { + } //empty constructor + WEIRD_BLOCK(ICOORDELT_LIST *points, INT32 id_no); + + void set_id(INT32 id_no); + + void show_attrs(DEBUG_WIN *debug); + + void set_id_no(INT32 new_id) { + id_number = new_id; + } + + void plot(WINDOW window, COLOUR colour); + + INT32 id_no() { + return id_number; + } + + //serialise to ascii + make_serialise (WEIRD_BLOCK) void serialise_asc ( + FILE * f); + void de_serialise_asc( //serialise from ascii + FILE *f); + + private: + INT32 id_number; //unique id + +}; + +void print_background(DEBUG_WIN *f, BITS16 background); +#endif diff --git a/ccstruct/pageres.cpp b/ccstruct/pageres.cpp new file mode 100644 index 0000000000..4a542b39bd --- /dev/null +++ b/ccstruct/pageres.cpp @@ -0,0 +1,325 @@ +/********************************************************************** + * File: pageres.cpp (Formerly page_res.c) + * 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. + * + **********************************************************************/ +#include "mfcpch.h" +#include +#ifdef __UNIX__ +#include +#endif +#include "pageres.h" +#include "notdll.h" + +ELISTIZE (BLOCK_RES) +CLISTIZE (BLOCK_RES) ELISTIZE (ROW_RES) ELISTIZE (WERD_RES) +/************************************************************************* + * PAGE_RES::PAGE_RES + * + * Constructor for page results + *************************************************************************/ +PAGE_RES::PAGE_RES( //recursive construct + BLOCK_LIST *the_block_list //real page + ) { + BLOCK_IT block_it(the_block_list); + BLOCK_RES_IT block_res_it(&block_res_list); + + char_count = 0; + rej_count = 0; + rejected = FALSE; + + for (block_it.mark_cycle_pt (); + !block_it.cycled_list (); block_it.forward ()) { + block_res_it.add_to_end (new BLOCK_RES (block_it.data ())); + } +} + + +/************************************************************************* + * BLOCK_RES::BLOCK_RES + * + * Constructor for BLOCK results + *************************************************************************/ + +BLOCK_RES::BLOCK_RES( //recursive construct + BLOCK *the_block //real 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 (row_it.data ())); + } +} + + +/************************************************************************* + * ROW_RES::ROW_RES + * + * Constructor for ROW results + *************************************************************************/ + +ROW_RES::ROW_RES( //recursive construct + ROW *the_row //real 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_RES *word_res; //current word + WERD *copy_word; + + char_count = 0; + rej_count = 0; + whole_word_rej_count = 0; + font_class = -1; + font_class_score = -1.0; + bold = FALSE; + italic = FALSE; + + row = the_row; + + for (word_it.mark_cycle_pt (); !word_it.cycled_list (); word_it.forward ()) { + word_res = new WERD_RES (word_it.data ()); + + if (word_res->word->flag (W_FUZZY_NON)) { + ASSERT_HOST (combo != NULL); + word_res->part_of_combo = TRUE; + combo->copy_on (word_res); + } + if (word_it.data_relative (1)->flag (W_FUZZY_NON)) { + if (combo == NULL) { + copy_word = new WERD; + //deep copy + *copy_word = *(word_it.data ()); + combo = new WERD_RES (copy_word); + 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= ( //assign word_res +const WERD_RES & source //from this +) { + this->ELIST_LINK::operator= (source); + if (source.combination) { + word = new WERD; + *word = *(source.word); //deep copy + } + else + word = source.word; //pt to same word + + if (source.outword != NULL) { + outword = new WERD; + *outword = *(source.outword);//deep copy + } + else + outword = NULL; + + denorm = source.denorm; + if (source.best_choice != NULL) { + best_choice = new WERD_CHOICE; + *best_choice = *(source.best_choice); + raw_choice = new WERD_CHOICE; + *raw_choice = *(source.raw_choice); + } + else { + best_choice = NULL; + raw_choice = NULL; + } + if (source.ep_choice != NULL) { + ep_choice = new WERD_CHOICE; + *ep_choice = *(source.ep_choice); + } + else + ep_choice = NULL; + reject_map = source.reject_map; + 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; + italic = source.italic; + bold = source.bold; + font1 = source.font1; + font1_count = source.font1_count; + font2 = source.font2; + font2_count = source.font2_count; + x_height = source.x_height; + caps_height = source.caps_height; + guessed_x_ht = source.guessed_x_ht; + guessed_caps_ht = source.guessed_caps_ht; + combination = source.combination; + part_of_combo = source.part_of_combo; + reject_spaces = source.reject_spaces; + return *this; +} + + +WERD_RES::~WERD_RES () { + if (combination) + delete word; + if (outword != NULL) + delete outword; + if (best_choice != NULL) { + delete best_choice; + delete raw_choice; + } + if (ep_choice != NULL) { + delete ep_choice; + } +} + + +/************************************************************************* + * PAGE_RES_IT::restart_page + * + * Set things up at the start of the page + *************************************************************************/ + +WERD_RES *PAGE_RES_IT::restart_page() { + block_res_it.set_to_list (&page_res->block_res_list); + block_res_it.mark_cycle_pt (); + block_res = NULL; + row_res = NULL; + word_res = NULL; + next_block_res = NULL; + next_row_res = NULL; + next_word_res = NULL; + internal_forward(TRUE); + return internal_forward (FALSE); +} + + +/************************************************************************* + * PAGE_RES_IT::internal_forward + * + * Find the next word on the page. Empty blocks and rows are skipped. + * 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(BOOL8 new_block) { + BOOL8 found_next_word = FALSE; + BOOL8 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; + + while (!found_next_word && !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 (); + new_row = TRUE; + } + while (!found_next_word && !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 (); + } + while (!found_next_word && !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 (); + found_next_word = TRUE; + do { + word_res_it.forward (); + } + while (word_res_it.data ()->part_of_combo); + } + if (!found_next_word) { //end of row reached + row_res_it.forward (); + new_row = TRUE; + } + } + if (!found_next_word) { //end of block reached + block_res_it.forward (); + new_block = TRUE; + } + } + if (!found_next_word) { //end of page reached + next_block_res = NULL; + next_row_res = NULL; + next_word_res = NULL; + } + return word_res; +} + + +/************************************************************************* + * PAGE_RES_IT::forward_block + * + * Move to the first word of the next block + * Can be followed by subsequent calls to forward() BUT at the first word in + * the block, the prev block, row and word are all NULL. + *************************************************************************/ + +WERD_RES *PAGE_RES_IT::forward_block() { + if (block_res == next_block_res) { + block_res_it.forward ();; + block_res = NULL; + row_res = NULL; + word_res = NULL; + next_block_res = NULL; + next_row_res = NULL; + next_word_res = NULL; + internal_forward(TRUE); + } + return internal_forward (FALSE); +} + + +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; +} diff --git a/ccstruct/pageres.h b/ccstruct/pageres.h new file mode 100644 index 0000000000..6ddadfba50 --- /dev/null +++ b/ccstruct/pageres.h @@ -0,0 +1,311 @@ +/********************************************************************** + * 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 "elst.h" +#include "ocrblock.h" +#include "ocrrow.h" +#include "werd.h" +#include "ratngs.h" +#include "rejctmap.h" +#include "notdll.h" +#include "notdll.h" + +/* 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; + + PAGE_RES() { + } //empty constructor + + PAGE_RES( //simple constructor + BLOCK_LIST *block_list); //real blocks + + ~PAGE_RES () { //destructor + } +}; + +/************************************************************************* + * BLOCK_RES - Block results + *************************************************************************/ + +class BLOCK_RES:public ELIST_LINK + //page block result +{ + 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( //simple constructor + BLOCK *the_block); //real block + + ~BLOCK_RES () { //destructor + } +}; + +/************************************************************************* + * ROW_RES - Row results + *************************************************************************/ + +class ROW_RES:public ELIST_LINK //row result +{ + 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; + float font_class_score; + INT16 font_class; // + INT32 italic; + INT32 bold; + INT8 font1; //primary font + INT8 font1_count; //no of voters + INT8 font2; //secondary font + INT8 font2_count; //no of voters + + ROW_RES() { + } //empty constructor + + ROW_RES( //simple constructor + 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 +}; + +class WERD_RES:public ELIST_LINK //word result +{ + public: + WERD * word; //non-bln real word + WERD *outword; //bln best choice + //segmentation + DENORM denorm; //for use on outword + WERD_CHOICE *best_choice; //tess output + WERD_CHOICE *raw_choice; //top choice permuter + WERD_CHOICE *ep_choice; //ep text + 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? + INT8 italic; + INT8 bold; + INT8 font1; //primary font + INT8 font1_count; //no of voters + INT8 font2; //secondary font + INT8 font2_count; //no of voters + CRUNCH_MODE unlv_crunch_mode; + float x_height; //Post match estimate + float caps_height; //Post match estimate + BOOL8 guessed_x_ht; + BOOL8 guessed_caps_ht; + /* + 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() { + } //empty constructor + + WERD_RES( //simple constructor + WERD *the_word) { //real word + word = the_word; + outword = NULL; + best_choice = NULL; + raw_choice = NULL; + ep_choice = NULL; + tess_failed = FALSE; + tess_accepted = FALSE; + tess_would_adapt = FALSE; + done = FALSE; + unlv_crunch_mode = CR_NONE; + italic = FALSE; + bold = FALSE; + font1 = -1; + font1_count = 0; + font2 = -1; + font2_count = 0; + x_height = 0.0; + caps_height = 0.0; + guessed_x_ht = TRUE; + guessed_caps_ht = TRUE; + combination = FALSE; + part_of_combo = FALSE; + reject_spaces = FALSE; + } + WERD_RES( //constr from WERD_RES + const WERD_RES &source) { + *this = source; //see operator= + } + + ~WERD_RES (); //destructor + + WERD_RES & operator= ( //assign word res + const WERD_RES & source); //from this + + void copy_on( //copy blobs onto word + WERD_RES *word_res) { //from this word + word->set_flag (W_EOL, word_res->word->flag (W_EOL)); + word->copy_on (word_res->word); + } +}; + +/************************************************************************* + * 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( //empty contructor + PAGE_RES *the_page_res) { //page result + page_res = the_page_res; + restart_page(); //ready to scan + } + + WERD_RES *restart_page(); //get ready + + WERD_RES *internal_forward( //get next word + BOOL8 new_block); + + WERD_RES *forward() { //get next word + return internal_forward (FALSE); + } + + WERD_RES *forward_block(); //get first word in + //next non-empty block + WERD_RES *prev_word() { //previous word + return prev_word_res; + } + ROW_RES *prev_row() { //row of prev word + return prev_row_res; + } + BLOCK_RES *prev_block() { //block of prev word + return prev_block_res; + } + WERD_RES *word() { //current word + return word_res; + } + ROW_RES *row() { //row of current word + return row_res; + } + BLOCK_RES *block() { //block of cur. word + return block_res; + } + WERD_RES *next_word() { //next word + return next_word_res; + } + ROW_RES *next_row() { //row of next word + return next_row_res; + } + BLOCK_RES *next_block() { //block of next word + return next_block_res; + } + void rej_stat_word(); //for page/block/row + + private: + 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 diff --git a/ccstruct/pdblock.cpp b/ccstruct/pdblock.cpp new file mode 100644 index 0000000000..182163aedb --- /dev/null +++ b/ccstruct/pdblock.cpp @@ -0,0 +1,363 @@ +/********************************************************************** + * 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 "mfcpch.h" +#include +#include "blckerr.h" +#include "pdblock.h" +#include "showim.h" + +#include "hpddef.h" //must be last (handpd.dll) + +#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_block = NULL; + 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)); +} + + +/********************************************************************** + * 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); +} + + +/********************************************************************** + * PDBLK::plot + * + * Plot the outline of a block in the given colour. + **********************************************************************/ + +#ifndef GRAPHICS_DISABLED +void PDBLK::plot( //draw outline + WINDOW window, //window to draw in + INT32 serial, //serial number + COLOUR 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 + line_color_index(window, colour); + text_color_index(window, colour); + character_height (window, (float) BLOCK_LABEL_HEIGHT); + text_font_index (window, 6); + + 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]; + #ifdef __UNIX__ + sprintf(temp_buff, INT32FORMAT, serial); + #else + ultoa (serial, temp_buff, 10); + #endif + text2d (window, startpt.x (), startpt.y (), temp_buff, 0, FALSE); + + move2d (window, startpt.x (), startpt.y ()); + do { + prevpt = *(it.data ()); //previous point + it.forward (); //move to next point + //draw round corner + draw2d (window, prevpt.x (), it.data ()->y ()); + draw2d (window, it.data ()->x (), it.data ()->y ()); + } + while (!it.at_last ()); //until end of list + endpt = *(it.data ()); //end point + + //other side of boundary + move2d (window, startpt.x (), startpt.y ()); + it.set_to_list (&rightside); + prevpt = startpt; + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + //draw round corner + draw2d (window, prevpt.x (), it.data ()->y ()); + draw2d (window, it.data ()->x (), it.data ()->y ()); + prevpt = *(it.data ()); //previous point + } + //close boundary + draw2d (window, endpt.x (), endpt.y ()); + if (hand_block != NULL) + hand_block->plot (window, colour, serial); + } +} +#endif + + +/********************************************************************** + * PDBLK::show + * + * Show the image corresponding to a block as its set of rectangles. + **********************************************************************/ + +#ifndef GRAPHICS_DISABLED +void PDBLK::show( //show image block + IMAGE *image, //image to show + WINDOW window //window to show in + ) { + 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); + // tprintf("Drawing a block with a bottom left of (%d,%d)\n", + // bleft.x(),bleft.y()); + //show it + show_sub_image (image, bleft.x (), bleft.y (), tright.x () - bleft.x (), tright.y () - bleft.y (), window, bleft.x (), bleft.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); + rightside.deep_copy (&source.rightside); + 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 +} diff --git a/ccstruct/pdblock.h b/ccstruct/pdblock.h new file mode 100644 index 0000000000..a5f1473abe --- /dev/null +++ b/ccstruct/pdblock.h @@ -0,0 +1,181 @@ +/********************************************************************** + * 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 "img.h" +#include "strngs.h" +#include "pageblk.h" + +#include "hpddef.h" //must be last (handpd.dll) + +class DLLSYM PDBLK; //forward decl + +CLISTIZEH (PDBLK) +class DLLSYM PDBLK //page block +{ + friend class BLOCK_RECT_IT; //block iterator + + //block label + friend void scan_hpd_blocks(const char *name, + PAGE_BLOCK_LIST *page_blocks, //head of full pag + INT32 &block_no, //no of blocks + PDBLK_C_IT *block_it); + friend BOOL8 read_vec_file( //read uscan output + STRING name, //basename of file + INT32 xsize, //page size //output list + INT32 ysize, + PDBLK_CLIST *blocks); + friend BOOL8 read_pd_file( //read uscan output + STRING name, //basename of file + INT32 xsize, //page size //output list + INT32 ysize, + PDBLK_CLIST *blocks); + + public: + PDBLK() { //empty constructor + hand_block = NULL; + hand_poly = NULL; + } + PDBLK( //simple constructor + INT16 xmin, //bottom left + INT16 ymin, + INT16 xmax, //top right + INT16 ymax); + + void set_sides( //set vertex lists + ICOORDELT_LIST *left, //list of left vertices + ICOORDELT_LIST *right); //list of right vertices + + ~PDBLK () { //destructor + } + + TEXT_REGION *text_region() { + return hand_block; + } + POLY_BLOCK *poly_block() { + return hand_poly; + } + void set_poly_block( //set the poly block + POLY_BLOCK *blk) { + hand_poly = blk; + } + void bounding_box( //get box + ICOORD &bottom_left, //bottom left + ICOORD &top_right) const { //topright + bottom_left = box.botleft (); + top_right = box.topright (); + } + //get real box + const BOX &bounding_box() const { + return box; + } + + BOOL8 contains( //is pt inside block + ICOORD pt); + + void move( // reposition block + const ICOORD vec); // by vector + + void plot( //draw histogram + WINDOW window, //window to draw in + INT32 serial, //serial number + COLOUR colour); //colour to draw in + + void show( //show image + IMAGE *image, //image to show + WINDOW window); //window to show in + + PDBLK & operator= ( //assignment + const PDBLK & source); //from this + + protected: + TEXT_REGION * hand_block; //if it exists + POLY_BLOCK *hand_poly; //wierd as well + ICOORDELT_LIST leftside; //left side vertices + ICOORDELT_LIST rightside; //right side vertices + BOX box; //bounding box +}; + +class DLLSYM BLOCK_RECT_IT //rectangle iterator +{ + public: + BLOCK_RECT_IT( //constructor + PDBLK *blkptr); //block to iterate + + //start (new) block + NEWDELETE2 (BLOCK_RECT_IT) void set_to_block ( + PDBLK * blkptr); //block to iterate + + void start_block(); //start iteration + + void forward(); //next rectangle + + BOOL8 cycled_rects() { //test end + return left_it.cycled_list () && right_it.cycled_list (); + } + + void bounding_box( //current rectangle + ICOORD &bleft, //bottom left + ICOORD &tright) { //top right + //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; +}; + +class DLLSYM BLOCK_LINE_IT //rectangle iterator +{ + public: + BLOCK_LINE_IT ( //constructor + PDBLK * blkptr) //from block + :rect_it (blkptr) { + block = blkptr; //remember block + } + + //start (new) block + NEWDELETE2 (BLOCK_LINE_IT) void set_to_block ( + PDBLK * blkptr) { //block to start + block = blkptr; //remember block + //set iterator + rect_it.set_to_block (blkptr); + } + + INT16 get_line( //get a line + INT16 y, //line to get + INT16 &xext); //output extent + + private: + PDBLK * block; //block to iterate + BLOCK_RECT_IT rect_it; //rectangle iterator +}; + +int decreasing_top_order( // + const void *row1, + const void *row2); +#endif diff --git a/ccstruct/pdclass.h b/ccstruct/pdclass.h new file mode 100644 index 0000000000..db5feda026 --- /dev/null +++ b/ccstruct/pdclass.h @@ -0,0 +1,54 @@ +/********************************************************************** + * File: pdclass.h (Formerly pdstruct.h) + * Description: Data structures for read_vec_file. + * Author: Ray Smith + * Created: Tue Nov 3 11:42:08 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 PDCLASS_H +#define PDCLASS_H + +#include "points.h" + +struct VEC_HEADER +{ + INT32 filesize; //bytes in file + INT16 bytesize; //sizeof a byte + INT16 arraysize; //no of blocks + INT16 width; //of image + INT16 height; + INT16 res; //not set + INT16 bpp; +}; + +struct BLOCK_HEADER +{ + UINT8 type; //block type + UINT8 valid; //useable flag + UINT8 charsize; //blob size + UINT8 downsamplerate; //?? + UINT8 subtype; //?? + UINT8 temp; //?? + UINT16 offset; //index in vectors + UINT16 order; //block number + UINT16 entries; //no of vectors +}; + +struct VEC_ENTRY +{ + ICOORD start; //start of vector + ICOORD end; //in clockwise dir +}; +#endif diff --git a/ccstruct/points.cpp b/ccstruct/points.cpp new file mode 100644 index 0000000000..e884bfef55 --- /dev/null +++ b/ccstruct/points.cpp @@ -0,0 +1,64 @@ +/********************************************************************** + * 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. + * + **********************************************************************/ + +#include "mfcpch.h" //precompiled headers +#include "serialis.h" +#include "points.h" + +ELISTIZE_S (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; +} + + +void ICOORD::serialise_asc( //convert to ascii + FILE *f //file to write + ) { + serialise_INT32(f, xcoord); + serialise_INT32(f, ycoord); +} + + +void ICOORD::de_serialise_asc( //convert from ascii + FILE *f //file to write + ) { + xcoord = (INT16) de_serialise_INT32 (f); + ycoord = (INT16) de_serialise_INT32 (f); +} + + +void ICOORDELT::serialise_asc( //convert to ascii + FILE *f //file to write + ) { + ((ICOORD *) this)->serialise_asc (f); +} + + +void ICOORDELT::de_serialise_asc( //convert from ascii + FILE *f //file to write + ) { + ((ICOORD *) this)->de_serialise_asc (f); +} diff --git a/ccstruct/points.h b/ccstruct/points.h new file mode 100644 index 0000000000..6eea0bf1a1 --- /dev/null +++ b/ccstruct/points.h @@ -0,0 +1,284 @@ +/********************************************************************** + * 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" +//#include "ipeerr.h" + +class FCOORD; + +class DLLSYM ICOORD //integer coordinate +{ + friend class FCOORD; + + public: + ICOORD() { //empty constructor + xcoord = ycoord = 0; //default zero + } + ICOORD( //constructor + INT16 xin, //x value + INT16 yin) { //y value + xcoord = xin; + ycoord = yin; + } + ~ICOORD () { //destructor + } + + //access function + NEWDELETE2 (ICOORD) INT16 x () const + { + return xcoord; + } + INT16 y() const { //access_function + return ycoord; + } + + void set_x( //rewrite function + INT16 xin) { + xcoord = xin; //write new value + } + void set_y( //rewrite function + INT16 yin) { //value to set + ycoord = yin; + } + + float sqlength() const { //find sq length + return (float) (xcoord * xcoord + ycoord * ycoord); + } + + float length() const { //find length + return (float) sqrt (sqlength ()); + } + + float pt_to_pt_sqdist( //sq dist between pts + const ICOORD &pt) const { + ICOORD gap; + + gap.xcoord = xcoord - pt.xcoord; + gap.ycoord = ycoord - pt.ycoord; + return gap.sqlength (); + } + + float pt_to_pt_dist( //Distance between pts + const ICOORD &pt) const { + return (float) sqrt (pt_to_pt_sqdist (pt)); + } + + float angle() const { //find angle + return (float) atan2 ((double) ycoord, (double) xcoord); + } + + BOOL8 operator== ( //test equality + const ICOORD & other) { + return xcoord == other.xcoord && ycoord == other.ycoord; + } + BOOL8 operator!= ( //test inequality + const ICOORD & other) { + return xcoord != other.xcoord || ycoord != other.ycoord; + } + friend ICOORD operator! ( //rotate 90 deg anti + const ICOORD &); + friend ICOORD operator- ( //unary minus + const ICOORD &); + friend ICOORD operator+ ( //add + const ICOORD &, const ICOORD &); + friend ICOORD & operator+= ( //add + ICOORD &, const ICOORD &); + friend ICOORD operator- ( //subtract + const ICOORD &, const ICOORD &); + friend ICOORD & operator-= ( //subtract + ICOORD &, const ICOORD &); + friend INT32 operator% ( //scalar product + const ICOORD &, const ICOORD &); + friend INT32 operator *( //cross product + const ICOORD &, + const ICOORD &); + friend ICOORD operator *( //multiply + const ICOORD &, + INT16); + friend ICOORD operator *( //multiply + INT16, + const ICOORD &); + friend ICOORD & operator*= ( //multiply + ICOORD &, INT16); + friend ICOORD operator/ ( //divide + const ICOORD &, INT16); + //divide + friend ICOORD & operator/= (ICOORD &, INT16); + void rotate( //rotate + const FCOORD& vec); //by vector + + void serialise_asc( //serialise to ascii + FILE *f); + void de_serialise_asc( //serialise from ascii + FILE *f); + + protected: + INT16 xcoord; //x value + INT16 ycoord; //y value +}; + +class DLLSYM ICOORDELT:public ELIST_LINK, public ICOORD + //embedded coord list +{ + public: + ICOORDELT() { //empty constructor + } + ICOORDELT ( //constructor + //from ICOORD + ICOORD icoord):ICOORD (icoord) { + } + ICOORDELT( //constructor + INT16 xin, //x value + INT16 yin) { //y value + xcoord = xin; + ycoord = yin; + } + + /* Note that prep_serialise() dump() and de_dump() dont need to do anything + more than terminate recursion. */ + + void prep_serialise() const { //set ptrs to counts + } + + void dump( //write external bits + FILE *) const { + } + + void de_dump( //read external bits + FILE *) { + } + + //serialise to ascii + make_serialise (ICOORDELT) void serialise_asc ( + FILE * f); + void de_serialise_asc( //serialise from ascii + FILE *f); + +}; + +ELISTIZEH_S (ICOORDELT) +class DLLSYM FCOORD +{ + public: + FCOORD() { + } //empty constructor + FCOORD( //constructor + float xvalue, //coords to set + 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; + } + void set_x( //rewrite function + float xin) { + xcoord = xin; //write new value + } + void set_y( //rewrite function + float yin) { //value to set + ycoord = yin; + } + + float sqlength() const { //find sq length + return xcoord * xcoord + ycoord * ycoord; + } + + float length() const { //find length + return (float) sqrt (sqlength ()); + } + + float pt_to_pt_sqdist( //sq dist between pts + const FCOORD &pt) const { + FCOORD gap; + + gap.xcoord = xcoord - pt.xcoord; + gap.ycoord = ycoord - pt.ycoord; + return gap.sqlength (); + } + + float pt_to_pt_dist( //Distance between pts + const FCOORD &pt) const { + return (float) sqrt (pt_to_pt_sqdist (pt)); + } + + float angle() const { //find angle + return (float) atan2 (ycoord, xcoord); + } + + bool normalise(); //Convert to unit vec + + BOOL8 operator== ( //test equality + const FCOORD & other) { + return xcoord == other.xcoord && ycoord == other.ycoord; + } + BOOL8 operator!= ( //test inequality + 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 &); + friend FCOORD operator *(const FCOORD &, float); + //multiply + friend FCOORD operator *(float, const FCOORD &); + //multiply + //multiply + friend FCOORD & operator*= (FCOORD &, float); + friend FCOORD operator/ (const FCOORD &, float); + //divide + void rotate( //rotate + const FCOORD vec); //by vector + //divide + friend FCOORD & operator/= (FCOORD &, float); + + private: + float xcoord; //2 floating coords + float ycoord; +}; + +#include "ipoints.h" /*do inline funcs */ +#endif diff --git a/ccstruct/polyaprx.cpp b/ccstruct/polyaprx.cpp new file mode 100644 index 0000000000..b9093af814 --- /dev/null +++ b/ccstruct/polyaprx.cpp @@ -0,0 +1,583 @@ +/********************************************************************** + * 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 "mfcpch.h" +#include +#ifdef __UNIX__ +#include +#endif +//#include "edgeloop.h" +#define MAXEDGELENGTH 16000 //must replace +#include "polyaprx.h" +#include "varable.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"); + +static int par1, par2; + +#define CONVEX 1 /*OUTLINE point is convex */ +#define CONCAVE 2 /*used and set only in edges */ +#define FIXED 4 /*OUTLINE point is fixed */ +#define ONHULL 8 /*on convex hull */ + +#define RUNLENGTH 1 /*length of run */ + +#define DIR 2 /*direction of run */ + +#define CORRECTION 3 /*correction of run */ +//#define MAXSHORT 32767 /*max value of short*/ +#define FLAGS 0 + +#define fixed_dist 20 //really an int_variable +#define approx_dist 15 //really an int_variable + +#define point_diff(p,p1,p2) (p).x = (p1).x - (p2).x ; (p).y = (p1).y - (p2).y +#define CROSS(a,b) ((a).x * (b).y - (a).y * (b).x) +#define LENGTH(a) ((a).x * (a).x + (a).y * (a).y) + +#define DISTANCE(a,b) (((b).x-(a).x) * ((b).x-(a).x) \ + + ((b).y-(a).y) * ((b).y-(a).y)) + +/********************************************************************** + * tesspoly_outline + * + * Approximate an outline from c form using the old tess algorithm. + **********************************************************************/ + +OUTLINE *tesspoly_outline( //old approximation + C_OUTLINE *c_outline, //input + float //xheight + ) { + EDGEPT *edgept; //converted steps + EDGEPT *startpt; //start of outline + BOX loop_box; //bounding box + INT32 area; //loop area + FCOORD pos; //vertex + FCOORD vec; //vector + POLYPT_LIST polypts; //output polygon + POLYPT *polypt; //converted point + POLYPT_IT poly_it = &polypts; //iterator + EDGEPT edgepts[MAXEDGELENGTH]; //converted path + + 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; + edgept = edgesteps_to_edgepts (c_outline, edgepts); + fix2(edgepts, area); + edgept = poly2 (edgepts, area);/*2nd approximation */ + startpt = edgept; + do { + pos = FCOORD (edgept->pos.x, edgept->pos.y); + vec = FCOORD (edgept->vec.x, edgept->vec.y); + polypt = new POLYPT (pos, vec); + //add to list + poly_it.add_after_then_move (polypt); + edgept = edgept->next; + } + while (edgept != startpt); + if (poly_it.length () <= 2) + return NULL; + else + //turn to outline + return new OUTLINE (&poly_it); +} + + +/********************************************************************** + * 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; + 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; + epindex++; + prevdir = dir; + prev_vec = vec; + count = 1; + } + 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].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) { + register EDGEPT *edgept; /*current point */ + register EDGEPT *edgept1; + register EDGEPT *loopstart; /*modified start of loop */ + register EDGEPT *linestart; /*start of line segment */ + register int dir1, dir2; /*directions of line */ + register 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; + register EDGEPT *edgefix, *startfix; + register 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); + 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--; + /* if ( plots[EDGE] & PATHS ) + mark(edgefd,edgefix2->pos.x,edgefix2->pos.y,PLUS); + */ + } + else { + edgefix1->flags[FLAGS] &= ~FIXED; + fixed_count--; + /* if ( plots[EDGE] & PATHS ) + mark(edgefd,edgefix1->pos.x,edgefix1->pos.y,PLUS); + */ + 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 */ + ) { + register EDGEPT *edgept; /*current outline point */ + EDGEPT *loopstart; /*starting point */ + register EDGEPT *linestart; /*start of line */ + register int edgesum; /*correction count */ + + if (area < 1200) + area = 1200; /*minimum value */ + + /*1200(4) */ + par1 = 4500 / (approx_dist * approx_dist); + /*1200(6) */ + par2 = 6750 / (approx_dist * approx_dist); + + 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 */ + ) { + register 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 */ + register 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); + } +} diff --git a/ccstruct/polyaprx.h b/ccstruct/polyaprx.h new file mode 100644 index 0000000000..6e6feaef57 --- /dev/null +++ b/ccstruct/polyaprx.h @@ -0,0 +1,51 @@ +/********************************************************************** + * 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 "tessclas.h" +#include "poutline.h" +#include "coutln.h" + +OUTLINE *tesspoly_outline( //old approximation + C_OUTLINE *c_outline, //input + float //xheight + ); +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 */ + ); +#define fixed_dist 20 //really an int_variable +#define point_diff(p,p1,p2) (p).x = (p1).x - (p2).x ; (p).y = (p1).y - (p2).y +#define CROSS(a,b) ((a).x * (b).y - (a).y * (b).x) +#define LENGTH(a) ((a).x * (a).x + (a).y * (a).y) +#endif diff --git a/ccstruct/polyblk.cpp b/ccstruct/polyblk.cpp new file mode 100644 index 0000000000..00a0b94c46 --- /dev/null +++ b/ccstruct/polyblk.cpp @@ -0,0 +1,397 @@ +/********************************************************************** + * 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 "mfcpch.h" +#include +#include +#include +#include "elst.h" +#include "polyblk.h" + +#include "hpddef.h" //must be last (handpd.dll) + +#define PBLOCK_LABEL_SIZE 150 +#define INTERSECTING MAX_INT16 + +int lessthan(const void *first, const void *second); + +POLY_BLOCK::POLY_BLOCK(ICOORDELT_LIST *points, POLY_TYPE t) { + ICOORDELT_IT v = &vertices; + + vertices.clear (); + v.move_to_first (); + v.add_list_before (points); + compute_bb(); + type = t; +} + + +/********************************************************************** + * 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 = BOX (ibl, itr); +} + + +/********************************************************************** + * POLY_BLOCK::winding_number + * + * Return the winding number of the outline around the given point. + **********************************************************************/ + +INT16 POLY_BLOCK::winding_number( //winding number + const ICOORD &point //point to wind around + ) { + 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 +} + + +/********************************************************************** + * POLY_BLOCK::contains + * + * Is poly within poly + **********************************************************************/ + +BOOL8 POLY_BLOCK::contains( //other outline + 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; +} + + +/********************************************************************** + * POLY_BLOCK::rotate + * + * Rotate the POLY_BLOCK. + **********************************************************************/ + +void POLY_BLOCK::rotate( //constructor + FCOORD rotation //cos,sin of angle + ) { + 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(); +} + + +/********************************************************************** + * POLY_BLOCK::move + * + * Move the POLY_BLOCK. + **********************************************************************/ + +void POLY_BLOCK::move( //constructor + ICOORD shift //cos,sin of angle + ) { + 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(WINDOW window, COLOUR colour, INT32 num) { + ICOORDELT_IT v = &vertices; + + line_color_index(window, colour); + v.move_to_first (); + + if (num > 0) { + text_color_index(window, colour); + character_height (window, (float) 80); + text_font_index (window, 6); + char temp_buff[34]; + #ifdef __UNIX__ + sprintf(temp_buff, INT32FORMAT, num); + #else + ltoa (num, temp_buff, 10); + #endif + text2d (window, v.data ()->x (), v.data ()->y (), temp_buff, 0, FALSE); + } + move2d (window, v.data ()->x (), v.data ()->y ()); + for (v.mark_cycle_pt (); !v.cycled_list (); v.forward ()) + draw2d (window, v.data ()->x (), v.data ()->y ()); + v.move_to_first (); + draw2d (window, v.data ()->x (), v.data ()->y ()); +} + + +void POLY_BLOCK::fill(WINDOW window, COLOUR colour) { + INT16 y; + INT16 width; + PB_LINE_IT *lines; + ICOORDELT_LIST *segments; + ICOORDELT_IT s_it; + + lines = new PB_LINE_IT (this); + + line_color_index(window, 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 (); + move2d (window, s_it.data ()->x (), y); + draw2d (window, s_it.data ()->x () + (float) width, y); + } + } + } +} +#endif + + +BOOL8 POLY_BLOCK::overlap( // do polys 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); +} + + +/********************************************************************** + * POLY_BLOCK::serialise_asc + * + * Converto to ascii file. + **********************************************************************/ + +void POLY_BLOCK::serialise_asc( //convert to ascii + FILE *f //file to use + ) { + vertices.serialise_asc (f); + box.serialise_asc (f); + serialise_INT32(f, type); +} + + +/********************************************************************** + * POLY_BLOCK::de_serialise_asc + * + * Converto from ascii file. + **********************************************************************/ + +void POLY_BLOCK::de_serialise_asc( //convert from ascii + FILE *f //file to use + ) { + vertices.de_serialise_asc (f); + box.de_serialise_asc (f); + type = (POLY_TYPE) de_serialise_INT32 (f); +} diff --git a/ccstruct/polyblk.h b/ccstruct/polyblk.h new file mode 100644 index 0000000000..6327ab0e3e --- /dev/null +++ b/ccstruct/polyblk.h @@ -0,0 +1,122 @@ +/********************************************************************** + * 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 "rect.h" +#include "points.h" +#include "grphics.h" +#include "elst.h" + +#include "hpddef.h" //must be last (handpd.dll) + +enum POLY_TYPE +{ + POLY_TEXT, // Text region + POLY_PAGE, // Page block + POLY_X // Don't care +}; + +class DLLSYM POLY_BLOCK //poly block +{ + + public: + POLY_BLOCK() { + } //empty constructor + POLY_BLOCK( //simple constructor + ICOORDELT_LIST *points, + POLY_TYPE t); + ~POLY_BLOCK () { + } //destructor + + BOX *bounding_box() { // access function + return &box; + } + + ICOORDELT_LIST *points() { // access function + return &vertices; + } + + void compute_bb(); + + POLY_TYPE isA() { + return type; + } + + void rotate( //rotate it + FCOORD rotation); + void move( //move it + ICOORD shift); //vector + + void plot(WINDOW window, COLOUR colour, INT32 num); + + void fill(WINDOW window, COLOUR colour); + + BOOL8 contains( // is poly inside poly + POLY_BLOCK *poly); + + BOOL8 overlap( // do polys overlap + POLY_BLOCK *poly); + + INT16 winding_number( // get winding number + const ICOORD &test_pt); // around this point + + void prep_serialise() { + vertices.prep_serialise (); + } + + void dump(FILE *f) { + vertices.dump (f); + } + + void de_dump(FILE *f) { + vertices.de_dump (f); + } + + //convert to ascii + make_serialise (POLY_BLOCK) void serialise_asc ( + FILE * f); + void de_serialise_asc( //convert from ascii + FILE *f); + + private: + ICOORDELT_LIST vertices; // vertices + BOX box; // bounding box + POLY_TYPE type; // Page block or + // text region +}; + +class DLLSYM PB_LINE_IT //line iterator +{ + public: + PB_LINE_IT( //constructor + POLY_BLOCK *blkptr) { + block = blkptr; + } + + NEWDELETE2 (PB_LINE_IT) void set_to_block (POLY_BLOCK * blkptr) { + block = blkptr; + } + + ICOORDELT_LIST *get_line(INT16 y); + + private: + POLY_BLOCK * block; +}; +#endif diff --git a/ccstruct/polyblob.cpp b/ccstruct/polyblob.cpp new file mode 100644 index 0000000000..a8bc10db39 --- /dev/null +++ b/ccstruct/polyblob.cpp @@ -0,0 +1,344 @@ +/********************************************************************** + * File: polyblob.cpp (Formerly blob.c) + * Description: Code for PBLOB class. + * Author: Ray Smith + * Created: Wed Oct 23 15:17:41 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 "mfcpch.h" +#include "varable.h" +#include "ocrrow.h" +#include "polyblob.h" +//#include "lapoly.h" +#include "polyaprx.h" + +#define EXTERN + +EXTERN BOOL_VAR (polygon_tess_approximation, TRUE, +"Do tess poly instead of greyscale"); + +ELISTIZE_S (PBLOB) +/********************************************************************** + * position_outline + * + * Position the outline in the given list at the relevant place + * according to its nesting. + **********************************************************************/ +static void position_outline( //put in place + OUTLINE *outline, //thing to place + OUTLINE_LIST *destlist //desstination list + ) { + OUTLINE *dest_outline; //outline from dest list + OUTLINE_IT it = destlist; //iterator + //iterator on children + 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 + OUTLINE_LIST *list, //outline to draw + WINDOW window, //window to draw in + COLOUR colour, //colour to use + COLOUR child_colour //colour of children + ) { + OUTLINE *outline; //current outline + 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); + } +} +#endif + + +/********************************************************************** + * PBLOB::PBLOB + * + * Constructor to build a PBLOB from a list of OUTLINEs. + * The OUTLINEs are not copied so the source list is emptied. + * The OUTLINEs are nested correctly in the blob. + **********************************************************************/ + +PBLOB::PBLOB( //constructor + OUTLINE_LIST *outline_list //in random order + ) { + OUTLINE *outline; //current outline + OUTLINE_IT it = outline_list; //iterator + + while (!it.empty ()) { //grab the list + outline = it.extract (); //get off the list + //put it in place + position_outline(outline, &outlines); + if (!it.empty ()) + it.forward (); + } +} + + +/********************************************************************** + * approximate_outline_list + * + * Convert a list of outlines to polygonal form. + **********************************************************************/ + +static void approximate_outline_list( //do list of outlines + C_OUTLINE_LIST *srclist, //list to convert + OUTLINE_LIST *destlist, //desstination list + float xheight //height of line + ) { + C_OUTLINE *src_outline; //outline from src list + OUTLINE *dest_outline; //result + C_OUTLINE_IT src_it = srclist; //source iterator + OUTLINE_IT dest_it = destlist; //iterator + + do { + src_outline = src_it.data (); + // if (polygon_tess_approximation) + dest_outline = tesspoly_outline (src_outline, xheight); + // else + // dest_outline=greypoly_outline(src_outline,xheight); + if (dest_outline != NULL) { + dest_it.add_after_then_move (dest_outline); + if (!src_outline->child ()->empty ()) + //do child list + approximate_outline_list (src_outline->child (), dest_outline->child (), xheight); + } + src_it.forward (); + } + while (!src_it.at_first ()); +} + + +/********************************************************************** + * PBLOB::PBLOB + * + * Constructor to build a PBLOB from a C_BLOB by polygonal approximation. + **********************************************************************/ + +PBLOB::PBLOB( //constructor + C_BLOB *cblob, //compact blob + float xheight //height of line + ) { + BOX bbox; //bounding box + + if (!cblob->out_list ()->empty ()) { + //get bounding box + bbox = cblob->bounding_box (); + if (bbox.height () > xheight) + xheight = bbox.height (); //max of line and blob + //copy it + approximate_outline_list (cblob->out_list (), &outlines, xheight); + } +} + + +/********************************************************************** + * PBLOB::bounding_box + * + * Return the bounding box of the blob. + **********************************************************************/ + +BOX PBLOB::bounding_box() { //bounding box + OUTLINE *outline; //current outline + OUTLINE_IT it = &outlines; //outlines of blob + BOX box; //bounding box + + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + outline = it.data (); + box += outline->bounding_box (); + } + return box; +} + + +/********************************************************************** + * PBLOB::area + * + * Return the area of the blob. + **********************************************************************/ + +float PBLOB::area() { //area + OUTLINE *outline; //current outline + OUTLINE_IT it = &outlines; //outlines of blob + float total; //total area + + total = 0.0f; + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + outline = it.data (); + total += outline->area (); + } + return total; +} + + +/********************************************************************** + * PBLOB::baseline_normalise + * + * Baseline normalize a blob + **********************************************************************/ + +PBLOB *PBLOB::baseline_normalise( //normalize blob + ROW *row, //row it came from + DENORM *denorm //inverse mapping + ) { + BOX blob_box = bounding_box (); + float x_centre = (blob_box.left () + blob_box.right ()) / 2.0; + PBLOB *bn_blob; //copied blob + + *denorm = DENORM (x_centre, bln_x_height / row->x_height (), row); + bn_blob = new PBLOB; //get one + *bn_blob = *this; //deep copy + bn_blob->move (FCOORD (-denorm->origin (), -row->base_line (x_centre))); + bn_blob->scale (denorm->scale ()); + bn_blob->move (FCOORD (0.0, bln_baseline_offset)); + return bn_blob; +} + + +/********************************************************************** + * PBLOB::baseline_denormalise + * + * DeBaseline Normalise the blob properly with the given denorm. + **********************************************************************/ + +void PBLOB::baseline_denormalise( // Tess style BL Norm + const DENORM *denorm //antidote + ) { + float blob_x_left; // Left edge of blob. + BOX blob_box; //blob bounding box + + move(FCOORD (0.0f, 0.0f - bln_baseline_offset)); + blob_box = bounding_box (); + blob_x_left = blob_box.left (); + scale (1.0 / denorm->scale_at_x (blob_x_left)); + move (FCOORD (denorm->origin (), + denorm->yshift_at_x (blob_x_left))); +} + + +/********************************************************************** + * PBLOB::move + * + * Move PBLOB by vector + **********************************************************************/ + +void PBLOB::move( // reposition blob + const FCOORD vec // by vector + ) { + OUTLINE_IT it(&outlines); // iterator + + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) + it.data ()->move (vec); // move each outline +} + + +/********************************************************************** + * PBLOB::scale + * + * Scale PBLOB by float multiplier + **********************************************************************/ + +void PBLOB::scale( // scale blob + const float f // by multiplier + ) { + OUTLINE_IT it(&outlines); // iterator + + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) + it.data ()->scale (f); // scale each outline +} + + +/********************************************************************** + * PBLOB::scale + * + * Scale PBLOB by float multiplier + **********************************************************************/ + +void PBLOB::scale( // scale blob + const FCOORD vec // by multiplier + ) { + OUTLINE_IT it(&outlines); // iterator + + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) + it.data ()->scale (vec); // scale each outline +} + + +/********************************************************************** + * PBLOB::plot + * + * Draw the PBLOB in the given colour. + **********************************************************************/ + +#ifndef GRAPHICS_DISABLED +void PBLOB::plot( //draw it + WINDOW window, //window to draw in + COLOUR blob_colour, //main colour + COLOUR child_colour //for holes + ) { + plot_outline_list(&outlines, window, blob_colour, child_colour); +} +#endif diff --git a/ccstruct/polyblob.h b/ccstruct/polyblob.h new file mode 100644 index 0000000000..74cec0026b --- /dev/null +++ b/ccstruct/polyblob.h @@ -0,0 +1,94 @@ +/********************************************************************** + * File: polyblob.h (Formerly blob.h) + * Description: Code for PBLOB class. + * Author: Ray Smith + * Created: Wed Oct 23 15:17:41 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 POLYBLOB_H +#define POLYBLOB_H + +#include "poutline.h" +#include "rect.h" +#include "normalis.h" +#include "stepblob.h" + +class PBLOB:public ELIST_LINK +{ + public: + PBLOB() { + } //empty constructor + PBLOB( //constructor + OUTLINE_LIST *outline_list); //in random order + PBLOB( //constructor + C_BLOB *cblob, //polygonal approx + float xheight); + + OUTLINE_LIST *out_list() { //get outline list + return &outlines; + } + + BOX bounding_box(); //compute bounding box + float area(); //get area of blob + + PBLOB *baseline_normalise( //normalise single blob + ROW *row, //row it came from + DENORM *denorm); //inverse mapping out + void baseline_denormalise( //denormalise + const DENORM *denorm); //antidote + + void plot( //draw one + WINDOW window, //window to draw in + COLOUR blob_colour, //for outer bits + COLOUR child_colour); //for holes + + void move( // reposition blob + const FCOORD vec); // by FLOAT vector + + void scale( // scale blob + const float f); // by multiplier + void scale( // scale blob + const FCOORD vec); // by FLOAT vector + + void prep_serialise() { //set ptrs to counts + outlines.prep_serialise (); + } + + void dump( //write external bits + FILE *f) { + outlines.dump (f); + } + + void de_dump( //read external bits + FILE *f) { + outlines.de_dump (f); + } + + //assignment + make_serialise (PBLOB) PBLOB & operator= ( + const PBLOB & source) { //from this + if (!outlines.empty ()) + outlines.clear (); + + outlines.deep_copy (&source.outlines); + return *this; + } + + private: + OUTLINE_LIST outlines; //master elements +}; + +ELISTIZEH_S (PBLOB) +#endif diff --git a/ccstruct/polyvert.cpp b/ccstruct/polyvert.cpp new file mode 100644 index 0000000000..cad11add90 --- /dev/null +++ b/ccstruct/polyvert.cpp @@ -0,0 +1,23 @@ +/********************************************************************** + * File: polyvert.cpp (Formerly polypt.c) + * Description: Code for the POLYPT class. + * Author: Ray Smith + * Created: Wed Oct 23 11:02:56 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 "mfcpch.h" +#include "polyvert.h" + +ELIST2IZE_S (POLYPT) diff --git a/ccstruct/polyvert.h b/ccstruct/polyvert.h new file mode 100644 index 0000000000..15b0628677 --- /dev/null +++ b/ccstruct/polyvert.h @@ -0,0 +1,52 @@ +/********************************************************************** + * File: polyvert.h (Formerly polypt.h) + * Description: Code for the POLYPT class. + * Author: Ray Smith + * Created: Wed Oct 23 11:02:56 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 POLYVERT_H +#define POLYVERT_H + +#include "elst2.h" +#include "rect.h" + +class POLYPT:public ELIST2_LINK +{ + public: + POLYPT() { //empty + } + POLYPT( //constructor + const FCOORD &position, //coords + const FCOORD &vector) { //step to next + pos = position; + vec = vector; //just copy + } + + void prep_serialise() { //set ptrs to counts + } + void dump( //write external bits + FILE *) { + } + void de_dump( //read external bits + FILE *) { + } + //really simple + make_serialise (POLYPT) FCOORD pos; + FCOORD vec; //vector to next +}; + +ELIST2IZEH_S (POLYPT) +#endif diff --git a/ccstruct/poutline.cpp b/ccstruct/poutline.cpp new file mode 100644 index 0000000000..8a65ec0c5c --- /dev/null +++ b/ccstruct/poutline.cpp @@ -0,0 +1,409 @@ +/********************************************************************** + * File: poutline.cpp (Formerly outline.c) + * Description: Code for OUTLINE class. + * Author: Ray Smith + * Created: Wed Oct 23 10:52: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 "mfcpch.h" +#include "poutline.h" + +ELISTIZE_S (OUTLINE) +/********************************************************************** + * OUTLINE::OUTLINE + * + * Constructor to build a OUTLINE from a compact LOOP. + **********************************************************************/ +OUTLINE::OUTLINE ( //constructor +const ICOORD & startpt, //start position +INT8 * compactloop, //from Tess format +BOOL8 invert, //reverse it +ICOORD bot_left, //bounding box +ICOORD top_right): +box (bot_left, top_right), +start(startpt) { + ICOORD pos; //current point + ICOORD vec; //vector to next + POLYPT *polypt; //new point + INT8 *vector; //compact loop + POLYPT_IT it = &outline; //iterator + + pos = startpt; + vector = compactloop; + do { + //vector to next + vec = ICOORD (*vector, *(vector + 1)); + //make a new one + polypt = new POLYPT (FCOORD (pos), FCOORD (vec)); + //add to list + it.add_after_then_move (polypt); + pos += vec; //move to next + vector += 2; + } + while (pos != startpt); + if (invert) + reverse(); //now reverse it +} + + +/********************************************************************** + * OUTLINE::OUTLINE + * + * Constructor to build an OUTLINE from a list of POLYPTs. + **********************************************************************/ + +OUTLINE::OUTLINE( //constructor + POLYPT_IT *polypts //input list + ) { + POLYPT_IT other_it = *polypts; //end of list + + polypts->move_to_first (); + other_it.move_to_last (); + //put in outline + outline.assign_to_sublist (polypts, &other_it); + compute_bb(); +} + + +/********************************************************************** + * OUTLINE::compute_bb + * + * Compute the bounding box from the outline points. + **********************************************************************/ + +void OUTLINE::compute_bb() { //constructor + ICOORD ibl, itr; //integer bb + FCOORD botleft; //bounding box + FCOORD topright; + FCOORD pos; //current pos; + POLYPT_IT polypts = &outline; //iterator + + botleft = polypts.data ()->pos; + topright = botleft; + start = ICOORD ((INT16) botleft.x (), (INT16) botleft.y ()); + do { + pos = polypts.data ()->pos; + if (pos.x () < botleft.x ()) + //get bounding box + botleft = FCOORD (pos.x (), botleft.y ()); + if (pos.y () < botleft.y ()) + botleft = FCOORD (botleft.x (), pos.y ()); + if (pos.x () > topright.x ()) + topright = FCOORD (pos.x (), topright.y ()); + if (pos.y () > topright.y ()) + topright = FCOORD (topright.x (), pos.y ()); + polypts.forward (); + } + while (!polypts.at_first ()); + ibl = ICOORD ((INT16) botleft.x (), (INT16) botleft.y ()); + itr = ICOORD ((INT16) topright.x () + 1, (INT16) topright.y () + 1); + box = BOX (ibl, itr); +} + + +/********************************************************************** + * OUTLINE::area + * + * Compute the area from the outline points. + **********************************************************************/ + +float OUTLINE::area() { //constructor + FCOORD origin; //startpt + FCOORD prev_vec; //previous value of vec + FCOORD vec; //from start to current + float total; //total area + POLYPT_IT poly_it = polypts ();//iterator + //child outline itertr + OUTLINE_IT child_it(&children); + + origin = poly_it.data ()->pos; + poly_it.forward (); + vec = poly_it.data ()->pos - origin; + poly_it.forward (); + total = 0.0f; + while (!poly_it.at_first ()) { + prev_vec = vec; + vec = poly_it.data ()->pos - origin; + total += prev_vec * vec; + poly_it.forward (); + } + total /= 2; + for (child_it.mark_cycle_pt (); !child_it.cycled_list (); + child_it.forward ()) { + //add ares of childrein + total += child_it.data ()->area (); + } + return total; +} + + +/********************************************************************** + * OUTLINE::operator< + * + * Return TRUE if the left operand is inside the right one. + **********************************************************************/ + +BOOL8 +OUTLINE::operator< ( //winding number +OUTLINE & other //other outline +) { + INT16 count; //winding count + POLYPT_IT it = &outline; //iterator + + if (!box.overlap (other.box)) + return FALSE; //can't be contained + + do { + count = other.winding_number (FCOORD (it.data ()->pos)); + //get winding number + if (count != INTERSECTING) + return count != 0; + it.forward (); + } + while (!it.at_first ()); + + //switch lists + it.set_to_list (&other.outline); + do { + //try other way round + count = winding_number (FCOORD (it.data ()->pos)); + if (count != INTERSECTING) + return count == 0; + it.forward (); + } + while (!it.at_first ()); + return TRUE; +} + + +/********************************************************************** + * OUTLINE::winding_number + * + * Return the winding number of the outline around the given point. + **********************************************************************/ + +INT16 OUTLINE::winding_number( //winding number + const FCOORD &point //point to wind around + ) { + INT16 count; //winding count + POLYPT *polypt; //current point + FCOORD vec; //to current point + float cross; //cross product + POLYPT_IT it = &outline; //iterator + + count = 0; + do { + polypt = it.data (); + vec = polypt->pos - point; + //crossing the line + if (vec.y () <= 0 && vec.y () + polypt->vec.y () > 0) { + cross = vec * polypt->vec; //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 () + polypt->vec.y () <= 0) { + cross = vec * polypt->vec; + if (cross < 0) + count--; //crossing back + else if (cross == 0) + return INTERSECTING; //illegal + } + it.forward (); + } + while (!it.at_first ()); + return count; //winding number +} + + +/********************************************************************** + * OUTLINE::reverse + * + * Reverse the direction of an outline. + **********************************************************************/ + +void OUTLINE::reverse() { //reverse direction + POLYPT_LIST back_list; //reversed list + POLYPT_IT dest_it = &back_list;//destination + POLYPT_IT src_it = &outline; //source list + POLYPT *polypt; //current point + + do { + polypt = src_it.extract (); + //copy in reverse + dest_it.add_after_then_move (polypt); + src_it.backward (); + } + while (!src_it.empty ()); + dest_it.move_to_first (); + do { + polypt = dest_it.data (); + polypt->vec = dest_it.data_relative (1)->pos - polypt->pos; + //vector to next + dest_it.forward (); + } + while (!dest_it.at_first ()); + dest_it.backward (); + src_it.set_to_list (&back_list); + //put it back + outline.assign_to_sublist (&src_it, &dest_it); +} + + +/********************************************************************** + * OUTLINE::move + * + * Move OUTLINE by vector + **********************************************************************/ + +void OUTLINE::move( // reposition OUTLINE + const FCOORD vec // by vector + ) { + //child outline itertr + OUTLINE_IT child_it(&children); + POLYPT_IT poly_it(&outline); //outline point itertr + + box.move (vec); + + start.set_x ((INT16) floor (start.x () + vec.x () + 0.5)); + // ?? Why ICOORD? + start.set_y ((INT16) floor (start.y () + vec.y () + 0.5)); + // ?? Why ICOORD? + + for (poly_it.mark_cycle_pt (); !poly_it.cycled_list (); poly_it.forward ()) + poly_it.data ()->pos += vec; + + for (child_it.mark_cycle_pt (); !child_it.cycled_list (); + child_it.forward ()) + child_it.data ()->move (vec); // move child outlines +} + + +/********************************************************************** + * OUTLINE::scale + * + * Scale OUTLINE by vector + **********************************************************************/ + +void OUTLINE::scale( // scale OUTLINE + const float f // by multiplier + ) { + //child outline itertr + OUTLINE_IT child_it(&children); + POLYPT_IT poly_it(&outline); //outline point itertr + POLYPT *pt; + + box.scale (f); + + // ?? Why ICOORD? + start.set_x ((INT16) floor (start.x () * f + 0.5)); + // ?? Why ICOORD? + start.set_y ((INT16) floor (start.y () * f + 0.5)); + + for (poly_it.mark_cycle_pt (); !poly_it.cycled_list (); poly_it.forward ()) { + pt = poly_it.data (); + pt->pos *= f; + pt->vec *= f; + } + + for (child_it.mark_cycle_pt (); !child_it.cycled_list (); + child_it.forward ()) + child_it.data ()->scale (f); //scale child outlines +} + + +/********************************************************************** + * OUTLINE::scale + * + * Scale OUTLINE by vector + **********************************************************************/ + +void OUTLINE::scale( // scale OUTLINE + const FCOORD vector //by fcoord + ) { + //child outline itertr + OUTLINE_IT child_it(&children); + POLYPT_IT poly_it(&outline); //outline point itertr + POLYPT *pt; + + box.scale (vector); + + start.set_x ((INT16) floor (start.x () * vector.x () + 0.5)); + // ?? Why ICOORD? + start.set_y ((INT16) floor (start.y () * vector.y () + 0.5)); + // ?? Why ICOORD? + + for (poly_it.mark_cycle_pt (); !poly_it.cycled_list (); poly_it.forward ()) { + pt = poly_it.data (); + pt->pos = + FCOORD (pt->pos.x () * vector.x (), pt->pos.y () * vector.y ()); + pt->vec = + FCOORD (pt->vec.x () * vector.x (), pt->vec.y () * vector.y ()); + } + + for (child_it.mark_cycle_pt (); !child_it.cycled_list (); + child_it.forward ()) + //scale child outlines + child_it.data ()->scale (vector); +} + + +/********************************************************************** + * OUTLINE::plot + * + * Draw the outline in the given colour. + **********************************************************************/ + +#ifndef GRAPHICS_DISABLED +void OUTLINE::plot( //draw it + WINDOW window, //window to draw in + COLOUR colour //colour to draw in + ) { + POLYPT *polypt; //current point + POLYPT_IT it = &outline; //iterator + + line_color_index(window, colour); + polypt = it.data (); + move2d (window, polypt->pos.x (), polypt->pos.y ()); + do { + it.forward (); + polypt = it.data (); + draw2d (window, polypt->pos.x (), polypt->pos.y ()); + } + while (!it.at_first ()); +} +#endif + + +/********************************************************************** + * OUTLINE::operator= + * + * Assignment - deep copy data + **********************************************************************/ + +OUTLINE & OUTLINE::operator= ( //assignment +const OUTLINE & source //from this +) { + box = source.box; + start = source.start; + if (!outline.empty ()) + outline.clear (); + outline.deep_copy (&source.outline); + if (!children.empty ()) + children.clear (); + children.deep_copy (&source.children); + return *this; +} diff --git a/ccstruct/poutline.h b/ccstruct/poutline.h new file mode 100644 index 0000000000..6ab98232a5 --- /dev/null +++ b/ccstruct/poutline.h @@ -0,0 +1,115 @@ +/********************************************************************** + * File: poutline.h (Formerly outline.h) + * Description: OUTLINE class definition. + * Author: Ray Smith + * Created: Wed Oct 23 10:42: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 POUTLINE_H +#define POUTLINE_H + +#include "grphics.h" +#include "polyvert.h" +#include "rect.h" +#include "blckerr.h" + +#define INTERSECTING MAX_INT16//no winding number + +class OUTLINE; //forward declaration + +ELISTIZEH_S (OUTLINE) +class OUTLINE:public ELIST_LINK +{ + public: + OUTLINE() { //empty constructor + } + OUTLINE( //constructor + const ICOORD &startpt, //start point + INT8 *compactloop, //from Tess format + BOOL8 reverse, //reverse it + ICOORD bot_left, //bounding box + ICOORD top_right); + OUTLINE( //constructor + POLYPT_IT *poly_it); //from list of pts + + OUTLINE_LIST *child() { //get child list + return &children; + } + + //access function + const BOX &bounding_box() const { + return box; + } + void compute_bb(); //set bounding box + + //get start position + const ICOORD &start_pos() const { + return start; + } + float area(); //return area + POLYPT_LIST *polypts() { //get poly + return &outline; + } + + BOOL8 operator< ( //containment test + OUTLINE & other); + BOOL8 operator> ( //containment test + OUTLINE & other) { + return other < *this; //use the < to do it + } + INT16 winding_number( //get winding number + const FCOORD &testpt); //around this point + void reverse(); //reverse it + + void move( // reposition outline + const FCOORD vec); // by FLOAT vector + + void scale( // scale outline + const float f); // by multiplier + void scale( // scale outline + const FCOORD vec); // by FLOAT vector + + void plot( //draw one + WINDOW window, //window to draw in + COLOUR colour); //colour to draw it + + void prep_serialise() { //set ptrs to counts + outline.prep_serialise (); + children.prep_serialise (); + } + + void dump( //write external bits + FILE *f) { + outline.dump (f); + children.dump (f); + } + + void de_dump( //read external bits + FILE *f) { + outline.de_dump (f); + children.de_dump (f); + } + + //assignment + make_serialise (OUTLINE) OUTLINE & operator= ( + const OUTLINE & source); //from this + + private: + BOX box; //boudning box + ICOORD start; //start coord + POLYPT_LIST outline; //outline points + OUTLINE_LIST children; //child elements +}; +#endif diff --git a/ccstruct/quadlsq.cpp b/ccstruct/quadlsq.cpp new file mode 100644 index 0000000000..8f744c5463 --- /dev/null +++ b/ccstruct/quadlsq.cpp @@ -0,0 +1,147 @@ +/********************************************************************** + * 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 "mfcpch.h" +#include +#include +#include "errcode.h" +#include "quadlsq.h" + +const ERRCODE EMPTY_QLSQ = "Can't delete from an empty QLSQ"; + +#define EXTERN + +/********************************************************************** + * QLSQ::clear + * + * Function to initialize a QLSQ. + **********************************************************************/ + +void QLSQ::clear() { //initialize + a = 0; + b = 0; + c = 0; + n = 0; //no elements + sigx = 0; //update accumulators + sigy = 0; + sigxx = 0; + sigxy = 0; + sigyy = 0; + sigxxx = 0; + sigxxy = 0; + sigxxxx = 0; +} + + +/********************************************************************** + * QLSQ::add + * + * Add an element to the accumulator. + **********************************************************************/ + +void QLSQ::add( //add an element + double x, //xcoord + double y //ycoord + ) { + n++; //count elements + sigx += x; //update accumulators + sigy += y; + sigxx += x * x; + sigxy += x * y; + sigyy += y * y; + sigxxx += (long double) x *x * x; + sigxxy += (long double) x *x * y; + sigxxxx += (long double) x *x * x * x; +} + + +/********************************************************************** + * QLSQ::remove + * + * Delete an element from the acculuator. + **********************************************************************/ + +void QLSQ::remove( //delete an element + double x, //xcoord + double y //ycoord + ) { + if (n <= 0) + //illegal + EMPTY_QLSQ.error ("QLSQ::remove", ABORT, NULL); + n--; //count elements + sigx -= x; //update accumulators + sigy -= y; + sigxx -= x * x; + sigxy -= x * y; + sigyy -= y * y; + sigxxx -= (long double) x *x * x; + sigxxy -= (long double) x *x * y; + sigxxxx -= (long double) x *x * x * x; +} + + +/********************************************************************** + * QLSQ::fit + * + * Fit the given degree of polynomial and store the result. + **********************************************************************/ + +void QLSQ::fit( //fit polynomial + int degree //degree to fit + ) { + long double cubetemp; //intermediates + long double squaretemp; + long double top96, bottom96; /*accurate top & bottom */ + + if (n >= 4 && degree >= 2) { + cubetemp = sigxxx * n - (long double) sigxx *sigx; + + top96 = + cubetemp * ((long double) sigxy * n - (long double) sigx * sigy); + + squaretemp = (long double) sigxx *n - (long double) sigx *sigx; + + top96 += squaretemp * ((long double) sigxx * sigy - sigxxy * n); + + bottom96 = cubetemp * cubetemp; + + bottom96 -= squaretemp * (sigxxxx * n - (long double) sigxx * sigxx); + + a = top96 / bottom96; + + top96 = ((long double) sigxx * sigx - sigxxx * n) * a + + (long double) sigxy *n - (long double) sigx *sigy; + bottom96 = (long double) sigxx *n - (long double) sigx *sigx; + b = top96 / bottom96; + + c = (sigy - a * sigxx - b * sigx) / n; + } + else if (n == 0 || degree < 0) { + a = b = c = 0; + } + else { + a = 0; + if (n > 1 && degree > 0) { + b = (sigxy * n - sigx * sigy) / (sigxx * n - sigx * sigx); + } + else + b = 0; + c = (sigy - b * sigx) / n; + } +} diff --git a/ccstruct/quadlsq.h b/ccstruct/quadlsq.h new file mode 100644 index 0000000000..2270369e2d --- /dev/null +++ b/ccstruct/quadlsq.h @@ -0,0 +1,67 @@ +/********************************************************************** + * 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 diff --git a/ccstruct/quadratc.cpp b/ccstruct/quadratc.cpp new file mode 100644 index 0000000000..2f10ab2265 --- /dev/null +++ b/ccstruct/quadratc.cpp @@ -0,0 +1,21 @@ +/********************************************************************** + * File: quadratc.cpp (Formerly quadrtic.c) + * 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. + * + **********************************************************************/ + +#include "mfcpch.h" +#include "quadratc.h" diff --git a/ccstruct/quadratc.h b/ccstruct/quadratc.h new file mode 100644 index 0000000000..9148f548a0 --- /dev/null +++ b/ccstruct/quadratc.h @@ -0,0 +1,63 @@ +/********************************************************************** + * 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 diff --git a/ccstruct/quspline.cpp b/ccstruct/quspline.cpp new file mode 100644 index 0000000000..f84183d0f0 --- /dev/null +++ b/ccstruct/quspline.cpp @@ -0,0 +1,382 @@ +/********************************************************************** + * 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 "mfcpch.h" +#include "memry.h" +#include "quadlsq.h" +#include "quspline.h" + +#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 +) { + register int pointindex; /*no along text line */ + register 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 + ) { + register 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 + WINDOW window, //window to draw in + COLOUR 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 + + line_color_index(window, 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) + move2d (window, x, quadratics[segment].y (x)); + else + draw2d (window, x, quadratics[segment].y (x)); + x += increment; + } + } +} +#endif diff --git a/ccstruct/quspline.h b/ccstruct/quspline.h new file mode 100644 index 0000000000..fc2a7e332b --- /dev/null +++ b/ccstruct/quspline.h @@ -0,0 +1,114 @@ +/********************************************************************** + * 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 "grphics.h" +#include "quadratc.h" +#include "serialis.h" +#include "memry.h" +#include "rect.h" + +class ROW; + +class QSPLINE +{ + friend void make_first_baseline(BOX *, + int, + int *, + int *, + QSPLINE *, + QSPLINE *, + float); + friend void make_holed_baseline(BOX *, int, QSPLINE *, QSPLINE *, float); + friend void tweak_row_baseline(ROW *); + 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 + WINDOW window, //in window + COLOUR colour) const; //in colour +#endif + + void prep_serialise() { //set ptrs to counts + } //not required + + void dump( //write external bits + FILE *f) { + serialise_bytes (f, (void *) xcoords, (segments + 1) * sizeof (INT32)); + serialise_bytes (f, (void *) quadratics, segments * sizeof (QUAD_COEFFS)); + } + + void de_dump( //read external bits + FILE *f) { + xcoords = (INT32 *) de_serialise_bytes (f, + (segments + 1) * sizeof (INT32)); + quadratics = (QUAD_COEFFS *) de_serialise_bytes (f, + segments * + sizeof (QUAD_COEFFS)); + } + + //assign copy + make_serialise (QSPLINE) 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 diff --git a/ccstruct/ratngs.cpp b/ccstruct/ratngs.cpp new file mode 100644 index 0000000000..f06f276c6c --- /dev/null +++ b/ccstruct/ratngs.cpp @@ -0,0 +1,262 @@ +/********************************************************************** + * 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. + * + **********************************************************************/ + +#include "mfcpch.h" +//#include "ipeerr.h" +#include "callcpp.h" +#include "ratngs.h" +//#include "tordvars.h" +extern FILE *matcher_fp; + +ELISTIZE (BLOB_CHOICE) CLISTIZE (BLOB_CHOICE_LIST) CLISTIZE (WERD_CHOICE) +//extern FILE* matcher_fp; +/********************************************************************** + * BLOB_CHOICE::BLOB_CHOICE + * + * Constructor to build a BLOB_CHOICE from a char, rating and certainty. + **********************************************************************/ +BLOB_CHOICE::BLOB_CHOICE( //constructor + char src_class, //character + float src_rating, //rating + float src_cert, //certainty + INT8 src_config //config (font) + ) { + blob_class = src_class; + blob_rating = src_rating; + blob_certainty = src_cert; //just copy them + blob_config = src_config; +} + + +/********************************************************************** + * WERD_CHOICE::WERD_CHOICE + * + * Constructor to build a WERD_CHOICE from a char, rating and certainty. + **********************************************************************/ + +WERD_CHOICE::WERD_CHOICE ( +//constructor +const char *src_string, //word string +float src_rating, //rating +float src_cert, //certainty +UINT8 src_permuter //permuter code +): +word_string(src_string) { + word_rating = src_rating; + word_certainty = src_cert; + word_permuter = src_permuter; //just copy them +} + + +/********************************************************************** + * 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 + **********************************************************************/ + + //add one on +WERD_CHOICE & WERD_CHOICE::operator+= ( +const WERD_CHOICE & second //second word +) { + if (word_string.length () == 0 || second.word_string.length () == 0) { + word_string = NULL; //make it empty + } + else { + //add ratings + word_rating += second.word_rating; + if (second.word_certainty < word_certainty) + //take min + word_certainty = second.word_certainty; + //cat strings + word_string += second.word_string; + if (second.word_permuter != word_permuter) + word_permuter = COMPOUND_PERM; + } + + return *this; +} + + +/********************************************************************** + * print_ratings_list + * + * Send all the ratings out to the logfile. + **********************************************************************/ + +void print_ratings_list( //print whole list + const char *msg, //intro message + BLOB_CHOICE_LIST *ratings //list of results + ) { + BLOB_CHOICE_IT + c_it = ratings; //iterator + + switch (ratings->length ()) { + case 0: + tprintf ("%s:", msg); + break; + case 1: + tprintf ("%s:%c/%g/%g", msg, + c_it.data ()->char_class (), + c_it.data ()->rating (), c_it.data ()->certainty ()); + break; + case 2: + tprintf ("%s:%c/%g/%g %c/%g/%g", msg, + c_it.data ()->char_class (), + c_it.data ()->rating (), + c_it.data ()->certainty (), + c_it.data_relative (1)->char_class (), + c_it.data_relative (1)->rating (), + c_it.data_relative (1)->certainty ()); + break; + case 3: + tprintf ("%s:%c/%g/%g %c/%g/%g %c/%g/%g", msg, + c_it.data ()->char_class (), + c_it.data ()->rating (), + c_it.data ()->certainty (), + c_it.data_relative (1)->char_class (), + c_it.data_relative (1)->rating (), + c_it.data_relative (1)->certainty (), + c_it.data_relative (2)->char_class (), + c_it.data_relative (2)->rating (), + c_it.data_relative (2)->certainty ()); + break; + case 4: + tprintf ("%s:%c/%g/%g %c/%g/%g %c/%g/%g %c/%g/%g", msg, + c_it.data ()->char_class (), + c_it.data ()->rating (), + c_it.data ()->certainty (), + c_it.data_relative (1)->char_class (), + c_it.data_relative (1)->rating (), + c_it.data_relative (1)->certainty (), + c_it.data_relative (2)->char_class (), + c_it.data_relative (2)->rating (), + c_it.data_relative (2)->certainty (), + c_it.data_relative (3)->char_class (), + c_it.data_relative (3)->rating (), + c_it.data_relative (3)->certainty ()); + break; + default: + tprintf ("%s:%c/%g/%g %c/%g/%g %c/%g/%g %c/%g/%g %c/%g/%g", msg, + c_it.data ()->char_class (), + c_it.data ()->rating (), + c_it.data ()->certainty (), + c_it.data_relative (1)->char_class (), + c_it.data_relative (1)->rating (), + c_it.data_relative (1)->certainty (), + c_it.data_relative (2)->char_class (), + c_it.data_relative (2)->rating (), + c_it.data_relative (2)->certainty (), + c_it.data_relative (3)->char_class (), + c_it.data_relative (3)->rating (), + c_it.data_relative (3)->certainty (), + c_it.data_relative (4)->char_class (), + c_it.data_relative (4)->rating (), + c_it.data_relative (4)->certainty ()); + c_it.forward (); + c_it.forward (); + c_it.forward (); + c_it.forward (); + while (!c_it.at_last ()) { + c_it.forward (); + tprintf ("%c/%g/%g", + c_it.data ()->char_class (), + c_it.data ()->rating (), c_it.data ()->certainty ()); + } + + break; + } +} + + +/********************************************************************** + * print_ratings_info + * + * Send all the ratings out to the logfile. + **********************************************************************/ + +void print_ratings_info( //print summary info + FILE *fp, //file to use + BLOB_CHOICE_LIST *ratings //list of results + ) { + INT32 + index; //to list + INT32 + best_index; //to list + FLOAT32 + best_rat; //rating + FLOAT32 + best_cert; //certainty + char + first_char; //character + FLOAT32 + first_rat; //rating + FLOAT32 + first_cert; //certainty + char + sec_char = 0; //character + FLOAT32 + sec_rat = 0.0f; //rating + FLOAT32 + sec_cert = 0.0f; //certainty + BLOB_CHOICE_IT + c_it = ratings; //iterator + + index = ratings->length (); + if (index > 0) { + first_char = c_it.data ()->char_class (); + first_rat = c_it.data ()->rating (); + first_cert = -c_it.data ()->certainty (); + if (index > 1) { + sec_char = c_it.data_relative (1)->char_class (); + sec_rat = c_it.data_relative (1)->rating (); + sec_cert = -c_it.data_relative (1)->certainty (); + } + else { + sec_char = '~'; + sec_rat = -1; + sec_cert = -1; + } + } + else { + first_char = '~'; + first_rat = -1; + first_cert = -1; + } + best_index = -1; + best_rat = -1; + best_cert = -1; + for (index = 0, c_it.mark_cycle_pt (); !c_it.cycled_list (); + c_it.forward (), index++) { + if (c_it.data ()->char_class () == blob_answer) { + best_index = index; + best_rat = c_it.data ()->rating (); + best_cert = -c_it.data ()->certainty (); + } + } + if (first_char == '\0' || first_char == ' ') + first_char = '~'; + if (sec_char == '\0' || sec_char == ' ') + sec_char = '~'; + fprintf (matcher_fp, + " " INT32FORMAT " " INT32FORMAT " %g %g %c %g %g %c %g %g\n", + ratings->length (), best_index, best_rat, best_cert, first_char, + first_rat, first_cert, sec_char, sec_rat, sec_cert); +} diff --git a/ccstruct/ratngs.h b/ccstruct/ratngs.h new file mode 100644 index 0000000000..6cae9b5877 --- /dev/null +++ b/ccstruct/ratngs.h @@ -0,0 +1,146 @@ +/********************************************************************** + * 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 "clst.h" +#include "werd.h" +#include "notdll.h" + +class BLOB_CHOICE:public ELIST_LINK +{ + public: + BLOB_CHOICE() { //empty + } + BLOB_CHOICE( //constructor + char src_class, //character + float src_rating, //rating + float src_cert, //certainty + INT8 src_config); //config (font) + + void set_class( //change it + char newchar) { + blob_class = newchar; + } + void set_rating( //change it + float newrat) { + blob_rating = newrat; + } + void set_certainty( //change it + float newrat) { + blob_certainty = newrat; + } + void set_config( //change it + INT8 newfont) { + blob_config = newfont; + } + + char char_class() const { //access function + return blob_class; + } + float rating() const { //access function + return blob_rating; + } + float certainty() const { //access function + return blob_certainty; + } + INT8 config() const { //access function + return blob_config; + } + + NEWDELETE private: + char blob_class; //char code + char blob_config; //char config (font) + INT16 junk2; + float blob_rating; //size related + float blob_certainty; //absolute +}; + + //make them listable +ELISTIZEH (BLOB_CHOICE) CLISTIZEH (BLOB_CHOICE_LIST) +/* permuter codes used in WERD_CHOICEs */ +# +#define MIN_PERM 1 +#define NO_PERM 0 +#define TOP_CHOICE_PERM 1 +#define LOWER_CASE_PERM 2 +#define UPPER_CASE_PERM 3 +#define NUMBER_PERM 4 +#define SYSTEM_DAWG_PERM 5 +#define DOC_DAWG_PERM 6 +#define USER_DAWG_PERM 7 +#define FREQ_DAWG_PERM 8 +#define COMPOUND_PERM 9 +#define MAX_PERM 9 +class +WERD_CHOICE +{ + public: + WERD_CHOICE() { //empty + } + WERD_CHOICE( //constructor + const char *src_string, //word string + float src_rating, //rating + float src_cert, //certainty + UINT8 src_permuter); //permuter code + + //access function + const STRING &string() const { + return word_string; + } + + float rating() const { //access function + return word_rating; + } + float certainty() const { //access function + return word_certainty; + } + UINT8 permuter() const { //access function + return word_permuter; + } + void set_permuter( //Override + UINT8 perm) { + word_permuter = perm; + } + + WERD_CHOICE & operator+= ( //concatanate + const WERD_CHOICE & second);//second on first + + NEWDELETE private: + STRING word_string; //text + float word_rating; //size related + float word_certainty; //absolute + UINT8 word_permuter; //permuter code +}; + +CLISTIZEH (WERD_CHOICE) +void print_ratings_list( //print whole list + const char *msg, //intro message + BLOB_CHOICE_LIST *ratings //list of results + ); +void print_ratings_info( //print summary info + FILE *fp, //file to use + BLOB_CHOICE_LIST *ratings //list of results + ); +typedef void (*POLY_MATCHER) (PBLOB *, PBLOB *, PBLOB *, WERD *, +DENORM *, BLOB_CHOICE_LIST &); +typedef void (*POLY_TESTER) (PBLOB *, DENORM *, BOOL8, char *, INT32, +BLOB_CHOICE_LIST *); +#endif diff --git a/ccstruct/rect.cpp b/ccstruct/rect.cpp new file mode 100644 index 0000000000..afdb4c2266 --- /dev/null +++ b/ccstruct/rect.cpp @@ -0,0 +1,232 @@ +/********************************************************************** + * 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 "mfcpch.h" //precompiled headers +#include "rect.h" + +/********************************************************************** + * BOX::BOX() Constructor from 2 ICOORDS + * + **********************************************************************/ + +BOX::BOX( //construtor + 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; + } + } +} + + +/********************************************************************** + * BOX::intersection() Build the largest box contained in both boxes + * + **********************************************************************/ + +BOX BOX::intersection( //shared area box + const BOX &box) const { + ICOORD bl; //bottom left + ICOORD tr; //top right + + if (overlap (box)) { + 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 ()); + } + else { + bl.set_x (MAX_INT16); + bl.set_y (MAX_INT16); + tr.set_x (-MAX_INT16); + tr.set_y (-MAX_INT16); + } + return BOX (bl, tr); +} + + +/********************************************************************** + * BOX::bounding_union() Build the smallest box containing both boxes + * + **********************************************************************/ + +BOX BOX::bounding_union( //box enclosing both + const BOX &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 BOX (bl, tr); +} + + +/********************************************************************** + * BOX::plot() Paint a box using specified settings + * + **********************************************************************/ + +#ifndef GRAPHICS_DISABLED +void BOX::plot( //paint box + WINDOW fd, //where to paint + INT16 style, //display style + INT16 edged, //show border? + COLOUR fill_colour, //colour for inside + COLOUR border_colour //colour for border + ) const { + interior_style(fd, style, edged); + fill_color_index(fd, fill_colour); + perimeter_color_index(fd, border_colour); + plot(fd); +} +#endif + + +/********************************************************************** + * operator+= + * + * Extend one box to include the other (In place union) + **********************************************************************/ + +DLLSYM BOX & +operator+= ( //bounding bounding bx +BOX & op1, //operands +const BOX & 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) + **********************************************************************/ + +DLLSYM BOX & +operator-= ( //inplace intersection +BOX & op1, //operands +const BOX & 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; +} + + +/********************************************************************** + * BOX::serialise_asc() Convert to ascii file. + * + **********************************************************************/ + +void BOX::serialise_asc( //convert to ascii + FILE *f //file to use + ) { + bot_left.serialise_asc (f); + top_right.serialise_asc (f); +} + + +/********************************************************************** + * BOX::de_serialise_asc() Convert from ascii file. + * + **********************************************************************/ + +void BOX::de_serialise_asc( //convert from ascii + FILE *f //file to use + ) { + bot_left.de_serialise_asc (f); + top_right.de_serialise_asc (f); +} diff --git a/ccstruct/rect.h b/ccstruct/rect.h new file mode 100644 index 0000000000..cb951bbc45 --- /dev/null +++ b/ccstruct/rect.h @@ -0,0 +1,287 @@ +/********************************************************************** + * 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 "grphics.h" +#include "tprintf.h" + +class DLLSYM BOX //bounding box +{ + public: + BOX (): //empty constructor + bot_left (MAX_INT16, MAX_INT16), top_right (-MAX_INT16, -MAX_INT16) { + } //null box + + BOX( //constructor + const ICOORD pt1, //one corner + const ICOORD pt2); //the other corner + BOX( //box around FCOORD + const FCOORD pt); + + BOOL8 null_box() const { //Is box null + return ((left () > right ()) || (top () < bottom ())); + } + + INT16 top() const { // coord of top + return top_right.y (); + } + + INT16 bottom() const { // coord of bottom + return bot_left.y (); + } + + INT16 left() const { // coord of left + return bot_left.x (); + } + + INT16 right() const { // coord of right + return top_right.x (); + } + + //access function + const ICOORD &botleft() const { + 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 ()); + } + + //access function + const ICOORD &topright() const { + 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; + } + + 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 + //round left + bot_left.set_x ((INT16) floor (bot_left.x () * f)); + //round down + bot_left.set_y ((INT16) floor (bot_left.y () * f)); + + 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 ())); + } + + void rotate( //rotate coords + const FCOORD vec) { //by vector + bot_left.rotate (vec); + top_right.rotate (vec); + *this = BOX (bot_left, top_right); + } + + BOOL8 contains( //is pt inside box + const FCOORD pt) const; + + BOOL8 contains( //is box inside box + const BOX &box) const; + + BOOL8 overlap( //do boxes overlap + const BOX &box) const; + + BOOL8 major_overlap( // Do boxes overlap more than half. + const BOX &box) const; + + BOX intersection( //shared area box + const BOX &box) const; + + BOX bounding_union( //box enclosing both + const BOX &box) const; + + void print() { //print + tprintf ("Bounding box=(%d,%d)->(%d,%d)\n", + left (), bottom (), right (), top ()); + } + +#ifndef GRAPHICS_DISABLED + void plot( //use current settings + WINDOW fd) const { //where to paint + rectangle (fd, bot_left.x (), bot_left.y (), top_right.x (), + top_right.y ()); + } + + void plot( //paint box + WINDOW fd, //where to paint + INT16 style, //display style + INT16 edged, //show border? + COLOUR fill_colour, //colour for inside + COLOUR border_colour) const; //colour for border +#endif + + friend DLLSYM BOX & operator+= (BOX &, const BOX &); + //in place union + friend DLLSYM BOX & operator-= (BOX &, const BOX &); + //in place intrsection + + void serialise_asc( //convert to ascii + FILE *f); + void de_serialise_asc( //convert from ascii + FILE *f); + + private: + ICOORD bot_left; //bottom left corner + ICOORD top_right; //top right corner +}; + +/********************************************************************** + * BOX::BOX() Constructor from 1 FCOORD + * + **********************************************************************/ + +inline BOX::BOX( //construtor + 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 ())); +} + + +/********************************************************************** + * BOX::contains() Is point within box + * + **********************************************************************/ + +inline BOOL8 BOX::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 ())); +} + + +/********************************************************************** + * BOX::contains() Is box within box + * + **********************************************************************/ + +inline BOOL8 BOX::contains(const BOX &box) const { + return (contains (box.bot_left) && contains (box.top_right)); +} + + +/********************************************************************** + * BOX::overlap() Do two boxes overlap? + * + **********************************************************************/ + +inline BOOL8 BOX::overlap( //do boxes overlap + const BOX &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 ())); +} + +/********************************************************************** + * BOX::major_overlap() Do two boxes overlap by at least half of the smallest? + * + **********************************************************************/ + +inline BOOL8 BOX::major_overlap( // Do boxes overlap more that half. + const BOX &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; +} +#endif diff --git a/ccstruct/rejctmap.cpp b/ccstruct/rejctmap.cpp new file mode 100644 index 0000000000..60cc230b80 --- /dev/null +++ b/ccstruct/rejctmap.cpp @@ -0,0 +1,545 @@ +/********************************************************************** + * 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 "mfcpch.h" +#include "hosthplb.h" +//#include "basefile.h" +#include "rejctmap.h" +#include "secname.h" + +#define EXTERN + +EXTERN BOOL_VAR (rejword_only_set_if_accepted, TRUE, "Mimic old reject_word"); +EXTERN BOOL_VAR (rejmap_allow_more_good_qual, FALSE, +"Use initial good qual setting"); +EXTERN BOOL_VAR (rej_use_1Il_rej, TRUE, "1Il rejection enabled"); + +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) && + (rejmap_allow_more_good_qual || + (!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 + if (rej_use_1Il_rej) + 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) { + #ifndef SECURE_NAMES + + 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"); + #endif +} + + +//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 (!rejword_only_set_if_accepted || 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 (!rejword_only_set_if_accepted || 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 (!rejword_only_set_if_accepted || 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 (!rejword_only_set_if_accepted || 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 (!rejword_only_set_if_accepted || 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 (!rejword_only_set_if_accepted || 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 (!rejword_only_set_if_accepted || 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 (!rejword_only_set_if_accepted || 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 (!rejword_only_set_if_accepted || 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 (!rejword_only_set_if_accepted || ptr[i].accepted ()) + ptr[i].setrej_row_rej (); + } +} diff --git a/ccstruct/rejctmap.h b/ccstruct/rejctmap.h new file mode 100644 index 0000000000..e93a1de7a1 --- /dev/null +++ b/ccstruct/rejctmap.h @@ -0,0 +1,284 @@ +/********************************************************************** + * 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 unneccessarily 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 "varable.h" +#include "notdll.h" + +extern BOOL_VAR_H (rejword_only_set_if_accepted, TRUE, +"Mimic old reject_word"); +extern BOOL_VAR_H (rejmap_allow_more_good_qual, FALSE, +"Use initial good qual setting"); +extern BOOL_VAR_H (rej_use_1Il_rej, TRUE, "1Il rejection enabled"); + +enum REJ_FLAGS +{ + /* Reject modes which are NEVER overridden */ + R_TESS_FAILURE, // PERM Tess didnt 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 didnt 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 inbetween 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 diff --git a/ccstruct/rwpoly.cpp b/ccstruct/rwpoly.cpp new file mode 100644 index 0000000000..c8d0c3e903 --- /dev/null +++ b/ccstruct/rwpoly.cpp @@ -0,0 +1,89 @@ +/********************************************************************** + * File: rwpoly.c (Formerly rw_poly.c) + * Description: latest version of manual page decomp tool + * Author: Sheelagh Lloyd + * Created: 16:05 24/3/93 + * + * This version constructs a list of blocks. + * + * (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 "mfcpch.h" +#include "pageblk.h" +#include "rwpoly.h" + +#include "hpddef.h" //must be last (handpd.dll) + +#define EXTERN + +EXTERN DLLSYM PAGE_BLOCK_LIST *page_block_list; +EXTERN PAGE_BLOCK_IT page_block_it; +EXTERN BOOL_VAR (blocks_read_asc, TRUE, "Read blocks in ascii format"); +EXTERN BOOL_VAR (blocks_write_asc, TRUE, "Write blocks in ascii format"); + +DLLSYM void write_poly_blocks(FILE *blfile, PAGE_BLOCK_LIST *blocks) { + + if (blocks_write_asc) + blocks->serialise_asc (blfile); + else + blocks->serialise (blfile); + + return; +} + + +DLLSYM PAGE_BLOCK_LIST *read_poly_blocks( //read file + const char *name //file to read + ) { + FILE *infp; + int c; + INT16 number_of_pblocks; + //output list + PAGE_BLOCK_LIST *pb_list = NULL; + PAGE_BLOCK *page_block; //new block for list + INT32 len; /*length to retrive */ + PAGE_BLOCK_IT it; + + if ((infp = fopen (name, "r")) != NULL) { + if (((c = fgetc (infp)) != EOF) && (ungetc (c, infp) != EOF)) { + if (blocks_read_asc) { + pb_list = new PAGE_BLOCK_LIST; + + len = de_serialise_INT32 (infp); + it.set_to_list (pb_list); + for (; len > 0; len--) { + page_block = PAGE_BLOCK::new_de_serialise_asc (infp); + /*put on the list */ + it.add_to_end (page_block); + } + } + else + pb_list = PAGE_BLOCK_LIST::de_serialise (infp); + page_block_list = pb_list; //set global for now + } + fclose(infp); + } + else { + //can't open file + CANTOPENFILE.error ("read_poly_blocks", LOG, name); + pb_list = new PAGE_BLOCK_LIST; + page_block_list = pb_list; //set global for now + } + page_block_it.set_to_list (pb_list); + number_of_pblocks = pb_list->length (); + + printf ("%d page blocks read\n", number_of_pblocks); + return pb_list; + +} diff --git a/ccstruct/rwpoly.h b/ccstruct/rwpoly.h new file mode 100644 index 0000000000..e177bdc565 --- /dev/null +++ b/ccstruct/rwpoly.h @@ -0,0 +1,45 @@ +/********************************************************************** + * File: rwpoly.h (Formerly rw_poly.h) + * Description: latest version of manual page decomp tool + * Author: Sheelagh Lloyd + * Created: 16:05 24/3/93 + * + * This version constructs a list of blocks. + * + * (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 RWPOLY_H +#define RWPOLY_H + +#include +#include +#ifdef __MSW32__ +#include +#else +#include +#endif +#include "elst.h" +#include "pageblk.h" +#include "varable.h" + +#include "hpddef.h" //must be last (handpd.dll) + +DLLSYM void write_poly_blocks(FILE *blfile, PAGE_BLOCK_LIST *blocks); + +extern DLLSYM PAGE_BLOCK_LIST *page_block_list; +extern PAGE_BLOCK_IT page_block_it; + //read file +DLLSYM PAGE_BLOCK_LIST *read_poly_blocks(const char *name //file to read + ); +#endif diff --git a/ccstruct/statistc.cpp b/ccstruct/statistc.cpp new file mode 100644 index 0000000000..55ec9b4f0d --- /dev/null +++ b/ccstruct/statistc.cpp @@ -0,0 +1,908 @@ +/********************************************************************** + * 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 "mfcpch.h" //precompiled headers +#include +#include +#include +#include "memry.h" +//#include "ipeerr.h" +#include "tprintf.h" +#include "statistc.h" + +#define SEED1 0x1234 //default seeds +#define SEED2 0x5678 +#define SEED3 0x9abc + +/********************************************************************** + * STATS::STATS + * + * Construct a new stats element by allocating and zeroing the memory. + **********************************************************************/ + +STATS::STATS( //constructor + INT32 min, //min of range + INT32 max //max of range + ) { + + if (max <= min) { + /* err.log(RESULT_LOGICAL_ERROR,E_LOC,ERR_PRIMITIVES, + ERR_SCROLLING,ERR_CONTINUE,ERR_ERROR, + "Illegal range for stats, Min=%d, Max=%d",min,max);*/ + min = 0; + max = 1; + } + rangemin = min; //setup + rangemax = max; + buckets = (INT32 *) alloc_mem ((max - min) * sizeof (INT32)); + if (buckets != NULL) + this->clear (); //zero it + /* else + err.log(RESULT_NO_MEMORY,E_LOC,ERR_PRIMITIVES, + ERR_SCROLLING,ERR_CONTINUE,ERR_ERROR, + "No memory for stats, Min=%d, Max=%d",min,max); */ +} + + +STATS::STATS() { //constructor + rangemax = 0; //empty + rangemin = 0; + buckets = NULL; +} + + +/********************************************************************** + * STATS::set_range + * + * Alter the range on an existing stats element. + **********************************************************************/ + +bool STATS::set_range( //constructor + INT32 min, //min of range + INT32 max //max of range + ) { + + if (max <= min) { + return false; + } + rangemin = min; //setup + rangemax = max; + if (buckets != NULL) + free_mem(buckets); //no longer want it + buckets = (INT32 *) alloc_mem ((max - min) * sizeof (INT32)); + /* if (buckets==NULL) + return err.log(RESULT_NO_MEMORY,E_LOC,ERR_PRIMITIVES, + ERR_SCROLLING,ERR_CONTINUE,ERR_ERROR, + "No memory for stats, Min=%d, Max=%d",min,max);*/ + + this->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 (INT32)); + //zero it +} + + +/********************************************************************** + * STATS::~STATS + * + * Destructor for a stats class. + **********************************************************************/ + +STATS::~STATS ( //destructor +) { + if (buckets != NULL) { + free_mem(buckets); + buckets = NULL; + } +} + + +/********************************************************************** + * STATS::add + * + * Add a set of samples to (or delete from) a pile. + **********************************************************************/ + +void STATS::add( //add sample + INT32 value, //bucket + INT32 count //no to add + ) { + if (buckets == NULL) { + /* err.log(RESULT_LOGICAL_ERROR,E_LOC,ERR_PRIMITIVES, + ERR_SCROLLING,ERR_CONTINUE,ERR_ERROR, + "Empty stats");*/ + return; + } + if (value <= rangemin) + buckets[0] += count; //silently clip to range + else if (value >= rangemax) + buckets[rangemax - rangemin - 1] += count; + else + //add count to cell + buckets[value - rangemin] += count; + total_count += count; //keep count of total +} + + +/********************************************************************** + * STATS::mode + * + * Find the mode of a stats class. + **********************************************************************/ + +INT32 STATS::mode() { //get mode of samples + INT32 index; //current index + INT32 max; //max cell count + INT32 maxindex; //index of max + + if (buckets == NULL) { + /* err.log(RESULT_LOGICAL_ERROR,E_LOC,ERR_PRIMITIVES, + ERR_SCROLLING,ERR_CONTINUE,ERR_ERROR, + "Empty stats");*/ + return rangemin; + } + for (max = 0, maxindex = 0, 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. + **********************************************************************/ + +float STATS::mean() { //get mean of samples + INT32 index; //current index + INT32 sum; //sum of cells + + if (buckets == NULL) { + /* err.log(RESULT_LOGICAL_ERROR,E_LOC,ERR_PRIMITIVES, + ERR_SCROLLING,ERR_CONTINUE,ERR_ERROR, + "Empty stats");*/ + return (float) rangemin; + } + for (sum = 0, index = rangemax - rangemin - 1; index >= 0; index--) { + //sum all buckets + sum += index * buckets[index]; + } + if (total_count > 0) + //mean value + return (float) sum / total_count + rangemin; + else + return (float) rangemin; //no mean +} + + +/********************************************************************** + * STATS::sd + * + * Find the standard deviation of a stats class. + **********************************************************************/ + +float STATS::sd() { //standard deviation + INT32 index; //current index + INT32 sum; //sum of cells + INT32 sqsum; //sum of squares + float variance; + + if (buckets == NULL) { + /* err.log(RESULT_LOGICAL_ERROR,E_LOC,ERR_PRIMITIVES, + ERR_SCROLLING,ERR_CONTINUE,ERR_ERROR, + "Empty stats"); */ + return (float) 0.0; + } + for (sum = 0, sqsum = 0, index = rangemax - rangemin - 1; index >= 0; + index--) { + //sum all buckets + sum += index * buckets[index]; + //and squares + sqsum += index * index * buckets[index]; + } + if (total_count > 0) { + variance = sum / ((float) total_count); + variance = sqsum / ((float) total_count) - variance * variance; + return (float) sqrt (variance); + } + else + return (float) 0.0; +} + + +/********************************************************************** + * STATS::ile + * + * Find an arbitrary %ile of a stats class. + **********************************************************************/ + +float STATS::ile( //percentile + float frac //fraction to find + ) { + INT32 index; //current index + INT32 sum; //sum of cells + float target; //target value + + if (buckets == NULL) { + /* err.log(RESULT_LOGICAL_ERROR,E_LOC,ERR_PRIMITIVES, + ERR_SCROLLING,ERR_CONTINUE,ERR_ERROR, + "Empty stats"); */ + return (float) rangemin; + } + target = frac * total_count; + if (target <= 0) + target = (float) 1; + if (target > total_count) + target = (float) total_count; + for (sum = 0, index = 0; index < rangemax - rangemin + && sum < target; sum += buckets[index], index++); + if (index > 0) + return rangemin + index - (sum - target) / buckets[index - 1]; + //better than just ints + else + return (float) rangemin; +} + + +/********************************************************************** + * STATS::median + * + * Finds a more usefule 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 + **********************************************************************/ + +float STATS::median() { //get median + float median; + INT32 min_pile; + INT32 median_pile; + INT32 max_pile; + + if (buckets == NULL) { + /* err.log(RESULT_LOGICAL_ERROR,E_LOC,ERR_PRIMITIVES, + ERR_SCROLLING,ERR_CONTINUE,ERR_ERROR, + "Empty stats");*/ + return (float) rangemin; + } + median = (float) ile ((float) 0.5); + median_pile = (INT32) floor (median); + if ((total_count > 1) && (pile_count (median_pile) == 0)) { + /* Find preceeding 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 = (float) ((min_pile + max_pile) / 2.0); + } + return median; +} + + +/********************************************************************** + * 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( //smooth samples + INT32 factor //size of triangle + ) { + INT32 entry; //bucket index + INT32 offset; //from entry + INT32 entrycount; //no of entries + INT32 bucket; //new smoothed pile + //output stats + STATS result(rangemin, rangemax); + + if (buckets == NULL) { + /* err.log(RESULT_LOGICAL_ERROR,E_LOC,ERR_PRIMITIVES, + ERR_SCROLLING,ERR_CONTINUE,ERR_ERROR, + "Empty stats"); */ + return; + } + if (factor < 2) + return; //is a no-op + entrycount = rangemax - rangemin; + for (entry = 0; entry < entrycount; entry++) { + //centre weight + bucket = buckets[entry] * factor; + for (offset = 1; offset < factor; offset++) { + if (entry - offset >= 0) + bucket += buckets[entry - offset] * (factor - offset); + if (entry + offset < entrycount) + bucket += buckets[entry + offset] * (factor - offset); + } + result.add (entry + rangemin, bucket); + } + total_count = result.total_count; + memcpy (buckets, result.buckets, entrycount * sizeof (INT32)); +} + + +/********************************************************************** + * 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( //cluster samples + 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 (max_clusters < 1) + return 0; + if (buckets == NULL) { + /* err.log(RESULT_LOGICAL_ERROR,E_LOC,ERR_PRIMITIVES, + ERR_SCROLLING,ERR_CONTINUE,ERR_ERROR, + "Empty stats");*/ + return 0; + } + centres = (float *) alloc_mem ((max_clusters + 1) * sizeof (float)); + if (centres == NULL) { + /* err.log(RESULT_NO_MEMORY,E_LOC,ERR_PRIMITIVES, + ERR_SCROLLING,ERR_CONTINUE,ERR_ERROR, + "No memory for centres"); */ + return 0; + } + for (cluster_count = 1; cluster_count <= max_clusters + && clusters[cluster_count].buckets != NULL + && clusters[cluster_count].total_count > 0; cluster_count++) { + centres[cluster_count] = + (float) clusters[cluster_count].ile ((float) 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 = (float) 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)) + return 0; + centres[cluster_count] = (float) 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] = + (float) clusters[cluster_count].ile ((float) 0.5); + } + } + while (new_cluster && cluster_count < max_clusters); + free_mem(centres); + return cluster_count; +} + + +/********************************************************************** + * STATS::local_min + * + * Return TRUE if this point is a local min. + **********************************************************************/ + +BOOL8 STATS::local_min( //test minness + INT32 x //of x + ) { + INT32 index; //table index + + if (buckets == NULL) { + /* err.log(RESULT_LOGICAL_ERROR,E_LOC,ERR_PRIMITIVES, + ERR_SCROLLING,ERR_CONTINUE,ERR_ERROR, + "Empty stats");*/ + return FALSE; + } + if (x < rangemin) + x = rangemin; + if (x >= rangemax) + x = rangemax - 1; + x -= rangemin; + if (buckets[x] == 0) + return TRUE; + 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::print + * + * Print a summary of the stats and optionally a dump of the table. + **********************************************************************/ + +void STATS::print( //print stats table + FILE *, //Now uses tprintf instead + BOOL8 dump //dump full table + ) { + INT32 index; //table index + + if (buckets == NULL) { + /* err.log(RESULT_LOGICAL_ERROR,E_LOC,ERR_PRIMITIVES, + ERR_SCROLLING,ERR_CONTINUE,ERR_ERROR, + "Empty stats"); */ + return; + } + if (dump) { + for (index = 0; index < rangemax - rangemin; index++) { + tprintf ("%4d:%-3d ", rangemin + index, buckets[index]); + if (index % 8 == 7) + tprintf ("\n"); + } + tprintf ("\n"); + } + + tprintf ("Total count=%d\n", total_count); + tprintf ("Min=%d\n", (INT32) (ile ((float) 0.0))); + tprintf ("Lower quartile=%.2f\n", ile ((float) 0.25)); + tprintf ("Median=%.2f\n", ile ((float) 0.5)); + tprintf ("Upper quartile=%.2f\n", ile ((float) 0.75)); + tprintf ("Max=%d\n", (INT32) (ile ((float) 0.99999))); + tprintf ("Mean= %.2f\n", mean ()); + tprintf ("SD= %.2f\n", sd ()); +} + + +/********************************************************************** + * STATS::min_bucket + * + * Find REAL minimum bucket - ile(0.0) isnt necessarily correct + **********************************************************************/ + +INT32 STATS::min_bucket() { //Find min + INT32 min; + + if (buckets == NULL) { + /* err.log(RESULT_LOGICAL_ERROR,E_LOC,ERR_PRIMITIVES, + ERR_SCROLLING,ERR_CONTINUE,ERR_ERROR, + "Empty stats");*/ + return rangemin; + } + + for (min = 0; (min < rangemax - rangemin) && (buckets[min] == 0); min++); + return rangemin + min; +} + + +/********************************************************************** + * STATS::max_bucket + * + * Find REAL maximum bucket - ile(1.0) isnt necessarily correct + **********************************************************************/ + +INT32 STATS::max_bucket() { //Find max + INT32 max; + + if (buckets == NULL) { + /* err.log(RESULT_LOGICAL_ERROR,E_LOC,ERR_PRIMITIVES, + ERR_SCROLLING,ERR_CONTINUE,ERR_ERROR, + "Empty stats");*/ + return rangemin; + } + + for (max = rangemax - rangemin - 1; + (max > 0) && (buckets[max] == 0); max--); + return rangemin + max; +} + + +/********************************************************************** + * STATS::short_print + * + * Print a summary of the stats and optionally a dump of the table. + * ( BUT ONLY THE PART OF THE TABLE BETWEEN MIN AND MAX) + **********************************************************************/ + +void STATS::short_print( //print stats table + FILE *, //Now uses tprintf instead + BOOL8 dump //dump full table + ) { + INT32 index; //table index + INT32 min = min_bucket (); + INT32 max = max_bucket (); + + if (buckets == NULL) { + /* err.log(RESULT_LOGICAL_ERROR,E_LOC,ERR_PRIMITIVES, + ERR_SCROLLING,ERR_CONTINUE,ERR_ERROR, + "Empty stats"); */ + return; + } + if (dump) { + for (index = min; index <= max; index++) { + tprintf ("%4d:%-3d ", rangemin + index, buckets[index]); + if ((index - min) % 8 == 7) + tprintf ("\n"); + } + tprintf ("\n"); + } + + tprintf ("Total count=%d\n", total_count); + tprintf ("Min=%d Really=%d\n", (INT32) (ile ((float) 0.0)), min); + tprintf ("Max=%d Really=%d\n", (INT32) (ile ((float) 1.1)), max); + tprintf ("Range=%d\n", max + 1 - min); + tprintf ("Lower quartile=%.2f\n", ile ((float) 0.25)); + tprintf ("Median=%.2f\n", ile ((float) 0.5)); + tprintf ("Upper quartile=%.2f\n", ile ((float) 0.75)); + 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( //plot stats table + WINDOW window, //to draw in + float xorigin, //bottom left + float yorigin, + float xscale, //one x unit + float yscale, //one y unit + COLOUR colour //colour to draw in + ) { + INT32 index; //table index + + if (buckets == NULL) { + /* err.log(RESULT_LOGICAL_ERROR,E_LOC,ERR_PRIMITIVES, + ERR_SCROLLING,ERR_CONTINUE,ERR_ERROR, + "Empty stats");*/ + return; + } + interior_style (window, INT_HOLLOW, 1); + perimeter_color_index(window, colour); + + for (index = 0; index < rangemax - rangemin; index++) { + rectangle (window, 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( //plot stats table + WINDOW window, //to draw in + float xorigin, //bottom left + float yorigin, + float xscale, //one x unit + float yscale, //one y unit + COLOUR colour //colour to draw in + ) { + INT32 index; //table index + + if (buckets == NULL) { + /* err.log(RESULT_LOGICAL_ERROR,E_LOC,ERR_PRIMITIVES, + ERR_SCROLLING,ERR_CONTINUE,ERR_ERROR, + "Empty stats"); */ + return; + } + line_color_index(window, colour); + line_type(window, SOLID); + + move2d (window, xorigin, yorigin + yscale * buckets[0]); + for (index = 0; index < rangemax - rangemin; index++) { + draw2d (window, 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. + **********************************************************************/ + +DLLSYM INT32 choose_nth_item( //fast median + INT32 index, //index to choose + float *array, //array of items + INT32 count //no of items + ) { + static UINT16 seeds[3] = { SEED1, SEED2, SEED3 }; + //for nrand + 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 lergal + else if (index >= count) + index = count - 1; + #ifdef __UNIX__ + equal_count = (INT32) (nrand48 (seeds) % count); + #else + equal_count = (INT32) (rand () % count); + #endif + 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 b the nth item in the array + * if the members were sorted, without actually sorting. + **********************************************************************/ + +DLLSYM INT32 +choose_nth_item ( //fast median +INT32 index, //index to choose +void *array, //array of items +INT32 count, //no of items +size_t size, //element size + //comparator +int (*compar) (const void *, const void *) +) { + static UINT16 seeds[3] = { SEED1, SEED2, SEED3 }; + //for nrand + 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 lergal + else if (index >= count) + index = count - 1; + #ifdef __UNIX__ + pivot = (INT32) (nrand48 (seeds) % count); + #else + pivot = (INT32) (rand () % count); + #endif + 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 abitrary size in-place in a table. + **********************************************************************/ + +void swap_entries( //swap in place + 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 = (char *) array + index1 * size; + ptr2 = (char *) array + index2 * size; + for (count = 0; count < size; count++) { + tmp = *ptr1; + *ptr1++ = *ptr2; + *ptr2++ = tmp; //tedious! + } +} diff --git a/ccstruct/statistc.h b/ccstruct/statistc.h new file mode 100644 index 0000000000..55484933cd --- /dev/null +++ b/ccstruct/statistc.h @@ -0,0 +1,134 @@ +/********************************************************************** + * 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 STATISTC_H +#define STATISTC_H + +#include +#include "grphics.h" + +class DLLSYM STATS //statistics package +{ + INT32 rangemin; //min of range + INT32 rangemax; //max of range + INT32 total_count; //no of samples + INT32 *buckets; //array of cells + + public: + STATS( //constructor + INT32 min, //min of range + INT32 max); //max of range + STATS(); //empty for arrays + + ~STATS (); //destructor + + bool set_range( //change range + INT32 min, //min of range + INT32 max); //max of range + + void clear(); //empty buckets + + void add( //add sample + INT32 value, //bucket + INT32 count); //no to add + + INT32 mode(); //get mode of samples + + float mean(); //get mean of samples + + float sd(); //standard deviation + + float ile( //percentile + float frac); //[0,1] for percentil + + INT32 min_bucket(); //Find min + + INT32 max_bucket(); //Find max + + float median(); //get median of samples + + void smooth( //apply blurring + INT32 factor); //filter to stats + INT32 cluster( //cluster samples + float lower, //thresholds + float upper, + float multiple, //distance threshold + INT32 max_clusters, //max no to make + STATS *clusters); //array of clusters + + INT32 pile_count( //access function + INT32 value //pile to count + ) { + return value > rangemin ? (value < rangemax + ? buckets[value - + rangemin] : buckets[rangemax - + rangemin - + 1]) : buckets[0]; + } + + INT32 get_total() { //access function + return total_count; //total of all piles + } + + BOOL8 local_min( //test local minness + INT32 x); + + void print( //print summary/table + FILE *fp, //file to print on + BOOL8 dump); //dump whole table + + void short_print( //print summary/table + FILE *fp, //file to print on + BOOL8 dump); //dump whole table + + void plot( //draw histogram rect + WINDOW 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 + COLOUR colour); //colour to draw in + + void plotline( //draw histogram line + WINDOW 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 + COLOUR colour); //colour to draw in +}; +DLLSYM INT32 choose_nth_item( //fast median + INT32 index, //index to choose + float *array, //array of items + INT32 count //no of items + ); +DLLSYM INT32 choose_nth_item ( //fast median +INT32 index, //index to choose +void *array, //array of items +INT32 count, //no of items +size_t size, //element size + //comparator +int (*compar) (const void *, const void *) +); +void swap_entries( //swap in place + void *array, //array of entries + size_t size, //size of entry + INT32 index1, //entries to swap + INT32 index2); +#endif diff --git a/ccstruct/stepblob.cpp b/ccstruct/stepblob.cpp new file mode 100644 index 0000000000..530494f728 --- /dev/null +++ b/ccstruct/stepblob.cpp @@ -0,0 +1,277 @@ +/********************************************************************** + * 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 "mfcpch.h" +#include "stepblob.h" + +ELISTIZE_S (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 + WINDOW window, //window to draw in + COLOUR colour, //colour to use + COLOUR 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); + } +} +#endif + + +/********************************************************************** + * reverse_outline_list + * + * Reverse a list of outlines and their children. + **********************************************************************/ + +static void reverse_outline_list( //reverse outlines + C_OUTLINE_LIST *list //outline to reverse + ) { + C_OUTLINE *outline; //current outline + C_OUTLINE_IT it = list; //iterator + + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + outline = it.data (); + outline->reverse (); //reverse it + 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( //constructor + C_OUTLINE_LIST *outline_list //in random order + ) { + C_OUTLINE *outline; //current outline + C_OUTLINE_IT it = outline_list;//iterator + + while (!it.empty ()) { //grab the list + outline = it.extract (); //get off the list + //put it in place + position_outline(outline, &outlines); + if (!it.empty ()) + it.forward (); + } + it.set_to_list (&outlines); + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + outline = 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); + } + } +} + + +/********************************************************************** + * C_BLOB::bounding_box + * + * Return the bounding box of the blob. + **********************************************************************/ + +BOX C_BLOB::bounding_box() { //bounding box + C_OUTLINE *outline; //current outline + C_OUTLINE_IT it = &outlines; //outlines of blob + BOX 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::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 +} + + +/********************************************************************** + * C_BLOB::plot + * + * Draw the C_BLOB in the given colour. + **********************************************************************/ + +#ifndef GRAPHICS_DISABLED +void C_BLOB::plot( //draw it + WINDOW window, //window to draw in + COLOUR blob_colour, //main colour + COLOUR child_colour //for holes + ) { + plot_outline_list(&outlines, window, blob_colour, child_colour); +} +#endif diff --git a/ccstruct/stepblob.h b/ccstruct/stepblob.h new file mode 100644 index 0000000000..76af723d96 --- /dev/null +++ b/ccstruct/stepblob.h @@ -0,0 +1,81 @@ +/********************************************************************** + * 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:public ELIST_LINK +{ + public: + C_BLOB() { + } //empty constructor + C_BLOB( //constructor //in random order + C_OUTLINE_LIST *outline_list); + + C_OUTLINE_LIST *out_list() { //get outline list + return &outlines; + } + + BOX bounding_box(); //compute bounding box + INT32 area(); //compute area + INT32 outer_area(); //compute area + INT32 count_transitions( //count maxima + INT32 threshold); //size threshold + + void move( // reposition blob + const ICOORD vec); // by vector + + void plot( //draw one + WINDOW window, //window to draw in + COLOUR blob_colour, //for outer bits + COLOUR child_colour); //for holes + + void prep_serialise() { //set ptrs to counts + outlines.prep_serialise (); + } + + void dump( //write external bits + FILE *f) { + outlines.dump (f); + } + + void de_dump( //read external bits + FILE *f) { + outlines.de_dump (f); + } + + //assignment + make_serialise (C_BLOB) C_BLOB & operator= ( + const C_BLOB & source) { //from this + if (!outlines.empty ()) + outlines.clear (); + + outlines.deep_copy (&source.outlines); + return *this; + } + + private: + C_OUTLINE_LIST outlines; //master elements +}; + +ELISTIZEH_S (C_BLOB) +#endif diff --git a/ccstruct/txtregn.cpp b/ccstruct/txtregn.cpp new file mode 100644 index 0000000000..37dd776f31 --- /dev/null +++ b/ccstruct/txtregn.cpp @@ -0,0 +1,230 @@ +/********************************************************************** + * File: txtregn.c (Formerly text_region.c) + * Description: Text region within a polygonal block + * 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 "mfcpch.h" +#include +#include +#include "txtregn.h" +#include "labls.h" + +#include "hpddef.h" //must be last (handpd.dll) + +ELISTIZE_S (TEXT_REGION) TEXT_REGION::TEXT_REGION (INT32 idno, ICOORDELT_LIST * points, TEXT_REGION_LIST * child):POLY_BLOCK (points, +POLY_TEXT) { + TEXT_REGION_IT + c = &txt_regions; + + id_number = idno; + txt_regions.clear (); + c.move_to_first (); + c.add_list_before (child); +} + + +TEXT_REGION::TEXT_REGION (INT32 idno, ICOORDELT_LIST * points):POLY_BLOCK (points, +POLY_TEXT) { + id_number = idno; + + txt_regions.clear (); +} + + +TEXT_REGION::TEXT_REGION ( //constructor +INT32 idno, ICOORDELT_LIST * points, INT8 hor, INT8 tex, INT8 ser, INT8 pro, INT8 nor, INT8 upr, INT8 sol, INT8 bla, INT8 und, INT8 dro):POLY_BLOCK (points, +POLY_TEXT) { + + id_number = idno; + horizontal = hor; + text = tex; + serif = ser; + proportional = pro; + normal = nor; + upright = upr; + solid = sol; + black = bla; + underlined = und; + dropcaps = dro; + + txt_regions.clear (); +} + + +void TEXT_REGION::set_attrs(INT8 hor, + INT8 tex, + INT8 ser, + INT8 pro, + INT8 nor, + INT8 upr, + INT8 sol, + INT8 bla, + INT8 und, + INT8 dro) { + + horizontal = hor; + text = tex; + serif = ser; + proportional = pro; + normal = nor; + upright = upr; + solid = sol; + black = bla; + underlined = und; + dropcaps = dro; +} + + +#include "hpddef.h" //must be last (handpd.dll) +void TEXT_REGION::show_attrs( //Now uses tprintf instead + DEBUG_WIN *f) { + TEXT_REGION_IT it = &txt_regions; + + if (id_number > -1) { + f-> + dprintf + ("Text region no. %d with attributes %s, %s, %s, %s, %s, %s, %s, %s, %s, %s\n", + id_number, tlabel[0][horizontal], tlabel[1][text], + tlabel[2][serif], tlabel[3][proportional], tlabel[4][normal], + tlabel[5][upright], tlabel[6][solid], tlabel[7][black], + tlabel[8][underlined], tlabel[9][dropcaps]); + if (!txt_regions.empty ()) { + f->dprintf ("with text subregions\n"); + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) + it.data ()->show_attrs (f); + f->dprintf ("end of subregions\n"); + } + } + else + f-> + dprintf + ("Text subregion with attributes %s, %s, %s, %s, %s, %s, %s, %s, %s, %s\n", + tlabel[0][horizontal], tlabel[1][text], tlabel[2][serif], + tlabel[3][proportional], tlabel[4][normal], tlabel[5][upright], + tlabel[6][solid], tlabel[7][black], tlabel[8][underlined], + tlabel[9][dropcaps]); +} + + +void TEXT_REGION::add_a_region(TEXT_REGION *newchild) { + TEXT_REGION_IT c = &txt_regions; + + c.move_to_first (); + c.add_to_end (newchild); +} + + +/********************************************************************** + * TEXT_REGION::rotate + * + * Rotate the TEXT_REGION and its children + **********************************************************************/ + +void TEXT_REGION::rotate( //cos,sin + FCOORD rotation) { + //sub block iterator + TEXT_REGION_IT child_it = &txt_regions; + TEXT_REGION *child; //child block + + for (child_it.mark_cycle_pt (); !child_it.cycled_list (); + child_it.forward ()) { + child = child_it.data (); + child->rotate (rotation); + } + POLY_BLOCK::rotate(rotation); +} + + +/********************************************************************** + * TEXT_REGION::move + * + * Move the TEXT_REGION and its children + **********************************************************************/ + +void TEXT_REGION::move(ICOORD shift //amount to move + ) { + //sub block iterator + TEXT_REGION_IT child_it = &txt_regions; + TEXT_REGION *child; //child block + + for (child_it.mark_cycle_pt (); !child_it.cycled_list (); + child_it.forward ()) { + child = child_it.data (); + child->move (shift); + } + POLY_BLOCK::move(shift); +} + + +/********************************************************************** + * TEXT_REGION::serialise_asc() Convert to ascii file. + * + **********************************************************************/ + +void TEXT_REGION::serialise_asc( //convert to ascii + FILE *f //file to use + ) { + ((POLY_BLOCK *) this)->serialise_asc (f); + serialise_INT32(f, id_number); //unique id + //horizontal, vertical, skewed + serialise_INT32(f, horizontal); + serialise_INT32(f, text); //text, table, form + serialise_INT32(f, serif); //serif, sansserif + //proportional, fixed + serialise_INT32(f, proportional); + serialise_INT32(f, normal); //normal, bold + serialise_INT32(f, upright); //upright, italic + serialise_INT32(f, solid); //solid, outline + serialise_INT32(f, black); //black, coloured, white, + //not underlined, underlined + serialise_INT32(f, underlined); + serialise_INT32(f, dropcaps); //not dropcaps, dropcaps + + txt_regions.serialise_asc (f); +} + + +/********************************************************************** + * TEXT_REGION::de_serialise_asc() Convert from ascii file. + * + **********************************************************************/ + +void TEXT_REGION::de_serialise_asc( //convert from ascii + FILE *f //file to use + ) { + ((POLY_BLOCK *) this)->de_serialise_asc (f); + //unique id + id_number = de_serialise_INT32 (f); + //horizontal, vertical, skewed + horizontal = de_serialise_INT32 (f); + text = de_serialise_INT32 (f); //text, table, form + serif = de_serialise_INT32 (f);//serif, sansserif + //proportional, fixed + proportional = de_serialise_INT32 (f); + //normal, bold + normal = de_serialise_INT32 (f); + //upright, italic + upright = de_serialise_INT32 (f); + solid = de_serialise_INT32 (f);//solid, outline + black = de_serialise_INT32 (f);//black, coloured, white, + //not underlined, underlined + underlined = de_serialise_INT32 (f); + //not dropcaps, dropcaps + dropcaps = de_serialise_INT32 (f); + + txt_regions.de_serialise_asc (f); +} diff --git a/ccstruct/txtregn.h b/ccstruct/txtregn.h new file mode 100644 index 0000000000..671d6012af --- /dev/null +++ b/ccstruct/txtregn.h @@ -0,0 +1,155 @@ +/********************************************************************** + * File: txtregn.h (Formerly text_region.h) + * Description: Text region within a polygonal block + * 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 TXTREGN_H +#define TXTREGN_H + +#include +#include "elst.h" +#include "hpdsizes.h" +#include "polyblk.h" +#include "debugwin.h" + +#include "hpddef.h" //must be last (handpd.dll) + +#define REGION_COLOUR CYAN +#define SUBREGION_COLOUR GREEN + +class DLLSYM TEXT_REGION; //forward decl + +ELISTIZEH_S (TEXT_REGION) +class DLLSYM TEXT_REGION:public ELIST_LINK, public POLY_BLOCK +//text REGION +{ + public: + TEXT_REGION() { + } //empty constructor + TEXT_REGION( //simple constructor + INT32 idno, + ICOORDELT_LIST *points, + TEXT_REGION_LIST *child); + + TEXT_REGION( //simple constructor + INT32 idno, + ICOORDELT_LIST *points); + + TEXT_REGION( //constructor + INT32 idno, + ICOORDELT_LIST *points, + INT8 hor, + INT8 tex, + INT8 ser, + INT8 pro, + INT8 nor, + INT8 upr, + INT8 sol, + INT8 bla, + INT8 und, + INT8 dro); + + ~TEXT_REGION () { //destructor + } + + void set_id_no(INT32 new_id) { + id_number = new_id; + } + + INT32 id_no() { + return id_number; + } + + INT32 nregions() { + return txt_regions.length (); + } + + BOOL8 is_prop() const { //test proportional + return !proportional; //stored negatively + } + + void set_prop(BOOL8 prop) { + if (prop) + proportional = 0; + else + proportional = 1; + } + + void add_a_region(TEXT_REGION *newchild); + + //get children + TEXT_REGION_LIST *regions() { + return &txt_regions; + } + + void set_attrs(INT8 hor, + INT8 tex, + INT8 ser, + INT8 pro, + INT8 nor, + INT8 upr, + INT8 sol, + INT8 bla, + INT8 und, + INT8 dro); + + void show_attrs(DEBUG_WIN *f); + + void rotate( //rotate it + FCOORD rotation); + void move( //move it + ICOORD shift); //vector + + void prep_serialise() { //set ptrs to counts + POLY_BLOCK::prep_serialise(); + txt_regions.prep_serialise (); + } + + void dump( //write external bits + FILE *f) { + POLY_BLOCK::dump(f); + txt_regions.dump (f); + } + + void de_dump( //read external bits + FILE *f) { + POLY_BLOCK::de_dump(f); + txt_regions.de_dump (f); + } + + //serialise to ascii + make_serialise (TEXT_REGION) void serialise_asc ( + FILE * f); + void de_serialise_asc( //serialise from ascii + FILE *f); + + private: + INT32 id_number; //unique id + INT8 horizontal; //horizontal, vertical, skewed + INT8 text; //text, table, form + INT8 serif; //serif, sansserif + INT8 proportional; //proportional, fixed + INT8 normal; //normal, bold + INT8 upright; //upright, italic + INT8 solid; //solid, outline + INT8 black; //black, coloured, white, + INT8 underlined; //not underlined, underlined + INT8 dropcaps; //not dropcaps, dropcaps + + TEXT_REGION_LIST txt_regions; +}; +#endif diff --git a/ccstruct/vecfuncs.cpp b/ccstruct/vecfuncs.cpp new file mode 100644 index 0000000000..33992e1c28 --- /dev/null +++ b/ccstruct/vecfuncs.cpp @@ -0,0 +1,63 @@ +/* -*-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 acces methods. + * This version ready for independent development. + */ +/*---------------------------------------------------------------------- + I n c l u d e s +----------------------------------------------------------------------*/ +#include "mfcpch.h" +#include "vecfuncs.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; +} diff --git a/ccstruct/vecfuncs.h b/ccstruct/vecfuncs.h new file mode 100644 index 0000000000..844d036f21 --- /dev/null +++ b/ccstruct/vecfuncs.h @@ -0,0 +1,91 @@ +/* -*-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 "tessclas.h" +#include + +/*---------------------------------------------------------------------- + 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, \ + (p)) + +/********************************************************************** + * 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); + +/* +#if defined(__STDC__) || defined(__cplusplus) || MAC_OR_DOS +# define _ARGS(s) s +#else +# define _ARGS(s) () +#endif*/ + +/* vecfuncs.c +int direction + _ARGS((EDGEPT *point)); + +#undef _ARGS +*/ +#endif diff --git a/ccstruct/werd.cpp b/ccstruct/werd.cpp new file mode 100644 index 0000000000..b4bff9dd8a --- /dev/null +++ b/ccstruct/werd.cpp @@ -0,0 +1,997 @@ +/********************************************************************** + * 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 "mfcpch.h" +#include "blckerr.h" +#include "linlsq.h" +#include "werd.h" + +#define FIRST_COLOUR RED //first rainbow colour + //last rainbow colour +#define LAST_COLOUR AQUAMARINE +#define CHILD_COLOUR BROWN //colour of children + +const ERRCODE CANT_SCALE_EDGESTEPS = +"Attempted to scale an edgestep format word"; + +#define EXTERN + +EXTERN BOOL_VAR (bln_numericmode, 0, "Optimize for numbers"); +EXTERN INT_VAR (bln_x_height, 128, "Baseline Normalisation X-height"); +EXTERN INT_VAR (bln_baseline_offset, 64, "Baseline Norm. offset of baseline"); +EXTERN double_VAR (bln_blshift_maxshift, -1.0, +"Fraction of xh before shifting"); +EXTERN double_VAR (bln_blshift_xfraction, 0.75, +"Size fraction of xh before shifting"); + +ELISTIZE_S (WERD) +/********************************************************************** + * 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 ( //constructor +C_BLOB_LIST * blob_list, //in word order +UINT8 blank_count, //blanks in front +const char *text //correct text +): +flags (0), +correct(text) { + C_BLOB_IT start_it = blob_list;//iterator + C_BLOB_IT end_it = blob_list; //another + //rejected blobs in wd + C_BLOB_IT rej_cblob_it = &rej_cblobs; + C_OUTLINE_IT c_outline_it; //coutline iterator + BOOL8 blob_inverted; + BOOL8 reject_blob; + INT16 inverted_vote = 0; + INT16 non_inverted_vote = 0; + + while (!end_it.at_last ()) + end_it.forward (); //move to last + //move to our list + cblobs.assign_to_sublist (&start_it, &end_it); + blanks = blank_count; + /* + 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 ()) { + c_outline_it.set_to_list (start_it.data ()->out_list ()); + blob_inverted = c_outline_it.data ()->flag (COUT_INVERSE); + reject_blob = FALSE; + 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 BLOBs. + * The BLOBs are not copied so the source list is emptied. + **********************************************************************/ + +WERD::WERD ( //constructor +PBLOB_LIST * blob_list, //in word order +UINT8 blank_count, //blanks in front +const char *text //correct text +): +flags (0), +correct(text) { + PBLOB_IT start_it = blob_list; //iterator + PBLOB_IT end_it = blob_list; //another + + while (!end_it.at_last ()) + end_it.forward (); //move to last + ((PBLOB_LIST *) (&cblobs))->assign_to_sublist (&start_it, &end_it); + //move to our list + //it's a polygon + flags.set_bit (W_POLYGON, TRUE); + blanks = blank_count; + // fprintf(stderr,"Wrong constructor!!!!\n"); +} + + +/********************************************************************** + * WERD::WERD + * + * Constructor to build a WERD from a list of BLOBs. + * The BLOBs are not copied so the source list is emptied. + **********************************************************************/ + +WERD::WERD ( //constructor +PBLOB_LIST * blob_list, //in word order +WERD * clone //sorce of flags +):flags (clone->flags), correct (clone->correct) { + PBLOB_IT start_it = blob_list; //iterator + PBLOB_IT end_it = blob_list; //another + + while (!end_it.at_last ()) + end_it.forward (); //move to last + ((PBLOB_LIST *) (&cblobs))->assign_to_sublist (&start_it, &end_it); + //move to our list + blanks = clone->blanks; + // fprintf(stderr,"Wrong constructor!!!!\n"); +} + + +/********************************************************************** + * 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 ( //constructor +C_BLOB_LIST * blob_list, //in word order +WERD * clone //sorce of flags +):flags (clone->flags), 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"); +} + + +/********************************************************************** + * WERD::poly_copy + * + * Make a copy of a WERD in polygon format. + * The source WERD is untouched. + **********************************************************************/ + +WERD *WERD::poly_copy( //make a poly copy + float xheight //row height + ) { + PBLOB *blob; //new blob + WERD *result = new WERD; //output word + C_BLOB_IT src_it = &cblobs; //iterator + // LARC_BLOB_IT larc_it=(LARC_BLOB_LIST*)(&cblobs); + PBLOB_IT dest_it = (PBLOB_LIST *) (&result->cblobs); + //another + + if (flags.bit (W_POLYGON)) { + *result = *this; //just copy it + } + else { + result->flags = flags; + result->correct = correct; //copy info + result->dummy = dummy; + if (!src_it.empty ()) { + // if (flags.bit(W_LINEARC)) + // { + // do + // { + // blob=new PBLOB; + // poly_linearc_outlines(larc_it.data()->out_list(), + // blob->out_list()); //convert outlines + // dest_it.add_after_then_move(blob); //add to dest list + // larc_it.forward(); + // } + // while (!larc_it.at_first()); + // } + // else + // { + do { + blob = new PBLOB (src_it.data (), xheight); + //convert blob + //add to dest list + dest_it.add_after_then_move (blob); + src_it.forward (); + } + while (!src_it.at_first ()); + // } + } + if (!rej_cblobs.empty ()) { + /* Polygonal approx of reject blobs */ + src_it.set_to_list (&rej_cblobs); + dest_it = (PBLOB_LIST *) (&result->rej_cblobs); + do { + //convert blob + blob = new PBLOB (src_it.data (), xheight); + //add to dest list + dest_it.add_after_then_move (blob); + src_it.forward (); + } + while (!src_it.at_first ()); + } + //polygon now + result->flags.set_bit (W_POLYGON, TRUE); + result->blanks = blanks; + } + return result; +} + + +/********************************************************************** + * 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. + **********************************************************************/ + +BOX WERD::bounding_box() { //bounding box + BOX box; //box being built + //rejected blobs in wd + C_BLOB_IT rej_cblob_it = &rej_cblobs; + + for (rej_cblob_it.mark_cycle_pt (); + !rej_cblob_it.cycled_list (); rej_cblob_it.forward ()) { + box += rej_cblob_it.data ()->bounding_box (); + } + + if (flags.bit (W_POLYGON)) { + //polygons + PBLOB_IT it = (PBLOB_LIST *) (&cblobs); + + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + box += it.data ()->bounding_box (); + } + } + else { + C_BLOB_IT it = &cblobs; //blobs of WERD + + 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( // reposition WERD + const ICOORD vec // by vector + ) { + PBLOB_IT blob_it ((PBLOB_LIST *) & cblobs); + // blob iterator + // LARC_BLOB_IT lblob_it((LARC_BLOB_LIST*)&cblobs); + C_BLOB_IT cblob_it(&cblobs); // cblob iterator + + if (flags.bit (W_POLYGON)) + for (blob_it.mark_cycle_pt (); + !blob_it.cycled_list (); blob_it.forward ()) + blob_it.data ()->move (vec); + // else if (flags.bit(W_LINEARC)) + // for( lblob_it.mark_cycle_pt(); + // !lblob_it.cycled_list(); + // lblob_it.forward() ) + // lblob_it.data()->move( vec ); + else + for (cblob_it.mark_cycle_pt (); + !cblob_it.cycled_list (); cblob_it.forward ()) + cblob_it.data ()->move (vec); +} + + +/********************************************************************** + * WERD::scale + * + * Scale WERD by multiplier + **********************************************************************/ + +void WERD::scale( // scale WERD + const float f // by multiplier + ) { + PBLOB_IT blob_it ((PBLOB_LIST *) & cblobs); + // blob iterator + // LARC_BLOB_IT lblob_it((LARC_BLOB_LIST*)&cblobs); + + if (flags.bit (W_POLYGON)) + for (blob_it.mark_cycle_pt (); + !blob_it.cycled_list (); blob_it.forward ()) + blob_it.data ()->scale (f); + // else if (flags.bit(W_LINEARC)) + // for (lblob_it.mark_cycle_pt(); + // !lblob_it.cycled_list(); + // lblob_it.forward() ) + // lblob_it.data()->scale( f ); + else + CANT_SCALE_EDGESTEPS.error ("WERD::scale", ABORT, NULL); +} + + +/********************************************************************** + * WERD::join_on + * + * Join other word onto this one. Delete the old word. + **********************************************************************/ + +void WERD::join_on( // join WERD + WERD *&other //other word + ) { + PBLOB_IT blob_it ((PBLOB_LIST *) & cblobs); + // blob iterator + PBLOB_IT src_it ((PBLOB_LIST *) & 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( //copy blobs + WERD *&other //from other + ) { + if (flags.bit (W_POLYGON)) { + PBLOB_IT blob_it ((PBLOB_LIST *) & cblobs); + // blob iterator + PBLOB_LIST blobs; + + blobs.deep_copy ((PBLOB_LIST *) (&other->cblobs)); + blob_it.move_to_last (); + blob_it.add_list_after (&blobs); + } + // else if (flags.bit(W_LINEARC)) + // { + // LARC_BLOB_IT larc_blob_it( (LARC_BLOB_LIST*)&cblobs ); + // LARC_BLOB_LIST larc_blobs; + + // larc_blobs.deep_copy((LARC_BLOB_LIST*)(&other->cblobs)); + // larc_blob_it.move_to_last(); + // larc_blob_it.add_list_after( &larc_blobs ); + // } + else { + C_BLOB_IT c_blob_it(&cblobs); + C_BLOB_LIST c_blobs; + + c_blobs.deep_copy (&other->cblobs); + 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); + rej_c_blob_it.move_to_last (); + rej_c_blob_it.add_list_after (&new_rej_c_blobs); + } +} + + +/********************************************************************** + * WERD::baseline_normalise + * + * Baseline Normalise the word in Tesseract style. (I.e origin at centre of + * word at bottom. x-height region scaled to region y = + * (bln_baseline_offset)..(bln_baseline_offset + bln_x_height) + * - usually 64..192) + **********************************************************************/ + +void WERD::baseline_normalise( // Tess style BL Norm + ROW *row, + DENORM *denorm //antidote + ) { + baseline_normalise_x (row, row->x_height (), denorm); + //Use standard x ht +} + + +/********************************************************************** + * WERD::baseline_normalise_x + * + * Baseline Normalise the word in Tesseract style. (I.e origin at centre of + * word at bottom. x-height region scaled to region y = + * (bln_baseline_offset)..(bln_baseline_offset + bln_x_height) + * - usually 64..192) + * USE A SPECIFIED X-HEIGHT - NOT NECESSARILY THE ONE IN row + **********************************************************************/ + +void WERD::baseline_normalise_x( // Tess style BL Norm + ROW *row, + float x_height, //non standard value + DENORM *denorm //antidote + ) { + BOOL8 using_row; //as baseline + float blob_x_centre; //middle of blob + float blob_offset; //bottom miss + float top_offset; //top miss + float blob_x_height; //xh for this blob + INT16 segments; //no of segments + INT16 segment; //current segment + DENORM_SEG *segs; //array of segments + float mean_x; //mean xheight + INT32 x_count; //no of xs + BOX word_box = bounding_box ();//word bounding box + BOX blob_box; //blob bounding box + PBLOB_IT blob_it ((PBLOB_LIST *) & cblobs); + // blob iterator + PBLOB *blob; + LLSQ line; //fitted line + double line_m, line_c; //fitted line + //inverse norm + DENORM antidote (word_box.left () + + + (word_box.right () - word_box.left ()) / 2.0, + bln_x_height / x_height, row); + + if (!flags.bit (W_POLYGON)) { + WRONG_WORD.error ("WERD::baseline_normalise", ABORT, + "Need to poly approx"); + } + + if (flags.bit (W_NORMALIZED)) { + WRONG_WORD.error ("WERD::baseline_normalise", ABORT, + "Baseline unnormalised"); + } + + if (bln_numericmode) { + segs = new DENORM_SEG[blob_it.length ()]; + segments = 0; + float factor; // For scaling to baseline normalised size. + for (blob_it.mark_cycle_pt (); !blob_it.cycled_list (); + blob_it.forward ()) { + blob = blob_it.data (); + blob_box = blob->bounding_box (); + blob->move (FCOORD (-antidote.origin (), + -blob_box.bottom ())); + factor = bln_x_height * 4.0f / (3 * blob_box.height ()); + // Constrain the scale factor as target numbers should be either + // cap height already or xheight. + if (factor < antidote.scale()) + factor = antidote.scale(); + else if (factor > antidote.scale() * 1.5f) + factor = antidote.scale() * 1.5f; + blob->scale (factor); + blob->move (FCOORD (0.0, bln_baseline_offset)); + segs[segments].xstart = blob->bounding_box().left(); + segs[segments].ycoord = blob_box.bottom(); + segs[segments++].scale_factor = factor; + } + antidote = DENORM (antidote.origin (), antidote.scale (), + 0.0f, 0.0f, segments, segs, true, row); + delete [] segs; + + //Repeat for rej blobs + blob_it.set_to_list ((PBLOB_LIST *) & rej_cblobs); + for (blob_it.mark_cycle_pt (); !blob_it.cycled_list (); + blob_it.forward ()) { + blob = blob_it.data (); + blob_box = blob->bounding_box (); + blob->move (FCOORD (-antidote.origin (), + -blob_box.bottom ())); + blob->scale (bln_x_height * 4.0f / (3 * blob_box.height ())); + blob->move (FCOORD (0.0, bln_baseline_offset)); + } + } + else if (bln_blshift_maxshift < 0) { + for (blob_it.mark_cycle_pt (); !blob_it.cycled_list (); + blob_it.forward ()) { + blob = blob_it.data (); + blob_box = blob->bounding_box (); + blob_x_centre = blob_box.left () + + (blob_box.right () - blob_box.left ()) / 2.0; + blob->move (FCOORD (-antidote.origin (), + -(row->base_line (blob_x_centre)))); + blob->scale (antidote.scale ()); + blob->move (FCOORD (0.0, bln_baseline_offset)); + } + + //Repeat for rej blobs + blob_it.set_to_list ((PBLOB_LIST *) & rej_cblobs); + for (blob_it.mark_cycle_pt (); !blob_it.cycled_list (); + blob_it.forward ()) { + blob = blob_it.data (); + blob_box = blob->bounding_box (); + blob_x_centre = blob_box.left () + + (blob_box.right () - blob_box.left ()) / 2.0; + blob->move (FCOORD (-antidote.origin (), + -(row->base_line (blob_x_centre)))); + blob->scale (antidote.scale ()); + blob->move (FCOORD (0.0, bln_baseline_offset)); + } + + } + else { + mean_x = x_height; + x_count = 1; + segs = new DENORM_SEG[blob_it.length ()]; + segments = 0; + for (blob_it.mark_cycle_pt (); !blob_it.cycled_list (); + blob_it.forward ()) { + blob = blob_it.data (); + blob_box = blob->bounding_box (); + if (blob_box.height () > bln_blshift_xfraction * x_height) { + blob_x_centre = blob_box.left () + + (blob_box.right () - blob_box.left ()) / 2.0; + blob_offset = + blob_box.bottom () - row->base_line (blob_x_centre); + top_offset = blob_offset + blob_box.height () - x_height - 1; + blob_x_height = top_offset + x_height; + if (top_offset < 0) + top_offset = -top_offset; + if (blob_offset < 0) + blob_offset = -blob_offset; + if (blob_offset < bln_blshift_maxshift * x_height) { + segs[segments].ycoord = blob_box.bottom (); + line.add (blob_x_centre, blob_box.bottom ()); + if (top_offset < bln_blshift_maxshift * x_height) { + segs[segments].scale_factor = blob_box.height () - 1.0f; + x_count++; + } + else + segs[segments].scale_factor = 0.0f; + //fix it later + } + else { + //not a goer + segs[segments].ycoord = -MAX_INT32; + if (top_offset < bln_blshift_maxshift * x_height) { + segs[segments].scale_factor = blob_x_height; + x_count++; + } + else + segs[segments].scale_factor = 0.0f; + //fix it later + } + } + else { + segs[segments].scale_factor = 0.0f; + segs[segments].ycoord = -MAX_INT32; + } + segs[segments].xstart = blob_box.left (); + segments++; + } + using_row = line.count () <= 1; + if (!using_row) { + line_m = line.m (); + line_c = line.c (line_m); + } + else + line_m = line_c = 0; + segments = 0; + for (blob_it.mark_cycle_pt (); !blob_it.cycled_list (); + blob_it.forward ()) { + blob = blob_it.data (); + blob_box = blob->bounding_box (); + blob_x_centre = blob_box.left () + + (blob_box.right () - blob_box.left ()) / 2.0; + if (segs[segments].ycoord == -MAX_INT32 + && segs[segments].scale_factor != 0 && !using_row) { + blob_offset = line_m * blob_x_centre + line_c; + segs[segments].scale_factor = blob_box.top () - blob_offset; + } + if (segs[segments].scale_factor != 0) + mean_x += segs[segments].scale_factor; + segments++; + } + mean_x /= x_count; + // printf("mean x=%g, count=%d, line_m=%g, line_c=%g\n", + // mean_x,x_count,line_m,line_c); + segments = 0; + for (blob_it.mark_cycle_pt (); !blob_it.cycled_list (); + blob_it.forward ()) { + blob = blob_it.data (); + blob_box = blob->bounding_box (); + blob_x_centre = blob_box.left () + + (blob_box.right () - blob_box.left ()) / 2.0; + if (segs[segments].ycoord != -MAX_INT32) + blob_offset = (float) segs[segments].ycoord; + else if (using_row) + blob_offset = row->base_line (blob_x_centre); + else + blob_offset = line_m * blob_x_centre + line_c; + if (segs[segments].scale_factor == 0) + segs[segments].scale_factor = mean_x; + segs[segments].scale_factor = + bln_x_height / segs[segments].scale_factor; + // printf("Blob sf=%g, top=%d, bot=%d, base=%g\n", + // segs[segments].scale_factor,blob_box.top(), + // blob_box.bottom(),blob_offset); + blob->move (FCOORD (-antidote.origin (), -blob_offset)); + blob-> + scale (FCOORD (antidote.scale (), segs[segments].scale_factor)); + blob->move (FCOORD (0.0, bln_baseline_offset)); + segments++; + } + + //Repeat for rej blobs + blob_it.set_to_list ((PBLOB_LIST *) & rej_cblobs); + segment = 0; + for (blob_it.mark_cycle_pt (); !blob_it.cycled_list (); + blob_it.forward ()) { + blob = blob_it.data (); + blob_box = blob->bounding_box (); + blob_x_centre = blob_box.left () + + (blob_box.right () - blob_box.left ()) / 2.0; + while (segment < segments - 1 + && segs[segment + 1].xstart <= blob_x_centre) + segment++; + if (segs[segment].ycoord != -MAX_INT32) + blob_offset = (float) segs[segment].ycoord; + else if (using_row) + blob_offset = row->base_line (blob_x_centre); + else + blob_offset = line_m * blob_x_centre + line_c; + blob->move (FCOORD (-antidote.origin (), -blob_offset)); + blob-> + scale (FCOORD (antidote.scale (), segs[segment].scale_factor)); + blob->move (FCOORD (0.0, bln_baseline_offset)); + } + if (line.count () > 0 || x_count > 1) + antidote = DENORM (antidote.origin (), antidote.scale (), + line_m, line_c, segments, segs, using_row, row); + delete[]segs; + } + if (denorm != NULL) + *denorm = antidote; + //it's normalised + flags.set_bit (W_NORMALIZED, TRUE); +} + + +/********************************************************************** + * WERD::baseline_denormalise + * + * Baseline DeNormalise the word in Tesseract style. (I.e origin at centre of + * word at bottom. x-height region scaled to region y = + * (bln_baseline_offset)..(bln_baseline_offset + bln_x_height) + * - usually 64..192) + **********************************************************************/ + +void WERD::baseline_denormalise( // Tess style BL Norm + const DENORM *denorm //antidote + ) { + PBLOB_IT blob_it ((PBLOB_LIST *) & cblobs); + // blob iterator + PBLOB *blob; + + if (!flags.bit (W_NORMALIZED)) { + WRONG_WORD.error ("WERD::baseline_denormalise", ABORT, + "Baseline normalised"); + } + + for (blob_it.mark_cycle_pt (); !blob_it.cycled_list (); blob_it.forward ()) { + blob = blob_it.data (); + //denormalise it + blob->baseline_denormalise (denorm); + } + + //Repeat for rej blobs + blob_it.set_to_list ((PBLOB_LIST *) & rej_cblobs); + for (blob_it.mark_cycle_pt (); !blob_it.cycled_list (); blob_it.forward ()) { + blob = blob_it.data (); + //denormalise it + blob->baseline_denormalise (denorm); + } + + //it's not normalised + flags.set_bit (W_NORMALIZED, FALSE); +} + + +/********************************************************************** + * WERD::print + * + * Display members + **********************************************************************/ + +void WERD::print( //print + FILE * //file to print on + ) { + 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_POLYGON = %s\n", flags.bit (W_POLYGON) ? "TRUE" : "FALSE "); + tprintf (" W_LINEARC = %s\n", flags.bit (W_LINEARC) ? "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 ()); +} + + +/********************************************************************** + * WERD::plot + * + * Draw the WERD in the given colour. + **********************************************************************/ + +#ifndef GRAPHICS_DISABLED +void WERD::plot( //draw it + WINDOW window, //window to draw in + COLOUR colour, //colour to draw in + BOOL8 solid //draw larcs solid + ) { + if (flags.bit (W_POLYGON)) { + //polygons + PBLOB_IT it = (PBLOB_LIST *) (&cblobs); + + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + it.data ()->plot (window, colour, colour); + } + } + // else if (flags.bit(W_LINEARC)) + // { + // LARC_BLOB_IT it=(LARC_BLOB_LIST*)(&cblobs); + + // for ( it.mark_cycle_pt(); !it.cycled_list(); it.forward() ) + // { + // it.data()->plot(window,solid,colour,solid ? BLACK : colour); + // } + // } + else { + C_BLOB_IT it = &cblobs; //blobs of WERD + + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + it.data ()->plot (window, colour, colour); + } + } + plot_rej_blobs(window, solid); +} +#endif + + +/********************************************************************** + * WERD::plot + * + * Draw the WERD in rainbow colours. + **********************************************************************/ + +#ifndef GRAPHICS_DISABLED +void WERD::plot( //draw it + WINDOW window, //window to draw in + BOOL8 solid //draw larcs solid + ) { + COLOUR colour = FIRST_COLOUR; //current colour + if (flags.bit (W_POLYGON)) { + //polygons + PBLOB_IT it = (PBLOB_LIST *) (&cblobs); + + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + it.data ()->plot (window, colour, CHILD_COLOUR); + colour = (COLOUR) (colour + 1); + if (colour == LAST_COLOUR) + colour = FIRST_COLOUR; //cycle round + } + } + // else if (flags.bit(W_LINEARC)) + // { + // LARC_BLOB_IT it=(LARC_BLOB_LIST*)(&cblobs); + + // for ( it.mark_cycle_pt(); !it.cycled_list(); it.forward() ) + // { + // it.data()->plot(window,solid,colour,solid ? BLACK : CHILD_COLOUR); + // colour=(COLOUR)(colour+1); + // if (colour==LAST_COLOUR) + // colour=FIRST_COLOUR; + // } + // } + else { + C_BLOB_IT it = &cblobs; //blobs of WERD + + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + it.data ()->plot (window, colour, CHILD_COLOUR); + colour = (COLOUR) (colour + 1); + if (colour == LAST_COLOUR) + colour = FIRST_COLOUR; //cycle round + } + } + plot_rej_blobs(window, solid); +} +#endif + + +/********************************************************************** + * WERD::plot_rej_blobs + * + * Draw the WERD rejected blobs - ALWAYS GREY + **********************************************************************/ + +#ifndef GRAPHICS_DISABLED +void WERD::plot_rej_blobs( //draw it + WINDOW window, //window to draw in + BOOL8 solid //draw larcs solid + ) { + if (flags.bit (W_POLYGON)) { + PBLOB_IT it = (PBLOB_LIST *) (&rej_cblobs); + //polygons + + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + it.data ()->plot (window, GREY, GREY); + } + } + // else if (flags.bit(W_LINEARC)) + // { + // LARC_BLOB_IT it=(LARC_BLOB_LIST*)(&rej_cblobs); + + // for ( it.mark_cycle_pt(); !it.cycled_list(); it.forward() ) + // { + // it.data()->plot(window,solid,GREY,solid ? BLACK : GREY); + // } + // } + else { + C_BLOB_IT it = &rej_cblobs; //blobs of WERD + + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + it.data ()->plot (window, GREY, GREY); + } + } +} +#endif + + +/********************************************************************** + * WERD::shallow_copy() + * + * Make a shallow copy of a word + **********************************************************************/ + +WERD *WERD::shallow_copy() { //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= ( //assign words +const WERD & source //from this +) { + this->ELIST_LINK::operator= (source); + blanks = source.blanks; + flags = source.flags; + dummy = source.dummy; + correct = source.correct; + if (flags.bit (W_POLYGON)) { + if (!cblobs.empty ()) + ((PBLOB_LIST *) (&cblobs))->clear (); + ((PBLOB_LIST *) (&cblobs))->deep_copy ((PBLOB_LIST *) (&source.cblobs)); + + if (!rej_cblobs.empty ()) + ((PBLOB_LIST *) (&rej_cblobs))->clear (); + ((PBLOB_LIST *) (&rej_cblobs))->deep_copy ((PBLOB_LIST *) (&source. + rej_cblobs)); + + } + // else if (flags.bit(W_LINEARC)) + // { + // if ( !cblobs.empty() ) + // ((LARC_BLOB_LIST*)(&cblobs))->clear(); + // ((LARC_BLOB_LIST*)(&cblobs))->deep_copy( + // (LARC_BLOB_LIST*)(&source.cblobs)); + + // if ( !rej_cblobs.empty() ) + // rej_cblobs.clear(); + // rej_cblobs.deep_copy( &source.rej_cblobs ); + // } + else { + if (!cblobs.empty ()) + cblobs.clear (); + cblobs.deep_copy (&source.cblobs); + + if (!rej_cblobs.empty ()) + rej_cblobs.clear (); + rej_cblobs.deep_copy (&source.rej_cblobs); + } + 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( //sort blobs + const void *word1p, //ptr to ptr to word1 + const void *word2p //ptr to ptr to word2 + ) { + WERD * + word1 = *(WERD **) word1p; + WERD * + word2 = *(WERD **) word2p; + + return word1->bounding_box ().left () - word2->bounding_box ().left (); +} diff --git a/ccstruct/werd.h b/ccstruct/werd.h new file mode 100644 index 0000000000..f35023d8a2 --- /dev/null +++ b/ccstruct/werd.h @@ -0,0 +1,277 @@ +/********************************************************************** + * 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 "varable.h" +#include "bits16.h" +#include "strngs.h" +#include "blckerr.h" +#include "stepblob.h" +#include "polyblob.h" +//#include "larcblob.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_POLYGON, //approximation + W_LINEARC, //linearc approx + 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 +}; + +class ROW; //forward decl + +class WERD:public ELIST_LINK +{ + public: + WERD() { + } //empty constructor + WERD( //constructor + C_BLOB_LIST *blob_list, //blobs in word + UINT8 blanks, //blanks in front + const char *text); //correct text + WERD( //constructor + PBLOB_LIST *blob_list, //blobs in word + UINT8 blanks, //blanks in front + const char *text); //correct text + WERD( //constructor + PBLOB_LIST *blob_list, //blobs in word + WERD *clone); //use these flags etc. + WERD( //constructor + C_BLOB_LIST *blob_list, //blobs in word + WERD *clone); //use these flags etc. + ~WERD () { //destructor + if (flags.bit (W_POLYGON)) { + //use right destructor + ((PBLOB_LIST *) & cblobs)->clear (); + //use right destructor + ((PBLOB_LIST *) & rej_cblobs)->clear (); + } + // else if (flags.bit(W_LINEARC)) + // ((LARC_BLOB_LIST*)&cblobs)->clear(); //use right destructor + } + + WERD *poly_copy( //make copy as poly + float xheight); //row xheight + WERD *larc_copy( //make copy as larc + float xheight); //row xheight + + //get DUFF compact blobs + C_BLOB_LIST *rej_cblob_list() { + if (flags.bit (W_POLYGON)) + WRONG_WORD.error ("WERD::rej_cblob_list", ABORT, NULL); + return &rej_cblobs; + } + + //get DUFF poly blobs + PBLOB_LIST *rej_blob_list() { + if (!flags.bit (W_POLYGON)) + WRONG_WORD.error ("WERD::rej_blob_list", ABORT, NULL); + return (PBLOB_LIST *) (&rej_cblobs); + } + + C_BLOB_LIST *cblob_list() { //get compact blobs + if (flags.bit (W_POLYGON) || flags.bit (W_LINEARC)) + WRONG_WORD.error ("WERD::cblob_list", ABORT, NULL); + return &cblobs; + } + PBLOB_LIST *blob_list() { //get poly blobs + if (!flags.bit (W_POLYGON)) + WRONG_WORD.error ("WERD::blob_list", ABORT, NULL); + //make it right type + return (PBLOB_LIST *) (&cblobs); + } + // LARC_BLOB_LIST *larc_blob_list() //get poly blobs + // { + // if (!flags.bit(W_LINEARC)) + // WRONG_WORD.error("WERD::larc_blob_list",ABORT,NULL); + // return (LARC_BLOB_LIST*)(&cblobs); //make it right type + // } + PBLOB_LIST *gblob_list() { //get generic blobs + //make it right type + return (PBLOB_LIST *) (&cblobs); + } + + const char *text() const { //correct text + return correct.string (); + } + UINT8 space() { //access function + return blanks; + } + void set_blanks( //set blanks + UINT8 new_blanks) { + blanks = new_blanks; + } + + void set_text( //replace correct text + const char *new_text) { //with this + correct = new_text; + } + + BOX bounding_box(); //compute bounding box + + BOOL8 flag( //test flag + WERD_FLAGS mask) const { //flag to test + return flags.bit (mask); + } + void set_flag( //set flag value + WERD_FLAGS mask, //flag to test + BOOL8 value) { //value to set + flags.set_bit (mask, value); + } + + BOOL8 display_flag( //test display flag + UINT8 flag) const { //flag to test + return disp_flags.bit (flag); + } + + void set_display_flag( //set display flag + UINT8 flag, //flag to set + BOOL8 value) { //value to set + disp_flags.set_bit (flag, value); + } + + WERD *shallow_copy(); //shallow copy word + + void move( // reposition word + const ICOORD vec); // by vector + + void scale( // scale word + const float vec); // by multiplier + + void join_on( //append word + WERD *&other); //Deleting other + + void copy_on( //copy blobs + WERD *&other); //from other + + void baseline_normalise ( // Tess style BL Norm + //optional antidote + ROW * row, DENORM * denorm = NULL); + + void baseline_normalise_x ( //Use non standard xht + ROW * row, float x_height, //Weird value to use + DENORM * denorm = NULL); //optional antidote + + void baseline_denormalise( //un-normalise + const DENORM *denorm); + + void print( //print + FILE *fp); //file to print on + + void plot ( //draw one + WINDOW window, //window to draw in + //uniform colour + COLOUR colour, BOOL8 solid = FALSE); + + void plot ( //draw one + //in rainbow colours + WINDOW window, BOOL8 solid = FALSE); + + void plot_rej_blobs ( //draw one + //in rainbow colours + WINDOW window, BOOL8 solid = FALSE); + + WERD & operator= ( //assign words + const WERD & source); //from this + + void prep_serialise() { //set ptrs to counts + correct.prep_serialise (); + if (flags.bit (W_POLYGON)) + ((PBLOB_LIST *) (&cblobs))->prep_serialise (); + // else if (flags.bit(W_LINEARC)) + // ((LARC_BLOB_LIST*)(&cblobs))->prep_serialise(); + else + cblobs.prep_serialise (); + rej_cblobs.prep_serialise (); + } + + void dump( //write external bits + FILE *f) { + correct.dump (f); + if (flags.bit (W_POLYGON)) + ((PBLOB_LIST *) (&cblobs))->dump (f); + // else if (flags.bit(W_LINEARC)) + // ((LARC_BLOB_LIST*)(&cblobs))->dump( f ); + else + cblobs.dump (f); + rej_cblobs.dump (f); + } + + void de_dump( //read external bits + FILE *f) { + correct.de_dump (f); + if (flags.bit (W_POLYGON)) + ((PBLOB_LIST *) (&cblobs))->de_dump (f); + // else if (flags.bit(W_LINEARC)) + // ((LARC_BLOB_LIST*)(&cblobs))->de_dump( f ); + else + cblobs.de_dump (f); + rej_cblobs.de_dump (f); + } + + make_serialise (WERD) private: + UINT8 blanks; //no of blanks + UINT8 dummy; //padding + BITS16 flags; //flags about word + BITS16 disp_flags; //display flags + INT16 dummy2; //padding + STRING correct; //correct text + C_BLOB_LIST cblobs; //compacted blobs + C_BLOB_LIST rej_cblobs; //DUFF blobs +}; + +ELISTIZEH_S (WERD) +#include "ocrrow.h" //placed here due to +extern BOOL_VAR_H (bln_numericmode, 0, "Optimize for numbers"); +extern INT_VAR_H (bln_x_height, 128, "Baseline Normalisation X-height"); +extern INT_VAR_H (bln_baseline_offset, 64, +"Baseline Norm. offset of baseline"); +//void poly_linearc_outlines( //do list of outlines +//LARC_OUTLINE_LIST *srclist, //list to convert +//OUTLINE_LIST *destlist //desstination list +//); +//OUTLINE *poly_larcline( //draw it +//LARC_OUTLINE *srcline //one to approximate +//); +int word_comparator( //sort blobs + const void *word1p, //ptr to ptr to word1 + const void *word2p //ptr to ptr to word2 + ); +#endif diff --git a/ccutil/Makefile.am b/ccutil/Makefile.am new file mode 100644 index 0000000000..b540b04a45 --- /dev/null +++ b/ccutil/Makefile.am @@ -0,0 +1,16 @@ +SUBDIRS = + +EXTRA_DIST = \ + basedir.h bits16.h clst.h debugwin.h elst2.h elst.h errcode.h \ + fileerr.h getopt.h globaloc.h hashfn.h host.h hosthplb.h lsterr.h \ + mainblk.h memblk.h memryerr.h memry.h mfcpch.h ndminx.h notdll.h \ + nwmain.h ocrclass.h ocrshell.h platform.h secname.h serialis.h \ + stderr.h strngs.h tessclas.h tprintf.h varable.h \ + mfcpch.cpp scanutils.cpp scanutils.h unichar.h + +noinst_LIBRARIES = libtesseract_ccutil.a +libtesseract_ccutil_a_SOURCES = \ + basedir.cpp bits16.cpp clst.cpp debugwin.cpp elst.cpp \ + elst2.cpp errcode.cpp globaloc.cpp hashfn.cpp mainblk.cpp \ + memblk.cpp memry.cpp ocrshell.cpp serialis.cpp strngs.cpp \ + tprintf.cpp varable.cpp unichar.cpp getopt.cpp diff --git a/ccutil/Makefile.in b/ccutil/Makefile.in new file mode 100644 index 0000000000..f6699a7885 --- /dev/null +++ b/ccutil/Makefile.in @@ -0,0 +1,556 @@ +# Makefile.in generated by automake 1.9.6 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005 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@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = .. +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +INSTALL = @INSTALL@ +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@ +subdir = ccutil +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \ + $(top_srcdir)/config/ac_define_versionlevel.m4 \ + $(top_srcdir)/config/acinclude_custom.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(SHELL) $(top_srcdir)/config/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config_auto.h +CONFIG_CLEAN_FILES = +LIBRARIES = $(noinst_LIBRARIES) +AR = ar +ARFLAGS = cru +libtesseract_ccutil_a_AR = $(AR) $(ARFLAGS) +libtesseract_ccutil_a_LIBADD = +am_libtesseract_ccutil_a_OBJECTS = basedir.$(OBJEXT) bits16.$(OBJEXT) \ + clst.$(OBJEXT) debugwin.$(OBJEXT) elst.$(OBJEXT) \ + elst2.$(OBJEXT) errcode.$(OBJEXT) globaloc.$(OBJEXT) \ + hashfn.$(OBJEXT) mainblk.$(OBJEXT) memblk.$(OBJEXT) \ + memry.$(OBJEXT) ocrshell.$(OBJEXT) serialis.$(OBJEXT) \ + strngs.$(OBJEXT) tprintf.$(OBJEXT) varable.$(OBJEXT) \ + unichar.$(OBJEXT) getopt.$(OBJEXT) +libtesseract_ccutil_a_OBJECTS = $(am_libtesseract_ccutil_a_OBJECTS) +DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/config/depcomp +am__depfiles_maybe = depfiles +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +CXXLD = $(CXX) +CXXLINK = $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) \ + -o $@ +SOURCES = $(libtesseract_ccutil_a_SOURCES) +DIST_SOURCES = $(libtesseract_ccutil_a_SOURCES) +RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \ + html-recursive info-recursive install-data-recursive \ + install-exec-recursive install-info-recursive \ + install-recursive installcheck-recursive installdirs-recursive \ + pdf-recursive ps-recursive uninstall-info-recursive \ + uninstall-recursive +ETAGS = etags +CTAGS = ctags +DIST_SUBDIRS = $(SUBDIRS) +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMDEP_FALSE = @AMDEP_FALSE@ +AMDEP_TRUE = @AMDEP_TRUE@ +AMTAR = @AMTAR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CXXRPOFLAGS = @CXXRPOFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +GNUWIN32_DIR = @GNUWIN32_DIR@ +HAVE_GNUWIN32_FALSE = @HAVE_GNUWIN32_FALSE@ +HAVE_GNUWIN32_TRUE = @HAVE_GNUWIN32_TRUE@ +HAVE_LIBTIFF_FALSE = @HAVE_LIBTIFF_FALSE@ +HAVE_LIBTIFF_TRUE = @HAVE_LIBTIFF_TRUE@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTIFF_CFLAGS = @LIBTIFF_CFLAGS@ +LIBTIFF_LIBS = @LIBTIFF_LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAINTAINER_MODE_FALSE = @MAINTAINER_MODE_FALSE@ +MAINTAINER_MODE_TRUE = @MAINTAINER_MODE_TRUE@ +MAKEINFO = @MAKEINFO@ +OBJEXT = @OBJEXT@ +OPTS = @OPTS@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_DATE = @PACKAGE_DATE@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PACKAGE_YEAR = @PACKAGE_YEAR@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +RPO_NO = @RPO_NO@ +RPO_YES = @RPO_YES@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +USING_CL_FALSE = @USING_CL_FALSE@ +USING_CL_TRUE = @USING_CL_TRUE@ +VERSION = @VERSION@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_RANLIB = @ac_ct_RANLIB@ +ac_ct_STRIP = @ac_ct_STRIP@ +am__fastdepCC_FALSE = @am__fastdepCC_FALSE@ +am__fastdepCC_TRUE = @am__fastdepCC_TRUE@ +am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@ +am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@ +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@ +datadir = @datadir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +SUBDIRS = +EXTRA_DIST = \ + basedir.h bits16.h clst.h debugwin.h elst2.h elst.h errcode.h \ + fileerr.h getopt.h globaloc.h hashfn.h host.h hosthplb.h lsterr.h \ + mainblk.h memblk.h memryerr.h memry.h mfcpch.h ndminx.h notdll.h \ + nwmain.h ocrclass.h ocrshell.h platform.h secname.h serialis.h \ + stderr.h strngs.h tessclas.h tprintf.h varable.h \ + mfcpch.cpp scanutils.cpp scanutils.h unichar.h + +noinst_LIBRARIES = libtesseract_ccutil.a +libtesseract_ccutil_a_SOURCES = \ + basedir.cpp bits16.cpp clst.cpp debugwin.cpp elst.cpp \ + elst2.cpp errcode.cpp globaloc.cpp hashfn.cpp mainblk.cpp \ + memblk.cpp memry.cpp ocrshell.cpp serialis.cpp strngs.cpp \ + tprintf.cpp varable.cpp unichar.cpp getopt.cpp + +all: all-recursive + +.SUFFIXES: +.SUFFIXES: .cpp .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 \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu ccutil/Makefile'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu 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 + +clean-noinstLIBRARIES: + -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) +libtesseract_ccutil.a: $(libtesseract_ccutil_a_OBJECTS) $(libtesseract_ccutil_a_DEPENDENCIES) + -rm -f libtesseract_ccutil.a + $(libtesseract_ccutil_a_AR) libtesseract_ccutil.a $(libtesseract_ccutil_a_OBJECTS) $(libtesseract_ccutil_a_LIBADD) + $(RANLIB) libtesseract_ccutil.a + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/basedir.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bits16.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/clst.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/debugwin.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/elst.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/elst2.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/errcode.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/getopt.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/globaloc.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hashfn.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mainblk.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/memblk.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/memry.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ocrshell.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/serialis.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/strngs.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tprintf.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/unichar.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/varable.Po@am__quote@ + +.cpp.o: +@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ $< + +.cpp.obj: +@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` +uninstall-info-am: + +# 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. +$(RECURSIVE_TARGETS): + @failcom='exit 1'; \ + for f in x $$MAKEFLAGS; do \ + case $$f in \ + *=* | --[!k]*);; \ + *k*) failcom='fail=yes';; \ + esac; \ + done; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + list='$(SUBDIRS)'; 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; \ + (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" + +mostlyclean-recursive clean-recursive distclean-recursive \ +maintainer-clean-recursive: + @failcom='exit 1'; \ + for f in x $$MAKEFLAGS; do \ + case $$f in \ + *=* | --[!k]*);; \ + *k*) failcom='fail=yes';; \ + esac; \ + done; \ + dot_seen=no; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + rev=''; for subdir in $$list; do \ + if test "$$subdir" = "."; then :; else \ + rev="$$subdir $$rev"; \ + fi; \ + done; \ + rev="$$rev ."; \ + target=`echo $@ | sed s/-recursive//`; \ + for subdir in $$rev; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done && test -z "$$fail" +tags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \ + done +ctags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \ + done + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + 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 || \ + tags="$$tags $$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique; \ + fi +ctags: CTAGS +CTAGS: ctags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(CTAGS_ARGS)$$tags$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +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)'; for file in $$list; do \ + case $$file in \ + $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \ + $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \ + esac; \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + dir="/$$dir"; \ + $(mkdir_p) "$(distdir)$$dir"; \ + else \ + dir=''; \ + fi; \ + if test -d $$d/$$file; then \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$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 \ + test -d "$(distdir)/$$subdir" \ + || $(mkdir_p) "$(distdir)/$$subdir" \ + || exit 1; \ + distdir=`$(am__cd) $(distdir) && pwd`; \ + top_distdir=`$(am__cd) $(top_distdir) && pwd`; \ + (cd $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$top_distdir" \ + distdir="$$distdir/$$subdir" \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-recursive +all-am: Makefile $(LIBRARIES) +installdirs: installdirs-recursive +installdirs-am: +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: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_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-recursive + +clean-am: clean-generic clean-noinstLIBRARIES mostlyclean-am + +distclean: distclean-recursive + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +info: info-recursive + +info-am: + +install-data-am: + +install-exec-am: + +install-info: install-info-recursive + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-compile mostlyclean-generic + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: uninstall-info-am + +uninstall-info: uninstall-info-recursive + +.PHONY: $(RECURSIVE_TARGETS) CTAGS GTAGS all all-am check check-am \ + clean clean-generic clean-noinstLIBRARIES clean-recursive \ + ctags ctags-recursive distclean distclean-compile \ + distclean-generic distclean-recursive distclean-tags distdir \ + dvi dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-exec install-exec-am \ + install-info install-info-am install-man install-strip \ + installcheck installcheck-am installdirs installdirs-am \ + maintainer-clean maintainer-clean-generic \ + maintainer-clean-recursive mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-recursive pdf pdf-am ps ps-am \ + tags tags-recursive uninstall uninstall-am uninstall-info-am + +# 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: diff --git a/ccutil/basedir.cpp b/ccutil/basedir.cpp new file mode 100644 index 0000000000..64fb63d346 --- /dev/null +++ b/ccutil/basedir.cpp @@ -0,0 +1,104 @@ +/********************************************************************** + * 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 "mfcpch.h" //precompiled headers +#include "strngs.h" +#ifdef __UNIX__ +#include +#include +#else +#include +#endif +#include +#include "basedir.h" +#include "notdll.h" //must be last include + +/********************************************************************** + * getpath + * + * Find the directory of the given executable using the usual path rules. + * This enables data to be located relative to the code. + **********************************************************************/ + +DLLSYM INT8 getpath( //get dir name of code + const char *code, //executable to locate + STRING &path //output path name + ) { + char directory[MAX_PATH]; //main directory + #ifdef __UNIX__ + INT16 dirind; //index in directory + register char *pathlist; //$PATH + int fd; //file descriptor + + strcpy(directory, code); //get demo directory + dirind = strlen (directory); + while (dirind > 0 && directory[dirind - 1] != '/') + dirind--; //look back for dirname + directory[dirind] = '\0'; //end at directory + if (dirind != 0) { + path = directory; //had it in arg + return 0; + } + pathlist = getenv ("PATH"); //find search path + while (pathlist != NULL && *pathlist) { + for (dirind = 0; *pathlist != '\0' && *pathlist != ':';) + //copy a directory + directory[dirind++] = *pathlist++; + if (*pathlist == ':') + pathlist++; + if (dirind == 0) + continue; + if (directory[dirind - 1] != '/'); + directory[dirind++] = '/'; //add ending slash + directory[dirind++] = '\0'; + path = directory; //try this path + strcat(directory, code); + fd = open (directory, 0); + if (fd >= 0) { + close(fd); //found it + return 0; + } + } + strcpy (directory, "./"); + path = directory; //in current? + strcat(directory, code); + fd = open (directory, 0); + if (fd >= 0) { + close(fd); + return 0; //in current after all + } + return -1; + #endif + #ifdef __MSW32__ + char *path_end; //end of dir + + if (GetModuleFileName (NULL, directory, MAX_PATH - 1) == 0) { + return -1; + } + while ((path_end = strchr (directory, '\\')) != NULL) + *path_end = '/'; + path_end = strrchr (directory, '/'); + if (path_end != NULL) + path_end[1] = '\0'; + else + strcpy (directory, "./"); + path = directory; + return 0; + #endif +} diff --git a/ccutil/basedir.h b/ccutil/basedir.h new file mode 100644 index 0000000000..04f5fb9769 --- /dev/null +++ b/ccutil/basedir.h @@ -0,0 +1,32 @@ +/********************************************************************** + * 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 "host.h" +#include "strngs.h" + +#include "notdll.h" //must be last include + +DLLSYM INT8 getpath( //get dir name of code + const char *code, //executable to locate + STRING &path //output path name + ); +#endif diff --git a/ccutil/bits16.cpp b/ccutil/bits16.cpp new file mode 100644 index 0000000000..2d90fe8593 --- /dev/null +++ b/ccutil/bits16.cpp @@ -0,0 +1,30 @@ +/********************************************************************** + * 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 "mfcpch.h" +#include "bits16.h" + +/********************************************************************** + * Constructor. Something to get it past the compiler as almost all inlined. + * + **********************************************************************/ +BITS16::BITS16( // constructor + UINT16 init) { // initial val + val = init; +} diff --git a/ccutil/bits16.h b/ccutil/bits16.h new file mode 100644 index 0000000000..02070c19f4 --- /dev/null +++ b/ccutil/bits16.h @@ -0,0 +1,61 @@ +/********************************************************************** + * 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 diff --git a/ccutil/clst.cpp b/ccutil/clst.cpp new file mode 100644 index 0000000000..5d03781858 --- /dev/null +++ b/ccutil/clst.cpp @@ -0,0 +1,591 @@ +/********************************************************************** + * 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 "mfcpch.h" //precompiled headers +#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; + + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("CLIST::internal_deep_clear", ABORT, NULL); + #endif + + 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; + + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("CLIST::shallow_clear", ABORT, NULL); + #endif + + 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::internal_deep_copy + * + * Used during explict deep copy of a list. The "copier" function passed + * allows each element to be correctly deep copied (assuming that each class + * in the inheritance hierarchy does properly deep copies its members). The + * function passing technique is as for "internal_clear". + **********************************************************************/ + +void + //ptr to copier functn +CLIST::internal_deep_copy (void *(*copier) (void *), +const CLIST * list) { //list being copied + CLIST_ITERATOR from_it ((CLIST *) list); + CLIST_ITERATOR to_it(this); + + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("CLIST::internal_deep_copy", ABORT, NULL); + if (!list) + BAD_PARAMETER.error ("CLIST::internal_deep_copy", ABORT, + "source list is NULL"); + #endif + + for (from_it.mark_cycle_pt (); !from_it.cycled_list (); from_it.forward ()) + to_it.add_after_then_move (copier (from_it.data ())); +} + + +/*********************************************************************** + * 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"; + + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("CLIST::assign_to_sublist", ABORT, NULL); + #endif + + 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() { //count elements + CLIST_ITERATOR it(this); + INT32 count = 0; + + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("CLIST::length", ABORT, NULL); + #endif + + 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; + + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("CLIST::sort", ABORT, NULL); + #endif + + /* 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); +} + + +/*********************************************************************** + * CLIST::prep_serialise + * + * Replace the last member with a count of elements for serialisation. + * This is used on list objects which are members of objects being + * serialised. The containing object has been shallow copied and this member + * function is invoked on the COPY. + **********************************************************************/ + +void CLIST::prep_serialise() { + CLIST_ITERATOR this_it(this); + INT32 count = 0; + + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("CLIST::prep_serialise", ABORT, NULL); + #endif + + count = 0; + if (!empty ()) + for (this_it.mark_cycle_pt (); + !this_it.cycled_list (); this_it.forward ()) + count++; + last = (CLIST_LINK *) count; +} + + +/*********************************************************************** + * CLIST::internal_dump + * + * Cause each element on the list to be serialised by walking the list and + * calling the element_serialiser function for each element. The + * element_serialiser simply does the appropriate coercion of the element to + * its real type and then invokes the elements serialise function + **********************************************************************/ + +void +CLIST::internal_dump (FILE * f, void element_serialiser (FILE *, void *)) { + CLIST_ITERATOR this_it(this); + + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("CLIST::internal_dump", ABORT, NULL); + #endif + + if (!empty ()) + for (this_it.mark_cycle_pt (); + !this_it.cycled_list (); this_it.forward ()) + element_serialiser (f, this_it.data ()); +} + + +/*********************************************************************** + * CLIST::internal_de_dump + * + * Cause each element on the list to be de_serialised by extracting the count + * of elements on the list, (held in the last member of the dumped version of + * the list object), and then de-serialising that number of list elements, + * adding each to the end of the reconstructed list. + **********************************************************************/ + +void +CLIST::internal_de_dump (FILE * f, void *element_de_serialiser (FILE *)) { + INT32 count = (ptrdiff_t) last; + CLIST_ITERATOR this_it; + + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("CLIST::internal_de_dump", ABORT, NULL); + #endif + + last = NULL; + this_it.set_to_list (this); + for (; count > 0; count--) + this_it.add_to_end (element_de_serialiser (f)); +} + + +/*********************************************************************** + * 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() { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("CLIST_ITERATOR::forward", ABORT, NULL); + 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; + } + else { + if (ex_current_was_cycle_pt) + cycle_pt = next; + } + current = next; + next = current->next; + + #ifdef _DEBUG + if (!current) + NULL_DATA.error ("CLIST_ITERATOR::forward", ABORT, NULL); + if (!next) + NULL_NEXT.error ("CLIST_ITERATOR::forward", ABORT, + "This is: %i Current is: %i", + (int) this, (int) 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; + + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("CLIST_ITERATOR::data_relative", ABORT, NULL); + 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); + + #ifdef _DEBUG + 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() { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("CLIST_ITERATOR::move_to_last", ABORT, NULL); + 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; + + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("CLIST_ITERATOR::exchange", ABORT, NULL); + 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"; + #ifdef _DEBUG + 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 (!this) + NULL_OBJECT.error ("CLIST_ITERATOR::extract_sublist", ABORT, NULL); + 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 ()) //cant 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; +} diff --git a/ccutil/clst.h b/ccutil/clst.h new file mode 100644 index 0000000000..71161e97f5 --- /dev/null +++ b/ccutil/clst.h @@ -0,0 +1,1078 @@ +/********************************************************************** + * 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 &) { //dont copy link + data = next = NULL; + } + + void operator= ( //dont copy links + const CLIST_LINK &) { + data = next = NULL; + } + + NEWDELETE2 (CLIST_LINK) + /* NOTE that none of the serialise member functions are required for + CLIST_LINKS as they are never serialised. */ +}; + +/********************************************************************** + * 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 dont + //delete data elements + + BOOL8 empty() { //is list empty? + return !last; + } + + BOOL8 singleton() { + return last != NULL ? (last == last->next) : FALSE; + } + + void shallow_copy( //dangerous!! + CLIST *from_list) { //beware destructors!! + last = from_list->last; + } + + //ptr to copier functn + void internal_deep_copy (void *(*copier) (void *), + const CLIST * list); //list being copied + + void assign_to_sublist( //to this list + CLIST_ITERATOR *start_it, //from list start + CLIST_ITERATOR *end_it); //from list end + + INT32 length(); //# elements in list + + void sort ( //sort elements + int comparator ( //comparison routine + const void *, const void *)); + + void internal_dump ( //serialise each elem + FILE * f, //to this file + void element_serialiser ( //using this function + FILE *, void *)); + + void internal_de_dump ( //de_serial each elem + FILE * f, //from this file + void *element_de_serialiser (//using this function + FILE *)); + + void prep_serialise(); //change last to count + + /* Note that dump() and de_dump() are not required as calls to dump/de_dump a + list class should be handled by a class derived from this. + + make_serialise is not required for a similar reason. + */ +}; + +/*********************************************************************** + * 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 + #ifdef _DEBUG + 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? + #ifdef _DEBUG + 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); //dont 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) { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("CLIST_ITERATOR::set_to_list", ABORT, NULL); + 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; + + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("CLIST_ITERATOR::add_after_then_move", ABORT, NULL); + 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; + + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("CLIST_ITERATOR::add_after_stay_put", ABORT, NULL); + 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; + + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("CLIST_ITERATOR::add_before_then_move", ABORT, NULL); + 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 dont 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; + + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("CLIST_ITERATOR::add_before_stay_put", ABORT, NULL); + 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 dont move the + * iterator. + **********************************************************************/ + +inline void CLIST_ITERATOR::add_list_after(CLIST *list_to_add) { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("CLIST_ITERATOR::add_list_after", ABORT, NULL); + 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) { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("CLIST_ITERATOR::add_list_before", ABORT, NULL); + 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; + + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("CLIST_ITERATOR::extract", ABORT, NULL); + 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 + prev = next = list->last = NULL; + // change the iterator + 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; + + 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() { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("CLIST_ITERATOR::move_to_first", ABORT, NULL); + 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->data; +} + + +/*********************************************************************** + * 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() { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("CLIST_ITERATOR::mark_cycle_pt", ABORT, NULL); + 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() { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("CLIST_ITERATOR::at_first", ABORT, NULL); + 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() { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("CLIST_ITERATOR::at_last", ABORT, NULL); + 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() { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("CLIST_ITERATOR::cycled_list", ABORT, NULL); + 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() { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("CLIST_ITERATOR::length", ABORT, NULL); + 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 *)) { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("CLIST_ITERATOR::sort", ABORT, NULL); + 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; + + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("CLIST_ITERATOR::add_to_end", ABORT, NULL); + 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 dont 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 + - An element serialiser function" CLASSNAME##_c1_serialiser + - An element de-serialiser function" CLASSNAME##_c1_de_serialiser + - 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 + +Four macros are provided: CLISTIZE, CLISTIZE_S, CLISTIZEH and CLISTIZEH_S +The ...IZEH macros just define the class names for use in .h files +The ...IZE macros define the code use in .c files +The _S versions define lists which can be serialised. They assume that the +make_serialise() macro is used in the list element class to define +serialise() and de_serialise() members for the list elements. +This, in turn, assumes that the list element class has prep_serialise() +dump() and de_dump() member functions. +***********************************************************************/ + +/*********************************************************************** + CLISTIZEH( CLASSNAME ) and CLISTIZEH_S( CLASSNAME ) MACROS + +These macros are constructed from 3 fragments CLISTIZEH_A, CLISTIZEH_B and +CLISTIZEH_C. CLISTIZEH is simply a concatenation of these parts. +CLISTIZEH_S has some additional bits thrown in the gaps. +***********************************************************************/ + +#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( /* dont 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 deep_copy( /* become a deep */ \ + const CLASSNAME##_CLIST*list) /* copy of src list*/ \ + { CLIST::internal_deep_copy( &CLASSNAME##_c1_copier, list ); } \ + \ +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 ) + +#define CLISTIZEH_S( CLASSNAME ) \ + \ +CLISTIZEH_A( CLASSNAME ) \ + \ +extern DLLSYM void CLASSNAME##_c1_serialiser( \ +FILE* f, \ +void* element); \ + \ +extern DLLSYM void* CLASSNAME##_c1_de_serialiser( \ +FILE* f); \ + \ +CLISTIZEH_B( CLASSNAME ) \ + \ + void dump( /* dump to file */ \ + FILE* f) \ + { CLIST::internal_dump( f, &CLASSNAME##_c1_serialiser );} \ + \ + void de_dump( /* get from file */ \ + FILE* f) \ + { CLIST::internal_de_dump( f, &CLASSNAME##_c1_de_serialiser );} \ + \ +make_serialise( CLASSNAME##_CLIST ) \ + \ +CLISTIZEH_C( CLASSNAME ) + +/*********************************************************************** + CLISTIZE( CLASSNAME ) and CLISTIZE_S( CLASSNAME ) MACROS +CLISTIZE_S is a simple extension to CLISTIZE +***********************************************************************/ + +#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 dont use a virtual destructor function. \ +**********************************************************************/ \ + \ +DLLSYM void CLASSNAME##_c1_zapper( /*delete a link*/ \ +void* link) /*link to delete*/ \ +{ \ +delete (CLASSNAME *) link; \ +} \ + \ + \ + \ +/*********************************************************************** \ +* CLASSNAME##_c1_copier \ +* \ +* A function which can generate a new, deep copy of a CLASSNAME element. \ +* This is passed to the generic deep copy list member function so that when \ +* a list is copied the elements on the list are properly copied from the \ +* base class, even though we dont use a virtual function. \ +* \ +**********************************************************************/ \ + \ +DLLSYM void* CLASSNAME##_c1_copier( /*deep copy a link*/ \ +void* old_element) /*source link*/ \ +{ \ + CLASSNAME* new_element; \ + \ +new_element = new CLASSNAME; \ +*new_element = *((CLASSNAME*) old_element); \ +return (void*) new_element; \ +} + +#define CLISTIZE_S( CLASSNAME ) \ + \ +CLISTIZE( CLASSNAME ) \ + \ +/*********************************************************************** \ +* CLASSNAME##_c1_serialiser \ +* \ +* A function which can serialise an element \ +* This is passed to the generic dump member function so that when a list is \ +* serialised the elements on the list are properly serialised. \ +**********************************************************************/ \ + \ +DLLSYM void CLASSNAME##_c1_serialiser( \ +FILE* f, \ +void* element) \ +{ \ +((CLASSNAME*) element)->serialise( f ); \ +} \ + \ + \ + \ +/*********************************************************************** \ +* CLASSNAME##_c1_de_serialiser \ +* \ +* A function which can de-serialise an element \ +* This is passed to the generic de-dump member function so that when a list \ +* is de-serialised the elements on the list are properly de-serialised. \ +**********************************************************************/ \ + \ +DLLSYM void* CLASSNAME##_c1_de_serialiser( \ +FILE* f) \ +{ \ +return CLASSNAME::de_serialise( f ); \ +} +#endif diff --git a/ccutil/debugwin.cpp b/ccutil/debugwin.cpp new file mode 100644 index 0000000000..f1a115828a --- /dev/null +++ b/ccutil/debugwin.cpp @@ -0,0 +1,500 @@ +/********************************************************************** + * File: debugwin.cpp + * Description: Portable debug window class. + * Author: Ray Smith + * Created: Wed Feb 21 15:36:59 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. + * + **********************************************************************/ + +#include "mfcpch.h" //precompiled headers +#include +#include "debugwin.h" + +DLLSYM INT_VAR (debug_lines, 256, "Number of lines in debug window"); + +#ifndef GRAPHICS_DISABLED + +#ifdef __MAC__ +#include +#include +//#include + +#define scrl_SCROLLER 101 +#define text_FLOWED 100 + +static LCommander *pCommander = NULL; +#endif + + //NT implementation +#if defined(__MSW32__) && !defined(_CONSOLE) + +#include +#define ID_DEBUG_MSG 32779 + +/********************************************************************** + * DEBUG_WIN::DEBUG_WIN + * + * Create a debug window with size according to the arguments. + **********************************************************************/ + +DEBUG_WIN::DEBUG_WIN( //constructor + const char *title, //of window + INT32 xpos, //initial position + INT32 ypos, //in pixels + INT32 xsize, //initial size + INT32 ysize, //in pixels + INT32 buflines //default scroll size + ) { + char cmd[1024]; + int parm; //output from scrolrwin + STARTUPINFO start_info; //process control + PROCESS_INFORMATION proc_info; //process ids + SECURITY_ATTRIBUTES security; //for handles + + handle = NULL; + shm_hand = NULL; + shm_mem = NULL; + msg_end = NULL; + dbg_process = NULL; //save handles + dbg_thread = NULL; + security.nLength = sizeof (security); + security.lpSecurityDescriptor = NULL; + security.bInheritHandle = TRUE;//make it inheritable + //anonymous + shm_hand = CreateFileMapping ((HANDLE) 0xffffffff, &security, PAGE_READWRITE, 0, 4096, NULL); + if (shm_hand == NULL) + return; //failed + shm_mem = (char *) MapViewOfFile (shm_hand, FILE_MAP_WRITE, 0, 0, 0); + if (shm_mem == NULL) + return; + shm_mem[5] = 0; + sprintf (cmd, "scrolwin.exe %d %d", buflines, shm_hand); + GetStartupInfo(&start_info); //clone our stuff + if (!CreateProcess (NULL, cmd, NULL, NULL, TRUE, + CREATE_NO_WINDOW | DETACHED_PROCESS | CREATE_SUSPENDED, + NULL, NULL, &start_info, &proc_info)) + return; + + //save handles + dbg_process = proc_info.hProcess; + dbg_thread = proc_info.hThread; + if (ResumeThread (dbg_thread) != 1) + return; + do + Sleep (100); + while (shm_mem[5] == 0); //wait for handle + parm = ((((UINT8) shm_mem[4] << 8) + (UINT8) shm_mem[3] << 8) + + (UINT8) shm_mem[2] << 8) + (UINT8) shm_mem[1]; + handle = (HWND) parm; + if (handle != NULL) { + //setup window + ::SetWindowText (handle, title); + ::MoveWindow (handle, xpos, ypos, xsize, ysize, TRUE); + ::ShowWindow (handle, SW_SHOW); + } +} + + +/********************************************************************** + * DEBUG_WIN::DEBUG_WIN + * + * Destroy a debug window. + **********************************************************************/ + +DEBUG_WIN::~DEBUG_WIN ( +//destructor +) { + if (IsWindow (handle)) + ::SendMessage (handle, WM_COMMAND, IDOK, 0); + if (shm_mem != NULL) + UnmapViewOfFile(shm_mem); + if (shm_hand != NULL) + CloseHandle(shm_hand); + if (dbg_thread != NULL) + CloseHandle(dbg_thread); + if (dbg_process == NULL) + CloseHandle(dbg_process); + +} + + +/********************************************************************** + * dprintf + * + * Print a message to the debug window. + * Like printf, this function can cope with messages which do not end + * in newline, but nothing is printed until the newline is received. + **********************************************************************/ + +void +DEBUG_WIN::dprintf ( //debug printf +const char *format, ... //special message +) { + va_list args; //variable args + char *msg_start; //for printing + + if (!IsWindow (handle)) + return; //destroyed + if (msg_end == NULL) + msg_end = shm_mem + 1; + va_start(args, format); //variable list + //Format into msg + vsprintf(msg_end, format, args); + va_end(args); + if (*msg_end == '\0') + return; + msg_start = shm_mem + 1; + do { + //end of line + msg_end = strchr (msg_start, '\n'); + if (msg_end == NULL) { + if (msg_start != shm_mem + 1) + //bring to front + strcpy (shm_mem + 1, msg_start); + //current end + msg_end = shm_mem + 1 + strlen (shm_mem + 1); + return; + } + *msg_end = '\0'; + while (IsWindow (handle) && shm_mem[0]) + Sleep (500); + if (IsWindow (handle)) { + //Visual C++2.0 macro + ::SendMessage (handle, WM_COMMAND, ID_DEBUG_MSG, (DWORD) (msg_start - shm_mem)); + } + msg_start = msg_end + 1; + } + while (*msg_start != '\0'); + msg_end = shm_mem + 1; //buffer empty +} + + +/********************************************************************** + * await_destruction + * + * Wait for the user to close the debug window. Then return. + **********************************************************************/ + +void DEBUG_WIN::await_destruction() { //wait for user to close + WaitForSingleObject (dbg_process, (unsigned long) -1); +} +#endif //NT Implmentation + + //UNIX implementation +#if defined(__UNIX__) || defined(_CONSOLE) +#ifdef __UNIX__ +#include +#include +#endif +//#include "basefile.h" + +/********************************************************************** + * DEBUG_WIN::DEBUG_WIN + * + * Create a debug window with size according to the arguments. + * Create an hpterm window with a pipe connected to it. + **********************************************************************/ + +DEBUG_WIN::DEBUG_WIN( //constructor + const char *title, //of window + INT32 xpos, //initial position + INT32 ypos, //in pixels + INT32 xsize, //initial size + INT32 ysize, //in pixels + INT32 buflines //default scroll size + ) { + #ifdef __UNIX__ + INT32 length; /*length of name */ + char command[MAX_PATH]; /*pipe command */ + pid_t pid; /*process id */ + char host[MAX_PATH]; //remote host + BOOL8 remote; //remote host + + // remote=remote_display(host); //check remote host + remote = FALSE; + if (remote) + //do it remotely + length = sprintf (command, "remsh %s 'DISPLAY=%s;export DISPLAY;", host, getenv ("DISPLAY")); + else + length = 0; + length += sprintf (command + length, "trap \"\" 1 2 3 13 15\n"); + length += + sprintf (command + length, + "/usr/bin/X11/xterm -sb -sl " INT32FORMAT " -geometry " + INT32FORMAT "x" INT32FORMAT "", buflines, xsize / 8, ysize / 16); + if (xpos >= 0) + command[length++] = '+'; + length += sprintf (command + length, INT32FORMAT, xpos); + if (ypos >= 0) + command[length++] = '+'; + length += + sprintf (command + length, + INT32FORMAT " -title \"%s\" -n \"%s\" -e /bin/sh -c ", ypos, + title, title); + pid = getpid (); /*random number */ + length += + sprintf (command + length, + "\"stty opost; tty >/tmp/debug%d; while [ -s /tmp/debug%d ]\ndo\nsleep 1\ndone\" &\n", + pid, pid); + length += + sprintf (command + length, "trap \"rm -f /tmp/debug%d; kill -9 $!\" 0\n", + pid); + length += sprintf (command + length, "trap \"exit\" 1 2 3 13 15\n"); + length += + sprintf (command + length, + "while [ ! -s /tmp/debug%d ]\ndo\nsleep 1\ndone\n", pid); + length += sprintf (command + length, "trap \"\" 1 2 3 13 15\n"); + length += sprintf (command + length, "ofile=`cat /tmp/debug%d`\n", pid); + length += + sprintf (command + length, "cat -u - >$ofile; rm /tmp/debug%d\n", pid); + if (remote) { + command[length++] = '\''; //terminate remsh + command[length] = '\0'; + } + fp = popen (command, "w"); /*create window */ + if (fp != NULL) { + /*set no buffering */ + if (setvbuf (fp, NULL, _IONBF, BUFSIZ)) { + pclose(fp); + fp = NULL; + } + } + #endif +} + + +/********************************************************************** + * DEBUG_WIN::DEBUG_WIN + * + * Close the file and destroy the window. + **********************************************************************/ + +DEBUG_WIN::~DEBUG_WIN ( +//destructor +) { + #ifdef __UNIX__ + pclose(fp); + #endif +} + + +/********************************************************************** + * dprintf + * + * Print a message to the debug window. + * Like printf, this function can cope with messages which do not end + * in newline, but nothing is printed until the newline is received. + **********************************************************************/ + +void +DEBUG_WIN::dprintf ( //debug printf +const char *format, ... //special message +) { + va_list args; //variable args + + va_start(args, format); //variable list + #ifdef __UNIX__ + vfprintf(fp, format, args); //Format into msg + #else + //Format into msg + vfprintf(stderr, format, args); + #endif + va_end(args); +} + + +/********************************************************************** + * await_destruction + * + * Wait for the user to close the debug window. Then return. + **********************************************************************/ + +void DEBUG_WIN::await_destruction() { //wait for user to close + #ifdef __UNIX__ + signal(SIGPIPE, SIG_IGN); + while (!ferror (fp)) { + sleep (1); + fputc (0, fp); //send nulls until error + } + #endif +} +#endif //UNIX Implmentation + +#ifdef __MAC__ //NT implementation +#include +//#include "textwindow.h" +#include +#include "ipcbase.h" //must be last include + +// Until I can figure a way to do this without linking in PowerPlant, +// the debug window will just have empty functions so compilation can take place. + +/********************************************************************** + * DEBUG_WIN::SetCommander + * + * Mac-specific function to set the commander for the next debug window + **********************************************************************/ +void DEBUG_WIN::SetCommander(LCommander *pNew) { + pCommander = pNew; +} + + +/********************************************************************** + * DEBUG_WIN::DEBUG_WIN + * + * Create a debug window with size according to the arguments. + * Create an hpterm window with a pipe connected to it. + **********************************************************************/ + +DEBUG_WIN::DEBUG_WIN( //constructor + const char *title, //of window + INT32 xpos, //initial position + INT32 ypos, //in pixels + INT32 xsize, //initial size + INT32 ysize, //in pixels + INT32 buflines //default scroll size + ) { + INT32 length; /*length of name */ + + // don't replace this DebugStr() with a call to DEBUG_WIN! + + //if (pCommander==NULL) DebugStr("\pDEBUG_WIN::DEBUG_WIN(), Commander not set"); + + // create the window + + //pWindow=LWindow::CreateWindow(2700,pCommander); +} + + +/********************************************************************** + * DEBUG_WIN::DEBUG_WIN + * + * Close the file and destroy the window. + **********************************************************************/ + +DEBUG_WIN::~DEBUG_WIN ( +//destructor +) { +} + + +/********************************************************************** + * dprintf + * + * Print a message to the debug window. + * Like printf, this function can cope with messages which do not end + * in newline, but nothing is printed until the newline is received. + **********************************************************************/ + +void +DEBUG_WIN::dprintf ( //debug printf +const char *format, ... //special message +) { + #if 0 + LTextEdit *pTextEdit; + va_list args; //variable args + static char msg[1024]; + + INT32 i; + INT32 OriginalLength; + INT32 NewLength; + TEHandle hTextEdit; + char *pTempBuffer; + CharsHandle hChar; + char *pOriginalText; + INT32 StringLength; + + pTextEdit = (LTextEdit *) pWindow->FindPaneByID (text_FLOWED); + if (pTextEdit == NULL) + DebugStr ("\pwhoops"); + + // get a C String from the format and args passed in + + va_start(args, format); //variable list + vsprintf(msg, format, args); //Format into msg + va_end(args); + + StringLength = strlen (msg); + + // get the handle for the text + + hTextEdit = pTextEdit->GetMacTEH (); + if (hTextEdit == NULL) + DebugStr ("\pDEBUG_WIN,WriteCharsToConsole()"); + + // get a pointer to the characters and the length of the character stream + + hChar = TEGetText (hTextEdit); + if (hChar == NULL) + DebugStr ("\pDEBUG_WIN,WriteCharsToConsole()"); + + pOriginalText = *hChar; // get pointer to existing text + + // get the length of the original data + OriginalLength = (*hTextEdit)->teLength; + + // setup a temporary buffer for the new text + + NewLength = OriginalLength + StringLength; + + pTempBuffer = NewPtr (NewLength); + if (pTempBuffer == NULL) + DebugStr ("\pDEBUG_WIN,WriteCharsToConsole()"); + + // copy the original data into the new buffer + + for (i = 0; i < OriginalLength; i++) + pTempBuffer[i] = pOriginalText[i]; + + // append the new data onto the end of the original buffer + + for (i = 0; i < StringLength; i++) { + if (msg[i] == '\n') + pTempBuffer[i + OriginalLength] = '\r'; + else + pTempBuffer[i + OriginalLength] = msg[i]; + } + + // put the new text into the text edit item + + TESetText(pTempBuffer, NewLength, hTextEdit); + + // clean up + + DisposePtr(pTempBuffer); + #endif +} +#endif //Mac Implmentation + +#else // Non graphical debugger + +DEBUG_WIN::DEBUG_WIN( const char*, INT32, INT32, INT32, INT32, INT32 ) { +} + +DEBUG_WIN::~DEBUG_WIN () { +} + +void DEBUG_WIN::dprintf (const char *format, ...) { + va_list ap; + va_start(ap, format); + vfprintf(stderr, format, ap); + va_end(ap); +} + +void await_destruction() { +} + + +#endif + diff --git a/ccutil/debugwin.h b/ccutil/debugwin.h new file mode 100644 index 0000000000..6115c69a51 --- /dev/null +++ b/ccutil/debugwin.h @@ -0,0 +1,103 @@ +/********************************************************************** + * File: debugwin.h + * Description: Portable debug window class. + * Author: Ray Smith + * Created: Wed Feb 21 15:36:59 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 DEBUGWIN_H +#define DEBUGWIN_H + +#include "host.h" +#include "varable.h" + +#ifdef __MAC__ +#include +#include +#endif + +//the following define the default position of a debug window +//if not specified at construction +#define DEBUG_WIN_XPOS 50 //default position +#define DEBUG_WIN_YPOS 30 //default position +#define DEBUG_WIN_XSIZE 700 //default size +#define DEBUG_WIN_YSIZE 300 //default size + +//number of lines in the scrollable area of the window +extern DLLSYM INT_VAR_H (debug_lines, 256, "Number of lines in debug window"); + +//the API for the debug window is simple, see below. +//Most of its behaviour is its UI. +//It has a scrollable text area (most of the window) +//It has a stop control. +//It has a clear button. +//A dprintf to the window causes the text to be sent to the +//text area. If the stop control is set, then the dprintf +//blocks (does not display anything or return) until the stop +//is released. +//In multi-threaded apps, other threads and the UI continue to +//function during the stop. Only the calling thread is blocked. +//Pressing the clear button erases all text from the window. +//As text is sent to the window, it scrolls up so that the most +//recent output is visible. If the user has scrolled back, this +//does not happen. If the user scrolls back to the bottom, then +//the scrolling turns back on. +//If the user destroys the window, it never comes back. + +class DLLSYM DEBUG_WIN +{ + public: + //the constructor creates the window, the destructor kills it + DEBUG_WIN ( //constructor + const char *title, //of window + INT32 xpos = DEBUG_WIN_XPOS,//initial position + INT32 ypos = DEBUG_WIN_YPOS,//in pixels + //initial size + INT32 xsize = DEBUG_WIN_XSIZE, + //in pixels + INT32 ysize = DEBUG_WIN_YSIZE, + //default scroll size (textlines) + INT32 buflines = debug_lines); + + ~DEBUG_WIN (); //destructor + + void dprintf ( //printf to window + const char *format, ...); //message + + void await_destruction(); //wait for user to close + + #ifdef __MAC__ + static void SetCommander(LCommander *pCommander); + #endif + + private: + + #ifdef __MSW32__ + HWND handle; //handle to window + char *shm_mem; //shared memory + char *msg_end; //current string + HANDLE shm_hand; //handle to it + HANDLE dbg_process; //handle to it + HANDLE dbg_thread; //handle to it + #endif + #ifdef __UNIX__ + FILE *fp; /*return file */ + #endif + + #ifdef __MAC__ + LWindow *pWindow; + #endif +}; +#endif diff --git a/ccutil/elst.cpp b/ccutil/elst.cpp new file mode 100644 index 0000000000..1d5233b4ad --- /dev/null +++ b/ccutil/elst.cpp @@ -0,0 +1,593 @@ +/********************************************************************** + * 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 "mfcpch.h" //precompiled headers +#include +#include "elst.h" + +/*********************************************************************** + * MEMBER FUNCTIONS OF CLASS: ELIST_LINK + * ===================================== + **********************************************************************/ + +/*********************************************************************** + * ELIST_LINK::serialise_asc + * + * Generates an error as it should never be called. + **********************************************************************/ + +void ELIST_LINK::serialise_asc( //default serialise + FILE *f) { + SERIALISE_LINKS.error ("ELIST_LINK::serialise_asc", ABORT, + "Don't call this, override!"); +} + + +void ELIST_LINK::de_serialise_asc( //default de_serialise + FILE *f) { + SERIALISE_LINKS.error ("ELIST_LINK::de_serialise_asc", ABORT, + "Don't call this, override!"); +} + + +/*********************************************************************** + * 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; + + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST::internal_clear", ABORT, NULL); + #endif + + 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::internal_deep_copy + * + * Used during explict deep copy of a list. The "copier" function passed + * allows each element to be correctly deep copied (assuming that each class + * in the inheritance hierarchy does properly deep copies its members). The + * function passing technique is as for "internal_clear". + **********************************************************************/ + +void + //ptr to copier functn +ELIST::internal_deep_copy (ELIST_LINK * (*copier) (ELIST_LINK *), +const ELIST * list) { //list being copied + ELIST_ITERATOR from_it ((ELIST *) list); + ELIST_ITERATOR to_it(this); + + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST::internal_deep_copy", ABORT, NULL); + if (!list) + BAD_PARAMETER.error ("ELIST::internal_deep_copy", ABORT, + "source list is NULL"); + #endif + + for (from_it.mark_cycle_pt (); !from_it.cycled_list (); from_it.forward ()) + to_it.add_after_then_move (copier (from_it.data ())); +} + + +/*********************************************************************** + * 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"; + + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST::assign_to_sublist", ABORT, NULL); + #endif + + 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() { //count elements + ELIST_ITERATOR it(this); + INT32 count = 0; + + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST::length", ABORT, NULL); + #endif + + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) + count++; + return count; +} + + +/*********************************************************************** + * ELIST::sort + * + * Sort elements on list + * NB If you dont 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; + + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST::sort", ABORT, NULL); + #endif + + /* 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); +} + + +/*********************************************************************** + * ELIST::prep_serialise + * + * Replace the last member with a count of elements for serialisation. + * This is used on list objects which are members of objects being + * serialised. The containing object has been shallow copied and this member + * function is invoked on the COPY. + **********************************************************************/ + +void ELIST::prep_serialise() { + ELIST_ITERATOR this_it(this); + INT32 count = 0; + + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST::prep_serialise", ABORT, NULL); + #endif + + count = 0; + if (!empty ()) + for (this_it.mark_cycle_pt (); + !this_it.cycled_list (); this_it.forward ()) + count++; + last = (ELIST_LINK *) count; +} + + +/*********************************************************************** + * ELIST::internal_dump + * + * Cause each element on the list to be serialised by walking the list and + * calling the element_serialiser function for each element. The + * element_serialiser simply does the appropriate coercion of the element to + * its real type and then invokes the elements serialise function + **********************************************************************/ + +void +ELIST::internal_dump (FILE * f, +void element_serialiser (FILE *, ELIST_LINK *)) { + ELIST_ITERATOR this_it(this); + + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST::internal_dump", ABORT, NULL); + #endif + + if (!empty ()) + for (this_it.mark_cycle_pt (); + !this_it.cycled_list (); this_it.forward ()) + element_serialiser (f, this_it.data ()); +} + + +/*********************************************************************** + * ELIST::internal_de_dump + * + * Cause each element on the list to be de_serialised by extracting the count + * of elements on the list, (held in the last member of the dumped version of + * the list object), and then de-serialising that number of list elements, + * adding each to the end of the reconstructed list. + **********************************************************************/ + +void +ELIST::internal_de_dump (FILE * f, +ELIST_LINK * element_de_serialiser (FILE *)) { + INT32 count = (ptrdiff_t) last; + ELIST_ITERATOR this_it; + ELIST_LINK *de_serialised_element; + + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST::internal_de_dump", ABORT, NULL); + #endif + + last = NULL; + this_it.set_to_list (this); + for (; count > 0; count--) { + de_serialised_element = element_de_serialiser (f); + //ignore old ptr + de_serialised_element->next = NULL; + this_it.add_to_end (de_serialised_element); + } +} + + +/*********************************************************************** + * 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() { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST_ITERATOR::forward", ABORT, NULL); + 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; + } + else { + if (ex_current_was_cycle_pt) + cycle_pt = next; + } + current = next; + next = current->next; + + #ifdef _DEBUG + if (!current) + NULL_DATA.error ("ELIST_ITERATOR::forward", ABORT, NULL); + if (!next) + NULL_NEXT.error ("ELIST_ITERATOR::forward", ABORT, + "This is: %i Current is: %i", + (int) this, (int) 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; + + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST_ITERATOR::data_relative", ABORT, NULL); + 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); + + #ifdef _DEBUG + 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() { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST_ITERATOR::move_to_last", ABORT, NULL); + 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; + + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST_ITERATOR::exchange", ABORT, NULL); + 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 + #ifdef _DEBUG + 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; + + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST_ITERATOR::extract_sublist", ABORT, NULL); + 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 ()) //cant 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; +} diff --git a/ccutil/elst.h b/ccutil/elst.h new file mode 100644 index 0000000000..6ee8f0407a --- /dev/null +++ b/ccutil/elst.h @@ -0,0 +1,1146 @@ +/********************************************************************** + * 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 heterogenous +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( //copy constructor + const ELIST_LINK &) { //dont copy link + next = NULL; + } + + void operator= ( //dont copy links + const ELIST_LINK &) { + next = NULL; + } + + void serialise_asc( //serialise to ascii + FILE *f); + void de_serialise_asc( //de-serialise from ascii + FILE *f); + + /* NOTE that none of the serialise member functions are required for + ELIST_LINKS as they are never serialised. (We demand that the derived + class terminates recursion - just to make sure that it defines the member + functions anyway.) + */ +}; + +/********************************************************************** + * 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 *)); + + BOOL8 empty() { //is list empty? + return !last; + } + + BOOL8 singleton() { + 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(); //# elements in list + + void sort ( //sort elements + int comparator ( //comparison routine + const void *, const void *)); + + void internal_dump ( //serialise each elem + FILE * f, //to this file + void element_serialiser ( //using this function + FILE *, ELIST_LINK *)); + + void internal_de_dump ( //de_serial each elem + FILE * f, //from this file + //using this function + ELIST_LINK * element_de_serialiser ( + FILE *)); + + void prep_serialise(); //change last to count + + /* Note that dump() and de_dump() are not required as calls to dump/de_dump a + list class should be handled by a class derived from this. + + make_serialise is not required for a similar reason. + */ +}; + +/*********************************************************************** + * 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 + BOOL8 ex_current_was_last; //current extracted + //was end of list + BOOL8 ex_current_was_cycle_pt; //current extracted + //was cycle point + ELIST_LINK *cycle_pt; //point we are cycling + //the list to. + BOOL8 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 + + ELIST_ITERATOR( //constructor + 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 + #ifdef _DEBUG + 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 + + BOOL8 empty() { //is list empty? + #ifdef _DEBUG + if (!list) + NO_LIST.error ("ELIST_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 & + ELIST_LINK *new_link); //dont 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) { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST_ITERATOR::set_to_list", ABORT, NULL); + 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) { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST_ITERATOR::add_after_then_move", ABORT, NULL); + 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) { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST_ITERATOR::add_after_stay_put", ABORT, NULL); + 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) { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST_ITERATOR::add_before_then_move", ABORT, NULL); + 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 dont move the + * iterator to the new element. + **********************************************************************/ + +inline void ELIST_ITERATOR::add_before_stay_put( // element to add + ELIST_LINK *new_element) { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST_ITERATOR::add_before_stay_put", ABORT, NULL); + 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 dont move the + * iterator. + **********************************************************************/ + +inline void ELIST_ITERATOR::add_list_after(ELIST *list_to_add) { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST_ITERATOR::add_list_after", ABORT, NULL); + 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) { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST_ITERATOR::add_list_before", ABORT, NULL); + 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; + + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST_ITERATOR::extract", ABORT, NULL); + 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 + prev = next = list->last = NULL; + // change the iterator + 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; + + 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() { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST_ITERATOR::move_to_first", ABORT, NULL); + 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() { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST_ITERATOR::mark_cycle_pt", ABORT, NULL); + 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 BOOL8 ELIST_ITERATOR::at_first() { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST_ITERATOR::at_first", ABORT, NULL); + 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 BOOL8 ELIST_ITERATOR::at_last() { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST_ITERATOR::at_last", ABORT, NULL); + 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 BOOL8 ELIST_ITERATOR::cycled_list() { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST_ITERATOR::cycled_list", ABORT, NULL); + 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() { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST_ITERATOR::length", ABORT, NULL); + 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 *)) { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST_ITERATOR::sort", ABORT, NULL); + 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) { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST_ITERATOR::add_to_end", ABORT, NULL); + 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 ) MACROS + ============================ + +CLASSNAME is assumed to be the name of a class which has a baseclass of +ELIST_LINK. + +NOTE: Because we dont 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 element copier function: CLASSNAME##_copier + - An element serialiser function" CLASSNAME##_serialiser + - An element de-serialiser function" CLASSNAME##_de_serialiser + - 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 + +Four macros are provided: ELISTIZE, ELISTIZE_S, ELISTIZEH and ELISTIZEH_S +The ...IZEH macros just define the class names for use in .h files +The ...IZE macros define the code use in .c files +The _S versions define lists which can be serialised. They assume that +the make_serialise() macro is used in the list element class derived from +ELIST_LINK to define serialise() and de_serialise() members for the list +elements. +***********************************************************************/ + +/*********************************************************************** + ELISTIZEH( CLASSNAME ) and ELISTIZEH_S( CLASSNAME ) MACROS + +These macros are constructed from 3 fragments ELISTIZEH_A, ELISTIZEH_B and +ELISTIZEH_C. ELISTIZEH is simply a concatenation of these parts. +ELISTIZEH_S has some additional bits thrown in the gaps. +***********************************************************************/ + +#define ELISTIZEH_A( CLASSNAME ) \ + \ +extern DLLSYM void CLASSNAME##_zapper( /*delete a link*/ \ +ELIST_LINK* link); /*link to delete*/ \ + \ +extern DLLSYM ELIST_LINK* CLASSNAME##_copier( /*deep copy a link*/ \ +ELIST_LINK* old_element); /*source link */ + +#define ELISTIZEH_B( CLASSNAME ) \ + \ +/*********************************************************************** \ +* CLASS - CLASSNAME##_LIST \ +* \ +* List class for class CLASSNAME \ +* \ +**********************************************************************/ \ + \ +class DLLSYM CLASSNAME##_LIST : public ELIST \ +{ \ +public: \ + CLASSNAME##_LIST():ELIST() {}\ + /* constructor */ \ + \ + CLASSNAME##_LIST( /* dont construct */ \ + const CLASSNAME##_LIST&) /*by initial assign*/\ + { DONT_CONSTRUCT_LIST_BY_COPY.error( QUOTE_IT( CLASSNAME##_LIST ), \ + ABORT, NULL ); } \ + \ +void clear() /* delete elements */\ + { ELIST::internal_clear( &CLASSNAME##_zapper ); } \ + \ + ~CLASSNAME##_LIST() /* destructor */ \ + { clear(); } \ + \ +void deep_copy( /* become a deep */ \ + const CLASSNAME##_LIST* list) /* copy of src list*/\ + { ELIST::internal_deep_copy( &CLASSNAME##_copier, list ); } \ + \ +void operator=( /* prevent assign */ \ + 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(){} \ + \ + CLASSNAME##_IT( \ +CLASSNAME##_LIST* list):ELIST_ITERATOR(list){} \ + \ + CLASSNAME* data() \ + { return (CLASSNAME*) ELIST_ITERATOR::data(); } \ + \ + CLASSNAME* data_relative( \ + INT8 offset) \ + { return (CLASSNAME*) ELIST_ITERATOR::data_relative( offset ); } \ + \ + CLASSNAME* forward() \ + { return (CLASSNAME*) ELIST_ITERATOR::forward(); } \ + \ + CLASSNAME* extract() \ + { return (CLASSNAME*) ELIST_ITERATOR::extract(); } \ + \ + CLASSNAME* move_to_first() \ + { return (CLASSNAME*) ELIST_ITERATOR::move_to_first(); } \ + \ + CLASSNAME* move_to_last() \ + { return (CLASSNAME*) ELIST_ITERATOR::move_to_last(); } \ +}; + +#define ELISTIZEH( CLASSNAME ) \ + \ +ELISTIZEH_A( CLASSNAME ) \ + \ +ELISTIZEH_B( CLASSNAME ) \ + \ +ELISTIZEH_C( CLASSNAME ) + +#define ELISTIZEH_S( CLASSNAME ) \ + \ +ELISTIZEH_A( CLASSNAME ) \ + \ +extern DLLSYM void CLASSNAME##_serialiser( \ +FILE* f, \ +ELIST_LINK* element); \ + \ +extern DLLSYM ELIST_LINK* CLASSNAME##_de_serialiser( \ +FILE* f); \ + \ +ELISTIZEH_B( CLASSNAME ) \ + \ + void dump( /* dump to file */ \ + FILE* f) \ + { ELIST::internal_dump( f, &CLASSNAME##_serialiser );} \ + \ + void de_dump( /* get from file */ \ + FILE* f) \ + { ELIST::internal_de_dump( f, &CLASSNAME##_de_serialiser );} \ + \ + void serialise_asc( /*dump to ascii*/ \ + FILE* f); \ + void de_serialise_asc( /*de-dump from ascii*/\ + FILE* f); \ + \ +make_serialise( CLASSNAME##_LIST ) \ + \ +ELISTIZEH_C( CLASSNAME ) + +/*********************************************************************** + ELISTIZE( CLASSNAME ) and ELISTIZE_S( CLASSNAME ) MACROS +ELISTIZE_S is a simple extension to ELISTIZE +***********************************************************************/ + +#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 dont use a virtual destructor function. \ +**********************************************************************/ \ + \ +DLLSYM void CLASSNAME##_zapper( /*delete a link*/ \ +ELIST_LINK* link) /*link to delete*/ \ +{ \ +delete (CLASSNAME *) link; \ +} \ + \ +/*********************************************************************** \ +* CLASSNAME##_copier \ +* \ +* A function which can generate a new, deep copy of a CLASSNAME element. \ +* This is passed to the generic deep copy list member function so that when \ +* a list is copied the elements on the list are properly copied from the \ +* base class, even though we dont use a virtual function. \ +**********************************************************************/ \ + \ +DLLSYM ELIST_LINK* CLASSNAME##_copier( /*deep copy a link*/ \ +ELIST_LINK* old_element) /*source link*/ \ +{ \ + CLASSNAME* new_element; \ + \ +new_element = new CLASSNAME; \ +*new_element = *((CLASSNAME*) old_element); \ +return (ELIST_LINK*) new_element; \ +} + +#define ELISTIZE_S( CLASSNAME ) \ + \ +ELISTIZE( CLASSNAME ) \ + \ + void CLASSNAME##_LIST::serialise_asc( \ + /*dump to ascii*/ \ + FILE* f) \ + { \ + CLASSNAME##_IT it(this); \ + \ + serialise_INT32(f,length()); \ + for (it.mark_cycle_pt();!it.cycled_list();it.forward()) \ + it.data()->serialise_asc(f); /*serialise the list*/\ + } \ + \ + void CLASSNAME##_LIST::de_serialise_asc( \ + /*de-dump from ascii*/\ + FILE* f) \ + { \ + INT32 len; /*length to retrive*/\ + CLASSNAME##_IT it; \ + CLASSNAME* new_elt=NULL; /*list element*/ \ + \ + len=de_serialise_INT32(f); \ + it.set_to_list(this); \ + for (;len>0;len--) \ + { \ + new_elt=new CLASSNAME; \ + new_elt->de_serialise_asc(f); \ + it.add_to_end(new_elt); /*put on the list*/ \ + } \ + return; \ + } \ + \ + \ +/*********************************************************************** \ +* CLASSNAME##_serialiser \ +* \ +* A function which can serialise an element \ +* This is passed to the generic dump member function so that when a list is \ +* serialised the elements on the list are properly serialised. \ +**********************************************************************/ \ + \ +DLLSYM void CLASSNAME##_serialiser( \ +FILE* f, \ +ELIST_LINK* element) \ +{ \ +((CLASSNAME*) element)->serialise( f ); \ +} \ + \ + \ + \ +/*********************************************************************** \ +* CLASSNAME##_de_serialiser \ +* \ +* A function which can de-serialise an element \ +* This is passed to the generic de-dump member function so that when a list \ +* is de-serialised the elements on the list are properly de-serialised. \ +**********************************************************************/ \ + \ +DLLSYM ELIST_LINK* CLASSNAME##_de_serialiser( \ +FILE* f) \ +{ \ +return (ELIST_LINK*) CLASSNAME::de_serialise( f ); \ +} +#endif diff --git a/ccutil/elst2.cpp b/ccutil/elst2.cpp new file mode 100644 index 0000000000..75a60c46eb --- /dev/null +++ b/ccutil/elst2.cpp @@ -0,0 +1,602 @@ +/********************************************************************** + * 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 "mfcpch.h" //precompiled headers +#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; + + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST2::internal_clear", ABORT, NULL); + #endif + + 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::internal_deep_copy + * + * Used during explict deep copy of a list. The "copier" function passed + * allows each element to be correctly deep copied (assuming that each class + * in the inheritance hierarchy does properly deep copies its members). The + * function passing technique is as for "internal_clear". + **********************************************************************/ + +void + //ptr to copier functn +ELIST2::internal_deep_copy (ELIST2_LINK * (*copier) (ELIST2_LINK *), +const ELIST2 * list) { //list being copied + ELIST2_ITERATOR from_it ((ELIST2 *) list); + ELIST2_ITERATOR to_it(this); + + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST2::internal_deep_copy", ABORT, NULL); + if (!list) + BAD_PARAMETER.error ("ELIST2::internal_deep_copy", ABORT, + "source list is NULL"); + #endif + + for (from_it.mark_cycle_pt (); !from_it.cycled_list (); from_it.forward ()) + to_it.add_after_then_move (copier (from_it.data ())); +} + + +/*********************************************************************** + * 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"; + + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST2::assign_to_sublist", ABORT, NULL); + #endif + + 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() { //count elements + ELIST2_ITERATOR it(this); + INT32 count = 0; + + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST2::length", ABORT, NULL); + #endif + + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) + count++; + return count; +} + + +/*********************************************************************** + * ELIST2::sort + * + * Sort elements on list + * NB If you dont 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; + + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST2::sort", ABORT, NULL); + #endif + + /* 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); +} + + +/*********************************************************************** + * ELIST2::prep_serialise + * + * Replace the last member with a count of elements for serialisation. + * This is used on list objects which are members of objects being + * serialised. The containing object has been shallow copied and this member + * function is invoked on the COPY. + **********************************************************************/ + +void ELIST2::prep_serialise() { + ELIST2_ITERATOR this_it(this); + INT32 count = 0; + + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST2::prep_serialise", ABORT, NULL); + #endif + + count = 0; + if (!empty ()) + for (this_it.mark_cycle_pt (); + !this_it.cycled_list (); this_it.forward ()) + count++; + last = (ELIST2_LINK *) count; +} + + +/*********************************************************************** + * ELIST2::internal_dump + * + * Cause each element on the list to be serialised by walking the list and + * calling the element_serialiser function for each element. The + * element_serialiser simply does the appropriate coercion of the element to + * its real type and then invokes the elements serialise function + **********************************************************************/ + +void +ELIST2::internal_dump (FILE * f, +void element_serialiser (FILE *, ELIST2_LINK *)) { + ELIST2_ITERATOR this_it(this); + + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST2::internal_dump", ABORT, NULL); + #endif + + if (!empty ()) + for (this_it.mark_cycle_pt (); + !this_it.cycled_list (); this_it.forward ()) + element_serialiser (f, this_it.data ()); +} + + +/*********************************************************************** + * ELIST2::internal_de_dump + * + * Cause each element on the list to be de_serialised by extracting the count + * of elements on the list, (held in the last member of the dumped version of + * the list object), and then de-serialising that number of list elements, + * adding each to the end of the reconstructed list. + **********************************************************************/ + +void +ELIST2::internal_de_dump (FILE * f, +ELIST2_LINK * element_de_serialiser (FILE *)) { + INT32 count = (ptrdiff_t) last; + ELIST2_ITERATOR this_it; + ELIST2_LINK *de_serialised_element; + + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST2::internal_de_dump", ABORT, NULL); + #endif + + last = NULL; + this_it.set_to_list (this); + for (; count > 0; count--) { + de_serialised_element = element_de_serialiser (f); + //ignore old ptr + de_serialised_element->next = NULL; + //ignore old ptr + de_serialised_element->prev = NULL; + this_it.add_to_end (de_serialised_element); + } +} + + +/*********************************************************************** + * 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() { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST2_ITERATOR::forward", ABORT, NULL); + 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; + } + else { + if (ex_current_was_cycle_pt) + cycle_pt = next; + } + current = next; + next = current->next; + + #ifdef _DEBUG + if (!current) + NULL_DATA.error ("ELIST2_ITERATOR::forward", ABORT, NULL); + if (!next) + NULL_NEXT.error ("ELIST2_ITERATOR::forward", ABORT, + "This is: %i Current is: %i", + (int) this, (int) 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() { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST2_ITERATOR::backward", ABORT, NULL); + 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; + } + else { + if (ex_current_was_cycle_pt) + cycle_pt = prev; + } + current = prev; + prev = current->prev; + + #ifdef _DEBUG + if (!current) + NULL_DATA.error ("ELIST2_ITERATOR::backward", ABORT, NULL); + if (!prev) + NULL_PREV.error ("ELIST2_ITERATOR::backward", ABORT, + "This is: %i Current is: %i", + (int) this, (int) 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; + + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST2_ITERATOR::data_relative", ABORT, NULL); + 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); + + #ifdef _DEBUG + 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; + + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST2_ITERATOR::exchange", ABORT, NULL); + 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 + #ifdef _DEBUG + 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; + + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST2_ITERATOR::extract_sublist", ABORT, NULL); + 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 ()) //cant 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; +} diff --git a/ccutil/elst2.h b/ccutil/elst2.h new file mode 100644 index 0000000000..65ec696158 --- /dev/null +++ b/ccutil/elst2.h @@ -0,0 +1,1137 @@ +/********************************************************************** + * 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 &) { //dont copy link + prev = next = NULL; + } + + void operator= ( //dont copy links + const ELIST2_LINK &) { + prev = next = NULL; + } + + /* NOTE that none of the serialise member functions are required for + ELIST2_LINKs as they are never serialised. (We demand that the derived + class terminates recursion - just to make sure that it defines the member + functions anyway.) + */ +}; + +/********************************************************************** + * 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 + + BOOL8 empty() { //is list empty? + return !last; + } + + BOOL8 singleton() { + 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(); //# elements in list + + void sort ( //sort elements + int comparator ( //comparison routine + const void *, const void *)); + + void internal_dump ( //serialise each elem + FILE * f, //to this file + void element_serialiser ( //using this function + FILE *, ELIST2_LINK *)); + + void internal_de_dump ( //de_serial each elem + FILE * f, //from this file + //using this function + ELIST2_LINK * element_de_serialiser ( + FILE *)); + + void prep_serialise(); //change last to count + + /* Note that dump() and de_dump() are not required as calls to dump/de_dump a + list class should be handled by a class derived from this. + + make_serialise is not required for a similar reason. + */ +}; + +/*********************************************************************** + * 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 + #ifdef _DEBUG + 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? + #ifdef _DEBUG + 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); //dont 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) { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST2_ITERATOR::set_to_list", ABORT, NULL); + 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) { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST2_ITERATOR::add_after_then_move", ABORT, NULL); + 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) { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST2_ITERATOR::add_after_stay_put", ABORT, NULL); + 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) { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST2_ITERATOR::add_before_then_move", ABORT, NULL); + 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 dont move the + * iterator to the new element. + **********************************************************************/ + +inline void ELIST2_ITERATOR::add_before_stay_put( // element to add + ELIST2_LINK *new_element) { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST2_ITERATOR::add_before_stay_put", ABORT, NULL); + 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 dont move the + * iterator. + **********************************************************************/ + +inline void ELIST2_ITERATOR::add_list_after(ELIST2 *list_to_add) { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST2_ITERATOR::add_list_after", ABORT, NULL); + 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) { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST2_ITERATOR::add_list_before", ABORT, NULL); + 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; + + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST2_ITERATOR::extract", ABORT, NULL); + 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 + prev = next = list->last = NULL; + //change the iterator + 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; + + 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() { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST2_ITERATOR::move_to_first", ABORT, NULL); + 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() { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST2_ITERATOR::move_to_last", ABORT, NULL); + 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() { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST2_ITERATOR::mark_cycle_pt", ABORT, NULL); + 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() { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST2_ITERATOR::at_first", ABORT, NULL); + 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() { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST2_ITERATOR::at_last", ABORT, NULL); + 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() { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST2_ITERATOR::cycled_list", ABORT, NULL); + 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() { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST2_ITERATOR::length", ABORT, NULL); + 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 *)) { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST2_ITERATOR::sort", ABORT, NULL); + 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) { + #ifdef _DEBUG + if (!this) + NULL_OBJECT.error ("ELIST2_ITERATOR::add_to_end", ABORT, NULL); + 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 dont 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 element copier function: + CLASSNAME##_copier + - An element serialiser function" CLASSNAME##_serialiser + - An element de-serialiser function" CLASSNAME##_de_serialiser + - 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 + +Four macros are provided: ELIST2IZE, ELIST2IZE_S, ELIST2IZEH and ELIST2IZEH_S +The ...IZEH macros just define the class names for use in .h files +The ...IZE macros define the code use in .c files +The _S versions define lists which can be serialised. They assume that +the make_serialise() macro is used in the list element class derived from +ELIST2_LINK to define serialise() and de_serialise() members for the list +elements. +***********************************************************************/ + +/*********************************************************************** + ELIST2IZEH( CLASSNAME ) and ELIST2IZEH_S( CLASSNAME ) MACROS + +These macros are constructed from 3 fragments ELIST2IZEH_A, ELIST2IZEH_B and +ELIST2IZEH_C. ELIST2IZEH is simply a concatenation of these parts. +ELIST2IZEH_S has some additional bits thrown in the gaps. +***********************************************************************/ + +#define ELIST2IZEH_A( CLASSNAME ) \ + \ +extern DLLSYM void CLASSNAME##_zapper( /*delete a link*/ \ +ELIST2_LINK* link); /*link to delete*/ \ + \ +extern DLLSYM ELIST2_LINK* CLASSNAME##_copier( /*deep copy a link*/ \ +ELIST2_LINK* old_element); /*source link */ + +#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( /* dont 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(); } \ + \ +void deep_copy( /* become a deep */ \ + const CLASSNAME##_LIST* list) /* copy of src list*/\ + { ELIST2::internal_deep_copy( &CLASSNAME##_copier, list ); } \ + \ +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 ) + +#define ELIST2IZEH_S( CLASSNAME ) \ + \ +ELIST2IZEH_A( CLASSNAME ) \ + \ +extern DLLSYM void CLASSNAME##_serialiser( \ +FILE* f, \ +ELIST2_LINK* element); \ + \ +extern DLLSYM ELIST2_LINK* CLASSNAME##_de_serialiser( \ +FILE* f); \ + \ +ELIST2IZEH_B( CLASSNAME ) \ + \ + void dump( /* dump to file */ \ + FILE* f) \ + { ELIST2::internal_dump( f, &CLASSNAME##_serialiser );} \ + \ + void de_dump( /* get from file */ \ + FILE* f) \ + { ELIST2::internal_de_dump( f, &CLASSNAME##_de_serialiser );} \ + \ +make_serialise( CLASSNAME##_LIST ) \ + \ +ELIST2IZEH_C( CLASSNAME ) + +/*********************************************************************** + ELIST2IZE( CLASSNAME ) and ELIST2IZE_S( CLASSNAME ) MACROS +ELIST2IZE_S is a simple extension to ELIST2IZE +***********************************************************************/ + +#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 dont use a virtual destructor function. \ +**********************************************************************/ \ + \ +DLLSYM void CLASSNAME##_zapper( /*delete a link*/ \ +ELIST2_LINK* link) /*link to delete*/ \ +{ \ +delete (CLASSNAME *) link; \ +} \ + \ + \ + \ +/*********************************************************************** \ +* CLASSNAME##_copier \ +* \ +* A function which can generate a new, deep copy of a CLASSNAME element. \ +* This is passed to the generic deep copy list member function so that when \ +* a list is copied the elements on the list are properly copied from the \ +* base class, even though we dont use a virtual function. \ +**********************************************************************/ \ + \ +DLLSYM ELIST2_LINK* CLASSNAME##_copier( /*deep copy a link*/ \ +ELIST2_LINK* old_element) /*source link*/ \ +{ \ + CLASSNAME* new_element; \ + \ +new_element = new CLASSNAME; \ +*new_element = *((CLASSNAME*) old_element); \ +return (ELIST2_LINK*) new_element; \ +} + +#define ELIST2IZE_S( CLASSNAME ) \ + \ +ELIST2IZE( CLASSNAME ) \ + \ +/*********************************************************************** \ +* CLASSNAME##_serialiser \ +* \ +* A function which can serialise an element \ +* This is passed to the generic dump member function so that when a list is \ +* serialised the elements on the list are properly serialised. \ +**********************************************************************/ \ + \ +DLLSYM void CLASSNAME##_serialiser( \ +FILE* f, \ +ELIST2_LINK* element) \ +{ \ +((CLASSNAME*) element)->serialise( f ); \ +} \ + \ + \ + \ +/*********************************************************************** \ +* CLASSNAME##_de_serialiser \ +* \ +* A function which can de-serialise an element \ +* This is passed to the generic de-dump member function so that when a list \ +* is de-serialised the elements on the list are properly de-serialised. \ +**********************************************************************/ \ + \ +DLLSYM ELIST2_LINK* CLASSNAME##_de_serialiser( \ +FILE* f) \ +{ \ +return (ELIST2_LINK*) CLASSNAME::de_serialise( f ); \ +} +#endif diff --git a/ccutil/errcode.cpp b/ccutil/errcode.cpp new file mode 100644 index 0000000000..ef78cef9db --- /dev/null +++ b/ccutil/errcode.cpp @@ -0,0 +1,103 @@ +/********************************************************************** + * 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 "mfcpch.h" //precompiled headers +#include +#include +#include +#include +#ifdef __UNIX__ +#include +#endif +#include "tprintf.h" +//#include "ipeerr.h" +#include "errcode.h" + +const ERRCODE BADERRACTION = "Illegal error action"; +#define MAX_MSG 1024 +extern INT16 global_abort_code; + +/********************************************************************** + * 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 +INT8 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 __MSW32__ + //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"); + + tprintf(msg); + if ((strstr (message, "File") != NULL) || + (strstr (message, "file") != NULL)) + global_abort_code = FILE_ABORT; + else if ((strstr (message, "List") != NULL) || + (strstr (message, "list") != NULL)) + global_abort_code = LIST_ABORT; + else if ((strstr (message, "Memory") != NULL) || + (strstr (message, "memory") != NULL)) + global_abort_code = MEMORY_ABORT; + else + global_abort_code = NO_ABORT_CODE; + + switch (action) { + case DBG: + case LOG: + return; //report only + case EXIT: + err_exit(); + case ABORT: + abort(); + default: + BADERRACTION.error ("error", ABORT, NULL); + } +} diff --git a/ccutil/errcode.h b/ccutil/errcode.h new file mode 100644 index 0000000000..dbff2adcc9 --- /dev/null +++ b/ccutil/errcode.h @@ -0,0 +1,104 @@ +/********************************************************************** + * 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()*/ +#define DBG -1 /*log without alert */ +#define LOG 0 /*alert user */ +#define EXIT 1 /*exit after erro */ +#define 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 +/* DONT 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 DLLSYM ERRCODE //error handler class +{ + const char *message; //error message + public: + void error ( //error print function + const char *caller, //function location + INT8 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,LOG,"in file %s, line %d", \ + __FILE__,__LINE__); \ +} + +void signal_exit( // + int signal_code //Signal which + ); +extern "C" +{ + void err_exit(); + //The real signal + void signal_termination_handler(int sig); +}; + +void set_global_loc_code(int loc_code); + +void set_global_subloc_code(int loc_code); + +void set_global_subsubloc_code(int loc_code); +#endif diff --git a/ccutil/fileerr.h b/ccutil/fileerr.h new file mode 100644 index 0000000000..d3b6993d21 --- /dev/null +++ b/ccutil/fileerr.h @@ -0,0 +1,34 @@ +/********************************************************************** + * 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 diff --git a/ccutil/getopt.cpp b/ccutil/getopt.cpp new file mode 100644 index 0000000000..ef28825f75 --- /dev/null +++ b/ccutil/getopt.cpp @@ -0,0 +1,62 @@ +/********************************************************************** + * File: getopt.c + * Description: Re-implementation of the unix code. + * Author: Ray Smith + * Created: Tue Nov 28 05:52:50 MST 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. + * + **********************************************************************/ + +#include "mfcpch.h" //precompiled headers +#include +#include +#include "getopt.h" +#include "notdll.h" //must be last include + +int optind; +char *optarg; + +/********************************************************************** + * getopt + * + * parse command line args. + **********************************************************************/ + +int +getopt ( //parse args +INT32 argc, //arg count +char *argv[], //args +const char *arglist //string of arg chars +) { + char *arg; //arg char + + if (optind == 0) + optind = 1; + if (optind < argc && argv[optind][0] == '-') { + arg = strchr (arglist, argv[optind][1]); + if (arg == NULL || *arg == ':') + return '?'; //dud option + optind++; + optarg = argv[optind]; + if (arg[1] == ':') { + if (argv[optind - 1][2] != '\0') + //immediately after + optarg = argv[optind - 1] + 2; + else + optind++; + } + return *arg; + } + else + return EOF; +} diff --git a/ccutil/getopt.h b/ccutil/getopt.h new file mode 100644 index 0000000000..b094654b6e --- /dev/null +++ b/ccutil/getopt.h @@ -0,0 +1,30 @@ +/********************************************************************** + * File: getopt.h + * Description: Re-implementation of the unix code. + * Author: Ray Smith + * Created: Tue Nov 28 05:52:50 MST 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. + * + **********************************************************************/ + +#include "host.h" +#include "notdll.h" //must be last include + +extern int optind; +extern char *optarg; + +int getopt ( //parse args +INT32 argc, //arg count +char *argv[], //args +const char *arglist //string of arg chars +); diff --git a/ccutil/globaloc.cpp b/ccutil/globaloc.cpp new file mode 100644 index 0000000000..439f4ee3e5 --- /dev/null +++ b/ccutil/globaloc.cpp @@ -0,0 +1,113 @@ +/********************************************************************** + * 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 "mfcpch.h" +#include +#include "errcode.h" +#include "tprintf.h" + +INT16 global_loc_code = LOC_INIT;//location code +INT16 global_subloc_code = SUBLOC_NORM; + //pass2 subloc code +INT16 global_subsubloc_code = SUBSUBLOC_OTHER; + //location code +INT16 global_abort_code = NO_ABORT_CODE; + //Prog abort code + +void signal_exit( // + int signal_code //Signal which + ) { + int exit_status; + + if ((global_loc_code == LOC_PASS2) || (global_loc_code == LOC_FUZZY_SPACE)) + global_loc_code += global_subloc_code + global_subsubloc_code; + + if (signal_code < 0) { + exit_status = global_loc_code * 8 + global_abort_code * 2 + 1; + tprintf ("Signal_exit %d ABORT. LocCode: %d AbortCode: %d\n", + exit_status, global_loc_code, global_abort_code); + } + else { + exit_status = global_loc_code * 8 + signal_code * 2; + tprintf ("Signal_exit %d SIGNAL ABORT. LocCode: %d SignalCode: %d\n", + exit_status, global_loc_code, signal_code); + } + + exit(exit_status); +} + + +/************************************************************************* + * err_exit() + * All program exits should go through this point. It allows a meaningful status + * code to be generated for the real exit() call. The status code is made up + * as follows: + * Bit 0 : 1 = Program Abort 0 = System Abort + * Bits 1,2 : IF bit 0 = 1 THEN ERRCODE::abort_code + * ELSE 0 = Bus Err or Seg Vi + * 1 = Floating point exception + * 2 = TimeOut (Signal 15 from command timer) + * 3 = Any other signal + * Bits 3..7 : Location code NEVER 0 ! + *************************************************************************/ + +//extern "C" { + +void err_exit() { + signal_exit (-1); +} + + +void signal_termination_handler( //The real signal + int sig) { + tprintf ("Signal_termination_handler called with signal %d\n", sig); + switch (sig) { + case SIGABRT: + signal_exit (-1); //use abort code + // case SIGBUS: + case SIGSEGV: + signal_exit (0); + case SIGFPE: + signal_exit (1); //floating point + case SIGTERM: + signal_exit (2); //timeout by cmdtimer + default: + signal_exit (3); //Anything else + } +} + + +//}; //end extern "C" + +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; + +} diff --git a/ccutil/globaloc.h b/ccutil/globaloc.h new file mode 100644 index 0000000000..b5086a2eaf --- /dev/null +++ b/ccutil/globaloc.h @@ -0,0 +1,40 @@ +/********************************************************************** + * 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 "hosthplb.h" +#include "notdll.h" + +void signal_exit( // + int signal_code //Signal which + ); +//extern "C" { +void err_exit(); + //The real signal +void signal_termination_handler(int sig); +//}; + +void set_global_loc_code(int loc_code); + +void set_global_subloc_code(int loc_code); + +void set_global_subsubloc_code(int loc_code); +#endif diff --git a/ccutil/hashfn.cpp b/ccutil/hashfn.cpp new file mode 100644 index 0000000000..fb5acc5542 --- /dev/null +++ b/ccutil/hashfn.cpp @@ -0,0 +1,57 @@ +/********************************************************************** + * File: hashfn.c (Formerly hash.c) + * Description: Simple hash function. + * Author: Ray Smith + * Created: Thu Jan 16 11:47:59 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 "mfcpch.h" //precompiled headers +#include "hashfn.h" + +/********************************************************************** + * hash + * + * Simple hash function working on power of 2 table sizes. + * Uses xor function. Needs linear rehash. + **********************************************************************/ + +INT32 hash( //hash function + INT32 bits, //bits in hash function + void *key, //key to hash + INT32 keysize //size of key + ) { + INT32 bitindex; //current bit count + UINT32 keybits; //bit buffer + UINT32 hcode; //current hash code + UINT32 mask; //bit mask + + mask = (1 << bits) - 1; + keysize *= 8; //in bits + bitindex = 0; + keybits = 0; + hcode = 0; + do { + while (keysize > 0 && bitindex <= 24) { + keybits |= *((UINT8 *) key) << bitindex; + key = (UINT8 *) key + 1; + bitindex += 8; + keysize -= 8; + } + hcode ^= keybits & mask; //key new key + keybits >>= bits; + } + while (keysize > 0); + return hcode; //initial hash +} diff --git a/ccutil/hashfn.h b/ccutil/hashfn.h new file mode 100644 index 0000000000..d1c5495bd9 --- /dev/null +++ b/ccutil/hashfn.h @@ -0,0 +1,30 @@ +/********************************************************************** + * File: hashfn.h (Formerly hash.h) + * Description: Simple hash function. + * Author: Ray Smith + * Created: Thu Jan 16 11:47:59 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 HASHFN_H +#define HASHFN_H + +#include "host.h" + +INT32 hash( //hash function + INT32 bits, //bits in hash function + void *key, //key to hash + INT32 keysize //size of key + ); +#endif diff --git a/ccutil/host.h b/ccutil/host.h new file mode 100644 index 0000000000..6b2a3c2c19 --- /dev/null +++ b/ccutil/host.h @@ -0,0 +1,286 @@ +/****************************************************************************** + ** 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 - + ** __MSW__ and __MSW32__ must be uncommented out. + ** 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__ + +/* +** Include automatically generated configuration file if running autoconf +*/ +#ifdef HAVE_CONFIG_H +#include "config_auto.h" +#if defined(MOTOROLA_BYTE_ORDER) || defined(WORDS_BIGENDIAN) +#define __MOTO__ // Big-endian. +#endif +#endif + + +/****************************************************************************** + ** IMPORTANT!!! ** + ** ** + ** Defines either __MSW__, __MSW32__, __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" +/* __MSW32__ */ +#ifdef __MSW32__ +#include +#include // winbase.h contains windows.h + +#define DLLIMPORT __declspec( dllimport) +#define DLLEXPORT __declspec( dllexport) + +typedef HANDLE FILE_HANDLE; +typedef HANDLE MEMORY_HANDLE; + +#else +/********************************************************/ +/* __MSW__ */ +#ifdef __MSW__ +#include // provides standard definitions (like HANDLE) + +#define DLLIMPORT __import +#define DLLEXPORT __export +/*----------------------------*/ +/*----------------------------*/ +typedef HANDLE FILE_HANDLE; +typedef HANDLE MEMORY_HANDLE; +/*----------------------------*/ +#ifndef BOOLEAN +typedef UINT16 BOOLEAN; +#endif // BOOLEAN +#endif +#endif + +/********************************************************/ +/* __MAC__ */ +#ifdef __MAC__ +#include +/*----------------------------*/ +/*----------------------------*/ +#define DLLIMPORT +#define DLLEXPORT + +// definitions of handles to relocatable blocks +typedef Handle HANDLE; // a handle to a relocatable memory block + +typedef short FILE_HANDLE; +typedef Handle MEMORY_HANDLE; +/*----------------------------*/ +#ifndef BOOLEAN +#define BOOLEAN Boolean +#endif +#endif +/********************************************************/ +#if defined(__UNIX__) || defined( __DOS__ ) || defined(__OS2__) || defined(__PM__) +/*----------------------------*/ +/* FarProc and FarData */ +/*----------------------------*/ +#define DLLIMPORT +#define DLLEXPORT +typedef void *HANDLE; +typedef HANDLE FILE_HANDLE; +typedef HANDLE MEMORY_HANDLE; +/*----------------------------*/ +#ifndef BOOLEAN +typedef unsigned short BOOLEAN; +#endif // BOOLEAN +#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) +#if (_MSC_VER < 1400) // For VC 8.0. +typedef SIGNED char INT8; +#endif +typedef unsigned char UINT8; +typedef short INT16; +typedef unsigned short UINT16; +#if (_MSC_VER < 1200) //%%% vkr for VC 6.0 +typedef int INT32; +typedef unsigned int UINT32; +#endif //%%% vkr for VC 6.0 +typedef float FLOAT32; +typedef double FLOAT64; +typedef unsigned char BOOL8; + +// definitions of pointers to portable data types +#if (_MSC_VER < 1400) // For VC 8.0. +typedef SIGNED char *PINT8; +#endif +typedef unsigned char *PUINT8; +typedef short *PINT16; +typedef unsigned short *PUINT16; +#if (_MSC_VER < 1200) //%%% vkr for VC 6.0 +typedef int *PINT32; +typedef unsigned int *PUINT32; +#endif //%%% vkr for VC 6.0 +typedef float *PFLOAT32; +typedef double *PFLOAT64; + +// these are pointers to constant values (not constant pointers) + +typedef const SIGNED char *PCINT8; +typedef const unsigned char *PCUINT8; +typedef const short *PCINT16; +typedef const unsigned short *PCUINT16; +typedef const int *PCINT32; +typedef const unsigned int *PCUINT32; +typedef const float *PCFLOAT32; +typedef const double *PCFLOAT64; + +typedef void *PVOID; + +#define INT32FORMAT "%d" + +#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 0x80000000 +#define MIN_UINT8 0x00 +#define MIN_UINT16 0x0000 +#define MIN_UINT32 0x00000000 +#define MIN_FLOAT32 ((float)1.17549435e-38) + +// Defines + +#ifndef OKAY +#define OKAY 0 +#endif + +#ifndef HPERR +#define HPERR -1 +#endif + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef NULL +#define NULL 0L +#endif +/****************************************************************************** + ** WARNING!! ** + ** Below are definition that will be obsoleted in the next version. Please ** + ** do not continue to use the definition under __OLDCODE__. ** + *****************************************************************************/ +#ifdef __OLDCODE__ +#ifdef __MSW32__ + +#ifdef ERROR // Use HPERR +#undef ERROR +#define ERROR -1 +#endif + +typedef double FLOATHP; +#else + +#ifdef __MSW__ +#ifdef ERROR // Use HPERR +#undef ERROR +#define ERROR -1 +#endif +typedef double FLOAT; +typedef double FLOATHP; +typedef FLOAT FD *PFLOAT; +#endif +#endif +#ifdef __MAC__ +typedef float FLOAT; +typedef float FLOATHP; +typedef FLOAT FD *PFLOAT; +#endif + +#ifdef __UNIX__ +typedef float FLOAT; +typedef float FLOATHP; +typedef FLOAT FD *PFLOAT; +#endif + +#ifdef __DOS__ +typedef float FLOAT; +typedef float FLOATHP; +typedef FLOAT FD *PFLOAT; +#endif + +// definitions of pointers to functions that take no parameters +// specific definitions should be provided for functions that take parameters + +typedef void (far * PFVOID) (); /* pointer to function */ +typedef INT16 (*PFINT16) (void); +typedef INT32 (*PFINT32) (void); + +typedef BOOLEAN *PBOOLEAN; // a pointer to a Boolean +#endif +#endif diff --git a/ccutil/hosthplb.h b/ccutil/hosthplb.h new file mode 100644 index 0000000000..d81d9bc503 --- /dev/null +++ b/ccutil/hosthplb.h @@ -0,0 +1 @@ +#include "host.h" diff --git a/ccutil/lsterr.h b/ccutil/lsterr.h new file mode 100644 index 0000000000..5be6688f61 --- /dev/null +++ b/ccutil/lsterr.h @@ -0,0 +1,43 @@ +/********************************************************************** + * 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"; + +#ifdef _DEBUG + +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 = +"Attemting to add an element with non NULL links, to a list"; +#endif +#endif diff --git a/ccutil/mainblk.cpp b/ccutil/mainblk.cpp new file mode 100644 index 0000000000..87186c7e6d --- /dev/null +++ b/ccutil/mainblk.cpp @@ -0,0 +1,117 @@ +/********************************************************************** + * 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. + * + **********************************************************************/ + +#define BLOB_MATCHING_ON + +#include "mfcpch.h" +#include "fileerr.h" +#ifdef __UNIX__ +#include +#include +#else +#include +#endif +#include +#include "basedir.h" +#include "mainblk.h" + +#define VARDIR "configs/" /*variables files */ +#define EXTERN + +EXTERN DLLSYM STRING datadir; //dir for data files + //name of image +EXTERN DLLSYM STRING imagebasename; +EXTERN BOOL_VAR (m_print_variables, FALSE, +"Print initial values of all variables"); +EXTERN STRING_VAR (m_data_sub_dir, "tessdata/", "Directory for data files"); +EXTERN INT_VAR (memgrab_size, 13000000, "Preallocation size for batch use"); +const ERRCODE NO_PATH = +"Warning:explicit path for executable will not be used for configs"; +static const ERRCODE USAGE = "Usage"; + +/********************************************************************** + * main_setup + * + * Main for mithras demo program. Read the arguments and set up globals. + **********************************************************************/ + +void main_setup( /*main demo program */ + const char *argv0, //program name + const char *basename, //name of image + int argc, /*argument count */ + const char *const *argv /*arguments */ + ) { + INT32 arg; /*argument */ + INT32 offset; //for flag + FILE *fp; /*variables file */ + char flag[2]; //+/- + STRING varfile; /*name of file */ + + imagebasename = basename; /*name of image */ + if (getpath (argv0, datadir) < 0) + #ifdef __UNIX__ + CANTOPENFILE.error ("main", ABORT, "%s to get path", argv[0]); + #else + NO_PATH.error ("main", DBG, NULL); + #endif + + for (arg = 0; arg < argc; arg++) { + if (argv[arg][0] == '+' || argv[arg][0] == '-') { + offset = 1; + flag[0] = argv[arg][0]; + } + else { + offset = 0; + } + flag[offset] = '\0'; + varfile = flag; + /*attempt open */ + fp = fopen (argv[arg] + offset, "r"); + if (fp != NULL) { + fclose(fp); /*was only to test */ + } + else { + varfile += datadir; + varfile += m_data_sub_dir; /*data directory */ + varfile += VARDIR; /*variables dir */ + } + /*actual name */ + varfile += argv[arg] + offset; + read_variables_file (varfile.string ()); + } + + if (m_print_variables) + print_variables(stdout); /*print them all */ + + datadir += m_data_sub_dir; /*data directory */ + + #ifdef __UNIX__ + if (memgrab_size > 0) { + void *membuf; //test virtual mem + //test memory + membuf = malloc (memgrab_size); + if (membuf == NULL) { + raise(SIGTTOU); //hangup for jobber + sleep (10); + } + else + free(membuf); + } + #endif +} diff --git a/ccutil/mainblk.h b/ccutil/mainblk.h new file mode 100644 index 0000000000..8c4fe11fd2 --- /dev/null +++ b/ccutil/mainblk.h @@ -0,0 +1,48 @@ +/********************************************************************** + * File: mainblk.h (Formerly main.h) + * 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. + * + **********************************************************************/ + +#ifndef MAINBLK_H +#define MAINBLK_H + +#include "varable.h" +#include "notdll.h" + +//#ifndef BLOB_MATCHING_ON +//class BLOB; +//class ROW; +// float compare_blobs(BLOB*,ROW*,BLOB*,ROW*) +//{ +// return (float)MAX_INT32; +//} +//#endif + +extern DLLSYM STRING datadir; //dir for data files + //name of image +extern DLLSYM STRING imagebasename; +extern BOOL_VAR_H (m_print_variables, FALSE, +"Print initial values of all variables"); +extern STRING_VAR_H (m_data_sub_dir, "data/", "Directory for data files"); +extern INT_VAR_H (memgrab_size, 13000000, "Preallocation size for batch use"); +void main_setup( /*main demo program */ + const char *argv0, //program name + const char *basename, //name of image + int argc, /*argument count */ + const char *const *argv /*arguments */ + ); +#endif diff --git a/ccutil/memblk.cpp b/ccutil/memblk.cpp new file mode 100644 index 0000000000..12dfba0ea9 --- /dev/null +++ b/ccutil/memblk.cpp @@ -0,0 +1,1106 @@ +/********************************************************************** + * File: memblk.c (Formerly memblock.c) + * Description: Enhanced instrumented memory allocator implemented as a class. + * Author: Ray Smith + * Created: Tue Jan 21 17:13:39 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 "mfcpch.h" //precompiled headers +#include +#include +#include "stderr.h" +#include "memryerr.h" +#include "hashfn.h" +#include "tprintf.h" +#include "memry.h" +#include "memblk.h" +#ifdef __UNIX__ +#include +#endif + +class UWREC +{ + public: + unsigned cur_frsize; //frame size + unsigned cursp; //stack + unsigned currls; //pc space + unsigned currlo; //pc offset + unsigned curdp; //data pointer + unsigned toprp; //rp + unsigned topmrp; //mrp + unsigned topsr0; //sr0 + unsigned topsr4; //sr4 + unsigned r3; //gr3 + unsigned cur_r19; //gr19 +}; + +MEMUNION *free_block = NULL; //head of freelist + +#define EXTERN + +EXTERN MEM_ALLOCATOR big_mem; +EXTERN MEM_ALLOCATOR main_mem; + //heads of freelists +EXTERN MEMUNION *free_structs[MAX_STRUCTS]; + //number issued +EXTERN INT32 structs_in_use[MAX_STRUCTS]; + //number issued +EXTERN INT32 blocks_in_use[MAX_STRUCTS]; + //head of block lists +EXTERN MEMUNION *struct_blocks[MAX_STRUCTS]; +EXTERN const char *owner_names[MAX_STRUCTS][MAX_CLASSES]; +EXTERN INT32 owner_counts[MAX_STRUCTS][MAX_CLASSES]; + //no of names +EXTERN INT16 name_counts[MAX_STRUCTS]; +EXTERN INT32 free_struct_blocks; //no of free blocks + +EXTERN INT_VAR (mem_mallocdepth, 0, "Malloc stack depth to trace"); +EXTERN INT_VAR (mem_mallocbits, 8, "Log 2 of hash table size"); +EXTERN INT_VAR (mem_freedepth, 0, "Free stack dpeth to trace"); +EXTERN INT_VAR (mem_freebits, 8, "Log 2 of hash table size"); +EXTERN INT_VAR (mem_countbuckets, 16, "No of buckets for histogram"); +EXTERN INT_VAR (mem_checkfreq, 0, "Calls to alloc_mem between owner counts"); + +/********************************************************************** + * MEM_ALLOCATOR::MEM_ALLOCATOR + * + * Constructor for a memory allocator. + **********************************************************************/ + +void +MEM_ALLOCATOR::init ( //initialize +void *(*ext_malloc) (INT32), //external source +void (*ext_free) (void *), //external free +INT32 firstsize, //size of first block +INT32 lastsize, //size of last block +INT32 maxchunk //biggest request +) { + blockcount = 0; + malloc_serial = 0; + topblock = NULL; + currblock = NULL; + callers = NULL; + malloc = ext_malloc; + free = ext_free; + maxsize = lastsize; + biggestblock = maxchunk; + totalmem = 0; + memsize = firstsize; + malloc_div_ratio = 1; + malloc_minor_serial = 0; + malloc_auto_count = 0; + call_bits = 0; + entries = 0; +} + + +/********************************************************************** + * MEM_ALLOCATOR::hash_caller + * + * Generate a hash code for a caller, setup the tables if necessary. + **********************************************************************/ + +UINT16 MEM_ALLOCATOR::hash_caller( //get hash code + void *addr //return address + ) { + INT32 index; //index to table + INT32 initial_hash; //initial index + + if (callers == NULL) + init_callers(); //setup table + //get hash code + initial_hash = hash (call_bits, &addr, sizeof (addr)); + if (initial_hash == 0) + initial_hash = 1; + index = initial_hash; + if (callers[index].caller != NULL && callers[index].caller != addr) { + do { + index++; + if (index >= entries) + index = 1; + } + while (callers[index].caller != NULL + && callers[index].caller != addr && index != initial_hash); + if (index == initial_hash) + index = 0; + } + if (callers[index].caller == NULL) { + if (index != 0) + callers[index].caller = addr; + if (callers[index].free_list == NULL) + //setup free table + callers[index].init_freeers (); + } + return (UINT16) index; +} + + +/********************************************************************** + * MALLOC_CALL::count_freeer + * + * Generate a hash code for a freeer, setup the tables if necessary. + * Then count the call. + **********************************************************************/ + +void MALLOC_CALL::count_freeer( //count calls to free + void *addr //return address + ) { + INT32 entries; //entries in table + INT32 index; //index to table + INT32 initial_hash; //initial index + + if (free_list == NULL) + init_freeers(); //setup table + entries = 1 << free_bits; + //get hash code + initial_hash = hash (free_bits, &addr, sizeof (addr)); + if (initial_hash == 0) + initial_hash = 1; + index = initial_hash; + if (free_list[index].freeer != NULL && free_list[index].freeer != addr) { + do { + index++; + if (index >= entries) + index = 1; + } + while (free_list[index].freeer != NULL + && free_list[index].freeer != addr && index != initial_hash); + if (index == initial_hash) + index = 0; + } + if (free_list[index].freeer == NULL && index != 0) { + free_list[index].freeer = addr; + } + free_list[index].count++; //count them +} + + +/********************************************************************** + * MEM_ALLOCATOR::init_callers + * + * Initialize the callers hash table. + **********************************************************************/ + +void MEM_ALLOCATOR::init_callers() { //setup hash table + INT32 depth = mem_mallocdepth; + + mem_mallocdepth.set_value (0); //can't register it + call_bits = mem_mallocbits; + entries = 1 << call_bits; + //make an array + callers = new MALLOC_CALL[entries]; + mem_mallocdepth.set_value (depth); +} + + +/********************************************************************** + * MALLOC_CALL::init_freeers + * + * Initialize the freeers hash table. + **********************************************************************/ + +void MALLOC_CALL::init_freeers() { //setup hash table + INT32 entries; //entries in table + INT32 depth = mem_mallocdepth; + + mem_mallocdepth.set_value (0); //can't register it + free_bits = mem_freebits; + entries = 1 << free_bits; + //make an array + free_list = new FREE_CALL[entries]; + mem_mallocdepth.set_value (depth); +} + + +/********************************************************************** + * MEM_ALLOCATOR::reduce_counts + * + * Divide all ages by 2 to get a log use of counts. + **********************************************************************/ + +void MEM_ALLOCATOR::reduce_counts() { //divide by 2 + MEMBLOCK *block; //current block + MEMUNION *chunk; //current chunk + INT32 chunksize; //size of chunk + INT32 blockindex; //index of block + + check_mem ("Reducing counts", JUSTCHECKS); + for (blockindex = 0; blockindex < blockcount; blockindex++) { + //current block + block = &memblocks[blockindex]; + //scan all chunks + for (chunk = block->blockstart; chunk != block->blockend; chunk += chunksize) { + chunksize = chunk->size; //size of chunk + if (chunksize < 0) + chunksize = -chunksize; //absolute size + chunk->age /= 2; //divide ages + } + } +} + + +/********************************************************************** + * MEM_ALLOCATOR::display_counts + * + * Send counts of outstanding blocks to stderr. + **********************************************************************/ + +void MEM_ALLOCATOR::display_counts() { //count up + MEMBLOCK *block; //current block + MEMUNION *chunk; //current chunk + INT32 chunksize; //size of chunk + INT32 blockindex; //index of block + INT32 buckets; //required buckets + INT32 bucketsize; //no in each bucket + INT32 callindex; //index to callers + INT32 freeindex; //index to freeers + INT32 freeentries; //table size + INT32 totalchunks; //total chunk counts + INT32 totalspace; //total mem space + INT32 totalpchunks; //permanent chunks + INT32 totalpspace; //permanent space + INT32 totalfrees; //total free calls + + if (callers == NULL) + return; //can't do anything + check_mem ("Displaying counts", JUSTCHECKS); + buckets = mem_countbuckets; + bucketsize = (malloc_serial - 1) / buckets + 1; + tprintf ("\nEach bucket covers %g counts.\n", + (double) bucketsize * malloc_div_ratio); + for (callindex = 0; callindex < entries; callindex++) { + if (callers[callindex].free_list != NULL) { + callers[callindex].counts = + (INT32 *) malloc (buckets * 4 * sizeof (INT32)); + memset (callers[callindex].counts, 0, + (size_t) (buckets * 4 * sizeof (INT32))); + } + } + for (blockindex = 0; blockindex < blockcount; blockindex++) { + //current block + block = &memblocks[blockindex]; + //scan all chunks + for (chunk = block->blockstart; chunk != block->topchunk; chunk += chunksize) { + chunksize = chunk->size; //size of chunk + if (chunksize < 0) { + chunksize = -chunksize; //absolute size + callindex = chunk->owner; + if (callers[callindex].counts != NULL) { + callers[callindex].counts[chunk->age / bucketsize * 4]++; + callers[callindex].counts[chunk->age / bucketsize * 4 + + 1] += chunksize; + } + } + } + //scan all chunks + for (; chunk != block->blockend; chunk += chunksize) { + chunksize = chunk->size; //size of chunk + if (chunksize < 0) { + chunksize = -chunksize; //absolute size + callindex = chunk->owner; + if (callers[callindex].counts != NULL) { + callers[callindex].counts[chunk->age / bucketsize * 4 + + 2]++; + callers[callindex].counts[chunk->age / bucketsize * 4 + + 3] += chunksize; + } + } + } + } + for (callindex = 0; callindex < entries; callindex++) { + if (callers[callindex].counts != NULL) { + for (totalspace = 0, totalchunks = 0, totalpspace = + 0, totalpchunks = 0, freeindex = 0; freeindex < buckets; + freeindex++) { + totalchunks += callers[callindex].counts[freeindex * 4]; + totalspace += callers[callindex].counts[freeindex * 4 + 1]; + totalpchunks += callers[callindex].counts[freeindex * 4 + 2]; + totalpspace += callers[callindex].counts[freeindex * 4 + 3]; + } + freeentries = 1 << callers[callindex].free_bits; + for (totalfrees = 0, freeindex = 0; freeindex < freeentries; + freeindex++) + totalfrees += callers[callindex].free_list[freeindex].count; + if (totalspace != 0 || totalfrees != 0) { + tprintf ("alloc_mem at %d : total held=%d(%d), frees=%d.\n", + callers[callindex].caller, + totalchunks, totalspace * sizeof (MEMUNION), + totalfrees); + } + if (totalspace > 0) { + for (freeindex = 0; freeindex < buckets; freeindex++) { + tprintf ("%d(%d) ", + callers[callindex].counts[freeindex * 4], + callers[callindex].counts[freeindex * 4 + + 1] * sizeof (MEMUNION)); + } + tprintf ("\n"); + } + if (totalfrees != 0) { + tprintf ("Calls to free : "); + for (freeindex = 0; freeindex < freeentries; freeindex++) { + if (callers[callindex].free_list[freeindex].count != 0) + tprintf ("%d : %d ", + callers[callindex].free_list[freeindex].freeer, + callers[callindex].free_list[freeindex].count); + } + tprintf ("\n"); + } + if (totalpspace != 0) { + tprintf ("alloc_mem_p at %d : total held=%d(%d).\n", + callers[callindex].caller, + totalpchunks, totalpspace * sizeof (MEMUNION)); + for (freeindex = 0; freeindex < buckets; freeindex++) { + tprintf ("%d(%d) ", + callers[callindex].counts[freeindex * 4 + 2], + callers[callindex].counts[freeindex * 4 + + 3] * sizeof (MEMUNION)); + } + tprintf ("\n"); + } + free (callers[callindex].counts); + callers[callindex].counts = NULL; + } + } +} + + +/********************************************************************** + * MEM_ALLOCATOR::check + * + * Check consistency of all memory controlled by this allocator. + **********************************************************************/ + +void MEM_ALLOCATOR::check( //check consistency + const char *string, //context message + INT8 level //level of check + ) { + MEMBLOCK *block; //current block + MEMUNION *chunk; //current chunk + MEMUNION *prevchunk; //previous chunk + INT32 chunksize; //size of chunk + INT32 usedcount; //no of used chunks + INT32 usedsize; //size of used chunks + INT32 freecount; //no of free chunks + INT32 freesize; //size of free chunks + INT32 biggest; //biggest free chunk + INT32 totusedcount; //no of used chunks + INT32 totusedsize; //size of used chunks + INT32 totfreecount; //no of free chunks + INT32 totfreesize; //size of free chunks + INT32 totbiggest; //biggest free chunk + INT32 totblocksize; //total size of blocks + INT32 chunkindex; //index of chunk + INT32 blockindex; //index of block + + if (level >= MEMCHECKS) + tprintf ("\nMEM_ALLOCATOR::check:at '%s'\n", string); + totusedcount = 0; //grand totals + totusedsize = 0; + totfreecount = 0; + totfreesize = 0; + totbiggest = 0; + totblocksize = 0; + for (blockindex = 0; blockindex < blockcount; blockindex++) { + //current block + block = &memblocks[blockindex]; + if (level >= MEMCHECKS) + tprintf ("Block %d:0x%x-0x%x, size=%d, top=0x%x, l=%d, u=%d\n", + blockindex, block->blockstart, block->blockend, + (block->blockend - block->blockstart) * sizeof (MEMUNION), + block->topchunk, block->lowerspace, block->upperspace); + usedcount = usedsize = 0; //zero counters + freecount = freesize = 0; //zero counters + biggest = 0; + //scan all chunks + for (chunkindex = 0, prevchunk = NULL, chunk = block->blockstart; chunk != block->blockend; chunkindex++, chunk += chunksize) { + chunksize = chunk->size; //size of chunk + if (chunksize < 0) + chunksize = -chunksize; //absolute size + if (level >= FULLMEMCHECKS) { + tprintf ("%5d=%8d%c caller=%d, age=%d ", (int) chunkindex, + chunksize * sizeof (MEMUNION), + chunk->size < 0 ? 'U' : 'F', chunk->owner, chunk->age); + if (chunkindex % 5 == 4) + tprintf ("\n"); + } + //illegal sizes + if (chunksize == 0 || chunk->size == -1 + //out of bounds + || chunk + chunksize - block->blockstart <= 0 || block->blockend - (chunk + chunksize) < 0) + BADMEMCHUNKS.error ("check_mem", ABORT, + "Block=%p, Prev chunk=%p, Chunk=%p, Size=%x", + block, prevchunk, chunk, + (int) chunk->size); + + else if (chunk->size < 0) { + usedcount++; //used block + usedsize += chunksize; + } + else { + freecount++; //free block + freesize += chunksize; + if (chunksize > biggest) + biggest = chunksize; + } + prevchunk = chunk; + } + if (level >= MEMCHECKS) { + if (level >= FULLMEMCHECKS) + tprintf ("\n"); + tprintf ("%d chunks in use, total size=%d bytes\n", + (int) usedcount, usedsize * sizeof (MEMUNION)); + tprintf ("%d chunks free, total size=%d bytes\n", + (int) freecount, freesize * sizeof (MEMUNION)); + tprintf ("Largest free fragment=%d bytes\n", + biggest * sizeof (MEMUNION)); + } + totusedcount += usedcount; //grand totals + totusedsize += usedsize; + totfreecount += freecount; + totfreesize += freesize; + if (biggest > totbiggest) + totbiggest = biggest; + totblocksize += block->blockend - block->blockstart; + } + if (level >= MEMCHECKS) { + tprintf ("%d total blocks in use, total size=%d bytes\n", + blockcount, totblocksize * sizeof (MEMUNION)); + tprintf ("%d total chunks in use, total size=%d bytes\n", + (int) totusedcount, totusedsize * sizeof (MEMUNION)); + tprintf ("%d total chunks free, total size=%d bytes\n", + (int) totfreecount, totfreesize * sizeof (MEMUNION)); + tprintf ("Largest free fragment=%d bytes\n", + totbiggest * sizeof (MEMUNION)); + } + if (level >= MEMCHECKS) + display_counts(); +} + + +/********************************************************************** + * MEM_ALLOCATOR::alloc_p + * + * Allocate permanent space which will never be returned. + * This space is allocated from the top end of a memory block to + * avoid the fragmentation which would result from alternate use + * of alloc_mem for permanent and temporary blocks. + **********************************************************************/ + +void *MEM_ALLOCATOR::alloc_p( //permanent space + INT32 count, //block size to allocate + void *caller //ptr to caller + ) { + MEMBLOCK *block; //current block + MEMUNION *chunk; //current chunk + + if (count < 1 || count > biggestblock) + //request too big + MEMTOOBIG.error ("alloc_mem_p", ABORT, "%d", (int) count); + + count += sizeof (MEMUNION) - 1;//round up to word + count /= sizeof (MEMUNION); + count++; //and add one + if (topblock == NULL) { + topblock = new_block (count);//get first block + currblock = topblock; + if (topblock == NULL) { + check_mem ("alloc_mem_p returning NULL", MEMCHECKS); + return NULL; + } + } + block = topblock; //current block + do { + chunk = block->topchunk; + if (chunk->size < count) + block = block->next; //try next block + } + //until all tried + while (chunk->size < count && block != topblock); + if (chunk->size < count) { //still no good + chunk = (MEMUNION *) alloc ((count - 1) * sizeof (MEMUNION), caller); + //try last resort + if (chunk != NULL) + return chunk; + check_mem ("alloc_mem_p returning NULL", MEMCHECKS); + return NULL; + } + block->upperspace -= count; //less above freechunk + if (chunk->size > count) { + chunk->size -= count; + chunk += chunk->size; + } + chunk->size = -count; //mark as in use + if (mem_mallocdepth > 0) { + set_owner(chunk, caller); + } + else { + chunk->owner = 0; + chunk->age = 0; + } + return chunk + 1; //created chunk +} + + +/********************************************************************** + * MEM_ALLOCATOR::alloc + * + * Return a pointer to a buffer of count bytes aligned for any type. + **********************************************************************/ + +void *MEM_ALLOCATOR::alloc( //get memory + INT32 count, //no of bytes to get + void *caller //ptr to caller + ) { + MEMBLOCK *block; //current block + MEMUNION *chunk; //current chunk + INT32 chunksize; //size of free chunk + MEMUNION *chunkstart; //start of free chunk + + if (count < 1 || count > biggestblock) + MEMTOOBIG.error ("alloc_mem", ABORT, "%d", (int) count); + //request too big + + count += sizeof (MEMUNION) - 1;//round up to word + count /= sizeof (MEMUNION); + count++; //and add one + if (currblock == NULL) { + //get first block + currblock = new_block (count); + topblock = currblock; + if (currblock == NULL) { + check_mem ("alloc_mem returning NULL", MEMCHECKS); + return NULL; + } + } + block = currblock; //current block + if (block->upperspace <= block->lowerspace) { + //restart chunklist + block->freechunk = block->blockstart; + block->upperspace += block->lowerspace; + block->lowerspace = 0; //correct space counts + } + chunk = block->freechunk; //current free chunk + if (chunk->size < count) { //big enough? + do { + //search for free chunk + chunk = block->find_chunk (count); + if (chunk->size < count) + block = block->next; //try next block + } + //until all tried + while (chunk->size < count && block != currblock); + if (chunk->size < count) { //still no good + //get a new block + currblock = new_block (count); + topblock = currblock; //set perms here too + if (currblock == NULL) { + check_mem ("alloc_mem returning NULL", MEMCHECKS); + return NULL; + } + block = currblock; + chunk = block->freechunk; //bound to be big enough + } + } + chunkstart = chunk; //start of chunk + if (chunk == block->topchunk && chunk + count != block->blockend) + block->topchunk += count; //top has moved + block->upperspace -= count; //upper part used + chunksize = chunk->size; //size of free chunk + chunk->size = -count; //mark as used + chunk += count; //next free space + totalmem -= count; //no of free elements + if (chunksize > count) //bigger than exact? + //remaining space + chunk->size = chunksize - count; + else if (chunk == block->blockend) { + chunk = block->blockstart; //restart block + block->upperspace = block->lowerspace; + block->lowerspace = 0; //fix space counts + } + block->freechunk = chunk; //next free block + if (mem_mallocdepth > 0) { + set_owner(chunkstart, caller); + } + else { + chunkstart->owner = 0; + chunkstart->age = 0; + } + chunkstart++; //start of block + return chunkstart; //pointer to block +} + + +/********************************************************************** + * MEM_ALLOCATOR::set_owner + * + * Set the owner and time stamp of the block and check if needed. + **********************************************************************/ + +void MEM_ALLOCATOR::set_owner( //get memory + MEMUNION *chunkstart, //chunk to set + void *caller //ptr to caller + ) { + UINT16 callindex; //hash code + + callindex = hash_caller (caller); + chunkstart->owner = callindex; + //store evidence + chunkstart->age = malloc_serial; + malloc_minor_serial++; + if (malloc_minor_serial >= malloc_div_ratio) { + malloc_minor_serial = 0; + malloc_serial++; //count calls + if (malloc_serial == 0) { + //wrap around + reduce_counts(); //fix serial numbers + malloc_serial = MAX_INT16 + 1; + //all worth double + malloc_div_ratio += malloc_div_ratio; + } + } + malloc_auto_count++; + if (mem_checkfreq > 0 && malloc_auto_count >= (UINT32) mem_checkfreq) { + malloc_auto_count = 0; + check_mem ("Auto check", MEMCHECKS); + } +} + + +/********************************************************************** + * MEM_ALLOCATOR::dealloc + * + * Free a block allocated by alloc (or alloc_p). + * It checks that the pointer is legal and maintains counts of the + * amount of free memory above and below the current free pointer. + **********************************************************************/ + +void MEM_ALLOCATOR::dealloc( //free memory + void *oldchunk, //chunk to free + void *caller //ptr to caller + ) { + MEMUNION *chunk; //current chunk + MEMBLOCK *block; //current block + + if (oldchunk == NULL) + FREENULLPTR.error ("free_mem", ABORT, NULL); + chunk = (MEMUNION *) oldchunk; + block = currblock; //current block + if (block == NULL) + NOTMALLOCMEM.error ("free_mem", ABORT, NULL); + do { + block = block->next; + } + //outside the block + while ((chunk - block->blockstart < 0 || block->blockend - chunk <= 0) + && block != currblock); + + if (chunk - block->blockstart < 0 || block->blockend - chunk <= 0) + //in no block + NOTMALLOCMEM.error ("free_mem", ABORT, NULL); + + chunk--; //point to size + if (chunk->size == 0) + //zero size + FREEILLEGALPTR.error ("free_mem", ABORT, NULL); + else if (chunk->size > 0) + //already free + FREEFREEDBLOCK.error ("free_mem", ABORT, NULL); + chunk->size = -chunk->size; //mark it free + if (mem_freedepth > 0 && callers != NULL) { + //count calls + callers[chunk->owner].count_freeer (caller); + } + totalmem += chunk->size; //total free memory + if (chunk - block->freechunk < 0) + //extra below + block->lowerspace += chunk->size; + else + //extra above + block->upperspace += chunk->size; +} + + +/********************************************************************** + * MEM_ALLOCATOR::new_block + * + * Gets a new big block of memory from malloc for use by alloc_mem. + **********************************************************************/ + +MEMBLOCK *MEM_ALLOCATOR::new_block( //get new big block + INT32 minsize //minimum size + ) { + MEMBLOCK *newblock; //new block + + if (blockcount >= MAXBLOCKS) { + //can't have another + NOMOREBLOCKS.error ("mem_new_block", LOG, NULL); + return NULL; + } + if (mem_checkfreq != 0) { + tprintf ("\nGetting new block due to request size of %d", + minsize * sizeof (MEMUNION)); + tprintf (" from %d from %d from %d from %d from %d\n", + trace_caller (3), trace_caller (4), trace_caller (5), + trace_caller (6), trace_caller (7)); + check_mem ("Getting new block", MEMCHECKS); + } + //get a new one + newblock = &memblocks[blockcount++]; + while (memsize < minsize) + memsize *= 4; //go up in sizes + //get a big block + newblock->blockstart = (MEMUNION *) + malloc (memsize * sizeof (MEMUNION)); + if (newblock->blockstart == NULL) { + NOMOREMEM.error ("mem_new_block", LOG, NULL); + + #ifdef __UNIX__ + raise(SIGTTOU); //hangup for js + #endif + return NULL; + } + //end of block + newblock->blockend = newblock->blockstart + memsize; + //first free chunk + newblock->freechunk = newblock->blockstart; + newblock->topchunk = newblock->blockstart; + newblock->lowerspace = 0; + newblock->upperspace = memsize;//amount available + //set pointer + newblock->freechunk->size = memsize; + newblock->freechunk->owner = 0; + newblock->freechunk->age = 0; + + totalmem += memsize; //total assigned mem + + if (memsize < maxsize) + memsize *= 4; //successively bigger + if (currblock == NULL) { + newblock->next = newblock; //first block + } + else { + //insert in list + newblock->next = currblock->next; + currblock->next = newblock; + } + return newblock; //new block +} + + +/********************************************************************** + * MEMBLOCK::find_chunk + * + * Find a chunk within the block which is big enough for the given request + **********************************************************************/ + +MEMUNION *MEMBLOCK::find_chunk( //find free chunk + INT32 count //size required + ) { + MEMUNION *chunk; //current chunk + INT32 chunksize; //size of free chunk + MEMUNION *chunkstart; //start of free chunk + INT32 spaceshift; //shift in lowerspace + + if (upperspace <= lowerspace) { + freechunk = blockstart; //restart chunklist + upperspace += lowerspace; + lowerspace = 0; //correct space counts + } + chunk = freechunk; //current free chunk + if (chunk->size < count) { //big enough? + spaceshift = 0; + do { + while (chunk->size < 0) { //find free chunk + chunk -= chunk->size; //skip forward + if (chunk == blockend) { + chunk = blockstart; //restart block + //gone back to start + spaceshift = -lowerspace; + } + if (chunk == freechunk) + return chunk; //gone all round & failed + } + chunkstart = chunk; //start of chunk + chunksize = chunk->size;; + chunk += chunk->size; + while (chunk != blockend //until end + && chunk->size > 0) { //or used + chunksize += chunk->size;//coalesce free blocks + //gone all round + if (chunk == freechunk) { + //ensure it is at end + freechunk += chunk->size; + upperspace -= chunk->size; + lowerspace += chunk->size; + spaceshift -= chunk->size; + } + if (chunk == topchunk) //got back to end one + topchunk = chunkstart; //end one bigger + chunk += chunk->size; //get next block + } + //new big block + chunkstart->size = chunksize; + if (chunksize < count) + spaceshift += chunksize; //skipping free block + if (chunk == blockend) { + chunk = blockstart; //back to start + if (freechunk == blockend) { + freechunk = blockstart;//so is freechunk + upperspace += lowerspace; + lowerspace = 0; + spaceshift = 0; + } + else + //so is shift + spaceshift = -lowerspace; + } + } + while (chunksize < count && chunk != freechunk); + if (chunksize < count) + return chunk; //failed + lowerspace += spaceshift; //get space counts right + upperspace -= spaceshift; + freechunk = chunkstart; + return chunkstart; //success + } + return chunk; //easy +} + + +#ifdef __UNIX__ +/********************************************************************** + * trace_caller + * + * Return the return address of the caller at a given depth back. + * 0 gives the return address of the caller to trace_caller. + * S300 ONLY!! + **********************************************************************/ +//#pragma OPTIMIZE OFF /*force link*/ + +void *trace_caller( //trace stack + INT32 depth //depth to trace + ) { + #ifdef hp9000s800 + + unsigned sp, pc, rp; //registers + UWREC rec1; //for unwinder + UWREC rec2; + + sp = (unsigned) (&depth + 9); + pc = *(int *) (sp - 20); + rp = 0; + get_pcspace(&rec1, pc); + rec1.cur_frsize = 0xc0; + rec1.currlo = pc & ~3; + rec1.curdp = 0; + rec1.toprp = rp; + + while (depth > 0) { + if (U_get_previous_frame (&rec1, &rec2)) + return NULL; + rec1.currlo = rec2.currlo; + rec1.cur_frsize = rec2.cur_frsize; + rec1.cursp = rec2.cursp; + rec1.currls = rec2.currls; + rec1.curdp = rec2.curdp; + depth--; + } + return (void *) rec1.currlo; + #else + void *a6; //address register + + a6 = &depth - 2; + while (depth > 0) { + a6 = *(void **) a6; //follow chain + depth--; + } + return *((void **) a6 + 1); + #endif +} + + +//#pragma OPTIMIZE ON + +#else + +// Fake procedure for non-UNIX +void *trace_caller( //trace stack + INT32 depth //depth to trace + ) { + return NULL; +} +#endif + +/********************************************************************** + * identify_struct_owner + * + * Get an index into the table of owners of structures. + * Implemented very inefficiently, but only a debug tool! + **********************************************************************/ + +INT32 identify_struct_owner( //get table index + INT32 struct_count, //cell size + const char *name //name of type + ) { + INT32 index; //index to structure + + for (index = 0; index < name_counts[struct_count] + && strcmp (name, owner_names[struct_count][index]); index++); + if (index < MAX_CLASSES) { + if (index == name_counts[struct_count]) { + name_counts[struct_count]++; + owner_names[struct_count][index] = name; + owner_counts[struct_count][index] = 0; + } + } + return index; +} + + +/********************************************************************** + * check_struct + * + * Check a particular structure size for consistency. + **********************************************************************/ + +void check_struct( //check a structure + INT8 level, //print control + INT32 count //no of bytes + ) { + MEMUNION *element; //current element + MEMUNION *block; //current block + INT32 struct_count; //no of required structs + INT32 block_count; //no of structure blocks + INT32 free_count; //size of freelist*/ + INT32 name_index; //named holder + INT32 named_total; //total held by names + + //no of MEMUNIONS-1 + struct_count = (count - 1) / sizeof (MEMUNION); + if (struct_count < 0 || struct_count >= MAX_STRUCTS) + //request too big + MEMTOOBIG.error ("check_struct", ABORT, "%d", (int) count); + + free_count = 0; //size of freelist + //count blocks + for (block_count = 0, block = struct_blocks[struct_count]; block != NULL; block = block->ptr, block_count++); + if (block_count > 0) { + //scan freelist + for (element = free_structs[struct_count]; element != NULL; element = element->ptr) + free_count++; + if (level >= MEMCHECKS) { + tprintf ("No of structs of size %d in use=%d,", + (int) count, (int) structs_in_use[struct_count]); + tprintf (" %d free", free_count); + tprintf (" in %d blocks, total space=%d\n", + (int) block_count, + block_count * STRUCT_BLOCK_SIZE * sizeof (MEMUNION)); + for (named_total = 0, name_index = 0; + name_index < name_counts[struct_count]; name_index++) { + tprintf ("No held by %s=%d\n", + owner_names[struct_count][name_index], + owner_counts[struct_count][name_index]); + named_total += owner_counts[struct_count][name_index]; + } + tprintf ("Total held by names=%d\n", named_total); + } + } + if (structs_in_use[struct_count] + free_count + != block_count * (STRUCT_BLOCK_SIZE / (struct_count + 1) - 1)) + BADSTRUCTCOUNT.error ("check_struct", ABORT, "%d+%d=%d", + structs_in_use[struct_count], free_count, + block_count * (STRUCT_BLOCK_SIZE / + (struct_count + 1) - 1)); +} + + +/********************************************************************** + * check_structs + * + * Reports statistics on each maintained structure type by calling + * free_struct(NULL) on each. Only active structure types are reported. + **********************************************************************/ + +void check_structs( //count in use on structs + INT8 level //print control + ) { + INT8 index; //index to structs + + for (index = 1; index <= MAX_STRUCTS; index++) + //check number allocated + check_struct (level, index * sizeof (MEMUNION)); +} + + +/********************************************************************** + * new_struct_block + * + * Allocate space for a new block of structures. The space is obtained + * from alloc_mem, and a freelist of such blocks is maintained for when + * the individual structure types get completely freed. + **********************************************************************/ + +void *new_struct_block() { //allocate memory + MEMUNION *element; //current element + MEMUNION *returnelement; //return value + + returnelement = free_block; + if (returnelement == NULL) { + //need a new block + element = + (MEMUNION *) alloc_mem_p (STRUCT_BLOCK_SIZE * sizeof (MEMUNION)); + if (element == NULL) + return NULL; //can't get more + returnelement = element; //going to return 1st + } + else { + //new free one + free_block = returnelement->ptr; + } + return returnelement; //free cell +} + + +/********************************************************************** + * old_struct_block + * + * Free memory allocated by new_struct_block. The block is returned + * to a freelist ready for a new call to new_struct_block. + * This saves confusion over freeing "permanent" blocks, yet + * allows them to be recycled for different structures. + **********************************************************************/ + +void old_struct_block( //free a structure block + MEMUNION *deadblock //block to free + ) { + if (deadblock != NULL) { + deadblock->ptr = free_block; //add to freelist + free_block = deadblock; + free_struct_blocks++; + } + if (free_struct_blocks > MAX_FREE_S_BLOCKS) { + MEMUNION *next_block; //next in list + deadblock = free_block; + do { + next_block = deadblock->ptr; + free_mem(deadblock); //really free it + deadblock = next_block; + } + while (deadblock != NULL); + free_struct_blocks = 0; + free_block = NULL; + } +} diff --git a/ccutil/memblk.h b/ccutil/memblk.h new file mode 100644 index 0000000000..210578dd13 --- /dev/null +++ b/ccutil/memblk.h @@ -0,0 +1,189 @@ +/********************************************************************** + * File: memblk.h (Formerly memblock.h) + * Description: Enhanced instrumented memory allocator implemented as a class. + * Author: Ray Smith + * Created: Tue Jan 21 17:13:39 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 MEMBLK_H +#define MEMBLK_H + +#include "varable.h" + +#define MAXBLOCKS 16 /*max allowed to grab */ +#define MAX_STRUCTS 20 //no of units maintained +#define MAX_CLASSES 24 //max classes of each size +#define MAX_FREE_S_BLOCKS 10 //max free list before all freed +#define STRUCT_BLOCK_SIZE 2521 +#define MAX_CHUNK 262144 //max single chunk +#define FIRSTSIZE 16384 //size of first block +#define LASTSIZE 262144 //biggest size to use +#define BIGSIZE 2100000 //size of big blocks +#define MAX_BIGCHUNK 20000000 //max chunk of big mem + +//#define TESTING_BIGSTUFF //define for big tests +//#define COUNTING_CLASS_STRUCTURES + +class MEMUNION +{ + public: + union + { + MEMUNION *ptr; //next chunk + INT32 size; //chunk size + }; + UINT16 owner; //owner of chunk + UINT16 age; //age of chunk +}; + +class MEMBLOCK +{ + public: + MEMUNION * blockstart; /*start of block */ + MEMUNION *blockend; /*end of block */ + MEMUNION *freechunk; /*next free chunk */ + MEMUNION *topchunk; /*top free chunk */ + MEMBLOCK *next; /*next block in chain */ + INT32 upperspace; /*space above freechunk */ + INT32 lowerspace; /*space below freechunk */ + + MEMUNION *find_chunk( //find free chunk + INT32 count); //size required +}; + +class FREE_CALL +{ + public: + void *freeer; //return addr + INT32 count; //no of frees + FREE_CALL() { //constructor + freeer = NULL; + count = 0; + } +}; +class MALLOC_CALL +{ + public: + void *caller; //return addr + FREE_CALL *free_list; //freeer counts + INT32 *counts; //no of blocks + INT32 free_bits; //bits in free table + + MALLOC_CALL() { //constructor + caller = NULL; + free_list = NULL; + counts = NULL; + free_bits = 0; + } + void count_freeer( //check a structure + void *addr); //return address + + void init_freeers(); //check a structure +}; + +class MEM_ALLOCATOR +{ + public: + INT16 blockcount; //blocks in use + UINT16 malloc_serial; //serial allocation + MEMBLOCK *topblock; //block for permanents + MEMBLOCK *currblock; //current block + MALLOC_CALL *callers; //hash table of callers + void *(*malloc) (INT32); //external allocator + void (*free) (void *); //external free + INT32 maxsize; //biggest block + INT32 biggestblock; //biggest chunk + INT32 totalmem; //total free memory + INT32 memsize; //current block size + UINT32 malloc_div_ratio; //scaling of malloc_serial + UINT32 malloc_minor_serial; //scaling counter + UINT32 malloc_auto_count; //counts auto checks + INT32 call_bits; //size of table + INT32 entries; //size of table + //all memory blocks + MEMBLOCK memblocks[MAXBLOCKS]; + + void init ( //initialize + void *(*ext_malloc) (INT32),//external source + void (*ext_free) (void *), //external free + INT32 firstsize, //size of first block + INT32 lastsize, //size of last block + INT32 maxchunk); //biggest request + + void *alloc( //allocator + INT32 size, //size of chunk + void *caller); //ptr to caller + void *alloc_p( //allocator + INT32 size, //size of chunk + void *caller); //ptr to caller + void dealloc( //deallocator + void *ptr, //mem to free + void *caller); //ptr to caller + void check( //check chunks + const char *string, //message + INT8 level); //amount of checks + + void reduce_counts(); //divide by 2 + void display_counts(); //count up + MEMBLOCK *new_block( //get new big block + INT32 minsize); //minimum size + UINT16 hash_caller( //check a structure + void *addr); //return address + + private: + void init_callers(); //check a structure + void set_owner( //set owner & date + MEMUNION *chunkstart, //chunk to set + void *caller); //ptr to caller +}; +extern MEM_ALLOCATOR big_mem; +extern MEM_ALLOCATOR main_mem; + //heads of freelists +extern MEMUNION *free_structs[MAX_STRUCTS]; + //number issued +extern INT32 structs_in_use[MAX_STRUCTS]; + //number issued +extern INT32 blocks_in_use[MAX_STRUCTS]; + //head of block lists +extern MEMUNION *struct_blocks[MAX_STRUCTS]; +extern INT32 owner_counts[MAX_STRUCTS][MAX_CLASSES]; + +extern INT_VAR_H (mem_mallocdepth, 0, "Malloc stack depth to trace"); +extern INT_VAR_H (mem_mallocbits, 8, "Log 2 of hash table size"); +extern INT_VAR_H (mem_freedepth, 0, "Free stack dpeth to trace"); +extern INT_VAR_H (mem_freebits, 8, "Log 2 of hash table size"); +extern INT_VAR_H (mem_countbuckets, 16, "No of buckets for histogram"); +extern INT_VAR_H (mem_checkfreq, 0, +"Calls to alloc_mem between owner counts"); + +void *trace_caller( //trace stack + INT32 depth //depth to trace + ); +INT32 identify_struct_owner( //get table index + INT32 struct_count, //cell size + const char *name //name of type + ); +void check_struct( //check a structure + INT8 level, //print control + INT32 count //no of bytes + ); +void check_structs( //count in use on structs + INT8 level //print control + ); +void *new_struct_block(); //allocate memory +void old_struct_block( //free a structure block + MEMUNION *deadblock //block to free + ); +#endif diff --git a/ccutil/memry.cpp b/ccutil/memry.cpp new file mode 100644 index 0000000000..5396240c9d --- /dev/null +++ b/ccutil/memry.cpp @@ -0,0 +1,531 @@ +/********************************************************************** + * 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 "mfcpch.h" +#include +#ifdef __UNIX__ +#include +#endif +#include "stderr.h" +#include "tprintf.h" +#include "memblk.h" +#include "memry.h" + +//#define COUNTING_CLASS_STRUCTURES + +/********************************************************************** + * new + * + * Replace global new to get at memory leaks etc. + **********************************************************************/ +/* +void* operator new( //allocate memory +size_t size //amount to allocate +) +{ + if (size==0) + { + err.log(RESULT_LOGICAL_ERROR,E_LOC,ERR_PRIMITIVES, + ERR_SCROLLING,ERR_CONTINUE,ERR_ERROR, + "Zero requested of new"); + size=1; + } + return alloc_big_mem(size); +} + +void operator delete( //free memory +void* addr //mem to free +) +{ + free_big_mem(addr); +}*/ + +/********************************************************************** + * check_mem + * + * Check consistency of all memory controlled by alloc_mem. + **********************************************************************/ + +DLLSYM void check_mem( //check consistency + const char *string, //context message + INT8 level //level of check + ) { + big_mem.check (string, level); + main_mem.check (string, level); + check_structs(level); +} + + +/********************************************************************** + * alloc_string + * + * Allocate space for a string. The space can only be used for chars as + * it is not aligned. Allocation is guaranteed to be fast and not cause + * fragmentation for small strings (upto 10*worst alignment). Calls for + * larger strings will be satisfied with alloc_mem. + * Use free_string to free the space from alloc_string. + **********************************************************************/ + +DLLSYM char *alloc_string( //allocate string + INT32 count //no of chars required + ) { +#ifdef RAYS_MALLOC + char *string; //allocated string + + if (count < 1 || count > MAX_CHUNK) { + tprintf ("Invalid size %d requested of alloc_string", count); + return NULL; + } + + count++; //add size byte + if (count <= MAX_STRUCTS * sizeof (MEMUNION)) { + string = (char *) alloc_struct (count, "alloc_string"); + //get a fast structure + if (string == NULL) { + tprintf ("No memory for alloc_string"); + return NULL; + } + string[0] = (INT8) count; //save its length + } + else { + //get a big block + string = (char *) alloc_mem (count); + if (string == NULL) { + tprintf ("No memory for alloc_string"); + return NULL; + } + string[0] = 0; //mark its id + } + return &string[1]; //string for user +#else + return static_cast(malloc(count)); +#endif +} + + +/********************************************************************** + * free_string + * + * Free a string allocated by alloc_string. + **********************************************************************/ + +DLLSYM void free_string( //free a string + char *string //string to free + ) { +#ifdef RAYS_MALLOC + if (((ptrdiff_t) string & 3) == 1) { //one over word + string--; //get id marker + if (*string == 0) { + free_mem(string); //generally free it + return; + } + else if (*string <= MAX_STRUCTS * sizeof (MEMUNION)) { + //free structure + free_struct (string, *string, "alloc_string"); + return; + } + } + tprintf ("Non-string given to free_string"); +#else + free(string); +#endif +} + + +/********************************************************************** + * alloc_struct + * + * Allocate space for a structure. This function is designed to be + * fast and fragmentation free for arbitrary combinations of small + * objects. (Upto 40 bytes in length.) + * It can be used for any size of object up to 512K, but you must use + * free_struct to release the memory it gives. alloc_mem is better + * for arbitrary data blocks of large size (>40 bytes.) + * alloc_struct always aborts if the allocation fails. + **********************************************************************/ + +DLLSYM void * +alloc_struct ( //allocate memory +INT32 count, //no of chars required +#if defined COUNTING_CLASS_STRUCTURES +const char *name //name of type +#else +const char * //name of type +#endif +) { +#ifdef RAYS_MALLOC + MEMUNION *element; //current element + MEMUNION *returnelement; //return value + INT32 struct_count; //no of required structs + INT32 blocksize; //no of structs in block + INT32 index; //index to structure + + if (count < 1 || count > MAX_CHUNK) { + tprintf ("Invalid size %d requested of alloc_struct", count); + return NULL; + } + + // tprintf("Allocating structure of size %d\n",count); + //no of MEMUNIONS-1 + struct_count = (count - 1) / sizeof (MEMUNION); + if (struct_count < MAX_STRUCTS) { + //can do fixed sizes + #ifdef COUNTING_CLASS_STRUCTURES + if (name != NULL) { + index = identify_struct_owner (struct_count, name); + if (index < MAX_CLASSES) + owner_counts[struct_count][index]++; + } + #endif + //head of freelist + returnelement = free_structs[struct_count]; + if (returnelement == NULL) { + //need a new block + //get one + element = (MEMUNION *) new_struct_block (); + if (element == NULL) { + tprintf ("No memory to satisfy request for %d", (int) count); + return NULL; + } + //add to block list + element->ptr = struct_blocks[struct_count]; + struct_blocks[struct_count] = element; + blocks_in_use[struct_count]++; + element++; //free cell + returnelement = element; //going to return 1st + blocksize = STRUCT_BLOCK_SIZE / (struct_count + 1) - 1; + + for (index = 1; index < blocksize; index++) { + //make links + element->ptr = element + struct_count + 1; + element += struct_count + 1; + } + element->ptr = NULL; //end of freelist + } + //new free one + free_structs[struct_count] = returnelement->ptr; + //count number issued + structs_in_use[struct_count]++; + } + else { + //just get some + returnelement = (MEMUNION *) alloc_mem (count); + if (returnelement == NULL) { + tprintf ("No memory to satisfy request for %d", (int) count); + return NULL; + } + } + return returnelement; //free cell +#else + return malloc(count); +#endif +} + + +/********************************************************************** + * free_struct + * + * Free memory allocated by alloc_struct. The size must be supplied. + **********************************************************************/ + +DLLSYM void +free_struct ( //free a structure +void *deadstruct, //structure to free +INT32 count, //no of bytes +#if defined COUNTING_CLASS_STRUCTURES +const char *name //name of type +#else +const char * //name of type +#endif +) { +#ifdef RAYS_MALLOC + MEMUNION *end_element; //current element + MEMUNION *element; //current element + MEMUNION *prev_element; //previous element + MEMUNION *prev_block; //previous element + MEMUNION *nextblock; //next block in list + MEMUNION *block; //next block in list + INT32 struct_count; //no of required structs + INT32 index; //to structure counts + + if (count < 1 || count > MAX_CHUNK) { + tprintf ("Invalid size %d requested of free_struct", count); + return; + } + + // tprintf("Freeing structure of size %d\n",count); + //no of MEMUNIONS-1 + struct_count = (count - 1) / sizeof (MEMUNION); + + if (deadstruct == NULL) { + //not really legal + check_struct(MEMCHECKS, count); + } + else { + if (struct_count < MAX_STRUCTS) { + //can do fixed sizes + #ifdef COUNTING_CLASS_STRUCTURES + if (name != NULL) { + index = identify_struct_owner (struct_count, name); + if (index < MAX_CLASSES) { + owner_counts[struct_count][index]--; + ASSERT_HOST (owner_counts[struct_count][index] >= 0); + } + } + #endif + element = (MEMUNION *) deadstruct; + //add to freelist + element->ptr = free_structs[struct_count]; + free_structs[struct_count] = element; + //one less in use + structs_in_use[struct_count]--; + if (structs_in_use[struct_count] == 0) { + index = 0; + for (element = struct_blocks[struct_count]; + element != NULL; element = nextblock) { + //traverse and destroy + nextblock = element->ptr; + //free all the blocks + old_struct_block(element); + index++; + } + //none left any more + struct_blocks[struct_count] = NULL; + //no free structs + free_structs[struct_count] = NULL; + blocks_in_use[struct_count] = 0; + } + else if (structs_in_use[struct_count] < 0) { + tprintf ("Negative number of structs of size %d in use", + (int) count); + } + else if (structs_in_use[struct_count] < blocks_in_use[struct_count]) { + prev_block = NULL; + for (block = struct_blocks[struct_count]; + block != NULL; block = nextblock) { + nextblock = block; + index = STRUCT_BLOCK_SIZE / (struct_count + 1) - 1; + end_element = block + STRUCT_BLOCK_SIZE; + for (element = free_structs[struct_count]; + element != NULL; element = element->ptr) { + if (element > nextblock && element < end_element) { + index--; + if (index == 0) + break; + } + } + if (index == 0) { + index = STRUCT_BLOCK_SIZE / (struct_count + 1) - 1; + for (element = + free_structs[struct_count], prev_element = NULL; + element != NULL; element = element->ptr) { + if (element > nextblock && element < end_element) { + index--; + if (prev_element != NULL) + prev_element->ptr = element->ptr; + else + free_structs[struct_count] = element->ptr; + if (index == 0) + break; + } + else + prev_element = element; + } + if (prev_block != NULL) + prev_block->ptr = block->ptr; + else + struct_blocks[struct_count] = block->ptr; + nextblock = block->ptr; + blocks_in_use[struct_count]--; + //free all the blocks + old_struct_block(block); + } + else { + prev_block = block; + //traverse and destroy + nextblock = block->ptr; + } + } + } + } + else + free_mem(deadstruct); //free directly + } +#else + free(deadstruct); +#endif +} + + +/********************************************************************** + * alloc_mem_p + * + * Allocate permanent space which will never be returned. + * This space is allocated from the top end of a memory block to + * avoid the fragmentation which would result from alternate use + * of alloc_mem for permanent and temporary blocks. + **********************************************************************/ + +//#ifdef __UNIX__ +//#pragma OPT_LEVEL 0 +//#endif +DLLSYM void *alloc_mem_p( //allocate permanent space + INT32 count //block size to allocate + ) { + #ifdef RAYS_MALLOC + #ifdef TESTING_BIGSTUFF + if (main_mem.biggestblock == 0) + main_mem.init (alloc_big_mem, free_big_mem, + FIRSTSIZE, LASTSIZE, MAX_CHUNK); + #else + if (main_mem.biggestblock == 0) + main_mem.init ((void *(*)(INT32)) malloc, free, + FIRSTSIZE, LASTSIZE, MAX_CHUNK); + #endif + if (mem_mallocdepth > 0) + return main_mem.alloc_p (count, trace_caller (mem_mallocdepth)); + else + return main_mem.alloc_p (count, NULL); + #else + return malloc ((size_t) count); + #endif +} + + +/********************************************************************** + * alloc_mem + * + * Return a pointer to a buffer of count bytes aligned for any type. + **********************************************************************/ + +DLLSYM void *alloc_mem( //get some memory + INT32 count //no of bytes to get + ) { + #ifdef RAYS_MALLOC + #ifdef TESTING_BIGSTUFF + if (main_mem.biggestblock == 0) + main_mem.init (alloc_big_mem, free_big_mem, + FIRSTSIZE, LASTSIZE, MAX_CHUNK); + #else + if (main_mem.biggestblock == 0) + main_mem.init ((void *(*)(INT32)) malloc, free, + FIRSTSIZE, LASTSIZE, MAX_CHUNK); + #endif + if (mem_mallocdepth > 0) + return main_mem.alloc (count, trace_caller (mem_mallocdepth)); + else + return main_mem.alloc (count, NULL); + #else + return malloc ((size_t) count); + #endif +} + + +/********************************************************************** + * alloc_big_mem + * + * Return a pointer to a buffer of count bytes aligned for any type. + **********************************************************************/ + +DLLSYM void *alloc_big_mem( //get some memory + INT32 count //no of bytes to get + ) { + #ifdef TESTING_BIGSTUFF + if (big_mem.biggestblock == 0) + big_mem.init ((void *(*)(INT32)) malloc, free, + BIGSIZE, BIGSIZE, MAX_BIGCHUNK); + if (mem_mallocdepth > 0) + return big_mem.alloc (count, trace_caller (mem_mallocdepth)); + else + return big_mem.alloc (count, NULL); + #else + return malloc ((size_t) count); + #endif +} + + +/********************************************************************** + * alloc_big_zeros + * + * Return a pointer to a buffer of count bytes aligned for any type. + **********************************************************************/ + +DLLSYM void *alloc_big_zeros( //get some memory + INT32 count //no of bytes to get + ) { + #ifdef TESTING_BIGSTUFF + if (big_mem.biggestblock == 0) + big_mem.init ((void *(*)(INT32)) malloc, free, + BIGSIZE, BIGSIZE, MAX_BIGCHUNK); + void *buf; //return value + + if (mem_mallocdepth > 0) + buf = big_mem.alloc (count, trace_caller (mem_mallocdepth)); + else + buf = big_mem.alloc (count, NULL); + memset (buf, 0, count); + return buf; + #else + return calloc ((size_t) count, 1); + #endif +} + + +/********************************************************************** + * free_mem + * + * Free a block allocated by alloc_mem (or alloc_mem_p). + * It checks that the pointer is legal and maintains counts of the + * amount of free memory above and below the current free pointer. + **********************************************************************/ + +DLLSYM void free_mem( //free mem from alloc_mem + void *oldchunk //chunk to free + ) { + #ifdef RAYS_MALLOC + if (mem_freedepth > 0 && main_mem.callers != NULL) + main_mem.dealloc (oldchunk, trace_caller (mem_freedepth)); + else + main_mem.dealloc (oldchunk, NULL); + #else + free(oldchunk); + #endif +} + + +/********************************************************************** + * free_big_mem + * + * Free a block allocated by alloc_big_mem. + * It checks that the pointer is legal and maintains counts of the + * amount of free memory above and below the current free pointer. + **********************************************************************/ + +DLLSYM void free_big_mem( //free mem from alloc_mem + void *oldchunk //chunk to free + ) { + #ifdef TESTING_BIGSTUFF + if (mem_freedepth > 0 && main_mem.callers != NULL) + big_mem.dealloc (oldchunk, trace_caller (mem_freedepth)); + else + big_mem.dealloc (oldchunk, NULL); + #else + free(oldchunk); + #endif +} diff --git a/ccutil/memry.h b/ccutil/memry.h new file mode 100644 index 0000000000..8f65ed7f07 --- /dev/null +++ b/ccutil/memry.h @@ -0,0 +1,192 @@ +/********************************************************************** + * 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" + +#define JUSTCHECKS 0 /*just check consistency */ +#define MEMCHECKS 1 /*report totals */ +#define FULLMEMCHECKS 2 /*report on all blocks */ + +#ifdef __MSW32__ +#define NEWDELETE /*replace new & delete*/\ + void *operator new( /*fast allocator*/\ + size_t size, /*size of object*/\ + const char* file=NULL, /*filename*/\ + INT32 line=0) /*line number*/\ + {\ + return alloc_struct(size); /*simple to do*/\ + }\ +\ + void operator delete( /*fast destructor*/\ + void *deadstruct, /*thing to free*/\ + size_t size) /*sizeof struct*/\ + {\ + free_struct(deadstruct,size); /*free it*/\ + }\ + +#define NEWDELETE2(name) /*replace new & delete*/\ + void *operator new( /*fast allocator*/\ + size_t size, /*size of object*/\ + const char* file=NULL, /*filename*/\ + INT32 line=0) /*line number*/\ + {\ + return alloc_struct(size,#name); /*simple to do*/\ + }\ +\ + void operator delete( /*fast destructor*/\ + void *deadstruct, /*thing to free*/\ + size_t size) /*sizeof struct*/\ + {\ + free_struct(deadstruct,size,#name); /*free it*/\ + }\ + + +#undef NEWDELETE +#define NEWDELETE +#undef NEWDELETE2 +#define NEWDELETE2(name) + +#else +#define NEWDELETE /*replace new & delete*/\ + void *operator new( /*fast allocator*/\ + size_t size) /*size of object*/\ + {\ + return alloc_struct(size); /*simple to do*/\ + }\ +\ + void operator delete( /*fast destructor*/\ + void *deadstruct, /*thing to free*/\ + size_t size) /*sizeof struct*/\ + {\ + free_struct(deadstruct,size); /*free it*/\ + }\ + +#define NEWDELETE2(name) /*replace new & delete*/\ + void *operator new( /*fast allocator*/\ + size_t size) /*size of object*/\ + {\ + return alloc_struct(size,#name); /*simple to do*/\ + }\ +\ + void operator delete( /*fast destructor*/\ + void *deadstruct, /*thing to free*/\ + size_t size) /*sizeof struct*/\ + {\ + free_struct(deadstruct,size,#name); /*free it*/\ + }\ + +#endif +/********************************************************************** + * ALLOC_2D_ARRAY + * + * Create a dynamic 2D array. + **********************************************************************/ + +#define ALLOC_2D_ARRAY(x,y,mem,ptrs,type) /*make 2d array*/\ +{ \ + INT32 TMP_i; \ + mem=(type*)alloc_mem((x)*(y)*sizeof(type)); /*get memory*/\ + ptrs=(type**)alloc_mem((x)*sizeof(type*)); /*get ptrs*/\ + for (TMP_i=0;TMP_i<(x);TMP_i++)\ + ptrs[TMP_i]=mem+(y)*TMP_i; /*set ptrs*/\ +} \ + +/********************************************************************** + * FREE_2D_ARRAY + * + * Destroy a 2D array created by ALLOC_2D_ARRAY + **********************************************************************/ + +#define FREE_2D_ARRAY(mem,ptrs) /*free a 2D array*/\ +{ \ + free_mem(mem); /*free the memory*/\ + free_mem(ptrs); /*and the ptrs*/\ +} \ + +/********************************************************************** + * ALLOC_BIG_2D_ARRAY + * + * Create a dynamic 2D array. Use a memory allocator that allows + * allocation of bigger chunks. + **********************************************************************/ + +#define ALLOC_BIG_2D_ARRAY(x,y,mem,ptrs,type) /*make 2d array*/\ +{ \ + INT32 TMP_i; \ + mem=(type*)alloc_big_mem((x)*(y)*sizeof(type)); /*get memory*/\ + ptrs=(type**)alloc_big_mem((x)*sizeof(type*)); /*get ptrs*/\ + for (TMP_i=0;TMP_i<(x);TMP_i++)\ + ptrs[TMP_i]=mem+(y)*TMP_i; /*set ptrs*/\ +} \ + +/********************************************************************** + * FREE_BIG_2D_ARRAY + * + * Destroy a 2D array created by ALLOC_BIG_2D_ARRAY + **********************************************************************/ + +#define FREE_BIG_2D_ARRAY(mem,ptrs) /*free a 2D array*/\ +{ \ + free_big_mem(mem); /*free the memory*/\ + free_big_mem(ptrs); /*and the ptrs*/\ +} \ + +extern DLLSYM void check_mem( //check consistency + const char *string, //context message + INT8 level //level of check + ); + //allocate string +extern DLLSYM char *alloc_string(INT32 count //no of chars required + ); +extern DLLSYM void free_string( //free a string + char *string //string to free + ); + //allocate memory +extern DLLSYM void *alloc_struct ( +INT32 count, //no of chars required +const char *name = NULL //class name +); +extern DLLSYM void free_struct ( //free a structure +void *deadstruct, //structure to free +INT32 count, //no of bytes +const char *name = NULL //class name +); +extern DLLSYM void *alloc_mem_p( //allocate permanent space + INT32 count //block size to allocate + ); +extern DLLSYM void *alloc_mem( //get some memory + INT32 count //no of bytes to get + ); + //get some memory +extern DLLSYM void *alloc_big_mem(INT32 count //no of bytes to get + ); + //get some memory +extern DLLSYM void *alloc_big_zeros(INT32 count //no of bytes to get + ); +extern DLLSYM void free_mem( //free mem from alloc_mem + void *oldchunk //chunk to free + ); +extern DLLSYM void free_big_mem( //free mem from alloc_mem + void *oldchunk //chunk to free + ); +#endif diff --git a/ccutil/memryerr.h b/ccutil/memryerr.h new file mode 100644 index 0000000000..6c8a025230 --- /dev/null +++ b/ccutil/memryerr.h @@ -0,0 +1,38 @@ +/********************************************************************** + * File: memryerr.h (Formerly: memerr.h) + * Description: File defining error messages for memory functions. + * Author: Ray Smith + * Created: Mon Aug 13 12:51: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 MEMRYERR_H +#define MEMRYERR_H + +#include "errcode.h" + +const ERRCODE MEMTOOBIG = "Memory request too big"; +const ERRCODE NOMOREBLOCKS = "Max total memory blocks exceeded"; +const ERRCODE NOMOREMEM = "No more memory available from malloc"; +const ERRCODE FREENULLPTR = "Attempt to free memory NULL pointer"; +const ERRCODE NOTMALLOCMEM = +"Attempt to free memory not belonging to memalloc"; +const ERRCODE FREEILLEGALPTR = "Pointer or memory corrupted"; +const ERRCODE FREEFREEDBLOCK = "Memory block already marked free"; +const ERRCODE BADMEMCHUNKS = "Inconsistency in memory chunks"; +const ERRCODE BADSTRUCTCOUNT = "Memory incorrect freelist length"; +const ERRCODE NEGATIVE_USED_STRUCTS = +"Negative number of used memory structures"; +const ERRCODE NOTASTRING = "Illegal pointer for memory strfree"; +#endif diff --git a/ccutil/mfcpch.cpp b/ccutil/mfcpch.cpp new file mode 100644 index 0000000000..60375c9f9d --- /dev/null +++ b/ccutil/mfcpch.cpp @@ -0,0 +1,5 @@ +// mfcpch.cpp : source file that includes just the standard includes +// elist.pch will be the pre-compiled header +// mfcpch.obj will contain the pre-compiled type information + +#include "mfcpch.h" //precompiled headers diff --git a/ccutil/mfcpch.h b/ccutil/mfcpch.h new file mode 100644 index 0000000000..d9d217238d --- /dev/null +++ b/ccutil/mfcpch.h @@ -0,0 +1,37 @@ +// mfcpch.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// +// For Unix and mac the file does nothing. It needs to be included in all cpp +// files for compatibility with the PC pre-compiled header mechanism. +#ifdef __MSW32__ +#ifdef __IPEREGDLL +#define WIN32_LEAN_AND_MEAN +#define STRICT 1 +#include +#include +#else +#define VC_EXTRALEAN // Exclude rarely-used stuff from Windows headers + +#include // mfc core and standard components +#include // mfc extensions + +#ifndef _AFX_NO_OLE_SUPPORT +#include // mfc ole classes +#include // mfc ole dialog classes +#include // mfc ole automation classes +#endif // _AFX_NO_OLE_SUPPORT + +#ifndef _AFX_NO_DB_SUPPORT +#include // mfc odbc database classes +#endif // _AFX_NO_DB_SUPPORT + +#ifndef _AFX_NO_DAO_SUPPORT +#include // mfc dao database classes +#endif // _AFX_NO_DAO_SUPPORT + +#ifndef _AFX_NO_AFXCMN_SUPPORT +#include // mfc support for windows 95 common controls +#endif // _AFX_NO_AFXCMN_SUPPORT +#endif +#endif diff --git a/ccutil/ndminx.h b/ccutil/ndminx.h new file mode 100644 index 0000000000..00d1fc79fe --- /dev/null +++ b/ccutil/ndminx.h @@ -0,0 +1,25 @@ +/********************************************************************** + * 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 + +#define MAX(a,b) ( (a>b) ? a : b ) +#define MIN(a,b) ( (am_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 +#ifdef __MSW32__ +#include +#endif +#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 */ + +/*Image parameters*/ +#define MIN_IMAGE_SIZE 64 /*smallest image that will be passed */ +#define IMAGE_ROUNDING 32 /*all sizes are multiple of this */ + +#if defined(__SLOW_TIMES__) +/*Maximum timeouts of various functions (in secs)*/ +#define STARTUP_TIMEOUT 100 /*start of OCR engine */ +#define SHUTDOWN_TIMEOUT 50 /*end of OCR engine */ +#define SENDIM_TIMEOUT 50 /*send of image */ +#define RELEASE_TIMEOUT 50 /*release of semaphore */ +#define READIM_TIMEOUT 100 /*read of image */ +#define READTEXT_TIMEOUT 50 /*read of text */ +#define PROGRESS_TIMEOUT 30 /*progress every 3 seconds */ +#define BADTIMES_TIMEOUT 7 /*max lack of progress */ +#else +/*Maximum timeouts of various functions (in secs)*/ +#define STARTUP_TIMEOUT 10 /*start of OCR engine */ +#define SHUTDOWN_TIMEOUT 6 /*end of OCR engine */ +#define SENDIM_TIMEOUT 5 /*send of image */ +#define RELEASE_TIMEOUT 5 /*release of semaphore */ +#define READIM_TIMEOUT 10 /*read of image */ +#define READTEXT_TIMEOUT 5 /*read of text */ +#define PROGRESS_TIMEOUT 3 /*progress every 3 seconds */ +#define BADTIMES_TIMEOUT 7 /*max lack of progress */ +#endif + +/*language definitions are identical to RTF*/ +#define LANGE_NONE 0x0400 /*no language */ +#define LANGE_ALBANIAN 0x041c /*Albanian */ +#define LANGE_BRITISH 0x0809 /*International English */ +#define LANGE_BULGARIAN 0x0402 /*Bulgarian */ +#define LANGE_CROATIAN 0x041a /*Croatian(latin alphabet) */ +#define LANGE_CZECH 0x0405 /*Czech */ +#define LANGE_DANISH 0x0406 /*Danish */ +#define LANGE_DUTCH 0x0413 /*Dutch */ +#define LANGE_FINNISH 0x040b /*Finnish */ +#define LANGE_FRENCH 0x040c /*French */ +#define LANGE_GERMAN 0x0407 /*German */ +#define LANGE_GREEK 0x0408 /*Greek */ +#define LANGE_HUNGARIAN 0x040e /*Hungarian */ +#define LANGE_ITALIAN 0x0410 /*Italian */ +#define LANGE_JAPANESE 0x0411 /*Japanese */ +#define LANGE_KOREAN 0x0412 /*Korean */ +#define LANGE_NORWEGIAN 0x0414 /*Bokmal */ +#define LANGE_POLISH 0x0415 /*Polish */ +#define LANGE_PORTUGESE 0x0416 /*Brazilian Portugese */ +#define LANGE_ROMANIAN 0x0418 /*Romanian */ +#define LANGE_RUSSIAN 0x0419 /*Russian */ +#define LANGE_SCHINESE 0x0804 /*Simplified Chinese */ +#define LANGE_SLOVAK 0x041b /*Slovak */ +#define LANGE_SPANISH 0x040a /*Castilian */ +#define LANGE_SWEDISH 0x041d /*Swedish */ +#define LANGE_TCHINESE 0x0404 /*Traditional Chinese */ +#define LANGE_TURKISH 0x041f /*Turkish */ +#define LANGE_USENGLISH 0x0409 /*American */ + +/*font family definitions are identical to RTF*/ +#define FFAM_NONE 0 /*unknown */ +#define FFAM_ROMAN 1 /*serifed prop */ +#define FFAM_SWISS 2 /*sans-serif prop */ +#define FFAM_MODERN 3 /*fixed pitch */ + +/*character set definitions are identical to RTF*/ +#define CHSET_ANSI 0 /*Ansi efigs */ +#define CHSET_SHIFT_JIS 128 /*JIS X 0208-1990 */ +#define CHSET_KOREAN 129 /*KS C 5601-1992 */ +#define CHSET_SCHINESE 134 /*GB 2312-80 */ +#define CHSET_BIG5 136 /*Big Five */ +#define CHSET_CYRILLIC 204 /*Cyrillic */ +#define CHSET_EEUROPE 238 /*Eastern Europe */ + +/*pitch set definitions are identical to RTF*/ +#define PITCH_DEF 0 /*default */ +#define PITCH_FIXED 1 /*fixed pitch */ +#define PITCH_VAR 2 /*variable pitch */ + +/*Bitmasks for character enhancements. +OR these together for enhancement in ocr_append_char*/ +#define EUC_BOLD 1 /*bold character */ +#define EUC_ITALIC 2 /*italic char */ +#define EUC_UNDERLINE 4 /*underlined char */ +#define EUC_SUBSCRIPT 8 /*subscript char */ +#define EUC_SUPERSCRIPT 16 /*superscript char */ + +/*enum for character rendering direction*/ +enum OCR_CHAR_DIRECTION +{ + OCR_CDIR_RIGHT_LEFT, /*right to left horizontal */ + OCR_CDIR_LEFT_RIGHT, /*left to right horizontal */ + OCR_CDIR_TOP_BOTTOM, /*top to bottom vertical */ + OCR_CDIR_BOTTOM_TOP /*bottom to top vertical */ +}; + +/*enum for line rendering direction*/ +enum OCR_LINE_DIRECTION +{ + OCR_LDIR_DOWN_RIGHT, /*horizontal lines go down */ + /*vertical lines go right */ + OCR_LDIR_UP_LEFT /*horizontal lines go up */ +}; + +/*enum for newline type*/ +enum OCR_NEWLINE_TYPE +{ + OCR_NL_NONE, /*not a newline */ + OCR_NL_NEWLINE, /*this is a newline but not new para */ + OCR_NL_NEWPARA /*this is a newline and a new para */ +}; + +/*error codes that can be returned from the API functions other than OKAY and HPERR*/ +#define OCR_API_NO_MEM (-2) /*filled output buffer */ +#define OCR_API_BAD_CHAR (-3) /*whitespace sent to ocr_append_char */ +#define OCR_API_BAD_STATE (-4) /*invalid call sequence */ + +/*error codes used for passing errors back to the HP side*/ +enum OCR_ERR_CODE +{ + OCR_ERR_NONE, /*no error */ + OCR_ERR_CLEAN_EXIT, /*no error */ + OCR_ERR_NO_MEM, /*out of memory */ + OCR_ERR_FILE_READ, /*failed to read data file */ + OCR_ERR_TMP_WRITE, /*failed to write temp file */ + OCR_ERR_TMP_READ, /*failed to read temp file */ + OCR_ERR_BAD_DLL, /*missing or invalid dll subcomponent */ + OCR_ERR_BAD_EXE, /*missing or invalid exe subcomponent */ + OCR_ERR_BAD_LOAD, /*failed to load subcomponent */ + OCR_ERR_BAD_LANG, /*unable to recognize requested language */ + OCR_ERR_BAD_STATE, /*engine did call out of sequence */ + OCR_ERR_INTERNAL1, /*internal error type 1 */ + OCR_ERR_INTERNAL2, /*internal error type 1 */ + OCR_ERR_INTERNAL3, /*internal error type 1 */ + OCR_ERR_INTERNAL4, /*internal error type 1 */ + OCR_ERR_INTERNAL5, /*internal error type 1 */ + OCR_ERR_INTERNAL6, /*internal error type 1 */ + OCR_ERR_INTERNAL7, /*internal error type 1 */ + OCR_ERR_INTERNAL8, /*internal error type 1 */ + OCR_ERR_TIMEOUT /*timed out in comms */ +}; /*for calls to ocr_error */ + +/********************************************************************** + * EFONT_DESC + * Description of one font. + * The information required is basically that used by RTF. + * The name may be either a valid font on the system or the empty string. + **********************************************************************/ + +typedef struct /*font description */ +{ + UINT16 language; /*default language */ + UINT8 font_family; /*serif/not, fixed/not */ + UINT8 char_set; /*character set standard */ + UINT8 pitch; /*fixed or prop */ + INT8 name[MAX_FONT_NAME + 1]; /*plain ascii name */ +} EFONT_DESC; /*font description */ + +/********************************************************************** + * EOCR_DESC + * Description of the OCR engine provided at startup. + * The name and version may be reported to the user at some point. + * The fonts array should indicate the fonts that the OCR system + * can recognize. + **********************************************************************/ + +typedef struct /*startup info */ +{ + INT32 protocol; /*interface version */ + UINT32 font_count; /*number of fonts */ + UINT16 language; /*default language */ + UINT16 name[MAX_OCR_NAME + 1]; /*name of engine */ + /*version of engine */ + UINT16 version[MAX_OCR_VERSION + 1]; + EFONT_DESC fonts[1]; /*array of fonts */ +} EOCR_DESC; /*startup info */ + +/********************************************************************** + * ESTRIP_DESC + * Description of the image strip as it is passed to the engine. + * The image is always 1 bit, with 1=black. + * The width is always a multiple of 32, so padding is always OK. + * The height of the full image is always a multiple of 32. + * The top y coordinate is 0, and increases down. + * The top leftmost pixel is in the most significant bit of the first byte. + **********************************************************************/ + +typedef struct /*bitmap strip */ +{ + INT16 x_size; /*width in pixels */ + INT16 y_size; /*of full image */ + INT16 strip_size; /*of this strip */ + INT16 resolution; /*pixels per inch */ + UINT8 data[8]; /*image data */ +} ESTRIP_DESC; /*bitmap strip */ + +/********************************************************************** + * 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 */ +{ + 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); + +typedef struct /*output header */ +{ + INT16 count; /*chars in this buffer(0) */ + INT16 progress; /*percent complete increasing (0-100) */ + INT8 more_to_come; /*true if not last */ + 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*/ + clock_t end_time; /*time to stop if not 0*/ + EANYCODE_CHAR text[1]; /*character data */ +} ETEXT_DESC; /*output header */ + +#ifdef __MSW32__ +/********************************************************************** + * ESHM_INFO + * This data structure is used internally to the API to hold the handles + * to the operating system tools used for interprocess communications. + * API users do not access this structure directly. + **********************************************************************/ +typedef struct /*shared mem info */ +{ + HANDLE shm_hand; /*handle to shm */ + HANDLE mutex; /*alive check */ + HANDLE ocr_sem; /*ocr semaphore */ + HANDLE hp_sem; /*hp semaphore */ + void *shm_mem; /*shared memory */ + INT32 shm_size; /*size of shm */ +} ESHM_INFO; /*shared mem info */ +#elif defined (__MAC__) +typedef struct /*shared mem info */ +{ + Boolean mutex; /*alive check */ + Boolean ocr_sem; /*ocr semaphore */ + Boolean hp_sem; /*hp semaphore */ + void *shm_mem; /*shared memory */ + INT32 shm_size; /*size of shm */ + INT16 language; + + // Process management information follows: + ProcessSerialNumber IPEProcess; + ProcessSerialNumber OCRProcess; +} ESHM_INFO; +#elif defined (__UNIX__) +typedef struct /*shared mem info */ +{ + void *shm_mem; /*shared memory */ + INT32 shm_size; /*size of shm */ +} ESHM_INFO; +#endif +#endif diff --git a/ccutil/ocrshell.cpp b/ccutil/ocrshell.cpp new file mode 100644 index 0000000000..0f814e5875 --- /dev/null +++ b/ccutil/ocrshell.cpp @@ -0,0 +1,759 @@ +/********************************************************************** + * File: ocrshell.cpp + * Description: Code for the OCR side of 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 code for the OCR side of the HP OCR interface. + * The code is designed to be used with either an ANSI C or C++ compiler. + * The structures are designed to allow them to be used with any + * structure alignment upto 8. + **********************************************************************/ + +#include "mfcpch.h" +#include "ocrshell.h" +#include "tprintf.h" +#include + +#define EXTERN + +#ifdef __UNIX__ +EXTERN ESHM_INFO shm; /*info on shm */ +#define TICKS 1 +#endif + +#ifdef __MSW32__ +EXTERN ESHM_INFO shm; /*info on shm */ +#define TICKS 1000 +#endif + +#ifdef __MAC__ + +#if defined(__CFM68K__) && !defined(__USING_STATIC_LIBS__) +#pragma import on +#endif + +extern volatile ESHM_INFO shm; /*info on shm */ +extern unsigned short WaitForSingleObject( /*"C" */ + volatile Boolean &semaphore, + unsigned long timeout); +extern unsigned short ReleaseSemaphore( /*"C" */ + volatile Boolean &semaphore); +#if defined(__CFM68K__) && !defined(__USING_STATIC_LIBS__) +#pragma import reset +#endif +#define WAIT_OBJECT_0 1 +#define TICKS 60 +#endif + +typedef enum { + OCS_UNINIT, /*uninitialized */ + OCS_SETUP_SHM, /*shm setup done */ + OCS_SETUP_INFO, /*startinfo sent */ + OCS_READING_STRIPS, /*read first but more to come */ + OCS_READ_STRIPS, /*read all but no monitor yet */ + OCS_RECOGNIZING, /*OCR incomplete */ + OCS_SENDING_TEXT, /*sent buffer but more to come */ + OCS_DEAD /*disconnected */ +} OCR_STATE; + +/* forward declarations - not in .h file as not needed outside this file*/ +INT16 ocr_internal_shutdown(); /*closedown */ +INT16 wait_for_mutex(); /*wait for HP to be ready */ +INT16 wait_for_hp( /*wait for semaphore */ + INT32 timeout /*in seconds */ + ); +INT16 release_mutex(); /*release mutex */ +INT16 release_ocr(); /*release semaphore */ + +static INT32 font_count = 0; /*number of fonts */ +static INT16 lines_read = 0; /*no read in this image */ + /*current state */ +static OCR_STATE ocr_state = OCS_UNINIT; + +#ifdef __MAC__ +pascal short TerminateOCR(AppleEvent *theEvent, + AppleEvent *theReply, + long refCon) { + ocr_internal_shutdown(); + ExitToShell(); + +} +#endif + +/********************************************************************** + * ocr_open_shm + * + * Attempt to connect to the shared memory segment and semaphores used + * in talking to the OCR engine. Called from OCR engine. + * The parameters are the command line arguments in order. + **********************************************************************/ +#ifdef __MAC__ +INT16 +ocr_open_shm (UINT16 * lang) +#else +INT16 +ocr_open_shm ( /*open the shm */ +const char *shm_h, /*handle of shm */ +const char *shm_size, /*size of shm segment */ +const char *mutex_h, /*hp mutex */ +const char *ocr_h, /*ocr semaphore */ +const char *hp_h, /*hp semaphore */ +const char *lang_str, /*language */ +UINT16 * lang /*required language */ +) +#endif +{ + font_count = 0; /*no fonts yet */ + #ifdef __MAC__ + if (shm.OCRProcess.lowLongOfPSN && shm.OCRProcess.highLongOfPSN) + return HPERR; + *lang = shm.language; + GetCurrentProcess (&shm.OCRProcess); + if (WakeUpProcess (&shm.IPEProcess)) + ExitToShell(); + AEInstallEventHandler (kCoreEventClass, kAEQuitApplication, + (AEEventHandlerUPP) TerminateOCR, 0, FALSE); + #else + if (lang != NULL) + /*get language */ + *lang = (UINT16) strtol (lang_str, NULL, 10); + #endif + if (ocr_state != OCS_UNINIT) { + ocr_error(OCR_ERR_BAD_STATE); + return OCR_API_BAD_STATE; /*incorrect state */ + } + #ifdef __MSW32__ + shm.shm_size = strtol (shm_size, NULL, 10); + /*convert to handle */ + shm.shm_hand = (HANDLE) strtol (shm_h, NULL, 10); + shm.shm_mem = MapViewOfFile (shm.shm_hand, FILE_MAP_WRITE, 0, 0, 0); + if (shm.shm_mem == NULL) + return HPERR; /*failed */ + /*convert to handle */ + shm.mutex = (HANDLE) strtol (mutex_h, NULL, 10); + /*convert to handle */ + shm.ocr_sem = (HANDLE) strtol (ocr_h, NULL, 10); + /*convert to handle */ + shm.hp_sem = (HANDLE) strtol (hp_h, NULL, 10); + #endif + + ocr_state = OCS_SETUP_SHM; /*record state */ + return OKAY; + +} + + +/********************************************************************** + * ocr_error + * + * Inform the HP side of an error. + * The OCR engine should do any cleanup of its own and exit aferwards. + * Uses the current state to determine how to send it and cleanup. + **********************************************************************/ + +void ocr_error( /*send an error code */ + OCR_ERR_CODE code /*error code */ + ) { + ESTRIP_DESC *strip = (ESTRIP_DESC *) shm.shm_mem; + /*strip info */ + ETEXT_DESC *monitor = (ETEXT_DESC *) shm.shm_mem; + /*progress monitor */ + + switch (ocr_state) { + case OCS_UNINIT: /*uninitialized */ + case OCS_DEAD: /*uninitialized */ + return; /*can't do anything else */ + case OCS_SETUP_SHM: /*shm setup done */ + if (font_count < 1) + font_count = 1; + ocr_setup_startinfo_ansi (-code, LANGE_NONE, "", ""); + /*report error */ + break; + case OCS_SETUP_INFO: /*startinfo sent */ + if (ocr_get_first_image_strip () == NULL) + break; /*disconnected */ + case OCS_READING_STRIPS: /*read first but more to come */ + strip->x_size = -code; /*report error */ + release_ocr(); /*send ack */ + release_mutex(); + break; + case OCS_READ_STRIPS: /*read all but no monitor yet */ + monitor->count = 0; /*chars in this buffer(-1) */ + monitor->progress = 0; /*percent complete increasing (0-100) */ + /*text not complete */ + monitor->more_to_come = FALSE; + monitor->ocr_alive = TRUE; /*ocr sets to 1, hp 0 */ + monitor->err_code = -code; /*report error */ + monitor->cancel = FALSE; /*0=continue, 1=cancel */ + release_ocr(); /*send ack */ + break; + case OCS_RECOGNIZING: /*OCR incomplete */ + case OCS_SENDING_TEXT: /*sent buffer but more to come */ + monitor->err_code = -code; /*report error */ + release_ocr(); /*send ack */ + } + ocr_internal_shutdown(); /*get ready for exit */ +} + + +/********************************************************************** + * ocr_append_fontinfo + * + * Initialize one of the font descriptors. + **********************************************************************/ + +INT16 ocr_append_fontinfo( /*put info into shm */ + UINT16 language, /*default language */ + UINT8 font_family, /*serif/not, fixed/not */ + UINT8 char_set, /*character set standard */ + UINT8 pitch, /*fixed or prop */ + const char *name /*plain ascii name */ + ) { + EOCR_DESC *desc; /*ocr engine info */ + int index; /*char index */ + INT32 font_index; /*which font */ + + if (ocr_state != OCS_SETUP_SHM) { + ocr_error(OCR_ERR_BAD_STATE); + return OCR_API_BAD_STATE; /*incorrect state */ + } + + /*turn to right type */ + desc = (EOCR_DESC *) shm.shm_mem; + if (font_count > + (INT32) ((shm.shm_size - sizeof (EOCR_DESC)) / sizeof (EFONT_DESC))) + return OCR_API_NO_MEM; /*insufficient space */ + font_index = font_count++; /*add a font */ + /*setup structure */ + desc->fonts[font_index].language = language; + /*setup structure */ + desc->fonts[font_index].font_family = font_family; + /*setup structure */ + desc->fonts[font_index].char_set = char_set; + /*setup structure */ + desc->fonts[font_index].pitch = pitch; + if (name != NULL) { + for (index = 0; index < MAX_FONT_NAME && name[index] != 0; index++) + desc->fonts[font_index].name[index] = name[index]; + } + else + index = 0; + desc->fonts[font_index].name[index] = 0; + return OKAY; +} + + +/********************************************************************** + * ocr_setup_startinfo + * + * Setup the info on the OCR engine. Uses 16 bit chars to name the + * engine. + **********************************************************************/ + +INT16 ocr_setup_startinfo( /*put info into shm */ + INT32 protocol, /*interface version */ + UINT16 language, /*default language */ + const UINT16 *name, /*name of engine */ + const UINT16 *version /*version of engine */ + ) { + EOCR_DESC *desc; /*ocr engine info */ + int index; /*char index */ + INT16 result; /*from open */ + + if (ocr_state != OCS_SETUP_SHM || font_count < 1) { + ocr_error(OCR_ERR_BAD_STATE); + return OCR_API_BAD_STATE; /*incorrect state */ + } + + /*turn to right type */ + desc = (EOCR_DESC *) shm.shm_mem; + desc->protocol = protocol; /*setup structure */ + desc->font_count = font_count; + desc->language = language; + for (index = 0; index < MAX_OCR_NAME && name[index] != 0; index++) + desc->name[index] = name[index]; + desc->name[index] = 0; + for (index = 0; index < MAX_OCR_VERSION && version[index] != 0; index++) + desc->version[index] = version[index]; + desc->version[index] = 0; + + result = release_ocr (); + if (result != OKAY) + return result; + ocr_state = OCS_SETUP_INFO; /*record state */ + return OKAY; +} + + +/********************************************************************** + * ocr_setup_startinfo_ansi + * + * Setup the info on the OCR engine. Uses 8 bit chars to name the + * engine. + **********************************************************************/ + +INT16 ocr_setup_startinfo_ansi( /*put info into shm */ + UINT32 protocol, /*interface version */ + UINT16 language, /*default language */ + const char *name, /*name of engine */ + const char *version /*version of engine */ + ) { + EOCR_DESC *desc; /*ocr engine info */ + int index; /*char index */ + INT16 result; /*from open */ + + if (ocr_state != OCS_SETUP_SHM || font_count < 1) { + ocr_error(OCR_ERR_BAD_STATE); + return OCR_API_BAD_STATE; /*incorrect state */ + } + + /*turn to right type */ + desc = (EOCR_DESC *) shm.shm_mem; + desc->protocol = protocol; /*setup structure */ + desc->font_count = font_count; + desc->language = language; + for (index = 0; index < MAX_OCR_NAME && name[index] != 0; index++) + desc->name[index] = name[index]; + desc->name[index] = 0; + for (index = 0; index < MAX_OCR_VERSION && version[index] != 0; index++) + desc->version[index] = version[index]; + desc->version[index] = 0; + + result = release_ocr (); + if (result != OKAY) + return result; + ocr_state = OCS_SETUP_INFO; /*record state */ + return OKAY; +} + + +/********************************************************************** + * ocr_get_first_image_strip + * + * Wait for the master to send the first image strip and return a + * pointer to it. The result is NULL if it is time to exit. + **********************************************************************/ + +ESTRIP_DESC *ocr_get_first_image_strip() { /*get image strip */ + ESTRIP_DESC *strip; /*strip info */ + INT16 result; /*of wait/release */ + + if (ocr_state != OCS_SETUP_INFO) { + tprintf ("Bad state reading strip"); + ocr_error(OCR_ERR_BAD_STATE); + return NULL; /*incorrect state */ + } + + /*strip info */ + strip = (ESTRIP_DESC *) shm.shm_mem; + lines_read = 0; + + result = wait_for_mutex (); + if (result != OKAY) { + tprintf ("Mutax wait failed reading strip"); + return NULL; /*HP dead */ + } + result = release_mutex (); + if (result != OKAY) { + tprintf ("Mutax release failed reading strip"); + return NULL; /*HP dead */ + } + result = wait_for_hp (READIM_TIMEOUT); + if (result != OKAY) { + tprintf ("Wait for HP failed reading strip"); + return NULL; /*HP dead */ + } + lines_read = strip->strip_size;/*lines read so far */ + if (lines_read < strip->y_size) + /*record state */ + ocr_state = OCS_READING_STRIPS; + else + ocr_state = OCS_READ_STRIPS; + if (strip->x_size == 0 || strip->y_size == 0) + return NULL; /*end of job */ + + return strip; +} + + +/********************************************************************** + * ocr_get_next_image_strip + * + * Wait for the master to send the next image strip and return a + * pointer to it. The result is NULL if it is time to exit. + **********************************************************************/ + +ESTRIP_DESC *ocr_get_next_image_strip() { /*get image strip */ + ESTRIP_DESC *strip; /*strip info */ + INT16 result; /*of wait/release */ + + if (ocr_state != OCS_READING_STRIPS) { + ocr_error(OCR_ERR_BAD_STATE); + return NULL; /*incorrect state */ + } + + /*strip info */ + strip = (ESTRIP_DESC *) shm.shm_mem; + result = release_ocr (); + if (result != OKAY) + return NULL; /*HP dead */ + result = wait_for_hp (READIM_TIMEOUT); + if (result != OKAY) + return NULL; /*HP dead */ + /*lines read so far */ + lines_read += strip->strip_size; + if (lines_read < strip->y_size) + /*record state */ + ocr_state = OCS_READING_STRIPS; + else + ocr_state = OCS_READ_STRIPS; + + return strip; +} + + +/********************************************************************** + * ocr_setup_monitor + * + * Setup the progress monitor. Call before starting the recognize task. + **********************************************************************/ + +ETEXT_DESC *ocr_setup_monitor() { /*setup monitor */ + ETEXT_DESC *monitor; /*progress monitor */ + + /*text info */ + monitor = (ETEXT_DESC *) shm.shm_mem; + monitor->count = 0; /*chars in this buffer(-1) */ + monitor->progress = 0; /*percent complete increasing (0-100) */ + monitor->more_to_come = TRUE; /*text not complete */ + monitor->ocr_alive = TRUE; /*ocr sets to 1, hp 0 */ + monitor->err_code = 0; /*used by ocr_error */ + monitor->cancel = FALSE; /*0=continue, 1=cancel */ + + if (release_ocr () != OKAY) + return NULL; /*release failed */ + + ocr_state = OCS_RECOGNIZING; /*record state */ + return monitor; +} + + +/********************************************************************** + * ocr_char_space + * + * Return the number of chars that can be fitted into the buffer. + **********************************************************************/ + +INT32 ocr_char_space() { /*put char into shm */ + ETEXT_DESC *buf; /*text buffer */ + int result; + + /*progress info */ + buf = (ETEXT_DESC *) shm.shm_mem; + result = + (shm.shm_size - sizeof (ETEXT_DESC)) / sizeof (EANYCODE_CHAR) - + buf->count + 1; + + // while (buf->hp_alive==-1) + // Sleep(50); /*wait for HP*/ + + return result; +} + + +/********************************************************************** + * ocr_append_char + * + * Add a character to the output. Returns OKAY if successful, OCR_API_NO_MEM + * if there was insufficient room in the buffer. + **********************************************************************/ + +INT16 ocr_append_char( /*put char into shm */ + 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 (-1) */ + 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 enhancement, /*char enhancement (0) */ + OCR_CHAR_DIRECTION text_dir, /*rendering direction (OCR_CDIR_RIGHT_LEFT) */ + OCR_LINE_DIRECTION line_dir, /*line rendering direction (OCR_LDIR_DOWN_RIGHT) */ + OCR_NEWLINE_TYPE nl_type /*type of newline (if any) (OCR_NL_NONE) */ + ) { + ETEXT_DESC *buf; /*text buffer */ + int index; /*char index */ + INT16 result; /*of callback */ + + if (ocr_state != OCS_RECOGNIZING && ocr_state != OCS_SENDING_TEXT) { + ocr_error(OCR_ERR_BAD_STATE); + return OCR_API_BAD_STATE; /*incorrect state */ + } + + if (char_code == ' ' || char_code == '\n' || char_code == '\r' + || char_code == '\t') + return OCR_API_BAD_CHAR; /*illegal char */ + + /*progress info */ + buf = (ETEXT_DESC *) shm.shm_mem; + + result = + (shm.shm_size - sizeof (ETEXT_DESC)) / sizeof (EANYCODE_CHAR) - + buf->count; + if (result < 1) + return OCR_API_NO_MEM; /*insufficient room */ + + index = buf->count++; /*count of chars */ + /*setup structure */ + buf->text[index].char_code = char_code; + buf->text[index].left = left; /*setup structure */ + buf->text[index].right = right;/*setup structure */ + buf->text[index].top = top; /*setup structure */ + /*setup structure */ + buf->text[index].bottom = bottom; + /*setup structure */ + buf->text[index].font_index = font_index; + /*setup structure */ + buf->text[index].confidence = confidence; + /*setup structure */ + buf->text[index].point_size = point_size; + /*setup structure */ + buf->text[index].blanks = blanks; + if (nl_type == OCR_NL_NONE) { + if (text_dir == OCR_CDIR_TOP_BOTTOM || text_dir == OCR_CDIR_BOTTOM_TOP) + buf->text[index].formatting = (text_dir << 5) | 128; + /*setup structure */ + else + /*setup structure */ + buf->text[index].formatting = text_dir << 5; + } + else { + buf->text[index].formatting = (nl_type << 6) | (line_dir << 5); + /*setup structure */ + } + buf->text[index].formatting |= enhancement & (~EUC_FORMAT_MASK); + return OKAY; +} + + +/********************************************************************** + * ocr_send_text + * + * Send the text to the host and wait for the ack. + * Use this function after a sequence of ocr_append_char calls to + * actually sent the text to the master process. + * Set more to come TRUE if there is more text in this page, FALSE + * if the OCR engine is now ready to receive another image. + **********************************************************************/ + +INT16 ocr_send_text( /*send shm */ + BOOL8 more_to_come /*any text left */ + ) { + ETEXT_DESC *buf; /*text buffer */ + + if (ocr_state != OCS_RECOGNIZING && ocr_state != OCS_SENDING_TEXT) { + ocr_error(OCR_ERR_BAD_STATE); + return OCR_API_BAD_STATE; /*incorrect state */ + } + + /*progress info */ + buf = (ETEXT_DESC *) shm.shm_mem; + + /*setup structure */ + buf->more_to_come = more_to_come; + if (more_to_come) { + if ((buf->text[buf->count - 1].formatting >> 6) != OCR_NL_NEWLINE + && (buf->text[buf->count - 1].formatting >> 6) != OCR_NL_NEWPARA) { + /*force line end */ + buf->text[buf->count - 1].formatting &= 63; + buf->text[buf->count - 1].formatting |= OCR_NL_NEWLINE << 6; + } + } + else { + if (buf->count < 1) + ocr_append_char ('~', -1, -1, -1, -1, 0, 100, 10, 0, + 0, OCR_CDIR_RIGHT_LEFT, OCR_LDIR_DOWN_RIGHT, + OCR_NL_NEWPARA); + /*dummy character */ + else if ((buf->text[buf->count - 1].formatting >> 6) != OCR_NL_NEWPARA) { + /*force para end */ + buf->text[buf->count - 1].formatting &= 63; + buf->text[buf->count - 1].formatting |= OCR_NL_NEWPARA << 6; + } + } + + if (release_ocr () != OKAY) + return HPERR; /*release failed */ + if (wait_for_hp (READTEXT_TIMEOUT) != OKAY) + return HPERR; + if (more_to_come) { + buf->count = 0; /*setup structure */ + ocr_state = OCS_SENDING_TEXT;/*record state */ + } + else + ocr_state = OCS_SETUP_INFO; /*record state */ + return OKAY; +} + + +/********************************************************************** + * ocr_shutdown + * + * Closedown communications with the HP side and free up handles. + **********************************************************************/ + +INT16 ocr_shutdown() { /*closedown */ + #ifdef __MAC__ + shm.OCRProcess.lowLongOfPSN = kNoProcess; + shm.OCRProcess.highLongOfPSN = 0; + #endif + ocr_error(OCR_ERR_CLEAN_EXIT); /*signal exit */ + + return OKAY; +} + + +/********************************************************************** + * ocr_internal_shutdown + * + * Free up handles or whatever to clean up without attempting to communicate. + **********************************************************************/ + +INT16 ocr_internal_shutdown() { /*closedown */ + ocr_state = OCS_DEAD; /*record state */ + #ifdef __MSW32__ + if (shm.shm_mem != NULL) { + UnmapViewOfFile (shm.shm_mem); + CloseHandle (shm.shm_hand); /*no longer used */ + CloseHandle (shm.mutex); /*release handles */ + CloseHandle (shm.ocr_sem); + CloseHandle (shm.hp_sem); + shm.shm_mem = NULL; + } + #elif defined (__MAC__) + shm.OCRProcess.lowLongOfPSN = kNoProcess; + shm.OCRProcess.highLongOfPSN = 0; + #endif + return OKAY; +} + + +/********************************************************************** + * wait_for_mutex + * + * Wait for the HP side to release its mutex. + * The return value is HPERR if the HP side has terminated. + **********************************************************************/ + +INT16 wait_for_mutex() { /*wait for HP to be ready */ + INT16 result = HPERR; /*return code */ + #if defined (__MSW32__) || defined (__MAC__) + result = WaitForSingleObject (shm.mutex, (unsigned long) -1) + /*wait for thread to move */ + /*bad if timeout */ + == WAIT_OBJECT_0 ? OKAY : HPERR; + #endif + if (result != OKAY) + ocr_internal_shutdown(); + return result; +} + + +/********************************************************************** + * wait_for_hp + * + * Wait for the HP side to release its semaphore. + * The return value is HPERR if the timeout (in seconds) elapsed. + **********************************************************************/ + +INT16 wait_for_hp( /*wait for semaphore */ + INT32 timeout /*in seconds */ + ) { + INT16 result = HPERR; /*return code */ + #if defined (__MSW32__) || defined (__MAC__) + /*wait for thread to move */ + result = WaitForSingleObject (shm.hp_sem, timeout * TICKS) + /*bad if timeout */ + == WAIT_OBJECT_0 ? OKAY : HPERR; + #endif + if (result != OKAY) + ocr_internal_shutdown(); + return result; +} + + +/********************************************************************** + * release_mutex + * + * Release the HP mutex. + * The return value is OKAY if the call succeeds. + **********************************************************************/ + +INT16 release_mutex() { /*release mutex */ + INT16 result = HPERR; /*return code */ + #ifdef __MSW32__ + /*release it */ + result = ReleaseMutex (shm.mutex) ? OKAY : HPERR; + #elif defined (__MAC__) + /*release it */ + result = ReleaseSemaphore (shm.mutex) ? OKAY : HPERR; + #endif + if (result != OKAY) + ocr_internal_shutdown(); + return result; +} + + +/********************************************************************** + * release_ocr + * + * Release the OCR semaphore. + * The return value is OKAY if the call succeeds. + **********************************************************************/ + +INT16 release_ocr() { /*release semaphore */ + INT32 timeout; //time allowed + + timeout = RELEASE_TIMEOUT * TICKS; + #ifdef __MSW32__ + BOOL result = 0; //of release + do { + //release it + result = ReleaseSemaphore (shm.ocr_sem, 1, NULL); + if (result == FALSE) { + timeout -= 50; + Sleep (50); + } + } + while (result == FALSE && timeout > 0); + if (!result) + ocr_internal_shutdown(); + return OKAY; + #elif defined (__MAC__) + INT16 result = HPERR; /*return code */ + /*release it */ + result = ReleaseSemaphore (shm.ocr_sem) ? OKAY : HPERR; + + if (result != OKAY) + ocr_internal_shutdown(); + return result; + #elif defined (__UNIX__) + return 0; + #endif +} diff --git a/ccutil/ocrshell.h b/ccutil/ocrshell.h new file mode 100644 index 0000000000..4008ac2793 --- /dev/null +++ b/ccutil/ocrshell.h @@ -0,0 +1,191 @@ +/********************************************************************** + * File: ocrshell.h + * Description: Code for the OCR side of 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. + * + **********************************************************************/ + +#ifndef OCRSHELL_H +#define OCRSHELL_H + +/********************************************************************** + * This file contains code for the OCR side of the HP OCR interface. + * The code is designed to be used with either an ANSI C or C++ compiler. + * The structures are designed to allow them to be used with any + * structure alignment upto 8. + **********************************************************************/ + +#include "ocrclass.h" + +#define EUC_FORMAT_MASK 0xe0 + +/********************************************************************** + * ocr_open_shm + * + * Attempt to connect to the shared memory segment and semaphores used + * in talking to the OCR engine. Called from OCR engine. + * The parameters are the command line arguments in order. + * The final parameter is a return value indicating the user-requested + * language. The value will be LANGE_NONE if the user wishes to use + * the default. + **********************************************************************/ +#ifdef __MAC__ +INT16 ocr_open_shm(UINT16 *lang); +#else +INT16 ocr_open_shm( /*open the shm */ + const char *shm_h, /*handle of shm */ + const char *shm_size, /*size of shm segment */ + const char *mutex_h, /*hp mutex */ + const char *ocr_h, /*ocr semaphore */ + const char *hp_h, /*hp semaphore */ + const char *lang_str, /*language */ + UINT16 *lang /*required language */ + ); +#endif + +/********************************************************************** + * ocr_append_fontinfo + * + * Initialize one of the font descriptors. + **********************************************************************/ + +INT16 ocr_append_fontinfo( /*put info into shm */ + UINT16 language, /*default language */ + UINT8 font_family, /*serif/not, fixed/not */ + UINT8 char_set, /*character set standard */ + UINT8 pitch, /*fixed or prop */ + const char *name /*plain ascii name */ + ); + +/********************************************************************** + * ocr_setup_startinfo + * + * Setup the info on the OCR engine. Uses 16 bit chars to name the + * engine. + **********************************************************************/ + +INT16 ocr_setup_startinfo( /*put info into shm */ + UINT32 protocol, /*interface version */ + UINT16 language, /*default language */ + const UINT16 *name, /*name of engine */ + const UINT16 *version /*version of engine */ + ); + +/********************************************************************** + * ocr_setup_startinfo_ansi + * + * Setup the info on the OCR engine. Uses 8 bit chars to name the + * engine. + **********************************************************************/ + +INT16 ocr_setup_startinfo_ansi( /*put info into shm */ + UINT32 protocol, /*interface version */ + UINT16 language, /*default language */ + const char *name, /*name of engine */ + const char *version /*version of engine */ + ); + +/********************************************************************** + * ocr_get_first_image_strip + * + * Wait for the master to send the first image strip and return a + * pointer to it. The result is NULL if it is time to exit. + **********************************************************************/ + + /*get image strip */ +ESTRIP_DESC *ocr_get_first_image_strip(); + +/********************************************************************** + * ocr_get_next_image_strip + * + * Wait for the master to send the next image strip and return a + * pointer to it. The result is NULL if it is time to exit. + **********************************************************************/ + + /*get image strip */ +ESTRIP_DESC *ocr_get_next_image_strip(); + +/********************************************************************** + * ocr_setup_monitor + * + * Setup the progress monitor. Call before starting the recognize task. + **********************************************************************/ + +ETEXT_DESC *ocr_setup_monitor(); /*setup monitor */ + +/********************************************************************** + * ocr_char_space + * + * Return the number of chars that can be fitted into the buffer. + **********************************************************************/ + +INT32 ocr_char_space(); /*put char into shm */ + +/********************************************************************** + * ocr_append_char + * + * Add a character to the output. Returns OKAY if successful, HPERR + * if there was insufficient room in the buffer. + **********************************************************************/ + +INT16 ocr_append_char( /*put char into shm */ + 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 (-1) */ + 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 enhancement, /*char enhancement (0) */ + OCR_CHAR_DIRECTION text_dir, /*rendering direction (OCR_CDIR_RIGHT_LEFT) */ + OCR_LINE_DIRECTION line_dir, /*line rendering direction (OCR_LDIR_DOWN_RIGHT) */ + OCR_NEWLINE_TYPE nl_type /*type of newline (if any) (OCR_NL_NONE) */ + ); + +/********************************************************************** + * ocr_send_text + * + * Send the text to the host and wait for the ack. + * Use this function after a sequence of ocr_append_text calls to + * actually sent the text to the master process. + * Set more to come TRUE if there is more text in this page, FALSE + * if the OCR engine is now ready to receive another image. + **********************************************************************/ + +INT16 ocr_send_text( /*send shm */ + BOOL8 more_to_come /*any text left */ + ); + +/********************************************************************** + * ocr_shutdown + * + * Closedown communications with the HP side and free up handles. + **********************************************************************/ + +INT16 ocr_shutdown(); /*closedown */ + +/********************************************************************** + * ocr_error + * + * Inform the HP side of an error. + * The OCR engine should do any cleanup of its own and exit aferwards. + * Uses the current state to determine how to send it and cleanup. + **********************************************************************/ + +void ocr_error( /*send an error code */ + OCR_ERR_CODE code /*error code */ + ); +#endif diff --git a/ccutil/platform.h b/ccutil/platform.h new file mode 100644 index 0000000000..ccdea18525 --- /dev/null +++ b/ccutil/platform.h @@ -0,0 +1,14 @@ +// Place holder +#define DLLSYM +#ifdef __MSW32__ +#define SIGNED +#else +#define __UNIX__ +#include +#ifndef PATH_MAX +#define MAX_PATH 4096 +#else +#define MAX_PATH PATH_MAX +#endif +#define SIGNED signed +#endif diff --git a/ccutil/scanutils.cpp b/ccutil/scanutils.cpp new file mode 100644 index 0000000000..0bc6dbca8a --- /dev/null +++ b/ccutil/scanutils.cpp @@ -0,0 +1,543 @@ +// 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. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "scanutils.h" + +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) +{ + if (ch >= '0' && ch <= '9') { + return ch-'0'; + } else if (ch >= 'A' && ch <= 'Z') { + return ch-'A'+10; + } else if (ch >= 'a' && ch <= 'z') { + return ch-'a'+10; + } else { + 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)) >= 0 && d < base; 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)) >= 0; c = fgetc(s)) + v = v*10 + d; + if (c == '.') { + for (c = fgetc(s); (c != EOF) && (d = DigitValue(c)) >= 0; c = fgetc(s)) { + w = w*10 + d; + k *= 10; + } + } else if (c == 'e' || c == 'E') + printf("WARNING: Scientific Notation not supported!"); + + ungetc(c, s); + double f = static_cast(v) + + static_cast(w) / static_cast(k); + + return minus ? -f : f; +} + +double strtofloat(const char* s) +{ + int minus = 0; + int v = 0; + int d, c; + 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)) >= 0; s++) + v = v*10 + d; + if (*s == '.') { + for (++s; *s && (d = DigitValue(*s)) >= 0; s++) { + w = w*10 + d; + k *= 10; + } + } else if (*s == 'e' || *s == 'E') + printf("WARNING: Scientific Notation not supported!"); + + double f = static_cast(v) + + static_cast(w) / static_cast(k); + + return minus ? -f : f; +} + +int fscanf(FILE* stream, const char *format, ...) +{ + va_list ap; + int rv; + + va_start(ap, format); + rv = vfscanf(stream, format, ap); + va_end(ap); + + return rv; +} + +int vfscanf(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 *oarg, *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)+(LongBit()-1))/LongBit()]; + 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: + switch (ch) { + case '*': + flags |= FL_SPLAT; + break; + + case '0' ... '9': + width = (ch-'0'); + state = ST_WIDTH; + flags |= FL_WIDTH; + break; + + default: + state = ST_MODIFIERS; + p--; // Process this character again + break; + } + 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); + converted++; + // fall through + + set_integer: + if (!(flags & FL_SPLAT)) { + 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); + switch(rank) { + case RANK_INT: + *va_arg(ap, float *) = static_cast(fval); + break; + case RANK_LONG: + *va_arg(ap, double *) = static_cast(fval); + break; + } + 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; + } + *sarg++ = q; + } + if (!bail) + 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; + } + *sp++ = q; + } + if (sarg != sp) { + *sp = '\0'; // Terminate output + converted++; + } else { + bail = BAIL_EOF; + } + } + 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; + } + *sarg++ = q; + } + if (oarg != sarg) { + *sarg = '\0'; + converted++; + } else { + bail = (q <= 0) ? BAIL_EOF : BAIL_ERR; + } + break; + } + } + + if (bail == BAIL_EOF && !converted) + converted = -1; // Return EOF (-1) + + return converted; +} + +int creat(const char *pathname, mode_t mode) +{ + return open(pathname, O_CREAT | O_TRUNC | O_WRONLY, mode); +} diff --git a/ccutil/scanutils.h b/ccutil/scanutils.h new file mode 100644 index 0000000000..fcfea8ade0 --- /dev/null +++ b/ccutil/scanutils.h @@ -0,0 +1,55 @@ +// 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 SCANUTILS_H +#define SCANUTILS_H + +#ifdef EMBEDDED + +#include +#include +#include +#include + +// 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 + +#endif diff --git a/ccutil/secname.h b/ccutil/secname.h new file mode 100644 index 0000000000..5ca4e1982d --- /dev/null +++ b/ccutil/secname.h @@ -0,0 +1,9 @@ +/* Include this file in any module which needs to have conditional compilation + of sensitive code for UNLV. In INTERNAL mode SECURE_NAMES is NOT defined. + For UNLV mode it IS defined, allowing multiple modules to do conditional + compilation on the same name. +*/ + +#ifndef SECURE_NAMES +/* #define SECURE_NAMES */ +#endif diff --git a/ccutil/serialis.cpp b/ccutil/serialis.cpp new file mode 100644 index 0000000000..9e31a961fd --- /dev/null +++ b/ccutil/serialis.cpp @@ -0,0 +1,112 @@ +/********************************************************************** + * 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 "mfcpch.h" //precompiled headers +#include "serialis.h" +#include "scanutils.h" + +/* ************************************************************************** + +These are the only routines that write/read data to/from the serialisation. + +"serialise_bytes" and "de_serialise_bytes" are used to serialise NON class +items. The "make_serialise" macro generates "serialise" and "de_serialise" +member functions for the class name specified in the macro parameter. + +************************************************************************** */ + +DLLSYM void *de_serialise_bytes(FILE *f, int size) { + void *ptr; + + ptr = alloc_mem (size); + /* + printf( "De_serialising bytes\n" ); + printf( " Addr: %d Size: %d\n", int(ptr), size ); + */ + if (fread (ptr, size, 1, f) != 1) + READFAILED.error ("de_serialise_bytes", ABORT, NULL); + return ptr; +} + + +DLLSYM void serialise_bytes(FILE *f, void *ptr, int size) { + /* + printf( "Serialising bytes\n" ); + printf( " Addr: %d Size: %d\n", int(ptr), size ); + */ + if (fwrite (ptr, size, 1, f) != 1) + WRITEFAILED.error ("serialise_bytes", ABORT, NULL); +} + + +DLLSYM void serialise_INT32(FILE *f, INT32 the_int) { + if (fprintf (f, INT32FORMAT "\n", the_int) < 0) + WRITEFAILED.error ("serialise_INT32", ABORT, NULL); +} + + +DLLSYM INT32 de_serialise_INT32(FILE *f) { + INT32 the_int; + + if (fscanf (f, INT32FORMAT, &the_int) != 1) + READFAILED.error ("de_serialise_INT32", ABORT, NULL); + return the_int; +} + + +DLLSYM void serialise_FLOAT64(FILE *f, double the_float) { + if (fprintf (f, "%g\n", the_float) < 0) + WRITEFAILED.error ("serialise_FLOAT64", ABORT, NULL); +} + + +DLLSYM double de_serialise_FLOAT64(FILE *f) { + double the_float; + + if (fscanf (f, "%lg", &the_float) != 1) + READFAILED.error ("de_serialise_FLOAT64", ABORT, NULL); + return the_float; +} + + +/********************************************************************** + * reverse32 + * + * Byte swap an INT32 or UINT32. + **********************************************************************/ + +DLLSYM UINT32 reverse32( //switch endian + UINT32 num //number to fix + ) { + return (reverse16 ((UINT16) (num & 0xffff)) << 16) + | reverse16 ((UINT16) ((num >> 16) & 0xffff)); +} + + +/********************************************************************** + * reverse16 + * + * Byte swap an INT16 or UINT16. + **********************************************************************/ + +DLLSYM UINT16 reverse16( //switch endian + UINT16 num //number to fix + ) { + return ((num & 0xff) << 8) | ((num >> 8) & 0xff); +} diff --git a/ccutil/serialis.h b/ccutil/serialis.h new file mode 100644 index 0000000000..15483deebf --- /dev/null +++ b/ccutil/serialis.h @@ -0,0 +1,95 @@ +/********************************************************************** + * 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 "memry.h" +#include "errcode.h" +#include "fileerr.h" + +/* ************************************************************************** + +These are the only routines that write/read data to/from the serialisation. + +"serialise_bytes" and "de_serialise_bytes" are used to serialise NON class +items. The "make_serialise" macro generates "serialise" and "de_serialise" +member functions for the class name specified in the macro parameter. + +************************************************************************** */ + +extern DLLSYM void *de_serialise_bytes(FILE *f, int size); +extern DLLSYM void serialise_bytes(FILE *f, void *ptr, int size); +extern DLLSYM void serialise_INT32(FILE *f, INT32 the_int); +extern DLLSYM INT32 de_serialise_INT32(FILE *f); +extern DLLSYM void serialise_FLOAT64(FILE *f, double the_float); +extern DLLSYM double de_serialise_FLOAT64(FILE *f); +extern DLLSYM UINT32 reverse32( //switch endian + UINT32 num //number to fix + ); +extern DLLSYM UINT16 reverse16( //switch endian + UINT16 num //number to fix + ); + +/*********************************************************************** + QUOTE_IT MACRO DEFINITION + =========================== +Replace with "". may be an arbitrary number of tokens +***********************************************************************/ + +#define QUOTE_IT( parm ) #parm + +#define make_serialise( CLASSNAME ) \ + \ + NEWDELETE2(CLASSNAME) \ + \ +void serialise( \ + FILE* f) \ +{ \ + CLASSNAME* shallow_copy; \ + \ + shallow_copy = (CLASSNAME*) alloc_struct( sizeof( *this ) ); \ + memmove( shallow_copy, this, sizeof( *this ) ); \ + \ + shallow_copy->prep_serialise(); \ + if (fwrite( (char*) shallow_copy, sizeof( *shallow_copy ), 1, f ) != 1)\ + WRITEFAILED.error( QUOTE_IT( CLASSNAME::serialise ), \ + ABORT, NULL ); \ + \ + free_struct( shallow_copy, sizeof( *this ) ); \ + this->dump( f ); \ +} \ + \ + static CLASSNAME* de_serialise( \ + FILE* f) \ +{ \ + CLASSNAME* restored; \ + \ + restored = (CLASSNAME*) alloc_struct( sizeof( CLASSNAME ) ); \ + if (fread( (char*) restored, sizeof( CLASSNAME ), 1, f ) != 1) \ + READFAILED.error( QUOTE_IT( CLASSNAME::de_serialise ), \ + ABORT, NULL ); \ + \ + restored->de_dump( f ); \ + return restored; \ +} +#endif diff --git a/ccutil/stderr.h b/ccutil/stderr.h new file mode 100644 index 0000000000..b5e96fbe7c --- /dev/null +++ b/ccutil/stderr.h @@ -0,0 +1,26 @@ +/********************************************************************** + * 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 diff --git a/ccutil/strngs.cpp b/ccutil/strngs.cpp new file mode 100644 index 0000000000..4f85215b33 --- /dev/null +++ b/ccutil/strngs.cpp @@ -0,0 +1,209 @@ +/********************************************************************** + * 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 "mfcpch.h" //precompiled headers +#include "tprintf.h" +#include "strngs.h" + +/********************************************************************** + * STRING::operator= + * + * Assign a char* to a STRING. + **********************************************************************/ + +STRING & STRING::operator= ( //assign char* +const char *string //string to copy +) { + if (string != NULL) { + INT32 + length = strlen (string) + 1;//length of source + + if (ptr == NULL) + //get space + ptr = alloc_string (length); + //got wrong size + else if (strlen (ptr) != (UINT32) length - 1) { + free_string(ptr); //free old space + //get new space + ptr = alloc_string (length); + } + if (ptr == NULL) { + tprintf ("No memory to allocate string"); + return *this; + } + strcpy(ptr, string); //copy string + } + else { + if (ptr == NULL) + ptr = alloc_string (1); //get space + //got wrong size + else if (strlen (ptr) != 0) { + free_string(ptr); //free old space + ptr = alloc_string (1); //get new space + } + if (ptr == NULL) { + tprintf ("No memory to allocate string"); + return *this; + } + *ptr = '\0'; //copy string + } + + return *this; +} + + +/********************************************************************** + * STRING::operator+ + * + * Concatenate 2 STRINGs. + **********************************************************************/ + +STRING +STRING::operator+ ( //concatenation +const STRING & string //second string +) const +{ + INT32 length; //length of 1st op + STRING result; //concatenated string + + if (ptr == NULL) + length = 0; + else + length = strlen (ptr); + result.ptr = alloc_string (length + strlen (string.ptr) + 1); + //total length + if (result.ptr == NULL) { + tprintf ("No memory to allocate string"); + return result; + } + result.ptr[0] = '\0'; + if (ptr != NULL) + strcpy (result.ptr, ptr); + if (string.ptr != NULL) + //put together + strcpy (result.ptr + length, string.ptr); + return result; +} + + +/********************************************************************** + * STRING::operator+ + * + * Concatenate char to STRING. + **********************************************************************/ + +STRING +STRING::operator+ ( //concatenation +const char ch //char +) const +{ + INT32 length; //length of 1st op + STRING result; //concatenated string + + if (ptr == NULL) + length = 0; + else + length = strlen (ptr); + //total length + result.ptr = alloc_string (length + 2); + if (result.ptr == NULL) { + tprintf ("No memory to allocate string"); + return result; + } + if (ptr != NULL) + strcpy (result.ptr, ptr); + result.ptr[length] = ch; //put together + result.ptr[length + 1] = '\0'; + return result; +} + + +/********************************************************************** + * STRING::operator+= + * + * Concatenate 2 strings putting the result straing back in the first + **********************************************************************/ + +STRING & STRING::operator+= ( //inplace cat +const char *string //string to add +) { + INT32 + length; //length of 1st op + char * + src; //source string + + if (string == NULL || string[0] == '\0') + return *this; //unchanged + if (ptr == NULL) + length = 0; + else + length = strlen (ptr); //length of 1st op + src = ptr; //temp copy + //new length + ptr = alloc_string (length + strlen (string) + 1); + if (ptr == NULL) { + tprintf ("No memory to allocate string"); + ptr = src; + return *this; + } + if (src != NULL) { + strcpy(ptr, src); //copy old + free_string(src); //free old one + } + strcpy (ptr + length, string); //add new + return *this; +} + + +/********************************************************************** + * STRING::operatot+= + * + * Concatenate a char t a string putting the result straing back in the string + **********************************************************************/ + +STRING & STRING::operator+= ( //inplace cat +const char ch //char to add +) { + INT32 + length; //length of 1st op + char * + src; //source string + + if (ch == '\0') + return *this; //unchanged + if (ptr == NULL) + length = 0; + else + length = strlen (ptr); //length of 1st op + src = ptr; //temp copy + //new length + ptr = alloc_string (length + 2); + if (ptr == NULL) { + tprintf ("No memory to allocate string"); + ptr = src; + return *this; + } + if (src != NULL) { + strcpy(ptr, src); //copy old + free_string(src); //free old one + } + ptr[length] = ch; //add new char + ptr[length + 1] = '\0'; + return *this; +} diff --git a/ccutil/strngs.h b/ccutil/strngs.h new file mode 100644 index 0000000000..37f918092d --- /dev/null +++ b/ccutil/strngs.h @@ -0,0 +1,178 @@ +/********************************************************************** + * 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 "memry.h" +#include "serialis.h" + +class DLLSYM STRING +{ + char *ptr; //ptr to the chars + + public: + STRING() { //constructor + ptr = NULL; //empty string + } + + STRING( //classwise copy + const STRING &string) { + if (string.ptr != NULL) { + //length of source + INT32 length = strlen (string.ptr) + 1; + + //get space + ptr = alloc_string (length); + strcpy (ptr, string.ptr);//and copy it + } + else { + ptr = alloc_string (1); + if (ptr != NULL) + *ptr = '\0'; + } + } + + STRING( //contruct from char* + const char *string) { + if (string != NULL) { + //length of source + INT32 length = strlen (string) + 1; + + //get space + ptr = alloc_string (length); + if (ptr != NULL) + strcpy(ptr, string); //and copy it + } + else { + ptr = alloc_string (1); + if (ptr != NULL) + *ptr = '\0'; + } + } + + ~STRING () { //destructor + if (ptr != NULL) + free_string(ptr); //give it back + } + + char &operator[] ( //access function + INT32 index) const //string index + { + return ptr[index]; //no bounds checks + } + + BOOL8 contains( //char in string? + const char c) const { + if ((ptr == NULL) || ((c != '\0') && strchr (ptr, c) == NULL)) + return FALSE; + else + return TRUE; + } + + INT32 length() const { //string length + if (ptr != NULL) + return strlen (ptr); + else + return 0; + } + + const char *string() const { //ptr to string + return ptr; + } + + BOOL8 operator== ( //string equality + const STRING & string) const + { + if (ptr != NULL && string.ptr != NULL) + return strcmp (ptr, string.ptr) == 0; + else + return (ptr == NULL || *ptr == '\0') + && (string.ptr == NULL || *(string.ptr) == '\0'); + } + + BOOL8 operator!= ( //string equality + const STRING & string) const + { + if (ptr != NULL && string.ptr != NULL) + return strcmp (ptr, string.ptr) != 0; + else + return !((ptr == NULL || *ptr == '\0') + && (string.ptr == NULL || *(string.ptr) == '\0')); + } + + BOOL8 operator!= ( //string equality + const char *string) const + { + if (ptr != NULL && string != NULL) + return strcmp (ptr, string) != 0; + else + return !((ptr == NULL || *ptr == '\0') + && (string == NULL || *string == '\0')); + } + + STRING & operator= ( //assignment + const char *string); //of char* + + STRING & operator= ( //assignment + const STRING & string) { //of string + *this = string.ptr; //as for char* + return *this; + } + + STRING operator+ ( //concatenation + const STRING & string) const; + + STRING operator+ ( //char concatenation + const char ch) const; + + STRING & operator+= ( //inplace cat + const char *string); + STRING & operator+= ( //inplace cat + const STRING & string) { + *this += string.ptr; + return *this; + } + + STRING & operator+= ( //inplace char cat + const char ch); + + void prep_serialise() { //set ptrs to counts + ptr = (char *) (length () + 1); + } + + void dump( //write external bits + FILE *f) { + serialise_bytes (f, (void *) ptr, (int) (length () + 1)); + } + + void de_dump( //read external bits + FILE *f) { + char *instring; //input from read + + instring = (char *) de_serialise_bytes (f, (ptrdiff_t) ptr); + ptr = NULL; + *this = instring; + free_mem(instring); + } + + make_serialise (STRING) +}; +#endif diff --git a/ccutil/tessclas.h b/ccutil/tessclas.h new file mode 100644 index 0000000000..2475fd043e --- /dev/null +++ b/ccutil/tessclas.h @@ -0,0 +1,135 @@ +#ifndef TESSCLAS_H +#define TESSCLAS_H 1 + +#define SPLINESIZE 23 /*max spline parts to a line */ + +#define TBLOBFLAGS 4 /*No of flags in a blob */ +#define MAX_WO_CLASSES 3 +#define EDGEPTFLAGS 4 /*concavity,length etc. */ + +typedef struct +{ + double a; /*x squared */ + double b; /*x */ + double c; /*constant */ +} QUAD_SPEC; /*definiton of quadratic */ + +typedef struct +{ + int segments; /*no of spline segments */ + int xstarts[SPLINESIZE]; /*start x coords */ + QUAD_SPEC quads[SPLINESIZE]; /*quadratic sections */ +} SPLINE_SPEC; /*quadratic spline */ + +typedef struct +{ + short x; /*absolute x coord */ + short y; /*absolute y coord */ +} TPOINT; +typedef TPOINT VECTOR; /*structure for coordinates */ + +typedef struct +{ + char dx; /*compact vectors */ + char dy; +} BYTEVEC; + +typedef struct edgeptstruct +{ + TPOINT pos; /*position */ + VECTOR vec; /*vector to next point */ + char flags[EDGEPTFLAGS]; /*concavity, length etc */ + struct edgeptstruct *next; /*anticlockwise element */ + struct edgeptstruct *prev; /*clockwise element */ +} EDGEPT; /*point on expanded outline */ + +typedef struct blobstruct +{ + struct olinestruct *outlines; /*list of outlines in blob */ + char flags[TBLOBFLAGS]; /*blob flags */ + char correct; /*correct text */ + char guess; /*best guess */ + /*quickie classification */ + unsigned char classes[MAX_WO_CLASSES]; + /*quickie ratings */ + unsigned char values[MAX_WO_CLASSES]; + struct blobstruct *next; /*next blob in block */ +} TBLOB; /*blob structure */ + +typedef struct olinestruct +{ + TPOINT topleft; /*top left of loop */ + TPOINT botright; /*bottom right of loop */ + TPOINT start; /*start of loop */ + BYTEVEC *compactloop; /*ptr to compacted loop */ + EDGEPT *loop; /*edgeloop */ + void *node; /*1st node on outline */ + struct olinestruct *next; /*next at this level */ + struct olinestruct *child; /*inner outline */ +} TESSLINE; /*outline structure */ + +typedef struct wordstruct +{ + struct textrowstruct *row; /*row it came from */ + char *correct; /*correct word string */ + char *guess; /*guess word string */ + TBLOB *blobs; /*blobs in word */ + int blanks; /*blanks before word */ + int blobcount; /*no of blobs in word */ + struct wordstruct *next; /*next word */ +} TWERD; /*word structure */ + +typedef struct textrowstruct +{ + int blobcount; /** count of blobs in row. **/ + TBLOB *blobs; /*list of blobs in row */ + TWERD *words; /*list of words in row */ + int mean_y; /** y coordinate of centre of row **/ + int max_y; /** y coordinate of top of row **/ + int min_y; /** y coordinate of bottom of row **/ + SPLINE_SPEC xheight; /*top of row */ + SPLINE_SPEC baseline; /*bottom of row */ + float descdrop; /*descender drop */ + float ascrise; /*ascender rise */ + float lineheight; /*average xheight-baseline */ + int kerning; /*kerning of row */ + int space; /*spacing of row */ + float space_threshold; /*Bayesian space limit */ + int p_spaced; /*proportinal flag */ + int b_space; /*block spacing */ + int b_kern; /*block kerning */ + struct textrowstruct *next; /*next row in block */ +} TEXTROW; + +typedef struct blockstruct /** list of coordinates **/ +{ + TBLOB *blobs; /*blobs in block */ + TEXTROW *rows; /*rows in block */ + int blobcount; /*no of blobs */ + short xmin; + short xmax; + short ymin; + short ymax; + char type; /** block type **/ + char p_spaced; /** flag to show propertianal spacing **/ + short rowcount; /** number of rows **/ + short leading; /** space between rows **/ + short kerning; /** space between characters **/ + short space; /** distance between char centres **/ + short minwidth; /*min width of char in block */ + short p_size; /** point size of text **/ + short l_margin; /** posn of left margin **/ + short italic; /** flag to show italic block **/ + short spurious; /** percentage of spurious characters **/ + struct blockstruct *next; /*next text block */ +} TEXTBLOCK; /*block from image */ + +/********************************************************************** + * iterate_blobs + * + * Visit all the words in a list using a local variable. + **********************************************************************/ + +#define iterate_blobs(blob,blobs) \ +for (blob = blobs; blob != NULL; blob = blob->next) +#endif diff --git a/ccutil/tprintf.cpp b/ccutil/tprintf.cpp new file mode 100644 index 0000000000..4d6aaf0851 --- /dev/null +++ b/ccutil/tprintf.cpp @@ -0,0 +1,136 @@ +/********************************************************************** + * 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 "mfcpch.h" //precompiled headers +#include +#include +#include "strngs.h" +#include "varable.h" +#include "debugwin.h" +//#include "ipeerr.h" +#include "tprintf.h" + +#define MAX_MSG_LEN 1024 + +#define EXTERN +#ifdef __MSW32__ +DLLSYM STRING_VAR (debug_file, "tesseract.log", "File to send tprintf output to"); +#else +DLLSYM STRING_VAR (debug_file, "", "File to send tprintf output to"); +#endif +DLLSYM BOOL_VAR (debug_window_on, FALSE, +"Send tprintf to window unless file set"); + +DLLSYM void +tprintf ( //Trace printf +const char *format, ... //special message +) { + va_list args; //variable args + static FILE *debugfp = NULL; //debug file + //debug window + static DEBUG_WIN *debugwin = NULL; + INT32 offset = 0; //into message + static char msg[MAX_MSG_LEN + 1]; + + va_start(args, format); //variable list + #ifdef __MSW32__ + //Format into msg + offset += _vsnprintf (msg + offset, MAX_MSG_LEN - offset, format, args); + #else + //Format into msg + offset += vsprintf (msg + offset, format, args); + #endif + va_end(args); + + if (debugfp == NULL && strlen (debug_file.string ()) > 0) + debugfp = fopen (debug_file.string (), "w"); + else if (debugfp != NULL && strlen (debug_file.string ()) == 0) { + fclose(debugfp); + debugfp = NULL; + } + if (debugfp != NULL) + fprintf (debugfp, "%s", msg); + else { + + if (debug_window_on) { + if (debugwin == NULL) + //in pixels + debugwin = new DEBUG_WIN ("Debug Window", DEBUG_WIN_XPOS, DEBUG_WIN_YPOS, + //in pixels + DEBUG_WIN_XSIZE, DEBUG_WIN_YSIZE, + debug_lines); + debugwin->dprintf (msg); + } + else { + #ifdef __UNIX__ + // output to stderr - like it used to + fprintf (stderr, "%s", msg); + #endif + + #ifdef __MSW32__ + TRACE ("%s", msg); //Visual C++2.0 macro + #endif + #ifdef __MAC__ + printf ("%s", msg); //Visual C++2.0 macro + #endif + } + } +} + + +/************************************************************************* + * pause_continue() + * UI for a debugging pause - to see an intermediate state + * Returns TRUE to continue as normal to the next pause in the current mode; + * FALSE to quit the current pausing mode. + *************************************************************************/ + +DLLSYM BOOL8 + //special message +pause_continue (const char *format, ... +) { + va_list args; //variable args + char msg[1000]; + STRING str = STRING ("DEBUG PAUSE:\n"); + + va_start(args, format); //variable list + vsprintf(msg, format, args); //Format into msg + va_end(args); + + #ifdef GRAPHICS_DISABLED + // No interaction allowed -> simply go on + return true; + #else + + #ifdef __UNIX__ + printf ("%s\n", msg); + printf ("Type \"c\" to cancel, anything else to continue: "); + char c = getchar (); + return (c != 'c'); + #endif + + #ifdef __MSW32__ + str += + STRING (msg) + STRING ("\nUse OK to continue, CANCEL to stop pausing"); + // return AfxMessageBox( str.string(), MB_OKCANCEL ) == IDOK; + return::MessageBox (NULL, msg, "IMGAPP", + MB_APPLMODAL | MB_OKCANCEL) == IDOK; + #endif + + #endif +} diff --git a/ccutil/tprintf.h b/ccutil/tprintf.h new file mode 100644 index 0000000000..81a9783c31 --- /dev/null +++ b/ccutil/tprintf.h @@ -0,0 +1,35 @@ +/********************************************************************** + * 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. + * + **********************************************************************/ + +#ifndef TPRINTF_H +#define TPRINTF_H + +#include "varable.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"); + +DLLSYM void tprintf ( //Trace printf +const char *format, ... //special message +); + //special message +DLLSYM BOOL8 pause_continue (const char *format, ... +); +#endif diff --git a/ccutil/unichar.cpp b/ccutil/unichar.cpp new file mode 100644 index 0000000000..6450e08926 --- /dev/null +++ b/ccutil/unichar.cpp @@ -0,0 +1,144 @@ +/////////////////////////////////////////////////////////////////////// +// 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" + +#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; utf8_str[len] != 0 && len < UNICHAR_LEN; ++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 += *src++; + uni <<= 6; + case 3: + uni += *src++; + uni <<= 6; + case 2: + uni += *src++; + uni <<= 6; + case 1: + uni += *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, + 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, + 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[*utf8_str]; +} diff --git a/ccutil/unichar.h b/ccutil/unichar.h new file mode 100644 index 0000000000..b73d401616 --- /dev/null +++ b/ccutil/unichar.h @@ -0,0 +1,76 @@ +/////////////////////////////////////////////////////////////////////// +// 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 THIRD_PARTY_TESSERACT_CCUTIL_UNICHAR_H__ +#define THIRD_PARTY_TESSERACT_CCUTIL_UNICHAR_H__ + +#include + +// 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 4 + +// 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); + + 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 // THIRD_PARTY_TESSERACT_CCUTIL_UNICHAR_H__ diff --git a/ccutil/varable.cpp b/ccutil/varable.cpp new file mode 100644 index 0000000000..9e8f077829 --- /dev/null +++ b/ccutil/varable.cpp @@ -0,0 +1,695 @@ +/********************************************************************** + * File: varable.c (Formerly variable.c) + * Description: Initialization and setting of VARIABLEs. + * 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 "mfcpch.h" //precompiled headers +#include +#include +#include +#include "tprintf.h" +//#include "ipeerr.h" +#include "varable.h" +#include "scanutils.h" + +#define PLUS '+' //flag states +#define MINUS '-' +#define EQUAL '=' + +CLISTIZE (INT_VARIABLE) +CLISTIZE (BOOL_VARIABLE) CLISTIZE (STRING_VARIABLE) CLISTIZE (double_VARIABLE) +INT_VAR_FROM +INT_VARIABLE::copy; +INT_VARIABLE_CLIST +INT_VARIABLE::head; //global definition +INT_VAR_TO +INT_VARIABLE::replace; +BOOL_VAR_FROM +BOOL_VARIABLE::copy; +BOOL_VARIABLE_CLIST +BOOL_VARIABLE::head; //global definition +BOOL_VAR_TO +BOOL_VARIABLE::replace; +STRING_VAR_FROM +STRING_VARIABLE::copy; +STRING_VARIABLE_CLIST +STRING_VARIABLE::head; //global definition +STRING_VAR_TO +STRING_VARIABLE::replace; +double_VAR_FROM +double_VARIABLE::copy; +double_VARIABLE_CLIST +double_VARIABLE::head; //global definition +double_VAR_TO +double_VARIABLE::replace; + +/********************************************************************** + * INT_VAR_FROM::INT_VAR_FROM + * + * Constructor to copy the list to a temporary location while the + * list head gets constructed. + **********************************************************************/ + +INT_VAR_FROM::INT_VAR_FROM() { //constructor + INT_VARIABLE_C_IT start_it = &INT_VARIABLE::head; + INT_VARIABLE_C_IT end_it = &INT_VARIABLE::head; + + if (!start_it.empty ()) { + while (!end_it.at_last ()) + end_it.forward (); + //move to copy + list.assign_to_sublist (&start_it, &end_it); + } +} + + +/********************************************************************** + * INT_VAR_TO::INT_VAR_TO + * + * Constructor to copy the list back to its rightful place. + **********************************************************************/ + +INT_VAR_TO::INT_VAR_TO() { //constructor + INT_VARIABLE_C_IT start_it = &INT_VARIABLE::copy.list; + INT_VARIABLE_C_IT end_it = &INT_VARIABLE::copy.list; + + if (!start_it.empty ()) { + while (!end_it.at_last ()) + end_it.forward (); + INT_VARIABLE::head.assign_to_sublist (&start_it, &end_it); + } +} + + +/********************************************************************** + * INT_VARIABLE::INT_VARIABLE + * + * Constructor for INT_VARIABLE. Add the variable to the static list. + **********************************************************************/ + +INT_VARIABLE::INT_VARIABLE( //constructor + INT32 v, //the variable + const char *vname, //of variable + const char *comment //info on variable + ) { + INT_VARIABLE_C_IT it = &head; //list iterator + + //tprintf("Constructing %s\n",vname); + set_value(v); //set the value + name = vname; //strings must be static + info = comment; + it.add_before_stay_put (this); //add it to stack +} + + +INT_VARIABLE::~INT_VARIABLE ( //constructor +) { + INT_VARIABLE_C_IT it = &head; //list iterator + + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) + if (it.data () == this) + it.extract (); +} + + +/********************************************************************** + * INT_VARIABLE::get_head + * + * Get the head of the list of the variables. + **********************************************************************/ + +INT_VARIABLE_CLIST *INT_VARIABLE::get_head() { //access to static + return &head; +} + + +/********************************************************************** + * INT_VARIABLE::print + * + * Print the entire list of INT_VARIABLEs. + **********************************************************************/ + +void INT_VARIABLE::print( //print full list + FILE *fp //file to print on + ) { + INT_VARIABLE_C_IT it = &head; //list iterator + INT_VARIABLE *elt; //current element + + if (fp == stdout) { + tprintf ("#Variables of type INT_VARIABLE:\n"); + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + elt = it.data (); + tprintf ("%s %d #%s\n", elt->name, elt->value, elt->info); + } + } + else { + fprintf (fp, "#Variables of type INT_VARIABLE:\n"); + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + elt = it.data (); + fprintf (fp, "%s " INT32FORMAT " #%s\n", elt->name, elt->value, + elt->info); + } + } +} + + +/********************************************************************** + * BOOL_VAR_FROM::BOOL_VAR_FROM + * + * Constructor to copy the list to a temporary location while the + * list head gets constructed. + **********************************************************************/ + +BOOL_VAR_FROM::BOOL_VAR_FROM() { //constructor + BOOL_VARIABLE_C_IT start_it = &BOOL_VARIABLE::head; + BOOL_VARIABLE_C_IT end_it = &BOOL_VARIABLE::head; + + if (!start_it.empty ()) { + while (!end_it.at_last ()) + end_it.forward (); + //move to copy + list.assign_to_sublist (&start_it, &end_it); + } +} + + +/********************************************************************** + * BOOL_VAR_TO::BOOL_VAR_TO + * + * Constructor to copy the list back to its rightful place. + **********************************************************************/ + +BOOL_VAR_TO::BOOL_VAR_TO() { //constructor + BOOL_VARIABLE_C_IT start_it = &BOOL_VARIABLE::copy.list; + BOOL_VARIABLE_C_IT end_it = &BOOL_VARIABLE::copy.list; + + if (!start_it.empty ()) { + while (!end_it.at_last ()) + end_it.forward (); + BOOL_VARIABLE::head.assign_to_sublist (&start_it, &end_it); + } +} + + +/********************************************************************** + * BOOL_VARIABLE::BOOL_VARIABLE + * + * Constructor for BOOL_VARIABLE. Add the variable to the static list. + **********************************************************************/ + +BOOL_VARIABLE::BOOL_VARIABLE( //constructor + BOOL8 v, //the variable + const char *vname, //of variable + const char *comment //info on variable + ) { + BOOL_VARIABLE_C_IT it = &head; //list iterator + + //tprintf("Constructing %s\n",vname); + set_value(v); //set the value + name = vname; //strings must be static + info = comment; + it.add_before_stay_put (this); //add it to stack + +} + + +/********************************************************************** + * BOOL_VARIABLE::BOOL_VARIABLE + * + * Constructor for BOOL_VARIABLE. Add the variable to the static list. + **********************************************************************/ + +BOOL_VARIABLE::~BOOL_VARIABLE () { + BOOL_VARIABLE_C_IT it = &head; //list iterator + + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) + if (it.data () == this) + it.extract (); +} + + +/********************************************************************** + * BOOL_VARIABLE::get_head + * + * Get the head of the list of the variables. + **********************************************************************/ + +BOOL_VARIABLE_CLIST *BOOL_VARIABLE::get_head() { //access to static + return &head; +} + + +/********************************************************************** + * BOOL_VARIABLE::print + * + * Print the entire list of BOOL_VARIABLEs. + **********************************************************************/ + +void BOOL_VARIABLE::print( //print full list + FILE *fp //file to print on + ) { + BOOL_VARIABLE_C_IT it = &head; //list iterator + BOOL_VARIABLE *elt; //current element + + if (fp == stdout) { + tprintf ("#Variables of type BOOL_VARIABLE:\n"); + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + elt = it.data (); + tprintf ("%s %c #%s\n", + elt->name, elt->value ? 'T' : 'F', elt->info); + } + } + else { + fprintf (fp, "#Variables of type BOOL_VARIABLE:\n"); + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + elt = it.data (); + fprintf (fp, "%s %c #%s\n", + elt->name, elt->value ? 'T' : 'F', elt->info); + } + } +} + + +/********************************************************************** + * STRING_VAR_FROM::STRING_VAR_FROM + * + * Constructor to copy the list to a temporary location while the + * list head gets constructed. + **********************************************************************/ + +STRING_VAR_FROM::STRING_VAR_FROM() { //constructor + STRING_VARIABLE_C_IT start_it = &STRING_VARIABLE::head; + STRING_VARIABLE_C_IT end_it = &STRING_VARIABLE::head; + + if (!start_it.empty ()) { + while (!end_it.at_last ()) + end_it.forward (); + //move to copy + list.assign_to_sublist (&start_it, &end_it); + } +} + + +/********************************************************************** + * STRING_VAR_TO::STRING_VAR_TO + * + * Constructor to copy the list back to its rightful place. + **********************************************************************/ + +STRING_VAR_TO::STRING_VAR_TO() { //constructor + STRING_VARIABLE_C_IT start_it = &STRING_VARIABLE::copy.list; + STRING_VARIABLE_C_IT end_it = &STRING_VARIABLE::copy.list; + + if (!start_it.empty ()) { + while (!end_it.at_last ()) + end_it.forward (); + STRING_VARIABLE::head.assign_to_sublist (&start_it, &end_it); + } +} + + +/********************************************************************** + * STRING_VARIABLE::STRING_VARIABLE + * + * Constructor for STRING_VARIABLE. Add the variable to the static list. + **********************************************************************/ + +STRING_VARIABLE::STRING_VARIABLE ( + //constructor +const char *v, //the variable +const char *vname, //of variable +const char *comment //info on variable +): +value(v) { + //list iterator + STRING_VARIABLE_C_IT it = &head; + + //tprintf("Constructing %s\n",vname); + name = vname; //strings must be static + info = comment; + it.add_before_stay_put (this); //add it to stack +} + + +/********************************************************************** + * STRING_VARIABLE::~STRING_VARIABLE + * + * Destructor for STRING_VARIABLE. Add the variable to the static list. + **********************************************************************/ + + //constructor +STRING_VARIABLE::~STRING_VARIABLE ( +) { + //list iterator + STRING_VARIABLE_C_IT it = &head; + + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) + if (it.data () == this) + it.extract (); +} + + +/********************************************************************** + * STRING_VARIABLE::get_head + * + * Get the head of the list of the variables. + **********************************************************************/ + +STRING_VARIABLE_CLIST *STRING_VARIABLE::get_head() { //access to static + return &head; +} + + +/********************************************************************** + * STRING_VARIABLE::print + * + * Print the entire list of STRING_VARIABLEs. + **********************************************************************/ + +void STRING_VARIABLE::print( //print full list + FILE *fp //file to print on + ) { + //list iterator + STRING_VARIABLE_C_IT it = &head; + STRING_VARIABLE *elt; //current element + + if (fp == stdout) { + tprintf ("#Variables of type STRING_VARIABLE:\n"); + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + elt = it.data (); + tprintf ("%s #%s %s\n", elt->name, elt->value.string (), elt->info); + } + } + else { + fprintf (fp, "#Variables of type STRING_VARIABLE:\n"); + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + elt = it.data (); + fprintf (fp, "%s #%s %s\n", + elt->name, elt->value.string (), elt->info); + } + } +} + + +/********************************************************************** + * double_VAR_FROM::double_VAR_FROM + * + * Constructor to copy the list to a temporary location while the + * list head gets constructed. + **********************************************************************/ + +double_VAR_FROM::double_VAR_FROM() { //constructor + double_VARIABLE_C_IT start_it = &double_VARIABLE::head; + double_VARIABLE_C_IT end_it = &double_VARIABLE::head; + + if (!start_it.empty ()) { + while (!end_it.at_last ()) + end_it.forward (); + //move to copy + list.assign_to_sublist (&start_it, &end_it); + } +} + + +/********************************************************************** + * double_VAR_TO::double_VAR_TO + * + * Constructor to copy the list back to its rightful place. + **********************************************************************/ + +double_VAR_TO::double_VAR_TO() { //constructor + double_VARIABLE_C_IT start_it = &double_VARIABLE::copy.list; + double_VARIABLE_C_IT end_it = &double_VARIABLE::copy.list; + + if (!start_it.empty ()) { + while (!end_it.at_last ()) + end_it.forward (); + double_VARIABLE::head.assign_to_sublist (&start_it, &end_it); + } +} + + +/********************************************************************** + * double_VARIABLE::double_VARIABLE + * + * Constructor for double_VARIABLE. Add the variable to the static list. + **********************************************************************/ + +double_VARIABLE::double_VARIABLE( //constructor + double v, //the variable + const char *vname, //of variable + const char *comment //info on variable + ) { + //list iterator + double_VARIABLE_C_IT it = &head; + + //tprintf("Constructing %s\n",vname); + set_value(v); //set the value + name = vname; //strings must be static + info = comment; + it.add_before_stay_put (this); //add it to stack +} + + +double_VARIABLE::~double_VARIABLE () { + //list iterator + double_VARIABLE_C_IT it = &head; + + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) + if (it.data () == this) + it.extract (); +} + + +/********************************************************************** + * double_VARIABLE::get_head + * + * Get the head of the list of the variables. + **********************************************************************/ + +double_VARIABLE_CLIST *double_VARIABLE::get_head() { //access to static + return &head; +} + + +/********************************************************************** + * double_VARIABLE::print + * + * Print the entire list of double_VARIABLEs. + **********************************************************************/ + +void double_VARIABLE::print( //print full list + FILE *fp //file to print on + ) { + //list iterator + double_VARIABLE_C_IT it = &head; + double_VARIABLE *elt; //current element + + if (fp == stdout) { + tprintf ("#Variables of type double_VARIABLE:\n"); + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + elt = it.data (); + tprintf ("%s %lg #%s\n", elt->name, elt->value, elt->info); + } + } + else { + fprintf (fp, "#Variables of type double_VARIABLE:\n"); + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + elt = it.data (); + fprintf (fp, "%s %g #%s\n", elt->name, elt->value, elt->info); + } + } +} + + +/********************************************************************** + * read_variables_file + * + * Read a file of variables definitions and set/modify the values therein. + * If the filename begins with a + or -, the BOOL_VARIABLEs 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. + **********************************************************************/ + +DLLSYM BOOL8 read_variables_file( //read the file + const char *file //name to read + ) { + BOOL8 anyerr; //true if any error + char flag; //file flag + BOOL8 foundit; //found variable + INT16 length; //length of line + INT16 nameoffset; //offset for real name + char *valptr; //value field + char *stringend; //end of string value + FILE *fp; //file pointer + INT32 intval; //value from file + double doubleval; //value form file + //iterators + INT_VARIABLE_C_IT int_it = &INT_VARIABLE::head; + BOOL_VARIABLE_C_IT BOOL_it = &BOOL_VARIABLE::head; + STRING_VARIABLE_C_IT STRING_it = &STRING_VARIABLE::head; + double_VARIABLE_C_IT double_it = &double_VARIABLE::head; + char line[MAX_PATH]; //input line + + anyerr = FALSE; + if (*file == PLUS) { + flag = PLUS; //file has flag + nameoffset = 1; + } + else if (*file == MINUS) { + flag = MINUS; + nameoffset = 1; + } + else { + flag = EQUAL; + nameoffset = 0; + } + + fp = fopen (file + nameoffset, "r"); + if (fp == NULL) { + tprintf ("read_variables_file:Can't open %s", file + nameoffset); + return TRUE; //can't open it + } + while (fgets (line, MAX_PATH, fp)) { + if (line[0] != '\n' && line[0] != '#') { + length = strlen (line); + if (line[length - 1] == '\n') + line[length - 1] = '\0'; //cut 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'); + + if (*valptr && *valptr != '#') { + //last char in string + stringend = valptr + strlen (valptr) - 1; + while (stringend != valptr) { + while (stringend != valptr + && (*stringend == ' ' || *stringend == '\t')) + //cut trailing blanks + stringend--; + stringend[1] = '\0'; //terminate string + + while (stringend != valptr + && (*stringend != ' ' && *stringend != '\t' + || stringend[1] != '#')) + stringend--; //find word start + } + } + } + foundit = FALSE; + + //find name + for (STRING_it.mark_cycle_pt (); !STRING_it.cycled_list () && strcmp (line, STRING_it.data ()->name); STRING_it.forward ()); + //found it + if (!STRING_it.cycled_list ()) { + foundit = TRUE; //found the varaible + if (*valptr == '\0' || *valptr == '#') + STRING_it.data ()->set_value ((char *) NULL); + //no value + else + //set its value + STRING_it.data ()->set_value (valptr); + } + + if (*valptr) { + //find name + for (int_it.mark_cycle_pt (); !int_it.cycled_list () && strcmp (line, int_it.data ()->name); int_it.forward ()); + //found it + if (!int_it.cycled_list () + && sscanf (valptr, INT32FORMAT, &intval) == 1) { + foundit = TRUE; //found the varaible + //set its value + int_it.data ()->set_value (intval); + } + + //find name + for (BOOL_it.mark_cycle_pt (); !BOOL_it.cycled_list () && strcmp (line, BOOL_it.data ()->name); BOOL_it.forward ()); + //found it + if (!BOOL_it.cycled_list ()) { + if (*valptr == 'T' || *valptr == 't' + || *valptr == 'Y' || *valptr == 'y' || *valptr == '1') { + foundit = TRUE; + if (flag == MINUS) + BOOL_it.data ()->set_value (FALSE); + //set to false + else + BOOL_it.data ()->set_value (TRUE); + //set to true + } + else if (*valptr == 'F' || *valptr == 'f' + || *valptr == 'N' || *valptr == 'n' + || *valptr == '0') { + foundit = TRUE; + if (flag == EQUAL) + BOOL_it.data ()->set_value (FALSE); + //set to false + } + } + + //find name + for (double_it.mark_cycle_pt (); !double_it.cycled_list () && strcmp (line, double_it.data ()->name); double_it.forward ()); + //found it + + #ifdef EMBEDDED + if (!double_it.cycled_list ()) { + doubleval = strtofloat(valptr); + #else + if (!double_it.cycled_list () + && sscanf (valptr, "%lf", &doubleval) == 1) { + #endif + foundit = TRUE; //found the varaible + double_it.data ()->set_value (doubleval); + //set its value + } + + if (!foundit) { + anyerr = TRUE; //had an error + tprintf ("read_variables_file:variable not found: %s", + line); + } + } + else if (!foundit) { + anyerr = TRUE; //had an error + tprintf ("read_variables_file:No value for variable %s", line); + } + } + } + fclose(fp); //close file + return anyerr; +} + + +/********************************************************************** + * print_variables + * + * Print all variable types to the given file + **********************************************************************/ + +DLLSYM void print_variables( //print all vars + FILE *fp //file to print on + ) { + INT_VARIABLE::print(fp); //print INTs + BOOL_VARIABLE::print(fp); //print BOOLs + STRING_VARIABLE::print(fp); //print STRINGs + double_VARIABLE::print(fp); //print doubles +} diff --git a/ccutil/varable.h b/ccutil/varable.h new file mode 100644 index 0000000000..8f92bf8e8c --- /dev/null +++ b/ccutil/varable.h @@ -0,0 +1,412 @@ +/********************************************************************** + * File: varable.h (Formerly variable.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 VARABLE_H +#define VARABLE_H + +#include +#include "clst.h" +#include "strngs.h" + +class DLLSYM INT_VARIABLE; + + //read the file +extern DLLSYM BOOL8 read_variables_file(const char *file //name to read + ); + //print all vars +extern DLLSYM void print_variables(FILE *fp //file to print on + ); + +CLISTIZEH (INT_VARIABLE) +class DLLSYM INT_VAR_FROM +{ + friend class INT_VAR_TO; + public: + INT_VAR_FROM(); //constructor + private: + INT_VARIABLE_CLIST list; //copy of list +}; + +class DLLSYM INT_VAR_TO +{ + public: + INT_VAR_TO(); //constructor + private: + INT_VARIABLE_CLIST dummy; +}; + +class DLLSYM INT_VARIABLE +{ + friend class INT_VAR_TO; + friend class INT_VAR_FROM; + //for setting values + friend DLLSYM BOOL8 read_variables_file(const char *file); //file to read + + public: + INT_VARIABLE( //constructor + INT32 v, //initial value + const char *vname, //name of variable + const char *comment); //info on variable + + INT_VARIABLE() { //for elist only + value = 0; + name = "NONAME"; + info = "Uninitialized"; + } + ~INT_VARIABLE (); //for elist only + + operator INT32() { //conversion + return value; //access as int + } + + void set_value( //assign to value + INT32 v) { //value to set + value = v; + } + + const char *name_str() { //access name + return name; + } + + const char *info_str() { //access name + return info; + } + + //access list head + static INT_VARIABLE_CLIST *get_head(); + + static void print( //print whole list + FILE *fp); //file to print on + + private: + INT32 value; //the variable + const char *name; //name of variable + const char *info; //for menus + static INT_VAR_FROM copy; //pre constructor + //start of list + static INT_VARIABLE_CLIST head; + static INT_VAR_TO replace; //post constructor +}; + +class DLLSYM BOOL_VARIABLE; + +CLISTIZEH (BOOL_VARIABLE) +class DLLSYM BOOL_VAR_FROM +{ + friend class BOOL_VAR_TO; + public: + BOOL_VAR_FROM(); //constructor + private: + BOOL_VARIABLE_CLIST list; //copy of list +}; + +class DLLSYM BOOL_VAR_TO +{ + public: + BOOL_VAR_TO(); //constructor + private: + BOOL_VARIABLE_CLIST dummy; +}; + +class DLLSYM BOOL_VARIABLE +{ + friend class BOOL_VAR_FROM; + friend class BOOL_VAR_TO; + //for setting values + friend DLLSYM BOOL8 read_variables_file(const char *file); //file to read + + public: + BOOL_VARIABLE( //constructor + BOOL8 v, //initial value + const char *vname, //name of variable + const char *comment); //info on variable + + BOOL_VARIABLE() { //for elist only + value = FALSE; + name = "NONAME"; + info = "Uninitialized"; + } + ~BOOL_VARIABLE (); //for elist only + + operator BOOL8() { //conversion + return value; //access as int + } + + void set_value( //assign to value + BOOL8 v) { //value to set + value = v; + } + + const char *name_str() { //access name + return name; + } + + const char *info_str() { //access name + return info; + } + + //access list head + static BOOL_VARIABLE_CLIST *get_head(); + + static void print( //print whole list + FILE *fp); //file to print on + + private: + BOOL8 value; //the variable + const char *name; //name of variable + const char *info; //for menus + static BOOL_VAR_FROM copy; //pre constructor + //start of list + static BOOL_VARIABLE_CLIST head; + static BOOL_VAR_TO replace; //post constructor +}; + +class DLLSYM STRING_VARIABLE; + +CLISTIZEH (STRING_VARIABLE) +class DLLSYM STRING_VAR_FROM +{ + friend class STRING_VAR_TO; + public: + STRING_VAR_FROM(); //constructor + private: + STRING_VARIABLE_CLIST list; //copy of list +}; + +class DLLSYM STRING_VAR_TO +{ + public: + STRING_VAR_TO(); //constructor + private: + STRING_VARIABLE_CLIST dummy; +}; + +class DLLSYM STRING_VARIABLE +{ + friend class STRING_VAR_TO; + friend class STRING_VAR_FROM; + //for setting values + friend DLLSYM BOOL8 read_variables_file(const char *file); //file to read + + public: + STRING_VARIABLE( //constructor + const char *v, //initial value + const char *vname, //name of variable + const char *comment); //info on variable + + STRING_VARIABLE() { //for elist only + name = "NONAME"; + info = "Uninitialized"; + } + ~STRING_VARIABLE (); //for elist only + + //conversion + operator const STRING &() { + return value; //access as int + } + + void set_value( //assign to value + STRING v) { //value to set + value = v; + } + + const char *string() const { //get string + return value.string (); + } + + const char *name_str() { //access name + return name; + } + + const char *info_str() { //access name + return info; + } + + //access list head + static STRING_VARIABLE_CLIST *get_head(); + + static void print( //print whole list + FILE *fp); //file to print on + + private: + STRING value; //the variable + const char *name; //name of variable + const char *info; //for menus + static STRING_VAR_FROM copy; //pre constructor + //start of list + static STRING_VARIABLE_CLIST head; + static STRING_VAR_TO replace;//post constructor +}; + +class DLLSYM double_VARIABLE; + +CLISTIZEH (double_VARIABLE) +class DLLSYM double_VAR_FROM +{ + friend class double_VAR_TO; + public: + double_VAR_FROM(); //constructor + private: + double_VARIABLE_CLIST list; //copy of list +}; + +class DLLSYM double_VAR_TO +{ + public: + double_VAR_TO(); //constructor + private: + double_VARIABLE_CLIST dummy; +}; + +class DLLSYM double_VARIABLE +{ + friend class double_VAR_TO; + friend class double_VAR_FROM; + //for setting values + friend DLLSYM BOOL8 read_variables_file(const char *file); //file to read + + public: + double_VARIABLE( //constructor + double v, //initial value + const char *vname, //name of variable + const char *comment); //info on variable + + double_VARIABLE() { //for elist only + value = 0.0; + name = "NONAME"; + info = "Uninitialized"; + } + ~double_VARIABLE (); //for elist only + + operator double() { //conversion + return value; //access as int + } + + void set_value( //assign to value + double v) { //value to set + value = v; + } + + const char *name_str() { //access name + return name; + } + + const char *info_str() { //access name + return info; + } + + //access list head + static double_VARIABLE_CLIST *get_head(); + + static void print( //print whole list + FILE *fp); //file to print on + + private: + double value; //the variable + const char *name; //name of variable + const char *info; //for menus + static double_VAR_FROM copy; //pre constructor + //start of list + static double_VARIABLE_CLIST head; + static double_VAR_TO replace;//post constructor +}; + +/************************************************************************* + * NOTE ON DEFINING VARIABLES + * + * For our normal code, the ***_VAR and ***_EVAR macros for variable + * definitions are identical. HOWEVER, for the code version to ship to NEVADA + * (or anywhere else where we want to hide the majority of variables) the + * **_VAR macros are changed so that the "#name" and "comment" parameters + * to the variable constructor are changed to empty strings. This prevents the + * variable name or comment string appearing in the object code file (after it + * has gone through strip). + * + * Certain variables can remain EXPOSED and hence be used in config files given + * to UNLV. These are variable which have been declared with the ***_EVAR + * macros. + * + *************************************************************************/ + +/* SECURE_NAMES is defined in senames.h when necessary */ +#ifdef SECURE_NAMES + +#define INT_VAR(name,val,comment) /*make INT_VARIABLE*/\ + INT_VARIABLE name(val,"","") + +#define BOOL_VAR(name,val,comment) /*make BOOL_VARIABLE*/\ + BOOL_VARIABLE name(val,"","") + +#define STRING_VAR(name,val,comment) /*make STRING_VARIABLE*/\ + STRING_VARIABLE name(val,"","") + +#define double_VAR(name,val,comment) /*make double_VARIABLE*/\ + double_VARIABLE name(val,"","") + +#else + +#define INT_VAR(name,val,comment) /*make INT_VARIABLE*/\ + INT_VARIABLE name(val,#name,comment) + +#define BOOL_VAR(name,val,comment) /*make BOOL_VARIABLE*/\ + BOOL_VARIABLE name(val,#name,comment) + +#define STRING_VAR(name,val,comment) /*make STRING_VARIABLE*/\ + STRING_VARIABLE name(val,#name,comment) + +#define double_VAR(name,val,comment) /*make double_VARIABLE*/\ + double_VARIABLE name(val,#name,comment) +#endif + +#define INT_VAR_H(name,val,comment) /*declare one*/\ + INT_VARIABLE name + +#define BOOL_VAR_H(name,val,comment) /*declare one*/\ + BOOL_VARIABLE name + +#define STRING_VAR_H(name,val,comment) /*declare one*/\ + STRING_VARIABLE name + +#define double_VAR_H(name,val,comment) /*declare one*/\ + double_VARIABLE name + +#define INT_EVAR(name,val,comment) /*make INT_VARIABLE*/\ + INT_VARIABLE name(val,#name,comment) + +#define INT_EVAR_H(name,val,comment) /*declare one*/\ + INT_VARIABLE name + +#define BOOL_EVAR(name,val,comment) /*make BOOL_VARIABLE*/\ + BOOL_VARIABLE name(val,#name,comment) + +#define BOOL_EVAR_H(name,val,comment) /*declare one*/\ + BOOL_VARIABLE name + +#define STRING_EVAR(name,val,comment) /*make STRING_VARIABLE*/\ + STRING_VARIABLE name(val,#name,comment) + +#define STRING_EVAR_H(name,val,comment) /*declare one*/\ + STRING_VARIABLE name + +#define double_EVAR(name,val,comment) /*make double_VARIABLE*/\ + double_VARIABLE name(val,#name,comment) + +#define double_EVAR_H(name,val,comment) /*declare one*/\ + double_VARIABLE name +#endif diff --git a/classify/Makefile.am b/classify/Makefile.am new file mode 100644 index 0000000000..27c782a1a6 --- /dev/null +++ b/classify/Makefile.am @@ -0,0 +1,24 @@ +SUBDIRS = +AM_CPPFLAGS = \ + -I$(top_srcdir)/cutil -I$(top_srcdir)/ccutil \ + -I$(top_srcdir)/ccstruct -I$(top_srcdir)/dict \ + -I$(top_srcdir)/viewer + +EXTRA_DIST = \ + adaptive.h adaptmatch.h baseline.h blobclass.h chartoname.h \ + cluster.h clusttool.h cutoffs.h extern.h extract.h featdefs.h \ + flexfx.h float2int.h fpoint.h fxdefs.h fxid.h hideedge.h \ + intfx.h intmatcher.h intproto.h kdtree.h mfdefs.h mf.h \ + mfoutline.h mfx.h normfeat.h normmatch.h ocrfeatures.h \ + outfeat.h picofeat.h protos.h sigmenu.h speckle.h xform2d.h + +noinst_LIBRARIES = libtesseract_classify.a +libtesseract_classify_a_SOURCES = \ + adaptive.cpp adaptmatch.cpp baseline.cpp blobclass.cpp \ + chartoname.cpp cluster.cpp clusttool.cpp cutoffs.cpp \ + extract.cpp featdefs.cpp flexfx.cpp float2int.cpp \ + fpoint.cpp fxdefs.cpp hideedge.cpp intfx.cpp intmatcher.cpp \ + intproto.cpp kdtree.cpp mf.cpp mfdefs.cpp mfoutline.cpp \ + mfx.cpp normfeat.cpp normmatch.cpp ocrfeatures.cpp \ + outfeat.cpp picofeat.cpp protos.cpp sigmenu.cpp speckle.cpp \ + xform2d.cpp diff --git a/classify/Makefile.in b/classify/Makefile.in new file mode 100644 index 0000000000..ba6c6243e3 --- /dev/null +++ b/classify/Makefile.in @@ -0,0 +1,584 @@ +# Makefile.in generated by automake 1.9.6 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005 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@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = .. +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +INSTALL = @INSTALL@ +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@ +subdir = classify +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \ + $(top_srcdir)/config/ac_define_versionlevel.m4 \ + $(top_srcdir)/config/acinclude_custom.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(SHELL) $(top_srcdir)/config/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config_auto.h +CONFIG_CLEAN_FILES = +LIBRARIES = $(noinst_LIBRARIES) +AR = ar +ARFLAGS = cru +libtesseract_classify_a_AR = $(AR) $(ARFLAGS) +libtesseract_classify_a_LIBADD = +am_libtesseract_classify_a_OBJECTS = adaptive.$(OBJEXT) \ + adaptmatch.$(OBJEXT) baseline.$(OBJEXT) blobclass.$(OBJEXT) \ + chartoname.$(OBJEXT) cluster.$(OBJEXT) clusttool.$(OBJEXT) \ + cutoffs.$(OBJEXT) extract.$(OBJEXT) featdefs.$(OBJEXT) \ + flexfx.$(OBJEXT) float2int.$(OBJEXT) fpoint.$(OBJEXT) \ + fxdefs.$(OBJEXT) hideedge.$(OBJEXT) intfx.$(OBJEXT) \ + intmatcher.$(OBJEXT) intproto.$(OBJEXT) kdtree.$(OBJEXT) \ + mf.$(OBJEXT) mfdefs.$(OBJEXT) mfoutline.$(OBJEXT) \ + mfx.$(OBJEXT) normfeat.$(OBJEXT) normmatch.$(OBJEXT) \ + ocrfeatures.$(OBJEXT) outfeat.$(OBJEXT) picofeat.$(OBJEXT) \ + protos.$(OBJEXT) sigmenu.$(OBJEXT) speckle.$(OBJEXT) \ + xform2d.$(OBJEXT) +libtesseract_classify_a_OBJECTS = \ + $(am_libtesseract_classify_a_OBJECTS) +DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/config/depcomp +am__depfiles_maybe = depfiles +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +CXXLD = $(CXX) +CXXLINK = $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) \ + -o $@ +SOURCES = $(libtesseract_classify_a_SOURCES) +DIST_SOURCES = $(libtesseract_classify_a_SOURCES) +RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \ + html-recursive info-recursive install-data-recursive \ + install-exec-recursive install-info-recursive \ + install-recursive installcheck-recursive installdirs-recursive \ + pdf-recursive ps-recursive uninstall-info-recursive \ + uninstall-recursive +ETAGS = etags +CTAGS = ctags +DIST_SUBDIRS = $(SUBDIRS) +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMDEP_FALSE = @AMDEP_FALSE@ +AMDEP_TRUE = @AMDEP_TRUE@ +AMTAR = @AMTAR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CXXRPOFLAGS = @CXXRPOFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +GNUWIN32_DIR = @GNUWIN32_DIR@ +HAVE_GNUWIN32_FALSE = @HAVE_GNUWIN32_FALSE@ +HAVE_GNUWIN32_TRUE = @HAVE_GNUWIN32_TRUE@ +HAVE_LIBTIFF_FALSE = @HAVE_LIBTIFF_FALSE@ +HAVE_LIBTIFF_TRUE = @HAVE_LIBTIFF_TRUE@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTIFF_CFLAGS = @LIBTIFF_CFLAGS@ +LIBTIFF_LIBS = @LIBTIFF_LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAINTAINER_MODE_FALSE = @MAINTAINER_MODE_FALSE@ +MAINTAINER_MODE_TRUE = @MAINTAINER_MODE_TRUE@ +MAKEINFO = @MAKEINFO@ +OBJEXT = @OBJEXT@ +OPTS = @OPTS@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_DATE = @PACKAGE_DATE@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PACKAGE_YEAR = @PACKAGE_YEAR@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +RPO_NO = @RPO_NO@ +RPO_YES = @RPO_YES@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +USING_CL_FALSE = @USING_CL_FALSE@ +USING_CL_TRUE = @USING_CL_TRUE@ +VERSION = @VERSION@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_RANLIB = @ac_ct_RANLIB@ +ac_ct_STRIP = @ac_ct_STRIP@ +am__fastdepCC_FALSE = @am__fastdepCC_FALSE@ +am__fastdepCC_TRUE = @am__fastdepCC_TRUE@ +am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@ +am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@ +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@ +datadir = @datadir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +SUBDIRS = +AM_CPPFLAGS = \ + -I$(top_srcdir)/cutil -I$(top_srcdir)/ccutil \ + -I$(top_srcdir)/ccstruct -I$(top_srcdir)/dict \ + -I$(top_srcdir)/viewer + +EXTRA_DIST = \ + adaptive.h adaptmatch.h baseline.h blobclass.h chartoname.h \ + cluster.h clusttool.h cutoffs.h extern.h extract.h featdefs.h \ + flexfx.h float2int.h fpoint.h fxdefs.h fxid.h hideedge.h \ + intfx.h intmatcher.h intproto.h kdtree.h mfdefs.h mf.h \ + mfoutline.h mfx.h normfeat.h normmatch.h ocrfeatures.h \ + outfeat.h picofeat.h protos.h sigmenu.h speckle.h xform2d.h + +noinst_LIBRARIES = libtesseract_classify.a +libtesseract_classify_a_SOURCES = \ + adaptive.cpp adaptmatch.cpp baseline.cpp blobclass.cpp \ + chartoname.cpp cluster.cpp clusttool.cpp cutoffs.cpp \ + extract.cpp featdefs.cpp flexfx.cpp float2int.cpp \ + fpoint.cpp fxdefs.cpp hideedge.cpp intfx.cpp intmatcher.cpp \ + intproto.cpp kdtree.cpp mf.cpp mfdefs.cpp mfoutline.cpp \ + mfx.cpp normfeat.cpp normmatch.cpp ocrfeatures.cpp \ + outfeat.cpp picofeat.cpp protos.cpp sigmenu.cpp speckle.cpp \ + xform2d.cpp + +all: all-recursive + +.SUFFIXES: +.SUFFIXES: .cpp .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 \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu classify/Makefile'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu 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 + +clean-noinstLIBRARIES: + -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) +libtesseract_classify.a: $(libtesseract_classify_a_OBJECTS) $(libtesseract_classify_a_DEPENDENCIES) + -rm -f libtesseract_classify.a + $(libtesseract_classify_a_AR) libtesseract_classify.a $(libtesseract_classify_a_OBJECTS) $(libtesseract_classify_a_LIBADD) + $(RANLIB) libtesseract_classify.a + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/adaptive.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/adaptmatch.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/baseline.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/blobclass.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/chartoname.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cluster.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/clusttool.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cutoffs.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/extract.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/featdefs.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/flexfx.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/float2int.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fpoint.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fxdefs.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hideedge.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/intfx.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/intmatcher.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/intproto.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/kdtree.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mf.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mfdefs.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mfoutline.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mfx.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/normfeat.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/normmatch.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ocrfeatures.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/outfeat.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/picofeat.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/protos.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sigmenu.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/speckle.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xform2d.Po@am__quote@ + +.cpp.o: +@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ $< + +.cpp.obj: +@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` +uninstall-info-am: + +# 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. +$(RECURSIVE_TARGETS): + @failcom='exit 1'; \ + for f in x $$MAKEFLAGS; do \ + case $$f in \ + *=* | --[!k]*);; \ + *k*) failcom='fail=yes';; \ + esac; \ + done; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + list='$(SUBDIRS)'; 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; \ + (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" + +mostlyclean-recursive clean-recursive distclean-recursive \ +maintainer-clean-recursive: + @failcom='exit 1'; \ + for f in x $$MAKEFLAGS; do \ + case $$f in \ + *=* | --[!k]*);; \ + *k*) failcom='fail=yes';; \ + esac; \ + done; \ + dot_seen=no; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + rev=''; for subdir in $$list; do \ + if test "$$subdir" = "."; then :; else \ + rev="$$subdir $$rev"; \ + fi; \ + done; \ + rev="$$rev ."; \ + target=`echo $@ | sed s/-recursive//`; \ + for subdir in $$rev; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done && test -z "$$fail" +tags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \ + done +ctags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \ + done + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + 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 || \ + tags="$$tags $$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique; \ + fi +ctags: CTAGS +CTAGS: ctags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(CTAGS_ARGS)$$tags$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +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)'; for file in $$list; do \ + case $$file in \ + $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \ + $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \ + esac; \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + dir="/$$dir"; \ + $(mkdir_p) "$(distdir)$$dir"; \ + else \ + dir=''; \ + fi; \ + if test -d $$d/$$file; then \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$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 \ + test -d "$(distdir)/$$subdir" \ + || $(mkdir_p) "$(distdir)/$$subdir" \ + || exit 1; \ + distdir=`$(am__cd) $(distdir) && pwd`; \ + top_distdir=`$(am__cd) $(top_distdir) && pwd`; \ + (cd $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$top_distdir" \ + distdir="$$distdir/$$subdir" \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-recursive +all-am: Makefile $(LIBRARIES) +installdirs: installdirs-recursive +installdirs-am: +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: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_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-recursive + +clean-am: clean-generic clean-noinstLIBRARIES mostlyclean-am + +distclean: distclean-recursive + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +info: info-recursive + +info-am: + +install-data-am: + +install-exec-am: + +install-info: install-info-recursive + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-compile mostlyclean-generic + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: uninstall-info-am + +uninstall-info: uninstall-info-recursive + +.PHONY: $(RECURSIVE_TARGETS) CTAGS GTAGS all all-am check check-am \ + clean clean-generic clean-noinstLIBRARIES clean-recursive \ + ctags ctags-recursive distclean distclean-compile \ + distclean-generic distclean-recursive distclean-tags distdir \ + dvi dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-exec install-exec-am \ + install-info install-info-am install-man install-strip \ + installcheck installcheck-am installdirs installdirs-am \ + maintainer-clean maintainer-clean-generic \ + maintainer-clean-recursive mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-recursive pdf pdf-am ps ps-am \ + tags tags-recursive uninstall uninstall-am uninstall-info-am + +# 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: diff --git a/classify/adaptive.cpp b/classify/adaptive.cpp new file mode 100644 index 0000000000..324fbd1445 --- /dev/null +++ b/classify/adaptive.cpp @@ -0,0 +1,534 @@ +/****************************************************************************** + ** 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" + +#ifdef __UNIX__ +#include +#endif +#include + +/**---------------------------------------------------------------------------- + Public Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +int AddAdaptedClass(ADAPT_TEMPLATES Templates, + ADAPT_CLASS Class, + CLASS_ID ClassId) { +/* + ** Parameters: + ** Templates set of templates to add new class to + ** Class new class to add to templates + ** ClassId class id to associate with new class + ** Globals: none + ** Operation: This routine adds a new adapted class to an existing + ** set of adapted templates. + ** Return: The class index of the new class. + ** Exceptions: none + ** History: Thu Mar 14 13:06:09 1991, DSJ, Created. + */ + INT_CLASS IntClass; + CLASS_INDEX ClassIndex; + + assert (Templates != NULL); + assert (Class != NULL); + assert (LegalClassId (ClassId)); + assert (UnusedClassIdIn (Templates->Templates, ClassId)); + assert (Class->NumPermConfigs == 0); + + IntClass = NewIntClass (1, 1); + ClassIndex = AddIntClass (Templates->Templates, ClassId, IntClass); + + assert (Templates->Class[ClassIndex] == NULL); + + Templates->Class[ClassIndex] = Class; + + return (ClassIndex); + +} /* AddAdaptedClass */ + + +/*---------------------------------------------------------------------------*/ +void FreeTempConfig(TEMP_CONFIG Config) { +/* + ** Parameters: + ** Config config to be freed + ** Globals: none + ** Operation: This routine frees all memory consumed by a temporary + ** configuration. + ** Return: none + ** Exceptions: none + ** History: Thu Mar 14 13:34:23 1991, DSJ, Created. + */ + assert (Config != NULL); + + destroy_nodes (Config->ContextsSeen, memfree); + FreeBitVector (Config->Protos); + c_free_struct (Config, sizeof (TEMP_CONFIG_STRUCT), "TEMP_CONFIG_STRUCT"); + +} /* FreeTempConfig */ + + +/*---------------------------------------------------------------------------*/ +void FreeTempProto(void *arg) { + PROTO proto = (PROTO) arg; + + c_free_struct (proto, sizeof (TEMP_PROTO_STRUCT), "TEMP_PROTO_STRUCT"); +} + + +/*---------------------------------------------------------------------------*/ +ADAPT_CLASS NewAdaptedClass() { +/* + ** Parameters: none + ** Globals: none + ** Operation: This operation allocates and initializes a new adapted + ** class data structure and returns a ptr to it. + ** Return: Ptr to new class data structure. + ** Exceptions: none + ** History: Thu Mar 14 12:58:13 1991, DSJ, Created. + */ + ADAPT_CLASS Class; + int i; + + Class = (ADAPT_CLASS) Emalloc (sizeof (ADAPT_CLASS_STRUCT)); + Class->NumPermConfigs = 0; + Class->TempProtos = NIL; + + 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) + Efree (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); +} + + +/*---------------------------------------------------------------------------*/ +ADAPT_TEMPLATES NewAdaptedTemplates() { +/* + ** Parameters: none + ** Globals: none + ** Operation: + ** Return: none + ** Exceptions: none + ** History: Fri Mar 8 10:15:28 1991, DSJ, Created. + */ + ADAPT_TEMPLATES Templates; + int i; + + Templates = (ADAPT_TEMPLATES) Emalloc (sizeof (ADAPT_TEMPLATES_STRUCT)); + + Templates->Templates = NewIntTemplates (); + Templates->NumPermClasses = 0; + + for (i = 0; i < MAX_NUM_CLASSES; i++) + Templates->Class[i] = NULL; + + return (Templates); + +} /* NewAdaptedTemplates */ + + +/*-------------------------------------------------------------------------------*/ +void free_adapted_templates(ADAPT_TEMPLATES templates) { + + if (templates != NULL) { + int i; + for (i = 0; i < NumClassesIn (templates->Templates); i++) + free_adapted_class (templates->Class[i]); + free_int_templates (templates->Templates); + Efree(templates); + } +} + + +/*---------------------------------------------------------------------------*/ +TEMP_CONFIG NewTempConfig(int MaxProtoId) { +/* + ** Parameters: + ** MaxProtoId max id of any proto in new config + ** Globals: none + ** Operation: This routine allocates and returns a new temporary + ** config. + ** Return: Ptr to new temp config. + ** Exceptions: none + ** History: Thu Mar 14 13:28:21 1991, DSJ, Created. + */ + TEMP_CONFIG Config; + int NumProtos = MaxProtoId + 1; + + Config = + (TEMP_CONFIG) c_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; + zero_all_bits (Config->Protos, Config->ProtoVectorSize); + + return (Config); + +} /* NewTempConfig */ + + +/*---------------------------------------------------------------------------*/ +TEMP_PROTO NewTempProto() { +/* + ** Parameters: none + ** Globals: none + ** Operation: This routine allocates and returns a new temporary proto. + ** Return: Ptr to new temporary proto. + ** Exceptions: none + ** History: Thu Mar 14 13:31:31 1991, DSJ, Created. + */ + return ((TEMP_PROTO) + c_alloc_struct (sizeof (TEMP_PROTO_STRUCT), "TEMP_PROTO_STRUCT")); +} /* NewTempProto */ + + +/*---------------------------------------------------------------------------*/ +void PrintAdaptedTemplates(FILE *File, ADAPT_TEMPLATES Templates) { +/* + ** Parameters: + ** File open text file to print Templates to + ** Templates adapted templates to print to File + ** Globals: none + ** Operation: This routine prints a summary of the adapted templates + ** in Templates to File. + ** Return: none + ** Exceptions: none + ** History: Wed Mar 20 13:35:29 1991, DSJ, Created. + */ + int i; + INT_CLASS IClass; + ADAPT_CLASS AClass; + + #ifndef SECURE_NAMES + fprintf (File, "\n\nSUMMARY OF ADAPTED TEMPLATES:\n\n"); + fprintf (File, "Num classes = %d; Num permanent classes = %d\n\n", + NumClassesIn (Templates->Templates), Templates->NumPermClasses); + fprintf (File, "Index Id NC NPC NP NPP\n"); + fprintf (File, "------------------------\n"); + + for (i = 0; i < NumClassesIn (Templates->Templates); i++) { + IClass = ClassForIndex (Templates->Templates, i); + AClass = Templates->Class[i]; + + fprintf (File, "%5d %c %3d %3d %3d %3d\n", + i, ClassIdForIndex (Templates->Templates, i), + NumIntConfigsIn (IClass), AClass->NumPermConfigs, + NumIntProtosIn (IClass), + NumIntProtosIn (IClass) - count (AClass->TempProtos)); + } + #endif + fprintf (File, "\n"); + +} /* PrintAdaptedTemplates */ + + +/*---------------------------------------------------------------------------*/ +ADAPT_CLASS ReadAdaptedClass(FILE *File) { +/* + ** Parameters: + ** File open file to read adapted class from + ** Globals: none + ** Operation: Read an adapted class description from File and return + ** a ptr to the adapted class. + ** Return: Ptr to new adapted class. + ** Exceptions: none + ** History: Tue Mar 19 14:11:01 1991, DSJ, Created. + */ + 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; + for (i = 0; i < NumTempProtos; i++) { + TempProto = + (TEMP_PROTO) c_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 */ + + +/*---------------------------------------------------------------------------*/ +ADAPT_TEMPLATES ReadAdaptedTemplates(FILE *File) { +/* + ** Parameters: + ** File open text file to read adapted templates from + ** Globals: none + ** Operation: Read a set of adapted templates from File and return + ** a ptr to the templates. + ** Return: Ptr to adapted templates read from File. + ** Exceptions: none + ** History: Mon Mar 18 15:18:10 1991, DSJ, Created. + */ + 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, FALSE); + + /* then read in the adaptive info for each class */ + for (i = 0; i < NumClassesIn (Templates->Templates); i++) { + Templates->Class[i] = ReadAdaptedClass (File); + } + return (Templates); + +} /* ReadAdaptedTemplates */ + + +/*---------------------------------------------------------------------------*/ +PERM_CONFIG ReadPermConfig(FILE *File) { +/* + ** Parameters: + ** File open file to read permanent config from + ** Globals: none + ** Operation: Read a permanent configuration description from File + ** and return a ptr to it. + ** Return: Ptr to new permanent configuration description. + ** Exceptions: none + ** History: Tue Mar 19 14:25:26 1991, DSJ, Created. + */ + PERM_CONFIG Config; + UINT8 NumAmbigs; + + fread ((char *) &NumAmbigs, sizeof (UINT8), 1, File); + Config = (PERM_CONFIG) Emalloc (sizeof (char) * (NumAmbigs + 1)); + fread (Config, sizeof (char), NumAmbigs, File); + Config[NumAmbigs] = '\0'; + + return (Config); + +} /* ReadPermConfig */ + + +/*---------------------------------------------------------------------------*/ +TEMP_CONFIG ReadTempConfig(FILE *File) { +/* + ** Parameters: + ** File open file to read temporary config from + ** Globals: none + ** Operation: Read a temporary configuration description from File + ** and return a ptr to it. + ** Return: Ptr to new temporary configuration description. + ** Exceptions: none + ** History: Tue Mar 19 14:29:59 1991, DSJ, Created. + */ + TEMP_CONFIG Config; + + Config = + (TEMP_CONFIG) c_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 */ + + +/*---------------------------------------------------------------------------*/ +void WriteAdaptedClass(FILE *File, ADAPT_CLASS Class, int NumConfigs) { +/* + ** Parameters: + ** File open file to write Class to + ** Class adapted class to write to File + ** NumConfigs number of configs in Class + ** Globals: none + ** Operation: This routine writes a binary representation of Class + ** to File. + ** Return: none + ** Exceptions: none + ** History: Tue Mar 19 13:33:51 1991, DSJ, Created. + */ + 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(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 */ + + +/*---------------------------------------------------------------------------*/ +void WriteAdaptedTemplates(FILE *File, ADAPT_TEMPLATES Templates) { +/* + ** Parameters: + ** File open text file to write Templates to + ** Templates set of adapted templates to write to File + ** Globals: none + ** Operation: This routine saves Templates to File in a binary format. + ** Return: none + ** Exceptions: none + ** History: Mon Mar 18 15:07:32 1991, DSJ, Created. + */ + 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); + + /* then write out the adaptive info for each class */ + for (i = 0; i < NumClassesIn (Templates->Templates); i++) { + WriteAdaptedClass (File, Templates->Class[i], + NumIntConfigsIn (ClassForIndex + (Templates->Templates, i))); + } +} /* WriteAdaptedTemplates */ + + +/*---------------------------------------------------------------------------*/ +void WritePermConfig(FILE *File, PERM_CONFIG Config) { +/* + ** Parameters: + ** File open file to write Config to + ** Config permanent config to write to File + ** Globals: none + ** Operation: This routine writes a binary representation of a + ** permanent configuration to File. + ** Return: none + ** Exceptions: none + ** History: Tue Mar 19 13:55:44 1991, DSJ, Created. + */ + UINT8 NumAmbigs; + + assert (Config != NULL); + + NumAmbigs = strlen (Config); + fwrite ((char *) &NumAmbigs, sizeof (UINT8), 1, File); + fwrite (Config, sizeof (char), NumAmbigs, File); + +} /* WritePermConfig */ + + +/*---------------------------------------------------------------------------*/ +void WriteTempConfig(FILE *File, TEMP_CONFIG Config) { +/* + ** Parameters: + ** File open file to write Config to + ** Config temporary config to write to File + ** Globals: none + ** Operation: This routine writes a binary representation of a + ** temporary configuration to File. + ** Return: none + ** Exceptions: none + ** History: Tue Mar 19 14:00:28 1991, DSJ, Created. + */ + 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 */ diff --git a/classify/adaptive.h b/classify/adaptive.h new file mode 100644 index 0000000000..17c8f52d95 --- /dev/null +++ b/classify/adaptive.h @@ -0,0 +1,199 @@ +/****************************************************************************** + ** 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; +} TEMP_CONFIG_STRUCT; +typedef TEMP_CONFIG_STRUCT *TEMP_CONFIG; + +typedef char *PERM_CONFIG; + +typedef union +{ + TEMP_CONFIG Temp; + PERM_CONFIG Perm; +} ADAPTED_CONFIG; + +typedef struct +{ + UINT8 NumPermConfigs; + UINT8 dummy[3]; + 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; + UINT8 NumPermClasses; + UINT8 dummy[3]; + ADAPT_CLASS Class[MAX_NUM_CLASSES]; +} ADAPT_TEMPLATES_STRUCT; +typedef ADAPT_TEMPLATES_STRUCT *ADAPT_TEMPLATES; + +/**---------------------------------------------------------------------------- + Public Function Prototypes +----------------------------------------------------------------------------**/ +#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++) + +int 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); + +ADAPT_TEMPLATES NewAdaptedTemplates(); + +void free_adapted_templates(ADAPT_TEMPLATES templates); + +TEMP_CONFIG NewTempConfig(int MaxProtoId); + +TEMP_PROTO NewTempProto(); + +void PrintAdaptedTemplates(FILE *File, ADAPT_TEMPLATES Templates); + +ADAPT_CLASS ReadAdaptedClass(FILE *File); + +ADAPT_TEMPLATES ReadAdaptedTemplates(FILE *File); + +PERM_CONFIG ReadPermConfig(FILE *File); + +TEMP_CONFIG ReadTempConfig(FILE *File); + +void WriteAdaptedClass(FILE *File, ADAPT_CLASS Class, int NumConfigs); + +void WriteAdaptedTemplates(FILE *File, ADAPT_TEMPLATES Templates); + +void WritePermConfig(FILE *File, PERM_CONFIG Config); + +void WriteTempConfig(FILE *File, TEMP_CONFIG Config); + +/* +#if defined(__STDC__) || defined(__cplusplus) +# define _ARGS(s) s +#else +# define _ARGS(s) () +#endif*/ + +/* adaptive.c +int AddAdaptedClass + _ARGS((ADAPT_TEMPLATES Templates, + ADAPT_CLASS Class, + CLASS_ID ClassId)); + +void FreeTempConfig + _ARGS((TEMP_CONFIG Config)); + +ADAPT_CLASS NewAdaptedClass + _ARGS((void)); + +ADAPT_TEMPLATES NewAdaptedTemplates + _ARGS((void)); + +TEMP_CONFIG NewTempConfig + _ARGS((int MaxProtoId)); + +TEMP_PROTO NewTempProto + _ARGS((void)); + +void PrintAdaptedTemplates + _ARGS((FILE *File, + ADAPT_TEMPLATES Templates)); + +ADAPT_CLASS ReadAdaptedClass + _ARGS((FILE *File)); + +ADAPT_TEMPLATES ReadAdaptedTemplates + _ARGS((FILE *File)); + +PERM_CONFIG ReadPermConfig + _ARGS((FILE *File)); + +TEMP_CONFIG ReadTempConfig + _ARGS((FILE *File)); + +void WriteAdaptedClass + _ARGS((FILE *File, + ADAPT_CLASS Class, + int NumConfigs)); + +void WriteAdaptedTemplates + _ARGS((FILE *File, + ADAPT_TEMPLATES Templates)); + +void WritePermConfig + _ARGS((FILE *File, + PERM_CONFIG Config)); + +void WriteTempConfig + _ARGS((FILE *File, + TEMP_CONFIG Config)); + +#undef _ARGS +*/ +#endif diff --git a/classify/adaptmatch.cpp b/classify/adaptmatch.cpp new file mode 100644 index 0000000000..7602e2e8fe --- /dev/null +++ b/classify/adaptmatch.cpp @@ -0,0 +1,3247 @@ +/****************************************************************************** + ** 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 +----------------------------------------------------------------------------**/ +#include +#include "adaptmatch.h" +#include "normfeat.h" +#include "mfoutline.h" +#include "picofeat.h" +#include "float2int.h" +#include "outfeat.h" +#include "emalloc.h" +#include "intfx.h" +#include "permnum.h" +#include "speckle.h" +#include "efio.h" +#include "normmatch.h" +#include "stopper.h" +#include "permute.h" +#include "context.h" +#include "ndminx.h" +#include "intproto.h" +#include "const.h" +#include "globals.h" +#include "werd.h" +#include "callcpp.h" +#include "tordvars.h" + +#include +#include +#include +#include +#include +#ifdef __UNIX__ +#include +#endif + +#define ADAPT_TEMPLATE_SUFFIX ".a" +#define BUILT_IN_TEMPLATES_FILE "tessdata/inttemp" +#define BUILT_IN_CUTOFFS_FILE "tessdata/pffmtable" + +#define MAX_MATCHES 10 +#define UNLIKELY_NUM_FEAT 200 +#define NO_DEBUG 0 +#define MAX_ADAPTABLE_WERD_SIZE 40 +#define ADAPTABLE_WERD (GOOD_NUMBER + 0.05) + +#define Y_DIM_OFFSET (Y_SHIFT - BASELINE_Y_SHIFT) + +#define WORST_POSSIBLE_RATING (1.0) + +typedef struct +{ + FLOAT32 BlobLength; + int NumMatches; + CLASS_ID Classes[MAX_NUM_CLASSES]; + FLOAT32 Ratings[MAX_CLASS_ID + 1]; + UINT8 Configs[MAX_CLASS_ID + 1]; + FLOAT32 BestRating; + CLASS_ID BestClass; + UINT8 BestConfig; +} + + +ADAPT_RESULTS; + +typedef struct +{ + ADAPT_TEMPLATES Templates; + CLASS_ID ClassId; + int ConfigId; +} + + +PROTO_KEY; + +/**---------------------------------------------------------------------------- + Private Macros +----------------------------------------------------------------------------**/ +#define MarginalMatch(Rating) \ +((Rating) > GreatAdaptiveMatch) + +#define TempConfigReliable(Config) \ +((Config)->NumTimesSeen > ReliableConfigThreshold) + +#define InitIntFX() (FeaturesHaveBeenExtracted = FALSE) + +/**---------------------------------------------------------------------------- + Private Function Prototypes +----------------------------------------------------------------------------**/ +void AdaptToChar(TBLOB *Blob, + LINE_STATS *LineStats, + CLASS_ID ClassId, + FLOAT32 Threshold); + +void AdaptToPunc(TBLOB *Blob, + LINE_STATS *LineStats, + CLASS_ID ClassId, + FLOAT32 Threshold); + +void AddNewResult(ADAPT_RESULTS *Results, + CLASS_ID ClassId, + FLOAT32 Rating, + int ConfigId); + +void AmbigClassifier(TBLOB *Blob, + LINE_STATS *LineStats, + INT_TEMPLATES Templates, + char *Ambiguities, + ADAPT_RESULTS *Results); + +char *BaselineClassifier(TBLOB *Blob, + LINE_STATS *LineStats, + ADAPT_TEMPLATES Templates, + ADAPT_RESULTS *Results); + +void make_config_pruner(INT_TEMPLATES templates, CONFIG_PRUNER *config_pruner); + +void CharNormClassifier(TBLOB *Blob, + LINE_STATS *LineStats, + INT_TEMPLATES Templates, + ADAPT_RESULTS *Results); + +void ClassifyAsNoise(TBLOB *Blob, + LINE_STATS *LineStats, + ADAPT_RESULTS *Results); + + //CLASS_ID *Class1, +int CompareCurrentRatings(const void *arg1, + const void *arg2); //CLASS_ID *Class2); + +LIST ConvertMatchesToChoices(ADAPT_RESULTS *Results); + +void DebugAdaptiveClassifier(TBLOB *Blob, + LINE_STATS *LineStats, + ADAPT_RESULTS *Results); + +void DoAdaptiveMatch(TBLOB *Blob, + LINE_STATS *LineStats, + ADAPT_RESULTS *Results); + +void GetAdaptThresholds (TWERD * Word, +LINE_STATS * LineStats, +const char *BestChoice, +const char *BestRawChoice, FLOAT32 Thresholds[]); + +char *GetAmbiguities(TBLOB *Blob, + LINE_STATS *LineStats, + CLASS_ID CorrectClass); + +int GetBaselineFeatures(TBLOB *Blob, + LINE_STATS *LineStats, + INT_TEMPLATES Templates, + INT_FEATURE_ARRAY IntFeatures, + CLASS_NORMALIZATION_ARRAY CharNormArray, + FLOAT32 *BlobLength); + +FLOAT32 GetBestRatingFor(TBLOB *Blob, LINE_STATS *LineStats, CLASS_ID ClassId); + +int GetCharNormFeatures(TBLOB *Blob, + LINE_STATS *LineStats, + INT_TEMPLATES Templates, + INT_FEATURE_ARRAY IntFeatures, + CLASS_NORMALIZATION_ARRAY CharNormArray, + FLOAT32 *BlobLength); + +int GetIntBaselineFeatures(TBLOB *Blob, + LINE_STATS *LineStats, + INT_TEMPLATES Templates, + INT_FEATURE_ARRAY IntFeatures, + CLASS_NORMALIZATION_ARRAY CharNormArray, + FLOAT32 *BlobLength); + +int GetIntCharNormFeatures(TBLOB *Blob, + LINE_STATS *LineStats, + INT_TEMPLATES Templates, + INT_FEATURE_ARRAY IntFeatures, + CLASS_NORMALIZATION_ARRAY CharNormArray, + FLOAT32 *BlobLength); + +void InitMatcherRatings(register FLOAT32 *Rating); + +void MakeNewTemporaryConfig(ADAPT_TEMPLATES Templates, + CLASS_ID ClassId, + int NumFeatures, + INT_FEATURE_ARRAY Features, + FEATURE_SET FloatFeatures); + +PROTO_ID MakeNewTempProtos (FEATURE_SET Features, +int NumBadFeat, +FEATURE_ID BadFeat[], +INT_CLASS IClass, +ADAPT_CLASS Class, BIT_VECTOR TempProtoMask); + +void MakePermanent(ADAPT_TEMPLATES Templates, + CLASS_ID ClassId, + int ConfigId, + TBLOB *Blob, + LINE_STATS *LineStats); + +int MakeTempProtoPerm(void *item1, void *item2); + +int NumBlobsIn(TWERD *Word); + +int NumOutlinesInBlob(TBLOB *Blob); + +void PrintAdaptiveMatchResults(FILE *File, ADAPT_RESULTS *Results); + +void RemoveBadMatches(ADAPT_RESULTS *Results); + +void RemoveExtraPuncs(ADAPT_RESULTS *Results); + +void SetAdaptiveThreshold(FLOAT32 Threshold); +void ShowBestMatchFor(TBLOB *Blob, + LINE_STATS *LineStats, + CLASS_ID ClassId, + BOOL8 AdaptiveOn, + BOOL8 PreTrainedOn); + +/* +#if defined(__STDC__) || defined(__cplusplus) +# define _ARGS(s) s +#else +# define _ARGS(s) () +#endif*/ + +/* /users/danj/wiseowl/src/danj/microfeatures/adaptmatch.c +int AdaptableWord + _ARGS((TWERD *Word, + char *BestChoice, + char *BestRawChoice)); + + void AdaptToChar + _ARGS((BLOB *Blob, + LINE_STATS *LineStats, + CLASS_ID ClassId, + FLOAT32 Threshold)); + + void AdaptToPunc + _ARGS((BLOB *Blob, + LINE_STATS *LineStats, + CLASS_ID ClassId, + FLOAT32 Threshold)); + + void AddNewResult + _ARGS((ADAPT_RESULTS *Results, + CLASS_ID ClassId, + FLOAT32 Rating, + int ConfigId)); + + void AmbigClassifier + _ARGS((BLOB *Blob, + LINE_STATS *LineStats, + INT_TEMPLATES Templates, + char *Ambiguities, + ADAPT_RESULTS *Results)); + + char *BaselineClassifier + _ARGS((BLOB *Blob, + LINE_STATS *LineStats, + ADAPT_TEMPLATES Templates, + ADAPT_RESULTS *Results)); + + void CharNormClassifier + _ARGS((BLOB *Blob, + LINE_STATS *LineStats, + INT_TEMPLATES Templates, + ADAPT_RESULTS *Results)); + + void ClassifyAsNoise + _ARGS((BLOB *Blob, + LINE_STATS *LineStats, + ADAPT_RESULTS *Results)); + + int CompareCurrentRatings + _ARGS((CLASS_ID *Class1, + CLASS_ID *Class2)); + + LIST ConvertMatchesToChoices + _ARGS((ADAPT_RESULTS *Results)); + + void DebugAdaptiveClassifier + _ARGS((BLOB *Blob, + LINE_STATS *LineStats, + ADAPT_RESULTS *Results)); + + void DoAdaptiveMatch + _ARGS((BLOB *Blob, + LINE_STATS *LineStats, + ADAPT_RESULTS *Results)); + + void GetAdaptThresholds + _ARGS((TWERD *Word, + LINE_STATS *LineStats, + char *BestChoice, + char *BestRawChoice, + FLOAT32 Thresholds [])); + +int GetAdaptiveFeatures + _ARGS((BLOB *Blob, + LINE_STATS *LineStats, + INT_FEATURE_ARRAY IntFeatures, + CHAR_DESC *FloatFeatures)); + + char *GetAmbiguities + _ARGS((BLOB *Blob, + LINE_STATS *LineStats, + CLASS_ID CorrectClass)); + + int GetBaselineFeatures + _ARGS((BLOB *Blob, + LINE_STATS *LineStats, + INT_TEMPLATES Templates, + INT_FEATURE_ARRAY IntFeatures, + CLASS_NORMALIZATION_ARRAY CharNormArray, + FLOAT32 *BlobLength)); + + FLOAT32 GetBestRatingFor + _ARGS((BLOB *Blob, + LINE_STATS *LineStats, + CLASS_ID ClassId)); + + int GetCharNormFeatures + _ARGS((BLOB *Blob, + LINE_STATS *LineStats, + INT_TEMPLATES Templates, + INT_FEATURE_ARRAY IntFeatures, + CLASS_NORMALIZATION_ARRAY CharNormArray, + FLOAT32 *BlobLength)); + + int GetIntBaselineFeatures + _ARGS((BLOB *Blob, + LINE_STATS *LineStats, + INT_TEMPLATES Templates, + INT_FEATURE_ARRAY IntFeatures, + CLASS_NORMALIZATION_ARRAY CharNormArray, + FLOAT32 *BlobLength)); + + int GetIntCharNormFeatures + _ARGS((BLOB *Blob, + LINE_STATS *LineStats, + INT_TEMPLATES Templates, + INT_FEATURE_ARRAY IntFeatures, + CLASS_NORMALIZATION_ARRAY CharNormArray, + FLOAT32 *BlobLength)); + + void InitMatcherRatings + _ARGS((FLOAT32 *Rating)); + +void MakeNewAdaptedClass + _ARGS((BLOB *Blob, + LINE_STATS *LineStats, + CLASS_ID ClassId, + ADAPT_TEMPLATES Templates)); + + void MakeNewTemporaryConfig + _ARGS((ADAPT_TEMPLATES Templates, + CLASS_ID ClassId, + int NumFeatures, + INT_FEATURE_ARRAY Features, + FEATURE_SET FloatFeatures)); + + PROTO_ID MakeNewTempProtos + _ARGS((FEATURE_SET Features, + int NumBadFeat, + FEATURE_ID BadFeat [], + INT_CLASS IClass, + ADAPT_CLASS Class, + BIT_VECTOR TempProtoMask)); + + void MakePermanent + _ARGS((ADAPT_TEMPLATES Templates, + CLASS_ID ClassId, + int ConfigId, + BLOB *Blob, + LINE_STATS *LineStats)); + + int MakeTempProtoPerm + _ARGS((TEMP_PROTO TempProto, + PROTO_KEY *ProtoKey)); + + int NumBlobsIn + _ARGS((TWERD *Word)); + + int NumOutlinesInBlob + _ARGS((BLOB *Blob)); + + void PrintAdaptiveMatchResults + _ARGS((FILE *File, + ADAPT_RESULTS *Results)); + + void RemoveBadMatches + _ARGS((ADAPT_RESULTS *Results)); + void RemoveExtraPuncs + _ARGS((ADAPT_RESULTS *Results)); + + void SetAdaptiveThreshold + _ARGS((FLOAT32 Threshold)); + + void ShowBestMatchFor + _ARGS((BLOB *Blob, + LINE_STATS *LineStats, + CLASS_ID ClassId, + BOOL8 AdaptiveOn, + BOOL8 PreTrainedOn)); + +#undef _ARGS +*/ + +/**---------------------------------------------------------------------------- + Global Data Definitions and Declarations +----------------------------------------------------------------------------**/ +/* name of current image file being processed */ +extern char imagefile[]; +//extern char *demodir; +INT_VAR (tessedit_single_match, FALSE, "Top choice only from CP"); + +//extern "C" int il1_adaption_test; //? +//extern int display_ratings; +//extern "C" int newcp_ratings_on; +//extern int config_pruner_enabled; +//extern "C" int feature_prune_percentile; +//extern "C" double newcp_duff_rating; +/* variables used to hold performance statistics */ +static int AdaptiveMatcherCalls = 0; +static int BaselineClassifierCalls = 0; +static int CharNormClassifierCalls = 0; +static int AmbigClassifierCalls = 0; +static int NumWordsAdaptedTo = 0; +static int NumCharsAdaptedTo = 0; +static int NumBaselineClassesTried = 0; +static int NumCharNormClassesTried = 0; +static int NumAmbigClassesTried = 0; +static int NumClassesOutput = 0; + +/* define globals used to hold onto extracted features. This is used +to map from the old scheme in which baseline features and char norm +features are extracted separately, to the new scheme in which they +are extracted at the same time. */ +static BOOL8 FeaturesHaveBeenExtracted = FALSE; +static BOOL8 FeaturesOK = TRUE; +static INT_FEATURE_ARRAY BaselineFeatures; +static INT_FEATURE_ARRAY CharNormFeatures; +//static CLASS_NORMALIZATION_ARRAY +// NormalizationAdjustments; +static INT_FX_RESULT_STRUCT FXInfo; + +/* use a global variable to hold onto the current ratings so that the +comparison function passes to qsort can get at them */ +static FLOAT32 *CurrentRatings; + +/* define globals to hold filenames of training data */ +static const char *BuiltInTemplatesFile = BUILT_IN_TEMPLATES_FILE; +static const char *BuiltInCutoffsFile = BUILT_IN_CUTOFFS_FILE; +static CLASS_CUTOFF_ARRAY CharNormCutoffs; +static CLASS_CUTOFF_ARRAY BaselineCutoffs; + +/* use global variables to hold onto built-in templates and adapted +templates */ +static INT_TEMPLATES PreTrainedTemplates; +static ADAPT_TEMPLATES AdaptedTemplates; + +/* create dummy proto and config masks for use with the built-in templates */ +static BIT_VECTOR AllProtosOn; +static BIT_VECTOR PrunedProtos; +static BIT_VECTOR AllConfigsOn; +static BIT_VECTOR AllProtosOff; +static BIT_VECTOR AllConfigsOff; +static BIT_VECTOR TempProtoMask; + +/* define control knobs for adaptive matcher */ +make_toggle_const (EnableAdaptiveMatcher, 1, MakeEnableAdaptiveMatcher); +/* PREV DEFAULT 0 */ + +make_toggle_const (UsePreAdaptedTemplates, 0, MakeUsePreAdaptedTemplates); +make_toggle_const (SaveAdaptedTemplates, 0, MakeSaveAdaptedTemplates); + +make_toggle_var (EnableAdaptiveDebugger, 0, MakeEnableAdaptiveDebugger, +18, 1, SetEnableAdaptiveDebugger, "Enable match debugger"); + +make_int_var (MatcherDebugLevel, 0, MakeMatcherDebugLevel, +18, 2, SetMatcherDebugLevel, "Matcher Debug Level: "); + +make_int_var (MatchDebugFlags, 0, MakeMatchDebugFlags, +18, 3, SetMatchDebugFlags, "Matcher Debug Flags: "); + +make_toggle_var (EnableLearning, 1, MakeEnableLearning, +18, 4, SetEnableLearning, "Enable learning"); +/* PREV DEFAULT 0 */ + /*record it for multiple pages */ +static int old_enable_learning = 1; + +make_int_var (LearningDebugLevel, 0, MakeLearningDebugLevel, +18, 5, SetLearningDebugLevel, "Learning Debug Level: "); + +make_float_var (GoodAdaptiveMatch, 0.125, MakeGoodAdaptiveMatch, +18, 6, SetGoodAdaptiveMatch, "Good Match (0-1): "); + +make_float_var (GreatAdaptiveMatch, 0.0, MakeGreatAdaptiveMatch, +18, 7, SetGreatAdaptiveMatch, "Great Match (0-1): "); +/* PREV DEFAULT 0.10 */ + +make_float_var (PerfectRating, 0.02, MakePerfectRating, +18, 8, SetPerfectRating, "Perfect Match (0-1): "); + +make_float_var (BadMatchPad, 0.15, MakeBadMatchPad, +18, 9, SetBadMatchPad, "Bad Match Pad (0-1): "); + +make_float_var (RatingMargin, 0.1, MakeRatingMargin, +18, 10, SetRatingMargin, "New template margin (0-1): "); + +make_float_var (NoiseBlobLength, 0.6, MakeNoiseBlobLength, +18, 11, SetNoiseBlobLength, "Avg. noise blob length: "); + +make_int_var (MinNumPermClasses, 3, MakeMinNumPermClasses, +18, 12, SetMinNumPermClasses, "Min # of permanent classes: "); +/* PREV DEFAULT 200 */ + +make_int_var (ReliableConfigThreshold, 2, MakeReliableConfigThreshold, +18, 13, SetReliableConfigThreshold, +"Reliable Config Threshold: "); + +make_float_var (MaxAngleDelta, 0.015, MakeMaxAngleDelta, +18, 14, SetMaxAngleDelta, +"Maximum angle delta for proto clustering: "); + +make_toggle_var (EnableIntFX, 1, MakeEnableIntFX, +18, 15, SetEnableIntFX, "Enable integer fx"); +/* PREV DEFAULT 0 */ + +make_toggle_var (EnableNewAdaptRules, 1, MakeEnableNewAdaptRules, +18, 16, SetEnableNewAdaptRules, +"Enable new adaptation rules"); +/* PREV DEFAULT 0 */ + +make_float_var (RatingScale, 30.0, MakeRatingScale, +18, 17, SetRatingScale, "Rating scale: "); + +make_float_var (CertaintyScale, 20.0, MakeCertaintyScale, +18, 18, SetCertaintyScale, "CertaintyScale: "); + +int tess_cn_matching = 0; +int tess_bn_matching = 0; + +/**---------------------------------------------------------------------------- + Public Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +LIST AdaptiveClassifier(TBLOB *Blob, TBLOB *DotBlob, TEXTROW *Row) { +/* + ** Parameters: + ** Blob blob to be classified + ** DotBlob (obsolete) + ** Row row of text that word appears in + ** Globals: + ** CurrentRatings + used by compare function for qsort +** Operation: 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. +** Return: List of choices found by adaptive matcher. +** Exceptions: none +** History: Mon Mar 11 10:00:58 1991, DSJ, Created. +*/ + LIST Choices; + ADAPT_RESULTS Results; + LINE_STATS LineStats; + + if (AdaptedTemplates == NULL) + AdaptedTemplates = NewAdaptedTemplates (); + EnterClassifyMode; + + Results.BlobLength = MAX_FLOAT32; + Results.NumMatches = 0; + Results.BestRating = WORST_POSSIBLE_RATING; + Results.BestClass = NO_CLASS; + Results.BestConfig = 0; + GetLineStatsFromRow(Row, &LineStats); + InitMatcherRatings (Results.Ratings); + + DoAdaptiveMatch(Blob, &LineStats, &Results); + RemoveBadMatches(&Results); + + /* save ratings in a global so that CompareCurrentRatings() can see them */ + CurrentRatings = Results.Ratings; + qsort ((void *) (Results.Classes), Results.NumMatches, + sizeof (CLASS_ID), CompareCurrentRatings); + RemoveExtraPuncs(&Results); + Choices = ConvertMatchesToChoices (&Results); + + if (MatcherDebugLevel >= 1) { + cprintf ("AD Matches = "); + PrintAdaptiveMatchResults(stdout, &Results); + } + + if (LargeSpeckle (Blob, Row)) + Choices = AddLargeSpeckleTo (Choices); + +#ifndef GRAPHICS_DISABLED + if (EnableAdaptiveDebugger) + DebugAdaptiveClassifier(Blob, &LineStats, &Results); +#endif + + NumClassesOutput += count (Choices); + if (Choices == NIL) { + if (!bln_numericmode) + printf ("Nil classification!\n"); // Should never normally happen. + return (append_choice (NIL, "", 50.0f, -20.0f, -1)); + } + + return (Choices); + +} /* AdaptiveClassifier */ + + +/*---------------------------------------------------------------------------*/ +void AdaptToWord(TWERD *Word, + TEXTROW *Row, + const char *BestChoice, + const char *BestRawChoice, + const char *rejmap) { +/* + ** Parameters: + ** Word + word to be adapted to +** Row + row of text that word is found in +** BestChoice + best choice for word found by system +** BestRawChoice + best choice for word found by classifier only +** Globals: +** EnableLearning + TRUE if learning is enabled +** Operation: This routine implements a preliminary version of the +** rules which are used to decide which characters to adapt to. +** A word is adapted to if it is in the dictionary or if it +** is a "good" number (no trailing units, etc.). It cannot +** contain broken or merged characters. Within that word, only +** letters and digits are adapted to (no punctuation). +** Return: none +** Exceptions: none +** History: Thu Mar 14 07:40:36 1991, DSJ, Created. +*/ + TBLOB *Blob; + LINE_STATS LineStats; + FLOAT32 Thresholds[MAX_ADAPTABLE_WERD_SIZE]; + FLOAT32 *Threshold; + const char *map = rejmap; + char map_char = '1'; + + if (EnableLearning) { + NumWordsAdaptedTo++; + + #ifndef SECURE_NAMES + if (LearningDebugLevel >= 1) + cprintf ("\n\nAdapting to word = %s\n", BestChoice); + #endif + GetLineStatsFromRow(Row, &LineStats); + + GetAdaptThresholds(Word, + &LineStats, + BestChoice, + BestRawChoice, + Thresholds); + + for (Blob = Word->blobs, Threshold = Thresholds; + Blob != NULL; Blob = Blob->next, BestChoice++, Threshold++) { + InitIntFX(); + + if (rejmap != NULL) + map_char = *map++; + + assert (map_char == '1' || map_char == '0'); + + if (map_char == '1') { + + if (isalnum (*BestChoice)) { + /* SPECIAL RULE: don't adapt to an 'i' which is the first char + in a word because they are too ambiguous with 'I'. + The new adaptation rules should account for this + automatically, since they exclude ambiguous words from + adaptation, but for safety's sake we'll leave the rule in. + Also, don't adapt to i's that have only 1 blob in them + because this creates too much ambiguity for broken + characters. */ + if ((*BestChoice == 'i' + || il1_adaption_test && *BestChoice == 'I' + && islower (BestChoice[1])) && (Blob == Word->blobs + || + ispunct (* + (BestChoice - + 1)) + || !il1_adaption_test + && + NumOutlinesInBlob + (Blob) != 2)) { + if (LearningDebugLevel >= 1) + cprintf ("Rejecting char = %c\n", *BestChoice); + } + else { + #ifndef SECURE_NAMES + if (LearningDebugLevel >= 1) + cprintf ("Adapting to char = %c, thr= %g\n", *BestChoice, *Threshold); + #endif + AdaptToChar(Blob, &LineStats, *BestChoice, *Threshold); + } + } + else + AdaptToPunc(Blob, &LineStats, *BestChoice, *Threshold); + } + } + if (LearningDebugLevel >= 1) + cprintf ("\n"); + } +} /* AdaptToWord */ + + +/*---------------------------------------------------------------------------*/ +void EndAdaptiveClassifier() { +/* + ** Parameters: none + ** Globals: + ** AdaptedTemplates + current set of adapted templates +** SaveAdaptedTemplates + TRUE if templates should be saved +** EnableAdaptiveMatcher + TRUE if adaptive matcher is enabled +** Operation: 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. +** Return: none +** Exceptions: none +** History: Tue Mar 19 14:37:06 1991, DSJ, Created. +*/ + char Filename[256]; + FILE *File; + + #ifndef SECURE_NAMES + if (EnableAdaptiveMatcher && SaveAdaptedTemplates) { + strcpy(Filename, imagefile); + strcat(Filename, ADAPT_TEMPLATE_SUFFIX); + File = fopen (Filename, "wb"); + if (File == NULL) + cprintf ("Unable to save adapted templates to %s!\n", Filename); + else { + cprintf ("\nSaving adapted templates to %s ...", Filename); + fflush(stdout); + WriteAdaptedTemplates(File, AdaptedTemplates); + cprintf ("\n"); + fclose(File); + } + } + #endif + EndDangerousAmbigs(); + FreeNormProtos(); + free_int_templates(PreTrainedTemplates); + PreTrainedTemplates = NULL; + FreeBitVector(AllProtosOn); + FreeBitVector(PrunedProtos); + FreeBitVector(AllConfigsOn); + FreeBitVector(AllProtosOff); + FreeBitVector(AllConfigsOff); + FreeBitVector(TempProtoMask); + AllProtosOn = NULL; + PrunedProtos = NULL; + AllConfigsOn = NULL; + AllProtosOff = NULL; + AllConfigsOff = NULL; + TempProtoMask = NULL; +} /* EndAdaptiveClassifier */ + + +/*---------------------------------------------------------------------------*/ +void InitAdaptiveClassifier() { +/* + ** Parameters: none + ** Globals: + ** BuiltInTemplatesFile + file to get built-in temps from +** BuiltInCutoffsFile + file to get avg. feat per class from +** PreTrainedTemplates + pre-trained configs and protos +** AdaptedTemplates + templates adapted to current page +** CharNormCutoffs + avg # of features per class +** AllProtosOn + dummy proto mask with all bits 1 +** AllConfigsOn + dummy config mask with all bits 1 +** UsePreAdaptedTemplates + enables use of pre-adapted templates +** Operation: This routine reads in the training information needed +** by the adaptive classifier and saves it into global +** variables. +** Return: none +** Exceptions: none +** History: Mon Mar 11 12:49:34 1991, DSJ, Created. +*/ + int i; + FILE *File; + char Filename[1024]; + + if (!EnableAdaptiveMatcher) + return; + + strcpy(Filename, demodir); + strcat(Filename, BuiltInTemplatesFile); + #ifndef SECURE_NAMES + // cprintf( "\nReading built-in templates from %s ...", + // Filename); + fflush(stdout); + #endif + + #ifdef __UNIX__ + File = Efopen (Filename, "r"); + #else + File = Efopen (Filename, "rb"); + #endif + PreTrainedTemplates = ReadIntTemplates (File, TRUE); + fclose(File); + + strcpy(Filename, demodir); + strcat(Filename, BuiltInCutoffsFile); + #ifndef SECURE_NAMES + // cprintf( "\nReading built-in pico-feature cutoffs from %s ...", + // Filename); + fflush(stdout); + #endif + ReadNewCutoffs (Filename, PreTrainedTemplates->IndexFor, CharNormCutoffs); + + GetNormProtos(); + + InitIntegerMatcher(); + InitIntegerFX(); + + AllProtosOn = NewBitVector (MAX_NUM_PROTOS); + PrunedProtos = NewBitVector (MAX_NUM_PROTOS); + AllConfigsOn = NewBitVector (MAX_NUM_CONFIGS); + AllProtosOff = NewBitVector (MAX_NUM_PROTOS); + AllConfigsOff = NewBitVector (MAX_NUM_CONFIGS); + TempProtoMask = NewBitVector (MAX_NUM_PROTOS); + set_all_bits (AllProtosOn, WordsInVectorOfSize (MAX_NUM_PROTOS)); + set_all_bits (PrunedProtos, WordsInVectorOfSize (MAX_NUM_PROTOS)); + set_all_bits (AllConfigsOn, WordsInVectorOfSize (MAX_NUM_CONFIGS)); + zero_all_bits (AllProtosOff, WordsInVectorOfSize (MAX_NUM_PROTOS)); + zero_all_bits (AllConfigsOff, WordsInVectorOfSize (MAX_NUM_CONFIGS)); + + if (UsePreAdaptedTemplates) { + strcpy(Filename, imagefile); + strcat(Filename, ADAPT_TEMPLATE_SUFFIX); + File = fopen (Filename, "rb"); + if (File == NULL) + AdaptedTemplates = NewAdaptedTemplates (); + else { + #ifndef SECURE_NAMES + cprintf ("\nReading pre-adapted templates from %s ...", Filename); + fflush(stdout); + #endif + AdaptedTemplates = ReadAdaptedTemplates (File); + cprintf ("\n"); + fclose(File); + PrintAdaptedTemplates(stdout, AdaptedTemplates); + + for (i = 0; i < NumClassesIn (AdaptedTemplates->Templates); i++) { + BaselineCutoffs[i] = + CharNormCutoffs[IndexForClassId (PreTrainedTemplates, + ClassIdForIndex + (AdaptedTemplates->Templates, + i))]; + } + } + } + else + AdaptedTemplates = NewAdaptedTemplates (); + old_enable_learning = EnableLearning; + +} /* InitAdaptiveClassifier */ + +void ResetAdaptiveClassifier() { + free_adapted_templates(AdaptedTemplates); + AdaptedTemplates = NULL; +} + + +/*---------------------------------------------------------------------------*/ +void InitAdaptiveClassifierVars() { +/* + ** Parameters: none + ** Globals: none + ** Operation: This routine installs the control knobs used by the + ** adaptive matcher. + ** Return: none + ** Exceptions: none + ** History: Mon Mar 11 12:49:34 1991, DSJ, Created. + */ + VALUE dummy; + + string_variable (BuiltInTemplatesFile, "BuiltInTemplatesFile", + BUILT_IN_TEMPLATES_FILE); + string_variable (BuiltInCutoffsFile, "BuiltInCutoffsFile", + BUILT_IN_CUTOFFS_FILE); + + MakeEnableAdaptiveMatcher(); + MakeUsePreAdaptedTemplates(); + MakeSaveAdaptedTemplates(); + + MakeEnableLearning(); + MakeEnableAdaptiveDebugger(); + MakeBadMatchPad(); + MakeGoodAdaptiveMatch(); + MakeGreatAdaptiveMatch(); + MakeNoiseBlobLength(); + MakeMinNumPermClasses(); + MakeReliableConfigThreshold(); + MakeMaxAngleDelta(); + MakeLearningDebugLevel(); + MakeMatcherDebugLevel(); + MakeMatchDebugFlags(); + MakeRatingMargin(); + MakePerfectRating(); + MakeEnableIntFX(); + MakeEnableNewAdaptRules(); + MakeRatingScale(); + MakeCertaintyScale(); + + InitPicoFXVars(); + InitOutlineFXVars(); //? + +} /* InitAdaptiveClassifierVars */ + + +/*---------------------------------------------------------------------------*/ +void PrintAdaptiveStatistics(FILE *File) { +/* + ** Parameters: + ** File + open text file to print adaptive statistics to +** Globals: none +** Operation: Print to File the statistics which have been gathered +** for the adaptive matcher. +** Return: none +** Exceptions: none +** History: Thu Apr 18 14:37:37 1991, DSJ, Created. +*/ + #ifndef SECURE_NAMES + + fprintf (File, "\nADAPTIVE MATCHER STATISTICS:\n"); + fprintf (File, "\tNum blobs classified = %d\n", AdaptiveMatcherCalls); + fprintf (File, "\tNum classes output = %d (Avg = %4.2f)\n", + NumClassesOutput, + ((AdaptiveMatcherCalls == 0) ? (0.0) : + ((float) NumClassesOutput / AdaptiveMatcherCalls))); + fprintf (File, "\t\tBaseline Classifier: %4d calls (%4.2f classes/call)\n", + BaselineClassifierCalls, + ((BaselineClassifierCalls == 0) ? (0.0) : + ((float) NumBaselineClassesTried / BaselineClassifierCalls))); + fprintf (File, "\t\tCharNorm Classifier: %4d calls (%4.2f classes/call)\n", + CharNormClassifierCalls, + ((CharNormClassifierCalls == 0) ? (0.0) : + ((float) NumCharNormClassesTried / CharNormClassifierCalls))); + fprintf (File, "\t\tAmbig Classifier: %4d calls (%4.2f classes/call)\n", + AmbigClassifierCalls, + ((AmbigClassifierCalls == 0) ? (0.0) : + ((float) NumAmbigClassesTried / AmbigClassifierCalls))); + + fprintf (File, "\nADAPTIVE LEARNER STATISTICS:\n"); + fprintf (File, "\tNumber of words adapted to: %d\n", NumWordsAdaptedTo); + fprintf (File, "\tNumber of chars adapted to: %d\n", NumCharsAdaptedTo); + + PrintAdaptedTemplates(File, AdaptedTemplates); + #endif +} /* PrintAdaptiveStatistics */ + + +/*---------------------------------------------------------------------------*/ +void SettupPass1() { +/* + ** Parameters: none + ** Globals: + ** EnableLearning + set to TRUE by this routine +** Operation: This routine prepares the adaptive matcher for the start +** of the first pass. Learning is enabled (unless it is +** disabled for the whole program). +** Return: none +** Exceptions: none +** History: Mon Apr 15 16:39:29 1991, DSJ, Created. +*/ + /* 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. */ + EnableLearning = old_enable_learning; + + SettupStopperPass1(); + +} /* SettupPass1 */ + + +/*---------------------------------------------------------------------------*/ +void SettupPass2() { +/* + ** Parameters: none + ** Globals: + ** EnableLearning + set to FALSE by this routine +** Operation: This routine prepares the adaptive matcher for the start +** of the second pass. Further learning is disabled. +** Return: none +** Exceptions: none +** History: Mon Apr 15 16:39:29 1991, DSJ, Created. +*/ + EnableLearning = FALSE; + SettupStopperPass2(); + +} /* SettupPass2 */ + + +/*---------------------------------------------------------------------------*/ +void MakeNewAdaptedClass(TBLOB *Blob, + LINE_STATS *LineStats, + CLASS_ID ClassId, + ADAPT_TEMPLATES Templates) { +/* + ** Parameters: + ** Blob + blob to model new class after +** LineStats + statistics for text row blob is in +** ClassId + id of new class to be created +** 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 +** Operation: This routine creates a new adapted class and uses Blob +** as the model for the first config in that class. +** Return: none +** Exceptions: none +** History: Thu Mar 14 12:49:39 1991, DSJ, Created. +*/ + FEATURE_SET Features; + int Fid, Pid; + FEATURE Feature; + int NumFeatures; + TEMP_PROTO TempProto; + PROTO Proto; + ADAPT_CLASS Class; + INT_CLASS IClass; + CLASS_INDEX ClassIndex; + TEMP_CONFIG Config; + + NormMethod = baseline; + Features = ExtractOutlineFeatures (Blob, LineStats); + NumFeatures = NumFeaturesIn (Features); + if (NumFeatures > UNLIKELY_NUM_FEAT) { + FreeFeatureSet(Features); + return; + } + + Class = NewAdaptedClass (); + ClassIndex = AddAdaptedClass (Templates, Class, ClassId); + Config = NewTempConfig (NumFeatures - 1); + TempConfigFor (Class, 0) = Config; + + /* this is a kludge to construct cutoffs for adapted templates */ + BaselineCutoffs[ClassIndex] = + CharNormCutoffs[IndexForClassId (PreTrainedTemplates, ClassId)]; + + IClass = ClassForClassId (Templates->Templates, ClassId); + + for (Fid = 0; Fid < NumFeaturesIn (Features); Fid++) { + Pid = AddIntProto (IClass); + assert (Pid != NO_PROTO); + + Feature = FeatureIn (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 */ + ProtoAngle (Proto) = ParamOf (Feature, OutlineFeatDir); + ProtoX (Proto) = ParamOf (Feature, OutlineFeatX); + ProtoY (Proto) = ParamOf (Feature, OutlineFeatY) - Y_DIM_OFFSET; + ProtoLength (Proto) = ParamOf (Feature, OutlineFeatLength); + FillABC(Proto); + + TempProto->ProtoId = Pid; + SET_BIT (Config->Protos, Pid); + + ConvertProto(Proto, Pid, IClass); + AddProtoToProtoPruner(Proto, Pid, IClass); + + Class->TempProtos = push (Class->TempProtos, TempProto); + } + FreeFeatureSet(Features); + + AddIntConfig(IClass); + ConvertConfig (AllProtosOn, 0, IClass); + + if (LearningDebugLevel >= 1) { + cprintf ("Added new class '%c' with index %d and %d protos.\n", + ClassId, ClassIndex, NumFeatures); + } + +} /* MakeNewAdaptedClass */ + + +/*---------------------------------------------------------------------------*/ +int GetAdaptiveFeatures(TBLOB *Blob, + LINE_STATS *LineStats, + INT_FEATURE_ARRAY IntFeatures, + FEATURE_SET *FloatFeatures) { +/* + ** Parameters: + ** Blob + blob to extract features from +** LineStats + statistics about text row blob is in +** IntFeatures + array to fill with integer features +** FloatFeatures + place to return actual floating-pt features +** Globals: none +** Operation: 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. +** Return: Number of pico-features returned (0 if an error occurred) +** Exceptions: none +** History: Tue Mar 12 17:55:18 1991, DSJ, Created. +*/ + FEATURE_SET Features; + int NumFeatures; + + NormMethod = baseline; + Features = ExtractPicoFeatures (Blob, LineStats); + + NumFeatures = NumFeaturesIn (Features); + if (NumFeatures > UNLIKELY_NUM_FEAT) { + FreeFeatureSet(Features); + return (0); + } + + ComputeIntFeatures(Features, IntFeatures); + *FloatFeatures = Features; + + return (NumFeatures); + +} /* GetAdaptiveFeatures */ + + +/**---------------------------------------------------------------------------- + Private Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +int AdaptableWord(TWERD *Word, + const char *BestChoice, + const char *BestRawChoice) { +/* + ** Parameters: + ** Word + current word +** BestChoice + best overall choice for word with context +** BestRawChoice + best choice for word without context +** Globals: none +** Operation: Return TRUE if the specified word is acceptable for +** adaptation. +** Return: TRUE or FALSE +** Exceptions: none +** History: Thu May 30 14:25:06 1991, DSJ, Created. +*/ + int BestChoiceLength; + + return ( /* rules that apply in general - simplest to compute first */ + /* EnableLearning && */ + /* new rules */ + BestChoice != NULL && BestRawChoice != NULL && Word != NULL && (BestChoiceLength = strlen (BestChoice)) > 0 && BestChoiceLength == NumBlobsIn (Word) && BestChoiceLength <= MAX_ADAPTABLE_WERD_SIZE && ( + EnableNewAdaptRules + && + CurrentBestChoiceAdjustFactor + () + <= + ADAPTABLE_WERD + && + AlternativeChoicesWorseThan + (ADAPTABLE_WERD) + && + CurrentBestChoiceIs + (BestChoice) + || + /* old rules */ + !EnableNewAdaptRules + && + BestChoiceLength + == + strlen + (BestRawChoice) + && + ((valid_word (BestChoice) && case_ok (BestChoice)) || (valid_number (BestChoice) && pure_number (BestChoice))) && punctuation_ok (BestChoice) != -1 && punctuation_ok (BestChoice) <= 1)); + +} /* AdaptableWord */ + + +/*---------------------------------------------------------------------------*/ +void AdaptToChar(TBLOB *Blob, + LINE_STATS *LineStats, + CLASS_ID ClassId, + FLOAT32 Threshold) { +/* + ** Parameters: + ** Blob + blob to add to templates for ClassId +** LineStats + statistics about text line blob is in +** ClassId + class to add blob to +** Threshold + minimum match rating to existing template +** Globals: +** AdaptedTemplates + current set of adapted templates +** AllProtosOn + dummy mask to match against all protos +** AllConfigsOn + dummy mask to match against all configs +** Operation: +** Return: none +** Exceptions: none +** History: Thu Mar 14 09:36:03 1991, DSJ, Created. +*/ + int NumFeatures; + INT_FEATURE_ARRAY IntFeatures; + INT_RESULT_STRUCT IntResult; + CLASS_INDEX ClassIndex; + INT_CLASS IClass; + ADAPT_CLASS Class; + TEMP_CONFIG TempConfig; + FEATURE_SET FloatFeatures; + + NumCharsAdaptedTo++; + if (!LegalClassId (ClassId)) + return; + + if (UnusedClassIdIn (AdaptedTemplates->Templates, ClassId)) { + MakeNewAdaptedClass(Blob, LineStats, ClassId, AdaptedTemplates); + } + else { + IClass = ClassForClassId (AdaptedTemplates->Templates, ClassId); + ClassIndex = IndexForClassId (AdaptedTemplates->Templates, ClassId); + Class = AdaptedTemplates->Class[ClassIndex]; + + NumFeatures = GetAdaptiveFeatures (Blob, LineStats, + IntFeatures, &FloatFeatures); + if (NumFeatures <= 0) + return; + + SetBaseLineMatch(); + IntegerMatcher (IClass, AllProtosOn, AllConfigsOn, + NumFeatures, NumFeatures, IntFeatures, 0, 0, + &IntResult, NO_DEBUG); + + SetAdaptiveThreshold(Threshold); + + if (IntResult.Rating <= Threshold) { + if (ConfigIsPermanent (Class, IntResult.Config)) { + if (LearningDebugLevel >= 1) + cprintf ("Found good match to perm config %d = %4.1f%%.\n", + IntResult.Config, (1.0 - IntResult.Rating) * 100.0); + FreeFeatureSet(FloatFeatures); + return; + } + + TempConfig = TempConfigFor (Class, IntResult.Config); + IncreaseConfidence(TempConfig); + if (LearningDebugLevel >= 1) + cprintf ("Increasing reliability of temp config %d to %d.\n", + IntResult.Config, TempConfig->NumTimesSeen); + + if (TempConfigReliable (TempConfig)) + MakePermanent (AdaptedTemplates, ClassId, IntResult.Config, + Blob, LineStats); + } + else { + if (LearningDebugLevel >= 1) + cprintf ("Found poor match to temp config %d = %4.1f%%.\n", + IntResult.Config, (1.0 - IntResult.Rating) * 100.0); + MakeNewTemporaryConfig(AdaptedTemplates, + ClassId, + NumFeatures, + IntFeatures, + FloatFeatures); + if (LearningDebugLevel >= 1) { + IntegerMatcher (IClass, AllProtosOn, AllConfigsOn, + NumFeatures, NumFeatures, IntFeatures, 0, 0, + &IntResult, NO_DEBUG); + cprintf ("Best match to temp config %d = %4.1f%%.\n", + IntResult.Config, (1.0 - IntResult.Rating) * 100.0); + if (LearningDebugLevel >= 2) { + UINT32 ConfigMask; + ConfigMask = 1 << IntResult.Config; + ShowMatchDisplay(); + IntegerMatcher (IClass, AllProtosOn, (BIT_VECTOR)&ConfigMask, + NumFeatures, NumFeatures, IntFeatures, 0, 0, + &IntResult, 6 | 0x19); + UpdateMatchDisplay(); + GetClassToDebug ("Adapting"); + } + } + } + FreeFeatureSet(FloatFeatures); + } +} /* AdaptToChar */ + + +/*---------------------------------------------------------------------------*/ +void AdaptToPunc(TBLOB *Blob, + LINE_STATS *LineStats, + CLASS_ID ClassId, + FLOAT32 Threshold) { +/* + ** Parameters: + ** Blob + blob to add to templates for ClassId +** LineStats + statistics about text line blob is in +** ClassId + class to add blob to +** Threshold + minimum match rating to existing template +** Globals: +** PreTrainedTemplates + current set of built-in templates +** Operation: +** Return: none +** Exceptions: none +** History: Thu Mar 14 09:36:03 1991, DSJ, Created. +*/ + ADAPT_RESULTS Results; + int i; + + Results.BlobLength = MAX_FLOAT32; + Results.NumMatches = 0; + Results.BestRating = WORST_POSSIBLE_RATING; + Results.BestClass = NO_CLASS; + Results.BestConfig = 0; + InitMatcherRatings (Results.Ratings); + CharNormClassifier(Blob, LineStats, PreTrainedTemplates, &Results); + RemoveBadMatches(&Results); + + if (Results.NumMatches != 1) { + if (LearningDebugLevel >= 1) { + cprintf ("Rejecting punc = %c (Alternatives = ", ClassId); + + for (i = 0; i < Results.NumMatches; i++) + cprintf ("%c", Results.Classes[i]); + cprintf (")\n"); + } + return; + } + + #ifndef SECURE_NAMES + if (LearningDebugLevel >= 1) + cprintf ("Adapting to punc = %c\n", ClassId); + #endif + AdaptToChar(Blob, LineStats, ClassId, Threshold); + +} /* AdaptToPunc */ + + +/*---------------------------------------------------------------------------*/ +void AddNewResult(ADAPT_RESULTS *Results, + CLASS_ID ClassId, + FLOAT32 Rating, + int ConfigId) { +/* + ** Parameters: + ** Results + results to add new result to +** ClassId + class of new result +** Rating + rating of new result +** ConfigId + config id of new result +** Globals: +** BadMatchPad + defines limits of an acceptable match +** Operation: 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. +** Return: none +** Exceptions: none +** History: Tue Mar 12 18:19:29 1991, DSJ, Created. +*/ + FLOAT32 OldRating; + INT_CLASS_STRUCT* CharClass = NULL; + + OldRating = Results->Ratings[ClassId]; + if (Rating <= Results->BestRating + BadMatchPad && Rating < OldRating) { + Results->Ratings[ClassId] = Rating; + if (ClassId != NO_CLASS) + CharClass = ClassForClassId(PreTrainedTemplates, ClassId); + if (CharClass != NULL && NumIntConfigsIn(CharClass) == 32) + Results->Configs[ClassId] = ConfigId; + else + Results->Configs[ClassId] = ~0; + + if (Rating < Results->BestRating) { + Results->BestRating = Rating; + Results->BestClass = ClassId; + Results->BestConfig = ConfigId; + } + + /* if this is first rating for class, add to list of classes matched */ + if (OldRating == WORST_POSSIBLE_RATING) + Results->Classes[Results->NumMatches++] = ClassId; + } +} /* AddNewResult */ + + +/*---------------------------------------------------------------------------*/ +void AmbigClassifier(TBLOB *Blob, + LINE_STATS *LineStats, + INT_TEMPLATES Templates, + char *Ambiguities, + ADAPT_RESULTS *Results) { +/* + ** Parameters: + ** Blob + blob to be classified +** LineStats + statistics for text line Blob is in +** Templates + built-in templates to classify against +** Ambiguities + string of class id's to match against +** Results + place to put match results +** Globals: +** AllProtosOn + mask that enables all protos +** AllConfigsOn + mask that enables all configs +** Operation: 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. +** Return: none +** Exceptions: none +** History: Tue Mar 12 19:40:36 1991, DSJ, Created. +*/ + int IntOutlineLength; + int NumFeatures; + INT_FEATURE_ARRAY IntFeatures; + CLASS_NORMALIZATION_ARRAY CharNormArray; + INT_RESULT_STRUCT IntResult; + CLASS_ID ClassId; + CLASS_INDEX ClassIndex; + + AmbigClassifierCalls++; + + NumFeatures = GetCharNormFeatures (Blob, LineStats, + Templates, + IntFeatures, CharNormArray, + &(Results->BlobLength)); + if (NumFeatures <= 0) + return; + + IntOutlineLength = (int) (Results->BlobLength / GetPicoFeatureLength ()); + + if (MatcherDebugLevel >= 2) + cprintf ("AM Matches = "); + + while (*Ambiguities) { + ClassId = *Ambiguities; + ClassIndex = IndexForClassId (Templates, ClassId); + + SetCharNormMatch(); + IntegerMatcher (ClassForClassId (Templates, ClassId), + AllProtosOn, AllConfigsOn, + IntOutlineLength, NumFeatures, IntFeatures, 0, + CharNormArray[ClassIndex], &IntResult, NO_DEBUG); + + if (MatcherDebugLevel >= 2) + cprintf ("%c-%-2d %2.0f ", ClassId, IntResult.Config, + IntResult.Rating * 100.0); + + AddNewResult (Results, ClassId, IntResult.Rating, IntResult.Config); + + Ambiguities++; + + NumAmbigClassesTried++; + } + if (MatcherDebugLevel >= 2) + cprintf ("\n"); + +} /* AmbigClassifier */ + + +/*---------------------------------------------------------------------------*/ +char *BaselineClassifier(TBLOB *Blob, + LINE_STATS *LineStats, + ADAPT_TEMPLATES Templates, + ADAPT_RESULTS *Results) { +/* + ** Parameters: + ** Blob + blob to be classified +** LineStats + statistics for text line Blob is in +** Templates + current set of adapted templates +** Results + place to put match results +** Globals: +** BaselineCutoffs + expected num features for each class +** Operation: 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. +** Return: String of possible ambiguous chars that should be checked. +** Exceptions: none +** History: Tue Mar 12 19:38:03 1991, DSJ, Created. +*/ + int IntOutlineLength; + int NumFeatures; + int NumClasses; + int i; + int config; + float best_rating; + INT_FEATURE_ARRAY IntFeatures; + CLASS_NORMALIZATION_ARRAY CharNormArray; + CLASS_PRUNER_RESULTS ClassPrunerResults; + INT_RESULT_STRUCT IntResult; + CLASS_ID ClassId; + CLASS_INDEX ClassIndex; + ADAPT_CLASS Class; + + BaselineClassifierCalls++; + + NumFeatures = GetBaselineFeatures (Blob, LineStats, + Templates->Templates, + IntFeatures, CharNormArray, + &(Results->BlobLength)); + if (NumFeatures <= 0) + return NULL; + + IntOutlineLength = (int) (Results->BlobLength / GetPicoFeatureLength ()); + + NumClasses = ClassPruner (Templates->Templates, NumFeatures, + IntFeatures, CharNormArray, + BaselineCutoffs, ClassPrunerResults, + MatchDebugFlags); + + NumBaselineClassesTried += NumClasses; + + if (MatcherDebugLevel >= 2 || display_ratings > 1) + cprintf ("BL Matches = "); + + best_rating = WORST_POSSIBLE_RATING; + for (i = 0; i < NumClasses + && ((newcp_ratings_on & 12) < 8 + || (newcp_ratings_on & 12) == 8 + && ClassPrunerResults[i].Rating < best_rating + BadMatchPad / 2 + && ClassPrunerResults[i].Rating < newcp_duff_rating + && NumClasses > 1); i++) { + ClassId = ClassPrunerResults[i].Class; + ClassIndex = IndexForClassId (Templates->Templates, ClassId); + + SetBaseLineMatch(); + IntegerMatcher (ClassForClassId (Templates->Templates, ClassId), + Templates->Class[ClassIndex]->PermProtos, + Templates->Class[ClassIndex]->PermConfigs, + IntOutlineLength, NumFeatures, IntFeatures, 0, + CharNormArray[ClassIndex], &IntResult, MatchDebugFlags); + + if (MatcherDebugLevel >= 2 || display_ratings > 1) { + cprintf ("%c-%-2d %2.1f(%2.1f/%2.1f) ", ClassId, IntResult.Config, + IntResult.Rating * 100.0, + ClassPrunerResults[i].Rating * 100.0, + ClassPrunerResults[i].Rating2 * 100.0); + if (i % 4 == 3) + cprintf ("\n"); + } + + AddNewResult (Results, ClassId, IntResult.Rating, IntResult.Config); + } + while (i < NumClasses) { + ClassId = ClassPrunerResults[i].Class; + ClassIndex = IndexForClassId (Templates->Templates, ClassId); + Class = Templates->Class[ClassIndex]; + config = + NumIntConfigsIn (ClassForIndex (Templates->Templates, ClassIndex)); + for (config--; config >= 0 && !ConfigIsPermanent (Class, config); + config--); + + if (MatcherDebugLevel >= 2 || display_ratings > 1) { + cprintf ("%c(%d) %2.1f(%2.1f) ", ClassId, config, + ClassPrunerResults[i].Rating * 200.0, + ClassPrunerResults[i].Rating2 * 100.0); + if (i % 4 == 3) + cprintf ("\n"); + } + + AddNewResult (Results, ClassId, ClassPrunerResults[i].Rating * 2, + config); + i++; + } + if (MatcherDebugLevel >= 2 || display_ratings > 1) + cprintf ("\n"); + + ClassId = Results->BestClass; + if (ClassId == NO_CLASS) + return (NULL); + /* this is a bug - maybe should return "" */ + + ClassIndex = IndexForClassId (Templates->Templates, ClassId); + return ((char *) (Templates->Class[ClassIndex]-> + Config[Results->BestConfig].Perm)); + +} /* BaselineClassifier */ + + +/*---------------------------------------------------------------------------*/ +void make_config_pruner(INT_TEMPLATES templates, + CONFIG_PRUNER *config_pruner) { + int classid; + int x; //feature coord + int word_index; //in faster version + int bit_index; + UINT32 XFeatureAddress; + UINT32 YFeatureAddress; + UINT32 ThetaFeatureAddress; + INT_CLASS ClassTemplate; + int ProtoSetIndex; + PROTO_SET ProtoSet; + UINT32 *ProtoPrunerPtr; + UINT32 ProtoNum; + INT32 proto_offset; + UINT32 ConfigWord; + UINT32 ProtoWord; + INT_PROTO Proto; + UINT32 x_config_mask; //forming mask + UINT32 y_config_mask; //forming mask + UINT32 th_config_mask; //forming mask + + for (classid = 0; classid < NumClassesIn (templates); classid++) { + ClassTemplate = ClassForIndex (templates, classid); + for (x = 0; x < NUM_PP_BUCKETS; x++) { + XFeatureAddress = (x << 1); + YFeatureAddress = (NUM_PP_BUCKETS << 1) + (x << 1); + ThetaFeatureAddress = (NUM_PP_BUCKETS << 2) + (x << 1); + x_config_mask = 0; + y_config_mask = 0; + th_config_mask = 0; + for (ProtoSetIndex = 0; + ProtoSetIndex < NumProtoSetsIn (ClassTemplate); + ProtoSetIndex++) { + ProtoSet = ProtoSetIn (ClassTemplate, ProtoSetIndex); + ProtoPrunerPtr = (UINT32 *) ((*ProtoSet).ProtoPruner); + for (ProtoNum = 0; ProtoNum < PROTOS_PER_PROTO_SET; + ProtoNum += (PROTOS_PER_PROTO_SET >> 1), ProtoPrunerPtr++) { + /* Prune Protos of current Proto Set */ + ProtoWord = *(ProtoPrunerPtr + XFeatureAddress); + for (proto_offset = 0; ProtoWord != 0; + proto_offset++, ProtoWord >>= 1) { + if (ProtoWord & 1) { + Proto = + &(ProtoSet->Protos[ProtoNum + proto_offset]); + ConfigWord = Proto->Configs[0]; + x_config_mask |= ConfigWord; + } + } + + ProtoWord = *(ProtoPrunerPtr + YFeatureAddress); + for (proto_offset = 0; ProtoWord != 0; + proto_offset++, ProtoWord >>= 1) { + if (ProtoWord & 1) { + Proto = + &(ProtoSet->Protos[ProtoNum + proto_offset]); + ConfigWord = Proto->Configs[0]; + y_config_mask |= ConfigWord; + } + } + + ProtoWord = *(ProtoPrunerPtr + ThetaFeatureAddress); + for (proto_offset = 0; ProtoWord != 0; + proto_offset++, ProtoWord >>= 1) { + if (ProtoWord & 1) { + Proto = + &(ProtoSet->Protos[ProtoNum + proto_offset]); + ConfigWord = Proto->Configs[0]; + th_config_mask |= ConfigWord; + } + } + } + } + for (word_index = 0; word_index < 4; word_index++) { + ConfigWord = 0; + for (bit_index = 0; bit_index < 8; bit_index++) { + if (x_config_mask & 1) + ConfigWord |= 1 << (bit_index * 4); + x_config_mask >>= 1; + } + config_pruner[classid][0][x][word_index] = ConfigWord; + + ConfigWord = 0; + for (bit_index = 0; bit_index < 8; bit_index++) { + if (y_config_mask & 1) + ConfigWord |= 1 << (bit_index * 4); + y_config_mask >>= 1; + } + config_pruner[classid][1][x][word_index] = ConfigWord; + + ConfigWord = 0; + for (bit_index = 0; bit_index < 8; bit_index++) { + if (th_config_mask & 1) + ConfigWord |= 1 << (bit_index * 4); + th_config_mask >>= 1; + } + config_pruner[classid][2][x][word_index] = ConfigWord; + } + } + } +} + + +/*---------------------------------------------------------------------------*/ +void CharNormClassifier(TBLOB *Blob, + LINE_STATS *LineStats, + INT_TEMPLATES Templates, + ADAPT_RESULTS *Results) { +/* + ** Parameters: + ** Blob + blob to be classified +** LineStats + statistics for text line Blob is in +** Templates + templates to classify unknown against +** 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 +** Operation: 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. +** Return: none +** Exceptions: none +** History: Tue Mar 12 16:02:52 1991, DSJ, Created. +*/ + int IntOutlineLength; + int NumFeatures; + int NumClasses; + int i; + INT32 min_misses; + float best_rating; + INT_FEATURE_ARRAY IntFeatures; + CLASS_NORMALIZATION_ARRAY CharNormArray; + CLASS_PRUNER_RESULTS ClassPrunerResults; + INT_RESULT_STRUCT IntResult; + CLASS_ID ClassId; + CLASS_INDEX ClassIndex; + + CharNormClassifierCalls++; + + NumFeatures = GetCharNormFeatures (Blob, LineStats, + Templates, + IntFeatures, CharNormArray, + &(Results->BlobLength)); + if (NumFeatures <= 0) + return; + + IntOutlineLength = (int) (Results->BlobLength / GetPicoFeatureLength ()); + + NumClasses = ClassPruner (Templates, NumFeatures, + IntFeatures, CharNormArray, + CharNormCutoffs, ClassPrunerResults, + MatchDebugFlags); + + if (feature_prune_percentile > 0) { + min_misses = feature_pruner (Templates, NumFeatures, + IntFeatures, NumClasses, + ClassPrunerResults); + NumClasses = + prune_configs(Templates, + min_misses, + NumFeatures, + IntFeatures, + CharNormArray, + NumClasses, + IntOutlineLength, + ClassPrunerResults, + MatchDebugFlags); + } + else + min_misses = 0; + if (tessedit_single_match && NumClasses > 1) + NumClasses = 1; + NumCharNormClassesTried += NumClasses; + + if (MatcherDebugLevel >= 2 || display_ratings > 1) + cprintf ("CN Matches = "); + + best_rating = WORST_POSSIBLE_RATING; + for (i = 0; i < NumClasses + && ((newcp_ratings_on & 3) < 2 + || (newcp_ratings_on & 3) == 2 + && ClassPrunerResults[i].Rating < best_rating + BadMatchPad / 2 + && ClassPrunerResults[i].Rating < newcp_duff_rating + && NumClasses > 1); i++) { + ClassId = ClassPrunerResults[i].Class; + ClassIndex = IndexForClassId (Templates, ClassId); + + SetCharNormMatch(); + + if (feature_prune_percentile > 0) + //xiaofan + config_mask_to_proto_mask (ClassForClassId (Templates, ClassId), (BIT_VECTOR) & ClassPrunerResults[i].config_mask, + PrunedProtos); + //xiaofan + IntegerMatcher (ClassForClassId (Templates, ClassId), PrunedProtos, (BIT_VECTOR) & ClassPrunerResults[i].config_mask, + IntOutlineLength, NumFeatures, IntFeatures, 0, + CharNormArray[ClassIndex], &IntResult, MatchDebugFlags); + + if (MatcherDebugLevel >= 2 || display_ratings > 1) { + cprintf ("%c-%-2d %2.1f(%2.1f/%2.1f) ", ClassId, IntResult.Config, + IntResult.Rating * 100.0, + ClassPrunerResults[i].Rating * 100.0, + ClassPrunerResults[i].Rating2 * 100.0); + if (i % 4 == 3) + cprintf ("\n"); + } + + AddNewResult (Results, ClassId, IntResult.Rating, IntResult.Config); + if (IntResult.Rating < best_rating) + best_rating = IntResult.Rating; + } + while (i < NumClasses) { + ClassId = ClassPrunerResults[i].Class; + ClassIndex = IndexForClassId (Templates, ClassId); + + if (MatcherDebugLevel >= 2 || display_ratings > 1) { + cprintf ("%c %2.1f(%2.1f) ", ClassId, + ClassPrunerResults[i].Rating * 200.0, + ClassPrunerResults[i].Rating2 * 100.0); + if (i % 4 == 3) + cprintf ("\n"); + } + + AddNewResult (Results, ClassId, ClassPrunerResults[i].Rating * 2, 0); + i++; + } + if (MatcherDebugLevel >= 2 || display_ratings > 1) + cprintf ("\n"); + +} /* CharNormClassifier */ + + +/*---------------------------------------------------------------------------*/ +void ClassifyAsNoise(TBLOB *Blob, + LINE_STATS *LineStats, + ADAPT_RESULTS *Results) { +/* + ** Parameters: + ** Blob + blob to be classified +** LineStats + statistics for text line Blob is in +** Results + results to add noise classification to +** Globals: +** NoiseBlobLength + avg. length of a noise blob +** Operation: 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. +** Return: none +** Exceptions: none +** History: Tue Mar 12 18:36:52 1991, DSJ, Created. +*/ + register FLOAT32 Rating; + + Rating = Results->BlobLength / NoiseBlobLength; + Rating *= Rating; + Rating /= 1.0 + Rating; + + AddNewResult (Results, NO_CLASS, Rating, 0); + +} /* ClassifyAsNoise */ + + +/*---------------------------------------------------------------------------*/ +int CompareCurrentRatings( //CLASS_ID *Class1, + const void *arg1, + const void *arg2) { //CLASS_ID *Class2) +/* + ** Parameters: + ** Class1, Class2 + classes whose ratings are to be compared +** Globals: +** CurrentRatings + contains actual ratings for each class +** Operation: This routine gets the ratings for the 2 specified classes +** from a global variable (CurrentRatings) and returns: +** -1 if Rating1 < Rating2 +** 0 if Rating1 = Rating2 +** 1 if Rating1 > Rating2 +** Return: Order of classes based on their ratings (see above). +** Exceptions: none +** History: Tue Mar 12 14:18:31 1991, DSJ, Created. +*/ + FLOAT32 Rating1, Rating2; + CLASS_ID *Class1 = (CLASS_ID *) arg1; + CLASS_ID *Class2 = (CLASS_ID *) arg2; + + Rating1 = CurrentRatings[*Class1]; + Rating2 = CurrentRatings[*Class2]; + + if (Rating1 < Rating2) + return (-1); + else if (Rating1 > Rating2) + return (1); + else + return (0); + +} /* CompareCurrentRatings */ + + +/*---------------------------------------------------------------------------*/ +LIST ConvertMatchesToChoices(ADAPT_RESULTS *Results) { +/* + ** Parameters: + ** Results + adaptive matcher results to convert to choices +** Globals: none +** Operation: This routine creates a choice for each matching class +** in Results (up to MAX_MATCHES) and returns a list of +** these choices. The match +** ratings are converted to be the ratings and certainties +** as used by the context checkers. +** Return: List of choices. +** Exceptions: none +** History: Tue Mar 12 08:55:37 1991, DSJ, Created. +*/ + char ChoiceString[2]; + int i; + LIST Choices; + CLASS_ID NextMatch; + FLOAT32 Rating; + FLOAT32 Certainty; + + ChoiceString[1] = '\0'; + if (Results->NumMatches > MAX_MATCHES) + Results->NumMatches = MAX_MATCHES; + + for (Choices = NIL, i = 0; i < Results->NumMatches; i++) { + NextMatch = Results->Classes[i]; + ChoiceString[0] = NextMatch; + Rating = Certainty = Results->Ratings[NextMatch]; + Rating *= RatingScale * Results->BlobLength; + Certainty *= -CertaintyScale; + Choices = append_choice (Choices, ChoiceString, Rating, Certainty, + Results->Configs[NextMatch]); + } + return (Choices); + +} /* ConvertMatchesToChoices */ + + +/*---------------------------------------------------------------------------*/ +#ifndef GRAPHICS_DISABLED +void DebugAdaptiveClassifier(TBLOB *Blob, + LINE_STATS *LineStats, + ADAPT_RESULTS *Results) { +/* + ** Parameters: + ** Blob + blob whose classification is being debugged +** LineStats + statistics for text line blob is in +** Results + results of match being debugged +** Globals: none +** Operation: +** Return: none +** Exceptions: none +** History: Wed Mar 13 16:44:41 1991, DSJ, Created. +*/ + const char *Prompt = + "\nType class id (or CTRL-A,B,C) in IntegerMatch Window ..."; + const char *DebugMode = "All Templates"; + CLASS_ID LastClass = Results->BestClass; + CLASS_ID ClassId; + BOOL8 AdaptiveOn = TRUE; + BOOL8 PreTrainedOn = TRUE; + + ShowMatchDisplay(); + cprintf ("\nDebugging class = %c (%s) ...\n", LastClass, DebugMode); + ShowBestMatchFor(Blob, LineStats, LastClass, AdaptiveOn, PreTrainedOn); + UpdateMatchDisplay(); + + while ((ClassId = GetClassToDebug (Prompt)) != 0) { + switch (ClassId) { + case 'b': + AdaptiveOn = TRUE; + PreTrainedOn = FALSE; + DebugMode = "Adaptive Templates Only"; + break; + + case 'c': + AdaptiveOn = FALSE; + PreTrainedOn = TRUE; + DebugMode = "PreTrained Templates Only"; + break; + + case 'a': + AdaptiveOn = TRUE; + PreTrainedOn = TRUE; + DebugMode = "All Templates"; + break; + + default: + LastClass = ClassId; + break; + } + + ShowMatchDisplay(); + cprintf ("\nDebugging class = %c (%s) ...\n", LastClass, DebugMode); + ShowBestMatchFor(Blob, LineStats, LastClass, AdaptiveOn, PreTrainedOn); + UpdateMatchDisplay(); + } +} /* DebugAdaptiveClassifier */ +#endif + +/*---------------------------------------------------------------------------*/ +void DoAdaptiveMatch(TBLOB *Blob, + LINE_STATS *LineStats, + ADAPT_RESULTS *Results) { +/* + ** Parameters: + ** Blob + blob to be classified +** LineStats + statistics for text line Blob is in +** Results + place to put match results +** Globals: +** PreTrainedTemplates + built-in training templates +** AdaptedTemplates + templates adapted for this page +** GreatAdaptiveMatch + rating limit for a great match +** Operation: 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. +** Return: none +** Exceptions: none +** History: Tue Mar 12 08:50:11 1991, DSJ, Created. +*/ + char *Ambiguities; + + AdaptiveMatcherCalls++; + InitIntFX(); + + if (AdaptedTemplates->NumPermClasses < MinNumPermClasses + || tess_cn_matching) { + CharNormClassifier(Blob, LineStats, PreTrainedTemplates, Results); + } + else { + Ambiguities = BaselineClassifier (Blob, LineStats, + AdaptedTemplates, Results); + + if (Results->NumMatches > 0 && MarginalMatch (Results->BestRating) + && !tess_bn_matching) { + CharNormClassifier(Blob, LineStats, PreTrainedTemplates, Results); + } + else if (Ambiguities && *Ambiguities) { + AmbigClassifier(Blob, + LineStats, + PreTrainedTemplates, + Ambiguities, + Results); + } + } + + if (Results->NumMatches == 0) + ClassifyAsNoise(Blob, LineStats, Results); + /**/} /* DoAdaptiveMatch */ + + /*---------------------------------------------------------------------------*/ + void + GetAdaptThresholds (TWERD * Word, + LINE_STATS * LineStats, + const char *BestChoice, + const char *BestRawChoice, FLOAT32 Thresholds[]) { + /* + ** Parameters: + ** Word + current word + ** LineStats + line stats for row word is in + ** BestChoice + best choice for current word with context + ** BestRawChoice + best choice for current word without context + ** Thresholds + array of thresholds to be filled in + ** Globals: + ** EnableNewAdaptRules + ** GoodAdaptiveMatch + ** PerfectRating + ** RatingMargin + ** Operation: This routine tries to estimate how tight the adaptation + ** threshold should be set for each character in the current + ** word. In general, the routine tries to set tighter + ** thresholds for a character when the current set of templates + ** would have made an error on that character. It tries + ** to set a threshold tight enough to eliminate the error. + ** Two different sets of rules can be used to determine the + ** desired thresholds. + ** Return: none (results are returned in Thresholds) + ** Exceptions: none + ** History: Fri May 31 09:22:08 1991, DSJ, Created. + */ + TBLOB *Blob; + + if (EnableNewAdaptRules) { /* new rules */ + FindClassifierErrors(PerfectRating, + GoodAdaptiveMatch, + RatingMargin, + Thresholds); + } + else { /* old rules */ + for (Blob = Word->blobs; + Blob != NULL; + Blob = Blob->next, BestChoice++, BestRawChoice++, Thresholds++) + if (*BestChoice == *BestRawChoice) + *Thresholds = GoodAdaptiveMatch; + else { + /* the blob was incorrectly classified - find the rating threshold + needed to create a template which will correct the error with + some margin. However, don't waste time trying to make + templates which are too tight. */ + *Thresholds = GetBestRatingFor (Blob, LineStats, *BestChoice); + *Thresholds *= (1.0 - RatingMargin); + if (*Thresholds > GoodAdaptiveMatch) + *Thresholds = GoodAdaptiveMatch; + if (*Thresholds < PerfectRating) + *Thresholds = PerfectRating; + } + } + } /* GetAdaptThresholds */ + + /*---------------------------------------------------------------------------*/ + char *GetAmbiguities(TBLOB *Blob, + LINE_STATS *LineStats, + CLASS_ID CorrectClass) { + /* + ** Parameters: + ** Blob + blob to get classification ambiguities for + ** LineStats + statistics for text line blob is in + ** CorrectClass + correct class for Blob + ** Globals: + ** CurrentRatings + used by qsort compare routine + ** PreTrainedTemplates + built-in templates + ** Operation: 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. + ** Return: String containing all possible ambiguous classes. + ** Exceptions: none + ** History: Fri Mar 15 08:08:22 1991, DSJ, Created. + */ + ADAPT_RESULTS Results; + char *Ambiguities; + int i; + + EnterClassifyMode; + + Results.NumMatches = 0; + Results.BestRating = WORST_POSSIBLE_RATING; + Results.BestClass = NO_CLASS; + Results.BestConfig = 0; + InitMatcherRatings (Results.Ratings); + + CharNormClassifier(Blob, LineStats, PreTrainedTemplates, &Results); + RemoveBadMatches(&Results); + + /* save ratings in a global so that CompareCurrentRatings() can see them */ + CurrentRatings = Results.Ratings; + qsort ((void *) (Results.Classes), Results.NumMatches, + sizeof (CLASS_ID), CompareCurrentRatings); + + /* copy the class id's into an string of ambiguities - don't copy if + the correct class is the only class id matched */ + Ambiguities = (char *) Emalloc (sizeof (char) * (Results.NumMatches + 1)); + if (Results.NumMatches > 1 || + Results.NumMatches == 1 && Results.Classes[0] != CorrectClass) { + for (i = 0; i < Results.NumMatches; i++) + Ambiguities[i] = Results.Classes[i]; + Ambiguities[i] = '\0'; + } + else + Ambiguities[0] = '\0'; + + return (Ambiguities); + + } /* GetAmbiguities */ + + /*---------------------------------------------------------------------------*/ + int GetBaselineFeatures(TBLOB *Blob, + LINE_STATS *LineStats, + INT_TEMPLATES Templates, + INT_FEATURE_ARRAY IntFeatures, + CLASS_NORMALIZATION_ARRAY CharNormArray, + FLOAT32 *BlobLength) { + /* + ** Parameters: + ** Blob + blob to extract features from + ** LineStats + statistics about text row blob is in + ** Templates + used to compute char norm adjustments + ** IntFeatures + array to fill with integer features + ** CharNormArray + array to fill with dummy char norm adjustments + ** BlobLength + length of blob in baseline-normalized units + ** Globals: none + ** Operation: 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. CharNormArray + ** is filled with 0's to indicate to the matcher that no + ** character normalization adjustment needs to be done. + ** The total length of all blob outlines + ** in baseline normalized units is also returned. + ** Return: Number of pico-features returned (0 if an error occurred) + ** Exceptions: none + ** History: Tue Mar 12 17:55:18 1991, DSJ, Created. + */ + FEATURE_SET Features; + int NumFeatures; + + if (EnableIntFX) + return (GetIntBaselineFeatures (Blob, LineStats, Templates, + IntFeatures, CharNormArray, BlobLength)); + + NormMethod = baseline; + Features = ExtractPicoFeatures (Blob, LineStats); + + NumFeatures = NumFeaturesIn (Features); + *BlobLength = NumFeatures * GetPicoFeatureLength (); + if (NumFeatures > UNLIKELY_NUM_FEAT) { + FreeFeatureSet(Features); + return (0); + } + + ComputeIntFeatures(Features, IntFeatures); + ClearCharNormArray(Templates, CharNormArray); + + FreeFeatureSet(Features); + return (NumFeatures); + + } /* GetBaselineFeatures */ + + /*---------------------------------------------------------------------------*/ + FLOAT32 GetBestRatingFor(TBLOB *Blob, + LINE_STATS *LineStats, + CLASS_ID ClassId) { + /* + ** Parameters: + ** Blob + blob to get best rating for + ** LineStats + statistics about text line blob is in + ** ClassId + class blob is to be compared to + ** Globals: + ** PreTrainedTemplates + built-in templates + ** AdaptedTemplates + current set of adapted templates + ** AllProtosOn + dummy mask to enable all protos + ** AllConfigsOn + dummy mask to enable all configs + ** Operation: This routine classifies Blob against both sets of + ** templates for the specified class and returns the best + ** rating found. + ** Return: Best rating for match of Blob to ClassId. + ** Exceptions: none + ** History: Tue Apr 9 09:01:24 1991, DSJ, Created. + */ + int CNOutlineLength, BLOutlineLength; + int NumCNFeatures, NumBLFeatures; + INT_FEATURE_ARRAY CNFeatures, BLFeatures; + INT_RESULT_STRUCT CNResult, BLResult; + CLASS_NORMALIZATION_ARRAY CNAdjust, BLAdjust; + CLASS_INDEX ClassIndex; + FLOAT32 BlobLength; + + CNResult.Rating = BLResult.Rating = 1.0; + + if (!LegalClassId (ClassId)) + return (1.0); + + if (!UnusedClassIdIn (PreTrainedTemplates, ClassId)) { + NumCNFeatures = GetCharNormFeatures (Blob, LineStats, + PreTrainedTemplates, + CNFeatures, CNAdjust, &BlobLength); + if (NumCNFeatures > 0) { + CNOutlineLength = (int) (BlobLength / GetPicoFeatureLength ()); + ClassIndex = IndexForClassId (PreTrainedTemplates, ClassId); + + SetCharNormMatch(); + IntegerMatcher (ClassForClassId (PreTrainedTemplates, ClassId), + AllProtosOn, AllConfigsOn, + CNOutlineLength, NumCNFeatures, CNFeatures, 0, + CNAdjust[ClassIndex], &CNResult, NO_DEBUG); + } + } + + if (!UnusedClassIdIn (AdaptedTemplates->Templates, ClassId)) { + NumBLFeatures = GetBaselineFeatures (Blob, LineStats, + AdaptedTemplates->Templates, + BLFeatures, BLAdjust, &BlobLength); + if (NumBLFeatures > 0) { + BLOutlineLength = (int) (BlobLength / GetPicoFeatureLength ()); + ClassIndex = IndexForClassId (AdaptedTemplates->Templates, ClassId); + + SetBaseLineMatch(); + IntegerMatcher (ClassForClassId + (AdaptedTemplates->Templates, ClassId), + AdaptedTemplates->Class[ClassIndex]->PermProtos, + AdaptedTemplates->Class[ClassIndex]->PermConfigs, + BLOutlineLength, NumBLFeatures, BLFeatures, 0, + BLAdjust[ClassIndex], &BLResult, NO_DEBUG); + } + } + + return (MIN (BLResult.Rating, CNResult.Rating)); + + } /* GetBestRatingFor */ + + /*---------------------------------------------------------------------------*/ + int GetCharNormFeatures(TBLOB *Blob, + LINE_STATS *LineStats, + INT_TEMPLATES Templates, + INT_FEATURE_ARRAY IntFeatures, + CLASS_NORMALIZATION_ARRAY CharNormArray, + FLOAT32 *BlobLength) { + /* + ** Parameters: + ** Blob + blob to extract features from + ** LineStats + statistics about text row blob is in + ** Templates + used to compute char norm adjustments + ** IntFeatures + array to fill with integer features + ** CharNormArray + array to fill with char norm adjustments + ** BlobLength + length of blob in baseline-normalized units + ** Globals: none + ** Operation: This routine sets up the feature extractor to extract + ** character normalization features and character normalized + ** pico-features. The extracted pico-features are converted + ** to integer form and placed in IntFeatures. The character + ** normalization features are matched to each class in + ** templates and the resulting adjustment factors are returned + ** in CharNormArray. The total length of all blob outlines + ** in baseline normalized units is also returned. + ** Return: Number of pico-features returned (0 if an error occurred) + ** Exceptions: none + ** History: Tue Mar 12 17:55:18 1991, DSJ, Created. + */ + return (GetIntCharNormFeatures (Blob, LineStats, Templates, + IntFeatures, CharNormArray, BlobLength)); + } /* GetCharNormFeatures */ + + /*---------------------------------------------------------------------------*/ + int GetIntBaselineFeatures(TBLOB *Blob, + LINE_STATS *LineStats, + INT_TEMPLATES Templates, + INT_FEATURE_ARRAY IntFeatures, + CLASS_NORMALIZATION_ARRAY CharNormArray, + FLOAT32 *BlobLength) { + /* + ** Parameters: + ** Blob + blob to extract features from + ** LineStats + statistics about text row blob is in + ** Templates + used to compute char norm adjustments + ** IntFeatures + array to fill with integer features + ** CharNormArray + array to fill with dummy char norm adjustments + ** BlobLength + length of blob in baseline-normalized units + ** Globals: + ** FeaturesHaveBeenExtracted + TRUE if fx has been done + ** BaselineFeatures + holds extracted baseline feat + ** CharNormFeatures + holds extracted char norm feat + ** FXInfo + holds misc. FX info + ** Operation: 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 baseline features into the IntFeatures + ** array provided by the caller. + ** Return: Number of features extracted or 0 if an error occured. + ** Exceptions: none + ** History: Tue May 28 10:40:52 1991, DSJ, Created. + */ + register INT_FEATURE Src, Dest, End; + + if (!FeaturesHaveBeenExtracted) { + FeaturesOK = ExtractIntFeat (Blob, BaselineFeatures, + CharNormFeatures, &FXInfo); + FeaturesHaveBeenExtracted = TRUE; + } + + if (!FeaturesOK) { + *BlobLength = FXInfo.Length * ComputeScaleFactor (LineStats); + return (0); + } + + for (Src = BaselineFeatures, End = Src + FXInfo.NumBL, Dest = IntFeatures; + Src < End; *Dest++ = *Src++); + + ClearCharNormArray(Templates, CharNormArray); + *BlobLength = FXInfo.Length * ComputeScaleFactor (LineStats); + return (FXInfo.NumBL); + + } /* GetIntBaselineFeatures */ + + /*---------------------------------------------------------------------------*/ + int GetIntCharNormFeatures(TBLOB *Blob, + LINE_STATS *LineStats, + INT_TEMPLATES Templates, + INT_FEATURE_ARRAY IntFeatures, + CLASS_NORMALIZATION_ARRAY CharNormArray, + FLOAT32 *BlobLength) { + /* + ** Parameters: + ** Blob + blob to extract features from + ** LineStats + statistics about text row blob is in + ** Templates + used to compute char norm adjustments + ** IntFeatures + array to fill with integer features + ** CharNormArray + array to fill with dummy char norm adjustments + ** BlobLength + length of blob in baseline-normalized units + ** Globals: + ** FeaturesHaveBeenExtracted + TRUE if fx has been done + ** BaselineFeatures + holds extracted baseline feat + ** CharNormFeatures + holds extracted char norm feat + ** FXInfo + holds misc. FX info + ** Operation: 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. + ** Return: Number of features extracted or 0 if an error occured. + ** Exceptions: none + ** History: Tue May 28 10:40:52 1991, DSJ, Created. + */ + register INT_FEATURE Src, Dest, End; + FEATURE NormFeature; + FLOAT32 Baseline, Scale; + + if (!FeaturesHaveBeenExtracted) { + FeaturesOK = ExtractIntFeat (Blob, BaselineFeatures, + CharNormFeatures, &FXInfo); + FeaturesHaveBeenExtracted = TRUE; + } + + if (!FeaturesOK) { + *BlobLength = FXInfo.Length * ComputeScaleFactor (LineStats); + return (0); + } + + for (Src = CharNormFeatures, End = Src + FXInfo.NumCN, Dest = IntFeatures; + Src < End; *Dest++ = *Src++); + + NormFeature = NewFeature (&CharNormDesc); + Baseline = BaselineAt (LineStats, FXInfo.Xmean); + Scale = ComputeScaleFactor (LineStats); + ParamOf (NormFeature, CharNormY) = (FXInfo.Ymean - Baseline) * Scale; + ParamOf (NormFeature, CharNormLength) = + FXInfo.Length * Scale / LENGTH_COMPRESSION; + ParamOf (NormFeature, CharNormRx) = FXInfo.Rx * Scale; + ParamOf (NormFeature, CharNormRy) = FXInfo.Ry * Scale; + ComputeIntCharNormArray(NormFeature, Templates, CharNormArray); + FreeFeature(NormFeature); + + *BlobLength = FXInfo.Length * Scale; + return (FXInfo.NumCN); + + } /* GetIntCharNormFeatures */ + + /*---------------------------------------------------------------------------*/ + void InitMatcherRatings(register FLOAT32 *Rating) { + /* + ** Parameters: + ** Rating + ptr to array of ratings to be initialized + ** Globals: none + ** Operation: This routine initializes the best rating for each class + ** to be the worst possible rating (1.0). + ** Return: none + ** Exceptions: none + ** History: Tue Mar 12 13:43:28 1991, DSJ, Created. + */ + register FLOAT32 *LastRating; + register FLOAT32 WorstRating = WORST_POSSIBLE_RATING; + + for (LastRating = Rating + MAX_CLASS_ID; + Rating <= LastRating; *Rating++ = WorstRating); + + } /* InitMatcherRatings */ + + /*---------------------------------------------------------------------------*/ + void MakeNewTemporaryConfig(ADAPT_TEMPLATES Templates, + CLASS_ID ClassId, + int NumFeatures, + INT_FEATURE_ARRAY Features, + FEATURE_SET FloatFeatures) { + /* + ** Parameters: + ** Templates + adapted templates to add new config to + ** ClassId + class id to associate with new config + ** NumFeatures + number of features in IntFeatures + ** Features + features describing model for new config + ** FloatFeatures + floating-pt representation of features + ** Globals: + ** AllProtosOn + mask to enable all protos + ** AllConfigsOff + mask to disable all configs + ** TempProtoMask + defines old protos matched in new config + ** Operation: + ** Return: none + ** Exceptions: none + ** History: Fri Mar 15 08:49:46 1991, DSJ, Created. + */ + CLASS_INDEX ClassIndex; + 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 (LearningDebugLevel >= 3) + debug_level = + PRINT_MATCH_SUMMARY | PRINT_FEATURE_MATCHES | PRINT_PROTO_MATCHES; + + ClassIndex = IndexForClassId (Templates->Templates, ClassId); + IClass = ClassForClassId (Templates->Templates, ClassId); + Class = Templates->Class[ClassIndex]; + + if (NumIntConfigsIn (IClass) >= MAX_NUM_CONFIGS) + return; + + OldMaxProtoId = NumIntProtosIn (IClass) - 1; + + NumOldProtos = FindGoodProtos (IClass, AllProtosOn, AllConfigsOff, + BlobLength, NumFeatures, Features, + OldProtos, debug_level); + NumOldProtos = 0; + + MaskSize = WordsInVectorOfSize (MAX_NUM_PROTOS); + zero_all_bits(TempProtoMask, MaskSize); + for (i = 0; i < NumOldProtos; i++) + SET_BIT (TempProtoMask, OldProtos[i]); + + NumBadFeatures = FindBadFeatures (IClass, TempProtoMask, AllConfigsOn, + BlobLength, NumFeatures, Features, + BadFeatures, debug_level); + + MaxProtoId = MakeNewTempProtos (FloatFeatures, NumBadFeatures, BadFeatures, + IClass, Class, TempProtoMask); + if (MaxProtoId == NO_PROTO) + return; + + ConfigId = AddIntConfig (IClass); + ConvertConfig(TempProtoMask, ConfigId, IClass); + Config = NewTempConfig (MaxProtoId); + TempConfigFor (Class, ConfigId) = Config; + copy_all_bits (TempProtoMask, Config->Protos, Config->ProtoVectorSize); + + if (LearningDebugLevel >= 1) + cprintf ("Making new temp config %d using %d old and %d new protos.\n", + ConfigId, NumOldProtos, MaxProtoId - OldMaxProtoId); + + } /* MakeNewTemporaryConfig */ + + /*---------------------------------------------------------------------------*/ + PROTO_ID + MakeNewTempProtos (FEATURE_SET Features, + int NumBadFeat, + FEATURE_ID BadFeat[], + INT_CLASS IClass, + ADAPT_CLASS Class, BIT_VECTOR TempProtoMask) { + /* + ** Parameters: + ** Features + floating-pt features describing new character + ** NumBadFeat + number of bad features to turn into protos + ** BadFeat + feature id's of bad features + ** IClass + integer class templates to add new protos to + ** Class + adapted class templates to add new protos to + ** TempProtoMask + proto mask to add new protos to + ** Globals: none + ** Operation: 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. + ** Return: Max proto id in class after all protos have been added. + ** Exceptions: none + ** History: Fri Mar 15 11:39:38 1991, DSJ, Created. + */ + 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 = FeatureIn (Features, *ProtoStart); + X1 = ParamOf (F1, PicoFeatX); + Y1 = ParamOf (F1, PicoFeatY); + A1 = ParamOf (F1, PicoFeatDir); + + for (ProtoEnd = ProtoStart + 1, + SegmentLength = GetPicoFeatureLength (); + ProtoEnd < LastBad; + ProtoEnd++, SegmentLength += GetPicoFeatureLength ()) { + F2 = FeatureIn (Features, *ProtoEnd); + X2 = ParamOf (F2, PicoFeatX); + Y2 = ParamOf (F2, PicoFeatY); + A2 = ParamOf (F2, PicoFeatDir); + + AngleDelta = fabs (A1 - A2); + if (AngleDelta > 0.5) + AngleDelta = 1.0 - AngleDelta; + + if (AngleDelta > MaxAngleDelta || + fabs (X1 - X2) > SegmentLength || + fabs (Y1 - Y2) > SegmentLength) + break; + } + + F2 = FeatureIn (Features, *(ProtoEnd - 1)); + X2 = ParamOf (F2, PicoFeatX); + Y2 = ParamOf (F2, PicoFeatY); + A2 = ParamOf (F2, 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 */ + ProtoLength (Proto) = SegmentLength; + ProtoAngle (Proto) = A1; + ProtoX (Proto) = (X1 + X2) / 2.0; + ProtoY (Proto) = (Y1 + Y2) / 2.0 - Y_DIM_OFFSET; + FillABC(Proto); + + TempProto->ProtoId = Pid; + SET_BIT(TempProtoMask, Pid); + + ConvertProto(Proto, Pid, IClass); + AddProtoToProtoPruner(Proto, Pid, IClass); + + Class->TempProtos = push (Class->TempProtos, TempProto); + } + return (NumIntProtosIn (IClass) - 1); + } /* MakeNewTempProtos */ + + /*---------------------------------------------------------------------------*/ + void MakePermanent(ADAPT_TEMPLATES Templates, + CLASS_ID ClassId, + int ConfigId, + TBLOB *Blob, + LINE_STATS *LineStats) { + /* + ** Parameters: + ** Templates + current set of adaptive templates + ** ClassId + class containing config to be made permanent + ** ConfigId + config to be made permanent + ** Blob + current blob being adapted to + ** LineStats + statistics about text line Blob is in + ** Globals: none + ** Operation: + ** Return: none + ** Exceptions: none + ** History: Thu Mar 14 15:54:08 1991, DSJ, Created. + */ + char *Ambigs; + TEMP_CONFIG Config; + CLASS_INDEX ClassIndex; + ADAPT_CLASS Class; + PROTO_KEY ProtoKey; + + ClassIndex = IndexForClassId (Templates->Templates, ClassId); + Class = Templates->Class[ClassIndex]; + Config = TempConfigFor (Class, ConfigId); + + MakeConfigPermanent(Class, ConfigId); + if (Class->NumPermConfigs == 0) + Templates->NumPermClasses++; + Class->NumPermConfigs++; + + ProtoKey.Templates = Templates; + ProtoKey.ClassId = ClassId; + ProtoKey.ConfigId = ConfigId; + Class->TempProtos = delete_d (Class->TempProtos, &ProtoKey, + MakeTempProtoPerm); + FreeTempConfig(Config); + + Ambigs = GetAmbiguities (Blob, LineStats, ClassId); + PermConfigFor (Class, ConfigId) = Ambigs; + + if (LearningDebugLevel >= 1) + cprintf ("Making config %d permanent with ambiguities '%s'.\n", + ConfigId, Ambigs); + + } /* MakePermanent */ + + /*---------------------------------------------------------------------------*/ + int MakeTempProtoPerm(void *item1, //TEMP_PROTO TempProto, + void *item2) { //PROTO_KEY *ProtoKey) + /* + ** Parameters: + ** TempProto + temporary proto to compare to key + ** ProtoKey + defines which protos to make permanent + ** Globals: none + ** Operation: This routine converts TempProto to be permanent if + ** its proto id is used by the configuration specified in + ** ProtoKey. + ** Return: TRUE if TempProto is converted, FALSE otherwise + ** Exceptions: none + ** History: Thu Mar 14 18:49:54 1991, DSJ, Created. + */ + CLASS_INDEX ClassIndex; + ADAPT_CLASS Class; + TEMP_CONFIG Config; + TEMP_PROTO TempProto; + PROTO_KEY *ProtoKey; + + TempProto = (TEMP_PROTO) item1; + ProtoKey = (PROTO_KEY *) item2; + + ClassIndex = IndexForClassId (ProtoKey->Templates->Templates, + ProtoKey->ClassId); + Class = ProtoKey->Templates->Class[ClassIndex]; + 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 */ + + /*---------------------------------------------------------------------------*/ + int NumBlobsIn(TWERD *Word) { + /* + ** Parameters: + ** Word + word to count blobs in + ** Globals: none + ** Operation: This routine returns the number of blobs in Word. + ** Return: Number of blobs in Word. + ** Exceptions: none + ** History: Thu Mar 14 08:30:27 1991, DSJ, Created. + */ + register TBLOB *Blob; + register int NumBlobs; + + if (Word == NULL) + return (0); + + for (Blob = Word->blobs, NumBlobs = 0; + Blob != NULL; Blob = Blob->next, NumBlobs++); + + return (NumBlobs); + + } /* NumBlobsIn */ + + /*---------------------------------------------------------------------------*/ + int NumOutlinesInBlob(TBLOB *Blob) { + /* + ** Parameters: + ** Blob + blob to count outlines in + ** Globals: none + ** Operation: This routine returns the number of OUTER outlines + ** in Blob. + ** Return: Number of outer outlines in Blob. + ** Exceptions: none + ** History: Mon Jun 10 15:46:20 1991, DSJ, Created. + */ + register TESSLINE *Outline; + register int NumOutlines; + + if (Blob == NULL) + return (0); + + for (Outline = Blob->outlines, NumOutlines = 0; + Outline != NULL; Outline = Outline->next, NumOutlines++); + + return (NumOutlines); + + } /* NumOutlinesInBlob */ + + /*---------------------------------------------------------------------------*/ + void PrintAdaptiveMatchResults(FILE *File, ADAPT_RESULTS *Results) { + /* + ** Parameters: + ** File + open text file to write Results to + ** Results + match results to write to File + ** Globals: none + ** Operation: This routine writes the matches in Results to File. + ** Return: none + ** Exceptions: none + ** History: Mon Mar 18 09:24:53 1991, DSJ, Created. + */ + int i; + + if (Results->NumMatches > 0) { + cprintf ("%c(%d) %4.1f ", Results->Classes[0], Results->Classes[0], + Results->Ratings[Results->Classes[0]] * 100.0); + + for (i = 1; i < Results->NumMatches; i++) { + cprintf ("%c(%d) %4.1f ", Results->Classes[i], + Results->Classes[i], + Results->Ratings[Results->Classes[i]] * 100.0); + } + } + } /* PrintAdaptiveMatchResults */ + + /*---------------------------------------------------------------------------*/ + void RemoveBadMatches(ADAPT_RESULTS *Results) { + /* + ** Parameters: + ** Results + contains matches to be filtered + ** Globals: + ** BadMatchPad + defines a "bad match" + ** Operation: This routine steps thru 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. + ** Return: none + ** Exceptions: none + ** History: Tue Mar 12 13:51:03 1991, DSJ, Created. + */ + int Next, NextGood; + FLOAT32 *Rating = Results->Ratings; + CLASS_ID *Match = Results->Classes; + FLOAT32 BadMatchThreshold; + static const char* romans = "ivxIVX"; + BadMatchThreshold = Results->BestRating + BadMatchPad; + + if (bln_numericmode) { + for (Next = NextGood = 0; Next < Results->NumMatches; Next++) { + if (Rating[Match[Next]] <= BadMatchThreshold) { + if (!isalpha(Match[Next]) || strchr(romans, Match[Next]) != NULL) { + Match[NextGood++] = Match[Next]; + } else if (Match[Next] == 'l' && Rating['1'] >= BadMatchThreshold) { + Match[NextGood++] = '1'; + Rating['1'] = Rating['l']; + } else if (Match[Next] == 'O' && Rating['0'] >= BadMatchThreshold) { + Match[NextGood++] = '0'; + Rating['0'] = Rating['O']; + } + } + } + } + else { + for (Next = NextGood = 0; Next < Results->NumMatches; Next++) { + if (Rating[Match[Next]] <= BadMatchThreshold) + Match[NextGood++] = Match[Next]; + } + } + + Results->NumMatches = NextGood; + + } /* RemoveBadMatches */ + + /*----------------------------------------------------------------------------------*/ + void RemoveExtraPuncs(ADAPT_RESULTS *Results) { + /* + ** Parameters: + ** Results + contains matches to be filtered + ** Globals: + ** BadMatchPad + defines a "bad match" + ** Operation: This routine steps thru 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. + ** Return: none + ** Exceptions: none + ** History: Tue Mar 12 13:51:03 1991, DSJ, Created. + */ + int Next, NextGood; + int punc_count; /*no of garbage characters */ + int digit_count; + CLASS_ID *Match = Results->Classes; + /*garbage characters */ + static char punc_chars[] = ".,;:/`~'-=\\|\"!_^"; + static char digit_chars[] = "0123456789"; + + punc_count = 0; + digit_count = 0; + for (Next = NextGood = 0; Next < Results->NumMatches; Next++) { + if (strchr (punc_chars, Match[Next]) == NULL) { + if (strchr (digit_chars, Match[Next]) == NULL) { + Match[NextGood++] = Match[Next]; + } + else { + if (digit_count < 1) + Match[NextGood++] = Match[Next]; + digit_count++; + } + } + else { + if (punc_count < 2) + Match[NextGood++] = Match[Next]; + punc_count++; /*count them */ + } + } + Results->NumMatches = NextGood; + } /* RemoveExtraPuncs */ + + /*---------------------------------------------------------------------------*/ + void SetAdaptiveThreshold(FLOAT32 Threshold) { + /* + ** Parameters: + ** Threshold + threshold for creating new templates + ** Globals: + ** GoodAdaptiveMatch + default good match rating + ** Operation: This routine resets the internal thresholds inside + ** the integer matcher to correspond to the specified + ** threshold. + ** Return: none + ** Exceptions: none + ** History: Tue Apr 9 08:33:13 1991, DSJ, Created. + */ + if (Threshold == GoodAdaptiveMatch) { + /* the blob was probably classified correctly - use the default rating + threshold */ + SetProtoThresh (0.9); + SetFeatureThresh (0.9); + } + else { + /* the blob was probably incorrectly classified */ + SetProtoThresh (1.0 - Threshold); + SetFeatureThresh (1.0 - Threshold); + } + } /* SetAdaptiveThreshold */ + + /*---------------------------------------------------------------------------*/ + void ShowBestMatchFor(TBLOB *Blob, + LINE_STATS *LineStats, + CLASS_ID ClassId, + BOOL8 AdaptiveOn, + BOOL8 PreTrainedOn) { + /* + ** Parameters: + ** Blob + blob to show best matching config for + ** LineStats + statistics for text line Blob is in + ** ClassId + class whose configs are to be searched + ** AdaptiveOn + TRUE if adaptive configs are enabled + ** PreTrainedOn + TRUE if pretrained configs are enabled + ** Globals: + ** PreTrainedTemplates + built-in training + ** AdaptedTemplates + adaptive templates + ** AllProtosOn + dummy proto mask + ** AllConfigsOn + dummy config mask + ** Operation: This routine compares Blob to both sets of templates + ** (adaptive and pre-trained) and then displays debug + ** information for the config which matched best. + ** Return: none + ** Exceptions: none + ** History: Fri Mar 22 08:43:52 1991, DSJ, Created. + */ + int CNOutlineLength = 0, BLOutlineLength = 0; + int NumCNFeatures = 0, NumBLFeatures = 0; + INT_FEATURE_ARRAY CNFeatures, BLFeatures; + INT_RESULT_STRUCT CNResult, BLResult; + CLASS_NORMALIZATION_ARRAY CNAdjust, BLAdjust; + CLASS_INDEX ClassIndex; + FLOAT32 BlobLength; + UINT32 ConfigMask; + static int next_config = -1; + + if (PreTrainedOn) next_config = -1; + + CNResult.Rating = BLResult.Rating = 2.0; + + if (!LegalClassId (ClassId)) { + cprintf ("%c is not a legal class!!\n", ClassId); + return; + } + + if (PreTrainedOn) + if (UnusedClassIdIn (PreTrainedTemplates, ClassId)) + cprintf ("No built-in templates for class '%c'\n", ClassId); + else { + NumCNFeatures = GetCharNormFeatures (Blob, LineStats, + PreTrainedTemplates, + CNFeatures, CNAdjust, + &BlobLength); + if (NumCNFeatures <= 0) + cprintf ("Illegal blob (char norm features)!\n"); + else { + CNOutlineLength = (int) (BlobLength / GetPicoFeatureLength ()); + ClassIndex = IndexForClassId (PreTrainedTemplates, ClassId); + + SetCharNormMatch(); + IntegerMatcher (ClassForClassId (PreTrainedTemplates, ClassId), + AllProtosOn, AllConfigsOn, + CNOutlineLength, NumCNFeatures, CNFeatures, 0, + CNAdjust[ClassIndex], &CNResult, NO_DEBUG); + + cprintf ("Best built-in template match is config %2d (%4.1f) (cn=%d)\n", + CNResult.Config, CNResult.Rating * 100.0, CNAdjust[ClassIndex]); + } + } + + if (AdaptiveOn) + if (UnusedClassIdIn (AdaptedTemplates->Templates, ClassId)) + cprintf ("No AD templates for class '%c'\n", ClassId); + else { + NumBLFeatures = GetBaselineFeatures (Blob, LineStats, + AdaptedTemplates->Templates, + BLFeatures, BLAdjust, + &BlobLength); + if (NumBLFeatures <= 0) + cprintf ("Illegal blob (baseline features)!\n"); + else { + BLOutlineLength = (int) (BlobLength / GetPicoFeatureLength ()); + ClassIndex = + IndexForClassId (AdaptedTemplates->Templates, ClassId); + + SetBaseLineMatch(); + IntegerMatcher (ClassForClassId + (AdaptedTemplates->Templates, ClassId), + AllProtosOn, AllConfigsOn, +// AdaptedTemplates->Class[ClassIndex]->PermProtos, +// AdaptedTemplates->Class[ClassIndex]->PermConfigs, + BLOutlineLength, NumBLFeatures, BLFeatures, 0, + BLAdjust[ClassIndex], &BLResult, NO_DEBUG); + + #ifndef SECURE_NAMES + cprintf ("Best adaptive template match is config %2d (%4.1f)\n", + BLResult.Config, BLResult.Rating * 100.0); + #endif + } + } + + cprintf ("\n"); + if (BLResult.Rating < CNResult.Rating) { + ClassIndex = IndexForClassId (AdaptedTemplates->Templates, ClassId); + if (next_config < 0) { + ConfigMask = 1 << BLResult.Config; + next_config = 0; + } else { + ConfigMask = 1 << next_config; + ++next_config; + } + NormMethod = baseline; + + SetBaseLineMatch(); + IntegerMatcher (ClassForClassId (AdaptedTemplates->Templates, ClassId), + AllProtosOn, +// AdaptedTemplates->Class[ClassIndex]->PermProtos, + (BIT_VECTOR) & ConfigMask, + BLOutlineLength, NumBLFeatures, BLFeatures, 0, + BLAdjust[ClassIndex], &BLResult, MatchDebugFlags); + cprintf ("Adaptive template match for config %2d is %4.1f\n", + BLResult.Config, BLResult.Rating * 100.0); + } + else { + ClassIndex = IndexForClassId (PreTrainedTemplates, ClassId); + ConfigMask = 1 << CNResult.Config; + NormMethod = character; + + SetCharNormMatch(); + //xiaofan + IntegerMatcher (ClassForClassId (PreTrainedTemplates, ClassId), AllProtosOn, (BIT_VECTOR) & ConfigMask, + CNOutlineLength, NumCNFeatures, CNFeatures, 0, + CNAdjust[ClassIndex], &CNResult, MatchDebugFlags); + } + } /* ShowBestMatchFor */ diff --git a/classify/adaptmatch.h b/classify/adaptmatch.h new file mode 100644 index 0000000000..6c020ce636 --- /dev/null +++ b/classify/adaptmatch.h @@ -0,0 +1,83 @@ +/****************************************************************************** + ** Filename: adaptmatch.h + ** Purpose: Interface to high-level adaptive matcher + ** Author: Dan Johnson + ** History: Mon Mar 11 11:48: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. + ******************************************************************************/ +#ifndef ADAPTMATCH_H +#define ADAPTMATCH_H + +/**---------------------------------------------------------------------------- + Include Files and Type Defines +----------------------------------------------------------------------------**/ +#include "oldlist.h" +#include "tessclas.h" +#include "fxdefs.h" +#include "matchdefs.h" +#include "adaptive.h" +#include "ocrfeatures.h" + +/*--------------------------------------------------------------------------- + Variables +----------------------------------------------------------------------------*/ +extern float GoodAdaptiveMatch; +extern float GreatAdaptiveMatch; +extern int ReliableConfigThreshold; +extern int tess_cn_matching; +extern int tess_bn_matching; +extern int LearningDebugLevel; + +/**---------------------------------------------------------------------------- + Public Function Prototypes +----------------------------------------------------------------------------**/ +LIST AdaptiveClassifier(TBLOB *Blob, TBLOB *DotBlob, TEXTROW *Row); +/**/ +void AdaptToWord(TWERD *Word, + TEXTROW *Row, + const char *BestChoice, + const char *BestRawChoice, + const char *rejmap); + +void EndAdaptiveClassifier(); + +void InitAdaptiveClassifier(); + +void ResetAdaptiveClassifier(); + +void InitAdaptiveClassifierVars(); + +void PrintAdaptiveStatistics(FILE *File); + +void SettupPass1(); + +void SettupPass2(); + +void MakeNewAdaptedClass(TBLOB *Blob, + LINE_STATS *LineStats, + CLASS_ID ClassId, + ADAPT_TEMPLATES Templates); + +int GetAdaptiveFeatures(TBLOB *Blob, + LINE_STATS *LineStats, + INT_FEATURE_ARRAY IntFeatures, + FEATURE_SET *FloatFeatures); + +int AdaptableWord(TWERD *Word, + const char *BestChoice, + const char *BestRawChoice); + +/**---------------------------------------------------------------------------- + Global Data Definitions and Declarations +----------------------------------------------------------------------------**/ +#endif diff --git a/classify/baseline.cpp b/classify/baseline.cpp new file mode 100644 index 0000000000..bcf1141aa8 --- /dev/null +++ b/classify/baseline.cpp @@ -0,0 +1,58 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: baseline.c (Formerly baseline.c) + * Description: + * Author: Mark Seaman, OCR Technology + * Created: Fri Oct 16 14:37:00 1987 + * Modified: Tue Jul 30 16:16: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 "baseline.h" +//#include "blobs.h" +#include "debug.h" +#include "hideedge.h" + +/*---------------------------------------------------------------------- + T y p e s +----------------------------------------------------------------------*/ +typedef TPOINT SCALE; + +/*---------------------------------------------------------------------- + V a r i a b l e s +----------------------------------------------------------------------*/ +INT8 baseline_normalized = TRUE; + +make_int_var (baseline_enable, 1, make_baseline_enable, +4, 3, set_baseline_enable, "Baseline Enable"); +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +/********************************************************************** + * init_baseline + * + * Initialize the needed baseline variables. + **********************************************************************/ +void init_baseline() { + make_baseline_enable(); + + baseline_normalized = baseline_enable; +} diff --git a/classify/baseline.h b/classify/baseline.h new file mode 100644 index 0000000000..63f2787c02 --- /dev/null +++ b/classify/baseline.h @@ -0,0 +1,91 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: baseline.h (Formerly baseline.h) + * Description: + * Author: Mark Seaman, SW Productivity + * Created: Fri Oct 16 14:37:00 1987 + * Modified: Wed Feb 27 13:39:35 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 BASELINE_H +#define BASELINE_H + +/*---------------------------------------------------------------------- + I n c l u d e s +----------------------------------------------------------------------*/ +#include "general.h" +#include "tessclas.h" + +/*---------------------------------------------------------------------- + T y p e s +----------------------------------------------------------------------*/ +#define BASELINE_OFFSET 64 +#define BASELINE_SCALE 128 + +/*---------------------------------------------------------------------- + V a r i a b l e s +----------------------------------------------------------------------*/ +extern INT8 baseline_normalized; +extern int baseline_enable; + +/*---------------------------------------------------------------------- + M a c r o s +----------------------------------------------------------------------*/ +/********************************************************************** + * is_baseline_normalized + * + * Check the baseline_normalized flag to see if it is set. + **********************************************************************/ +#define is_baseline_normalized() \ +(baseline_normalized) + +/********************************************************************** + * reset_baseline_normalized + * + * Reset the baseline_normalized flag to show that it is not being done. + **********************************************************************/ +#define reset_baseline_normalized() \ +(baseline_normalized = FALSE) + +/********************************************************************** + * set_baseline_normalized + * + * Set the baseline_normalized flag to show that it is being done. + **********************************************************************/ +#define set_baseline_normalized() \ +(baseline_normalized = TRUE) + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +void init_baseline(); + +/* +#if defined(__STDC__) || defined(__cplusplus) +# define _ARGS(s) s +#else +# define _ARGS(s) () +#endif*/ + +/* baseline.c +void init_baseline + _ARGS((void)); + +#undef _ARGS +*/ +#endif diff --git a/classify/blobclass.cpp b/classify/blobclass.cpp new file mode 100644 index 0000000000..a086de0f18 --- /dev/null +++ b/classify/blobclass.cpp @@ -0,0 +1,131 @@ +/****************************************************************************** + ** 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 "fxdefs.h" +#include "variables.h" +#include "extract.h" +#include "efio.h" +#include "callcpp.h" +#include "chartoname.h" + +#include +#include +#include + +#define MAXFILENAME 80 +#define MAXMATCHES 10 + +// define default font name to be used in training +#define FONT_NAME "UnknownFont" + +/**---------------------------------------------------------------------------- + Global Data Definitions and Declarations +----------------------------------------------------------------------------**/ +/* name of current image file being processed */ +extern char imagefile[]; + +/* parameters used to control the training process */ +static const char *FontName = FONT_NAME; + +/**---------------------------------------------------------------------------- + Public Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +void InitBlobClassifierVars() { +/* + ** Parameters: none + ** Globals: + ** FontName name of font being trained on + ** Operation: Install blob classifier variables into the wiseowl + ** variable system. + ** Return: none + ** Exceptions: none + ** History: Fri Jan 19 16:13:33 1990, DSJ, Created. + */ + VALUE dummy; + + string_variable (FontName, "FontName", FONT_NAME); + +} /* InitBlobClassifierVars */ + + +/*---------------------------------------------------------------------------*/ +void +LearnBlob (TBLOB * Blob, TEXTROW * Row, char BlobText[], int TextLength) +/* + ** Parameters: + ** Blob blob whose micro-features are to be learned + ** Row row of text that blob came from + ** BlobText text that corresponds to blob + ** TextLength number of characters in blob + ** Globals: + ** imagefile base filename of the page being learned + ** FontName name of font currently being trained on + ** Operation: + ** Extract micro-features from the specified blob and append + ** them to the appropriate file. + ** Return: none + ** Exceptions: none + ** History: 7/28/89, DSJ, Created. + */ +#define MAXFILENAME 80 +#define MAXCHARNAME 20 +#define MAXFONTNAME 20 +#define TRAIN_SUFFIX ".tr" +{ + static FILE *FeatureFile = NULL; + char Filename[MAXFILENAME]; + char CharName[MAXCHARNAME]; + CHAR_DESC CharDesc; + LINE_STATS LineStats; + + EnterLearnMode; + + // throw out blobs which do not represent only one character + if (TextLength != 1) + return; + + GetLineStatsFromRow(Row, &LineStats); + + CharDesc = ExtractBlobFeatures (Blob, &LineStats); + + // if a feature file is not yet open, open it + // the name of the file is the name of the image plus TRAIN_SUFFIX + if (FeatureFile == NULL) { + strcpy(Filename, imagefile); + strcat(Filename, TRAIN_SUFFIX); + FeatureFile = Efopen (Filename, "w"); + + cprintf ("TRAINING ... Font name = %s.\n", FontName); + } + + // get the name of the character for this blob + chartoname (CharName, BlobText[0], ""); + + // label the features with a class name and font name + fprintf (FeatureFile, "\n%s %s ", FontName, CharName); + + // write micro-features to file and clean up + WriteCharDescription(FeatureFile, CharDesc); + FreeCharDescription(CharDesc); + +} // LearnBlob diff --git a/classify/blobclass.h b/classify/blobclass.h new file mode 100644 index 0000000000..478ca35ec6 --- /dev/null +++ b/classify/blobclass.h @@ -0,0 +1,49 @@ +/****************************************************************************** + ** 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 "oldlist.h" +#include "tessclas.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 +----------------------------------------------------------------------------**/ +void InitBlobClassifierVars(); + +void LearnBlob (TBLOB * Blob, TEXTROW * Row, char BlobText[], int TextLength); + +/**---------------------------------------------------------------------------- + Global Data Definitions and Declarations +----------------------------------------------------------------------------**/ +/*parameter used to turn on/off output of recognized chars to the screen */ +#endif diff --git a/classify/chartoname.cpp b/classify/chartoname.cpp new file mode 100644 index 0000000000..0a1d473b6d --- /dev/null +++ b/classify/chartoname.cpp @@ -0,0 +1,74 @@ +/************************************************************************** + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT 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 + +/*chartoname(name,c,dir) converts c into a useful filename*/ +void chartoname(register char *name, /*result */ + char c, /*char to convert */ + const char *dir) { /*directory to use */ + char file[3]; /*filename */ + int index; /*index of namelist */ + static const char *namelist[] = { + "!bang", + "\"doubleq", + "#hash", + "$dollar", + "%percent", + "&and", + "'quote", + "(lround", + ")rround", + "*asterisk", + "+plus", + ",comma", + "-minus", + ".dot", + "/slash", + ":colon", + ";semic", + "greater", + "?question", + "@at", + "[lsquare", + "\\backsl", + "]rsquare", + "^uparr", + "_unders", + "`grave", + "{lbrace", + "|bar", + "}rbrace", + "~tilde" + }; + + strcpy(name, dir); /*add specific directory */ + for (index = 0; index < sizeof namelist / sizeof (char *) + && c != namelist[index][0]; index++); + if (index < sizeof namelist / sizeof (char *)) + /*add text name */ + strcat (name, &namelist[index][1]); + else { + if (isupper (c)) { + file[0] = 'c'; /*direct a-z or A-Z */ + file[1] = c; /*direct a-z or A-Z */ + file[2] = '\0'; + } + else { + file[0] = c; /*direct a-z or A-Z */ + file[1] = '\0'; + } + strcat(name, file); /*append filename */ + } +} diff --git a/classify/chartoname.h b/classify/chartoname.h new file mode 100644 index 0000000000..07ded25f33 --- /dev/null +++ b/classify/chartoname.h @@ -0,0 +1,21 @@ +#ifndef CHARTONAME_H +#define CHARTONAME_H + + /*result */ +void chartoname(register char *name, + char c, /*char to convert */ + const char *dir); /*directory to use */ + +/* +#if defined(__STDC__) || defined(__cplusplus) +# define _P(s) s +#else +# define _P(s) () +#endif*/ + +/* chartoname.c +int chartoname _P((char *name, int c, char *dir)); + +#undef _P +*/ +#endif diff --git a/classify/cluster.cpp b/classify/cluster.cpp new file mode 100644 index 0000000000..c88749f64d --- /dev/null +++ b/classify/cluster.cpp @@ -0,0 +1,2786 @@ +/****************************************************************************** + ** 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 "oldheap.h" +#include "const.h" +#include "cluster.h" +#include "emalloc.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%. +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.0001 + +/* 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 MINBUCKETS 5 +#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 + +typedef struct +{ + CLUSTER *Cluster; + CLUSTER *Neighbor; +} + + +TEMPCLUSTER; + +typedef struct +{ + FLOAT32 AvgVariance; + FLOAT32 *CoVariance; + FLOAT32 *Min; // largest negative distance from the mean + FLOAT32 *Max; // largest positive distance from the mean +} + + +STATISTICS; + +typedef struct +{ + 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 occurence histogram + FLOAT32 *ExpectedCount; // expected histogram +} + + +BUCKETS; + +typedef struct +{ + UINT16 DegreesOfFreedom; + FLOAT64 Alpha; + FLOAT64 ChiSquared; +} + + +CHISTRUCT; + +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 are declared as global so that routines which +are called from the kd-tree walker can get to them. */ +static HEAP *Heap; +static TEMPCLUSTER *TempCluster; +static KDTREE *Tree; +static INT32 CurrentTemp; + +/* 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 FLOAT64 NormalStdDev = BUCKETTABLESIZE / (2.0 * NORMALEXTENT); +static FLOAT64 NormalVariance = +(BUCKETTABLESIZE * BUCKETTABLESIZE) / (4.0 * NORMALEXTENT * NORMALEXTENT); +static FLOAT64 NormalMagnitude = +(2.0 * NORMALEXTENT) / (SqrtOf2Pi * BUCKETTABLESIZE); +static FLOAT64 NormalMean = BUCKETTABLESIZE / 2; + +// keep a list of histogram buckets to minimize recomputing them +static LIST OldBuckets[] = { NIL, NIL, NIL }; + +/* 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 MAXBUCKETS 39 +#define MAXDEGREESOFFREEDOM MAXBUCKETS + +static UINT32 CountTable[LOOKUPTABLESIZE] = { + MINSAMPLES, 200, 400, 600, 800, 1000, 1500, 2000 +}; +static UINT16 BucketsTable[LOOKUPTABLESIZE] = { + MINBUCKETS, 16, 20, 24, 27, 30, 35, MAXBUCKETS +}; + +/*------------------------------------------------------------------------- + Private Function Prototypes +--------------------------------------------------------------------------*/ +void CreateClusterTree(CLUSTERER *Clusterer); + +void MakePotentialClusters(CLUSTER *Cluster, VISIT Order, 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, + 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(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-------------------------------------- +/** MakeClusterer ********************************************************** +Parameters: SampleSize number of dimensions in feature space + ParamDesc description of each dimension +Globals: None +Operation: This routine creates a new clusterer data structure, + initializes it, and returns a pointer to it. +Return: pointer to the new clusterer data structure +Exceptions: None +History: 5/29/89, DSJ, Created. +****************************************************************************/ +CLUSTERER * +MakeClusterer (INT16 SampleSize, 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; + + // 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); + + // execute hook for monitoring clustering operation + // (*ClustererCreationHook)( Clusterer ); + + return (Clusterer); +} // MakeClusterer + + +/** MakeSample *********************************************************** +Parameters: Clusterer clusterer data structure to add sample to + Feature feature to be added to clusterer + CharID unique ident. of char that sample came from +Globals: None +Operation: 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. +Return: Pointer to the new sample data structure +Exceptions: ALREADYCLUSTERED MakeSample can't be called after + ClusterSamples has been called +History: 5/29/89, DSJ, Created. +*****************************************************************************/ +SAMPLE * +MakeSample (CLUSTERER * Clusterer, 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 + + +/** ClusterSamples *********************************************************** +Parameters: Clusterer data struct containing samples to be clustered + Config parameters which control clustering process +Globals: None +Operation: 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. +Return: Pointer to a list of prototypes +Exceptions: None +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; + + //compute prototypes starting at the root node in the tree + ComputePrototypes(Clusterer, Config); + return (Clusterer->ProtoList); +} // ClusterSamples + + +/** FreeClusterer ************************************************************* +Parameters: Clusterer pointer to data structure to be freed +Globals: None +Operation: 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. +Return: None +Exceptions: None +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); + iterate (Clusterer->ProtoList) { + ((PROTOTYPE *) (first (Clusterer->ProtoList)))->Cluster = NULL; + } + memfree(Clusterer); + } +} // FreeClusterer + + +/** FreeProtoList ************************************************************ +Parameters: ProtoList pointer to list of prototypes to be freed +Globals: None +Operation: 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. +Return: None +Exceptions: None +History: 6/6/89, DSJ, Created. +*****************************************************************************/ +void FreeProtoList(LIST *ProtoList) { + destroy_nodes(*ProtoList, FreePrototype); +} // FreeProtoList + + +/** FreePrototype ************************************************************ +Parameters: Prototype prototype data structure to be deallocated +Globals: None +Operation: 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. +Return: None +Exceptions: None +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 + + +/** NextSample ************************************************************ +Parameters: SearchState ptr to list containing clusters to be searched +Globals: None +Operation: 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. +Return: Pointer to the next leaf cluster (sample) or NULL. +Exceptions: None +History: 6/16/89, DSJ, Created. +****************************************************************************/ +CLUSTER *NextSample(LIST *SearchState) { + CLUSTER *Cluster; + + if (*SearchState == NIL) + return (NULL); + Cluster = (CLUSTER *) first (*SearchState); + *SearchState = pop (*SearchState); + while (TRUE) { + if (Cluster->Left == NULL) + return (Cluster); + *SearchState = push (*SearchState, Cluster->Right); + Cluster = Cluster->Left; + } +} // NextSample + + +/** Mean *********************************************************** +Parameters: Proto prototype to return mean of + Dimension dimension whose mean is to be returned +Globals: none +Operation: This routine returns the mean of the specified + prototype in the indicated dimension. +Return: Mean of Prototype in Dimension +Exceptions: none +History: 7/6/89, DSJ, Created. +*********************************************************************/ +FLOAT32 Mean(PROTOTYPE *Proto, UINT16 Dimension) { + return (Proto->Mean[Dimension]); +} // Mean + + +/** StandardDeviation ************************************************* +Parameters: Proto prototype to return standard deviation of + Dimension dimension whose stddev is to be returned +Globals: none +Operation: This routine returns the standard deviation of the + prototype in the indicated dimension. +Return: Standard deviation of Prototype in Dimension +Exceptions: none +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]); + } + } + return 0.0f; +} // StandardDeviation + + +/*--------------------------------------------------------------------------- + Private Code +----------------------------------------------------------------------------*/ +/** CreateClusterTree ******************************************************* +Parameters: Clusterer data structure holdings samples to be clustered +Globals: Tree kd-tree holding samples + TempCluster array of temporary clusters + CurrentTemp index of next temp cluster to be used + Heap heap used to hold temp clusters - "best" on top +Operation: 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. +Return: None (the Clusterer data structure is changed) +Exceptions: None +History: 5/29/89, DSJ, Created. +******************************************************************************/ +void CreateClusterTree(CLUSTERER *Clusterer) { + HEAPENTRY HeapEntry; + TEMPCLUSTER *PotentialCluster; + + // save the kd-tree in a global variable so kd-tree walker can get at it + Tree = Clusterer->KDTree; + + // allocate memory to to hold all of the "potential" clusters + TempCluster = (TEMPCLUSTER *) + Emalloc (Clusterer->NumberOfSamples * sizeof (TEMPCLUSTER)); + CurrentTemp = 0; + + // each sample and its nearest neighbor form a "potential" cluster + // save these in a heap with the "best" potential clusters on top + Heap = MakeHeap (Clusterer->NumberOfSamples); + KDWalk (Tree, (void_proc) MakePotentialClusters); + + // form potential clusters into actual clusters - always do "best" first + while (GetTopOfHeap (Heap, &HeapEntry) != EMPTY) { + PotentialCluster = (TEMPCLUSTER *) (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 (Tree, PotentialCluster->Cluster, + &(HeapEntry.Key)); + if (PotentialCluster->Neighbor != NULL) { + HeapStore(Heap, &HeapEntry); + } + } + + // if neither cluster is already clustered, form permanent cluster + else { + PotentialCluster->Cluster = + MakeNewCluster(Clusterer, PotentialCluster); + PotentialCluster->Neighbor = + FindNearestNeighbor (Tree, PotentialCluster->Cluster, + &(HeapEntry.Key)); + if (PotentialCluster->Neighbor != NULL) { + HeapStore(Heap, &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(Tree); + Clusterer->KDTree = NULL; + FreeHeap(Heap); + memfree(TempCluster); +} // CreateClusterTree + + +/** MakePotentialClusters ************************************************** +Parameters: Cluster current cluster being visited in kd-tree walk + Order order in which cluster is being visited + Level level of this cluster in the kd-tree +Globals: Tree kd-tree to be searched for neighbors + TempCluster array of temporary clusters + CurrentTemp index of next temp cluster to be used + Heap heap used to hold temp clusters - "best" on top +Operation: 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. +Return: none +Exceptions: none +History: 5/29/89, DSJ, Created. + 7/13/89, DSJ, Removed visibility of kd-tree node data struct. +******************************************************************************/ +void MakePotentialClusters(CLUSTER *Cluster, VISIT Order, INT32 Level) { + HEAPENTRY HeapEntry; + + if ((Order == preorder) || (Order == leaf)) { + TempCluster[CurrentTemp].Cluster = Cluster; + HeapEntry.Data = (char *) &(TempCluster[CurrentTemp]); + TempCluster[CurrentTemp].Neighbor = + FindNearestNeighbor (Tree, TempCluster[CurrentTemp].Cluster, + &(HeapEntry.Key)); + if (TempCluster[CurrentTemp].Neighbor != NULL) { + HeapStore(Heap, &HeapEntry); + CurrentTemp++; + } + } +} // MakePotentialClusters + + +/** FindNearestNeighbor ********************************************************* +Parameters: Tree kd-tree to search in for nearest neighbor + Cluster cluster whose nearest neighbor is to be found + Distance ptr to variable to report distance found +Globals: none +Operation: 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. +Return: Pointer to the nearest neighbor of Cluster, or NULL +Exceptions: none +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]; + INT32 NumberOfNeighbors; + INT32 i; + CLUSTER *BestNeighbor; + + // find the 2 nearest neighbors of the cluster + NumberOfNeighbors = KDNearestNeighborSearch + (Tree, Cluster->Mean, MAXNEIGHBORS, MAXDISTANCE, 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 + + +/** MakeNewCluster ************************************************************* +Parameters: Clusterer current clustering environment + TempCluster potential cluster to make permanent +Globals: none +Operation: 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. + Return: Pointer to the new permanent cluster +Exceptions: none +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 + + +/** MergeClusters ************************************************************ +Parameters: N # of dimensions (size of arrays) + ParamDesc array of dimension descriptions + n1, n2 number of samples in each old cluster + m array to hold mean of new cluster + m1, m2 arrays containing means of old clusters +Globals: None +Operation: 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. +Return: The number of samples in the new cluster. +Exceptions: None +History: 5/31/89, DSJ, Created. +*********************************************************************************/ +INT32 +MergeClusters (INT16 N, +register PARAM_DESC ParamDesc[], +register INT32 n1, +register INT32 n2, +register FLOAT32 m[], +register FLOAT32 m1[], register FLOAT32 m2[]) { + register 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 + + +/** ComputePrototypes ******************************************************* +Parameters: Clusterer data structure holding cluster tree + Config parameters used to control prototype generation +Globals: None +Operation: 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. +Return: None +Exceptions: None +History: 5/30/89, DSJ, Created. +*******************************************************************************/ +void ComputePrototypes(CLUSTERER *Clusterer, CLUSTERCONFIG *Config) { + LIST ClusterStack = NIL; + 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, Clusterer->Root); + + // loop until we have analyzed all clusters which are potential prototypes + while (ClusterStack != NIL) { + // 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 (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 + + +/** MakePrototype *********************************************************** +Parameters: Clusterer data structure holding cluster tree + Config parameters used to control prototype generation + Cluster cluster to be made into a prototype +Globals: None +Operation: 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. +Return: Pointer to new prototype or NULL +Exceptions: None +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, Cluster, Statistics); + if (Proto != NULL) { + FreeStatistics(Statistics); + return Proto; + } + } + + // create a histogram data structure used to evaluate distributions + Buckets = GetBuckets (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; + } + FreeBuckets(Buckets); + FreeStatistics(Statistics); + return (Proto); +} // MakePrototype + + +/** MakeDegenerateProto ****************************************************** +Parameters: N number of dimensions + Cluster cluster being analyzed + Statistics statistical info about cluster + Style type of prototype to be generated + MinSamples minimum number of samples in a cluster +Globals: None +Operation: 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. +Return: Pointer to degenerate prototype or NULL. +Exceptions: None +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 + +/** TestEllipticalProto **************************************************** +Parameters: Clusterer data struct containing samples being clustered + Cluster cluster to be made into an elliptical prototype + Statistics statistical info about cluster +Globals: None +Operation: 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. +Return: Pointer to new elliptical prototype or NULL. +****************************************************************************/ +PROTOTYPE *TestEllipticalProto(CLUSTERER *Clusterer, + CLUSTER *Cluster, + STATISTICS *Statistics) { + 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) + return NULL; + FLOAT32* Inverse = (FLOAT32 *) Emalloc(N * N * sizeof(FLOAT32)); + FLOAT32* Delta = (FLOAT32*) Emalloc(N * sizeof(FLOAT32)); + double err = InvertMatrix(Statistics->CoVariance, N, Inverse); + if (err > 1) { + cprintf("Clustering error: Matrix inverse failed with error %g\n", err); + } + for (int dim = 0; dim < N; ++dim) { + Delta[dim] = Left->Mean[dim] - Right->Mean[dim]; + } + // 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(Inverse); + memfree(Delta); + Tsq *= Left->SampleCount * Right->SampleCount / TotalDims; + double F = Tsq * (TotalDims - N - 1) / ((TotalDims - N) * 2); + int Fx = N; + if (Fx > FTABLE_X) + Fx = FTABLE_X; + --Fx; + int Fy = TotalDims - N - 1; + if (Fy > FTABLE_Y) + Fy = FTABLE_Y; + --Fy; + if (F < FTable[Fy][Fx]) { + return NewEllipticalProto (Clusterer->SampleSize, Cluster, Statistics); + } + return NULL; +} + +/* MakeSphericalProto ******************************************************* +Parameters: Clusterer data struct containing samples being clustered + Cluster cluster to be made into a spherical prototype + Statistics statistical info about cluster + Buckets histogram struct used to analyze distribution +Globals: None +Operation: 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. +Return: Pointer to new spherical prototype or NULL. +Exceptions: None +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 + + +/** MakeEllipticalProto **************************************************** +Parameters: Clusterer data struct containing samples being clustered + Cluster cluster to be made into an elliptical prototype + Statistics statistical info about cluster + Buckets histogram struct used to analyze distribution +Globals: None +Operation: 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. +Return: Pointer to new elliptical prototype or NULL. +Exceptions: None +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 + + +/** MakeMixedProto *********************************************************** +Parameters: Clusterer data struct containing samples being clustered + Cluster cluster to be made into a prototype + Statistics statistical info about cluster + NormalBuckets histogram struct used to analyze distribution + Confidence confidence level for alternate distributions +Globals: None +Operation: 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. +Return: Pointer to new mixed prototype or NULL. +Exceptions: None +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 (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 (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; + } + if (UniformBuckets != NULL) + FreeBuckets(UniformBuckets); + if (RandomBuckets != NULL) + FreeBuckets(RandomBuckets); + return (Proto); +} // MakeMixedProto + + +/* MakeDimRandom ************************************************************* +Parameters: i index of dimension to be changed + Proto prototype whose dimension is to be altered + ParamDesc description of specified dimension +Globals: None +Operation: This routine alters the ith dimension of the specified + mixed prototype to be D_random. +Return: None +Exceptions: None +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 + + +/** MakeDimUniform *********************************************************** +Parameters: i index of dimension to be changed + Proto prototype whose dimension is to be altered + Statistics statistical info about prototype +Globals: None +Operation: This routine alters the ith dimension of the specified + mixed prototype to be uniform. +Return: None +Exceptions: None +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 + + +/** ComputeStatistics ********************************************************* +Parameters: N number of dimensions + ParamDesc array of dimension descriptions + Cluster cluster whose stats are to be computed +Globals: None +Operation: 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. +Return: Pointer to new data structure containing statistics +Exceptions: None +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 + + +/** NewSpericalProto ********************************************************* +Parameters: N number of dimensions + Cluster cluster to be made into a spherical prototype + Statistics statistical info about samples in cluster +Globals: None +Operation: 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. +Return: Pointer to a new spherical prototype data structure +Exceptions: None +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 + + +/** NewEllipticalProto ******************************************************* +Parameters: N number of dimensions + Cluster cluster to be made into an elliptical prototype + Statistics statistical info about samples in cluster +Globals: None +Operation: 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. +Return: Pointer to a new elliptical prototype data structure +Exceptions: None +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 + + +/** MewMixedProto ************************************************************ +Parameters: N number of dimensions + Cluster cluster to be made into a mixed prototype + Statistics statistical info about samples in cluster +Globals: None +Operation: 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. +Return: Pointer to a new mixed prototype data structure +Exceptions: None +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 + + +/** NewSimpleProto *********************************************************** +Parameters: N number of dimensions + Cluster cluster to be made into a prototype +Globals: None +Operation: This routine allocates memory to hold a simple prototype + data structure, i.e. one without independent distributions + and variances for each dimension. +Return: Pointer to new simple prototype +Exceptions: None +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->Style = spherical; + Proto->NumSamples = Cluster->SampleCount; + Proto->Cluster = Cluster; + Proto->Cluster->Prototype = TRUE; + return (Proto); +} // NewSimpleProto + + +/** Independent *************************************************************** +Parameters: ParamDesc descriptions of each feature space dimension + N number of dimensions + CoVariance ptr to a covariance matrix + Independence max off-diagonal correlation coefficient +Globals: None +Operation: 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). +Return: TRUE if dimensions are independent, FALSE otherwise +Exceptions: None +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 + + +/** GetBuckets ************************************************************** +Parameters: Distribution type of probability distribution to test for + SampleCount number of samples that are available + Confidence probability of a Type I error +Globals: none +Operation: 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. +Return: Bucket data structure +Exceptions: none +History: Thu Aug 3 12:58:10 1989, DSJ, Created. +*****************************************************************************/ +BUCKETS *GetBuckets(DISTRIBUTION Distribution, + UINT32 SampleCount, + FLOAT64 Confidence) { + UINT16 NumberOfBuckets; + BUCKETS *Buckets; + + // search for an old bucket structure with the same number of buckets + NumberOfBuckets = OptimumNumberOfBuckets (SampleCount); + Buckets = (BUCKETS *) first (search (OldBuckets[(int) Distribution], + &NumberOfBuckets, NumBucketsMatch)); + + // if a matching bucket structure is found, delete it from the list + if (Buckets != NULL) { + OldBuckets[(int) Distribution] = + delete_d (OldBuckets[(int) Distribution], Buckets, ListEntryMatch); + if (SampleCount != Buckets->SampleCount) + AdjustBuckets(Buckets, SampleCount); + if (Confidence != Buckets->Confidence) { + Buckets->Confidence = Confidence; + Buckets->ChiSquared = ComputeChiSquared + (DegreesOfFreedom (Distribution, Buckets->NumberOfBuckets), + Confidence); + } + InitBuckets(Buckets); + } + else // otherwise create a new structure + Buckets = MakeBuckets (Distribution, SampleCount, Confidence); + return (Buckets); +} // GetBuckets + + +/** Makebuckets ************************************************************* +Parameters: Distribution type of probability distribution to test for + SampleCount number of samples that are available + Confidence probability of a Type I error +Globals: None +Operation: 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. +Return: Pointer to new histogram data structure +Exceptions: None +History: 6/4/89, DSJ, Created. +*****************************************************************************/ +BUCKETS *MakeBuckets(DISTRIBUTION Distribution, + UINT32 SampleCount, + FLOAT64 Confidence) { + static 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 = (BUCKETS *) Emalloc (sizeof (BUCKETS)); + Buckets->NumberOfBuckets = OptimumNumberOfBuckets (SampleCount); + Buckets->SampleCount = SampleCount; + Buckets->Confidence = Confidence; + Buckets->Count = + (UINT32 *) Emalloc (Buckets->NumberOfBuckets * sizeof (UINT32)); + Buckets->ExpectedCount = + (FLOAT32 *) 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 + + +//--------------------------------------------------------------------------- +UINT16 OptimumNumberOfBuckets(UINT32 SampleCount) { +/* + ** Parameters: + ** SampleCount number of samples to be tested + ** Globals: + ** CountTable lookup table for number of samples + ** BucketsTable lookup table for necessary number of buckets + ** Operation: + ** 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. + ** Return: + ** Optimum number of histogram buckets + ** Exceptions: + ** None + ** History: + ** 6/5/89, DSJ, Created. + */ + UINT8 Last, Next; + FLOAT32 Slope; + + if (SampleCount < CountTable[0]) + return (BucketsTable[0]); + + for (Last = 0, Next = 1; Next < LOOKUPTABLESIZE; Last++, Next++) { + if (SampleCount <= CountTable[Next]) { + Slope = (FLOAT32) (BucketsTable[Next] - BucketsTable[Last]) / + (FLOAT32) (CountTable[Next] - CountTable[Last]); + return ((UINT16) (BucketsTable[Last] + + Slope * (SampleCount - CountTable[Last]))); + } + } + return (BucketsTable[Last]); +} // OptimumNumberOfBuckets + + +//--------------------------------------------------------------------------- +FLOAT64 +ComputeChiSquared (UINT16 DegreesOfFreedom, FLOAT64 Alpha) +/* + ** Parameters: + ** DegreesOfFreedom determines shape of distribution + ** Alpha probability of right tail + ** Globals: none + ** Operation: + ** 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. + ** Return: Desired chi-squared value + ** Exceptions: none + ** History: 6/5/89, DSJ, Created. + */ +#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. + if (Alpha < MINALPHA) + Alpha = MINALPHA; + if (Alpha > 1.0) + Alpha = 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 (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 + + +//--------------------------------------------------------------------------- +FLOAT64 NormalDensity(INT32 x) { +/* + ** Parameters: + ** x number to compute the normal probability density for + ** Globals: + ** NormalMean mean of a discrete normal distribution + ** NormalVariance variance of a discrete normal distribution + ** NormalMagnitude magnitude of a discrete normal distribution + ** Operation: + ** This routine computes the probability density function + ** of a discrete normal distribution defined by the global + ** variables NormalMean, NormalVariance, and NormalMagnitude. + ** Normal magnitude could, of course, be computed in terms of + ** the normal variance but it is precomputed for efficiency. + ** Return: + ** The value of the normal distribution at x. + ** Exceptions: + ** None + ** History: + ** 6/4/89, DSJ, Created. + */ + FLOAT64 Distance; + + Distance = x - NormalMean; + return (NormalMagnitude * + exp (-0.5 * Distance * Distance / NormalVariance)); +} // NormalDensity + + +//--------------------------------------------------------------------------- +FLOAT64 UniformDensity(INT32 x) { +/* + ** Parameters: + ** x number to compute the uniform probability density for + ** Globals: + ** BUCKETTABLESIZE determines range of distribution + ** Operation: + ** 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. + ** Return: + ** The value of the uniform distribution at x. + ** Exceptions: + ** None + ** History: + ** 6/5/89, DSJ, Created. + */ + static FLOAT64 UniformDistributionDensity = (FLOAT64) 1.0 / BUCKETTABLESIZE; + + if ((x >= 0.0) && (x <= BUCKETTABLESIZE)) + return (UniformDistributionDensity); + else + return ((FLOAT64) 0.0); +} // UniformDensity + + +//--------------------------------------------------------------------------- +FLOAT64 Integral(FLOAT64 f1, FLOAT64 f2, FLOAT64 Dx) { +/* + ** Parameters: + ** f1 value of function at x1 + ** f2 value of function at x2 + ** Dx x2 - x1 (should always be positive) + ** Globals: + ** None + ** Operation: + ** This routine computes a trapezoidal approximation to the + ** integral of a function over a small delta in x. + ** Return: + ** Approximation of the integral of the function from x1 to x2. + ** Exceptions: + ** None + ** History: + ** 6/5/89, DSJ, Created. + */ + return ((f1 + f2) * Dx / 2.0); +} // Integral + + +//--------------------------------------------------------------------------- +void FillBuckets(BUCKETS *Buckets, + CLUSTER *Cluster, + UINT16 Dim, + PARAM_DESC *ParamDesc, + FLOAT32 Mean, + FLOAT32 StdDev) { +/* + ** Parameters: + ** Buckets histogram buckets to count samples + ** Cluster cluster whose samples are being analyzed + ** Dim dimension of samples which is being analyzed + ** ParamDesc description of the dimension + ** Mean "mean" of the distribution + ** StdDev "standard deviation" of the distribution + ** Globals: + ** None + ** Operation: + ** 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. + ** Return: + ** None (the Buckets data structure is filled in) + ** Exceptions: + ** None + ** History: + ** 6/5/89, DSJ, Created. + */ + 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 + + +//---------------------------------------------------------------------------*/ +UINT16 NormalBucket(PARAM_DESC *ParamDesc, + FLOAT32 x, + FLOAT32 Mean, + FLOAT32 StdDev) { +/* + ** Parameters: + ** ParamDesc used to identify circular dimensions + ** x value to be normalized + ** Mean mean of normal distribution + ** StdDev standard deviation of normal distribution + ** Globals: + ** NormalMean mean of discrete normal distribution + ** NormalStdDev standard deviation of discrete normal dist. + ** BUCKETTABLESIZE range of the discrete distribution + ** Operation: + ** This routine determines which bucket x falls into in the + ** discrete normal distribution defined by NormalMean + ** and NormalStdDev. x values which exceed the range of + ** the discrete distribution are clipped. + ** Return: + ** Bucket number into which x falls + ** Exceptions: + ** None + ** History: + ** 6/5/89, DSJ, Created. + */ + 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) * NormalStdDev + NormalMean; + if (X < 0) + return ((UINT16) 0); + if (X > BUCKETTABLESIZE - 1) + return ((UINT16) (BUCKETTABLESIZE - 1)); + return ((UINT16) floor ((FLOAT64) X)); +} // NormalBucket + + +//--------------------------------------------------------------------------- +UINT16 UniformBucket(PARAM_DESC *ParamDesc, + FLOAT32 x, + FLOAT32 Mean, + FLOAT32 StdDev) { +/* + ** Parameters: + ** ParamDesc used to identify circular dimensions + ** x value to be normalized + ** Mean center of range of uniform distribution + ** StdDev 1/2 the range of the uniform distribution + ** Globals: + ** BUCKETTABLESIZE range of the discrete distribution + ** Operation: + ** 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. + ** Return: + ** Bucket number into which x falls + ** Exceptions: + ** None + ** History: + ** 6/5/89, DSJ, Created. + */ + 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 ((UINT16) 0); + if (X > BUCKETTABLESIZE - 1) + return ((UINT16) (BUCKETTABLESIZE - 1)); + return ((UINT16) floor ((FLOAT64) X)); +} // UniformBucket + + +//--------------------------------------------------------------------------- +BOOL8 DistributionOK(BUCKETS *Buckets) { +/* + ** Parameters: + ** Buckets histogram data to perform chi-square test on + ** Globals: + ** None + ** Operation: + ** 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. + ** Return: + ** TRUE if samples match distribution, FALSE otherwise + ** Exceptions: + ** None + ** History: + ** 6/5/89, DSJ, Created. + */ + 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 + + +//--------------------------------------------------------------------------- +void FreeStatistics(STATISTICS *Statistics) { +/* + ** Parameters: + ** Statistics pointer to data structure to be freed + ** Globals: + ** None + ** Operation: + ** This routine frees the memory used by the statistics + ** data structure. + ** Return: + ** None + ** Exceptions: + ** None + ** History: + ** 6/5/89, DSJ, Created. + */ + memfree (Statistics->CoVariance); + memfree (Statistics->Min); + memfree (Statistics->Max); + memfree(Statistics); +} // FreeStatistics + + +//--------------------------------------------------------------------------- +void FreeBuckets(BUCKETS *Buckets) { +/* + ** Parameters: + ** Buckets pointer to data structure to be freed + ** Globals: none + ** Operation: + ** This routine places the specified histogram data structure + ** at the front of a list of histograms so that it can be + ** reused later if necessary. A separate list is maintained + ** for each different type of distribution. + ** Return: none + ** Exceptions: none + ** History: 6/5/89, DSJ, Created. + */ + int Dist; + + if (Buckets != NULL) { + Dist = (int) Buckets->Distribution; + OldBuckets[Dist] = (LIST) push (OldBuckets[Dist], Buckets); + } + +} // FreeBuckets + + +//--------------------------------------------------------------------------- +void FreeCluster(CLUSTER *Cluster) { +/* + ** Parameters: + ** Cluster pointer to cluster to be freed + ** Globals: + ** None + ** Operation: + ** This routine frees the memory consumed by the specified + ** cluster and all of its subclusters. This is done by + ** recursive calls to FreeCluster(). + ** Return: + ** None + ** Exceptions: + ** None + ** History: + ** 6/6/89, DSJ, Created. + */ + if (Cluster != NULL) { + FreeCluster (Cluster->Left); + FreeCluster (Cluster->Right); + memfree(Cluster); + } +} // FreeCluster + + +//--------------------------------------------------------------------------- +UINT16 DegreesOfFreedom(DISTRIBUTION Distribution, UINT16 HistogramBuckets) { +/* + ** Parameters: + ** Distribution distribution being tested for + ** HistogramBuckets number of buckets in chi-square test + ** Globals: none + ** Operation: + ** 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. + ** Return: The number of degrees of freedom for a chi-square test + ** Exceptions: none + ** History: Thu Aug 3 14:04:18 1989, DSJ, Created. + */ + static UINT8 DegreeOffsets[] = { 3, 3, 1 }; + + UINT16 AdjustedNumBuckets; + + AdjustedNumBuckets = HistogramBuckets - DegreeOffsets[(int) Distribution]; + if (Odd (AdjustedNumBuckets)) + AdjustedNumBuckets++; + return (AdjustedNumBuckets); + +} // DegreesOfFreedom + + +//--------------------------------------------------------------------------- +int NumBucketsMatch(void *arg1, //BUCKETS *Histogram, + void *arg2) { //UINT16 *DesiredNumberOfBuckets) +/* + ** Parameters: + ** Histogram current histogram being tested for a match + ** DesiredNumberOfBuckets match key + ** Globals: none + ** Operation: + ** 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. + ** Return: TRUE if Histogram matches DesiredNumberOfBuckets + ** Exceptions: none + ** History: Thu Aug 3 14:17:33 1989, DSJ, Created. + */ + BUCKETS *Histogram = (BUCKETS *) arg1; + UINT16 *DesiredNumberOfBuckets = (UINT16 *) arg2; + + return (*DesiredNumberOfBuckets == Histogram->NumberOfBuckets); + +} // NumBucketsMatch + + +//--------------------------------------------------------------------------- +int ListEntryMatch(void *arg1, //ListNode + void *arg2) { //Key +/* + ** Parameters: none + ** Globals: none + ** Operation: + ** 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 + ** Exceptions: none + ** History: Thu Aug 3 14:23:58 1989, DSJ, Created. + */ + return (arg1 == arg2); + +} // ListEntryMatch + + +//--------------------------------------------------------------------------- +void AdjustBuckets(BUCKETS *Buckets, UINT32 NewSampleCount) { +/* + ** Parameters: + ** Buckets histogram data structure to adjust + ** NewSampleCount new sample count to adjust to + ** Globals: none + ** Operation: + ** This routine multiplies each ExpectedCount histogram entry + ** by NewSampleCount/OldSampleCount so that the histogram + ** is now adjusted to the new sample count. + ** Return: none + ** Exceptions: none + ** History: Thu Aug 3 14:31:14 1989, DSJ, Created. + */ + 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 + + +//--------------------------------------------------------------------------- +void InitBuckets(BUCKETS *Buckets) { +/* + ** Parameters: + ** Buckets histogram data structure to init + ** Globals: none + ** Operation: + ** This routine sets the bucket counts in the specified histogram + ** to zero. + ** Return: none + ** Exceptions: none + ** History: Thu Aug 3 14:31:14 1989, DSJ, Created. + */ + int i; + + for (i = 0; i < Buckets->NumberOfBuckets; i++) { + Buckets->Count[i] = 0; + } + +} // InitBuckets + + +//--------------------------------------------------------------------------- +int AlphaMatch(void *arg1, //CHISTRUCT *ChiStruct, + void *arg2) { //CHISTRUCT *SearchKey) +/* + ** Parameters: + ** ChiStruct chi-squared struct being tested for a match + ** SearchKey chi-squared struct that is the search key + ** Globals: none + ** Operation: + ** 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. + ** Return: TRUE if ChiStruct's Alpha matches SearchKey's Alpha + ** Exceptions: none + ** History: Thu Aug 3 14:17:33 1989, DSJ, Created. + */ + CHISTRUCT *ChiStruct = (CHISTRUCT *) arg1; + CHISTRUCT *SearchKey = (CHISTRUCT *) arg2; + + return (ChiStruct->Alpha == SearchKey->Alpha); + +} // AlphaMatch + + +//--------------------------------------------------------------------------- +CHISTRUCT *NewChiStruct(UINT16 DegreesOfFreedom, FLOAT64 Alpha) { +/* + ** Parameters: + ** DegreesOfFreedom degrees of freedom for new chi value + ** Alpha confidence level for new chi value + ** Globals: none + ** Operation: + ** 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. + ** Return: none + ** Exceptions: none + ** History: Fri Aug 4 11:04:59 1989, DSJ, Created. + */ + CHISTRUCT *NewChiStruct; + + NewChiStruct = (CHISTRUCT *) Emalloc (sizeof (CHISTRUCT)); + NewChiStruct->DegreesOfFreedom = DegreesOfFreedom; + NewChiStruct->Alpha = Alpha; + return (NewChiStruct); + +} // NewChiStruct + + +//--------------------------------------------------------------------------- +FLOAT64 +Solve (SOLVEFUNC Function, +void *FunctionParams, FLOAT64 InitialGuess, FLOAT64 Accuracy) +/* + ** Parameters: + ** Function function whose zero is to be found + ** FunctionParams arbitrary data to pass to function + ** InitialGuess point to start solution search at + ** Accuracy maximum allowed error + ** Globals: none + ** Operation: + ** 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. + ** Return: Solution of function ( x for which f(x) = 0 ). + ** Exceptions: none + ** History: Fri Aug 4 11:08:59 1989, DSJ, Created. + */ +#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 + + +//--------------------------------------------------------------------------- +FLOAT64 ChiArea(CHISTRUCT *ChiParams, FLOAT64 x) { +/* + ** Parameters: + ** ChiParams contains degrees of freedom and alpha + ** x value of chi-squared to evaluate + ** Globals: none + ** Operation: + ** 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. + ** Return: Error between actual and desired area under the chi curve. + ** Exceptions: none + ** History: Fri Aug 4 12:48:41 1989, DSJ, Created. + */ + 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 + + +//--------------------------------------------------------------------------- +BOOL8 +MultipleCharSamples (CLUSTERER * Clusterer, +CLUSTER * Cluster, FLOAT32 MaxIllegal) +/* + ** Parameters: + ** Clusterer data structure holding cluster tree + ** Cluster cluster containing samples to be tested + ** MaxIllegal max percentage of samples allowed to have + ** more than 1 feature in the cluster + ** Globals: none + ** Operation: + ** 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. + ** Return: TRUE if the cluster should be split, FALSE otherwise. + ** Exceptions: none + ** History: Wed Aug 30 11:13:05 1989, DSJ, Created. + ** 2/22/90, DSJ, Added MaxIllegal control rather than always + ** splitting illegal clusters. + */ +#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) + 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) { + double** U; // The upper triangular array. + double* Umem; + double** U_inv; // The inverse of U. + double* U_invmem; + double** L; // The lower triangular array. + double* Lmem; + + // Allocate memory for the 2D arrays. + ALLOC_2D_ARRAY(size, size, Umem, U, double); + ALLOC_2D_ARRAY(size, size, U_invmem, U_inv, double); + ALLOC_2D_ARRAY(size, size, Lmem, L, double); + + // 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; +} + + diff --git a/classify/cluster.h b/classify/cluster.h new file mode 100644 index 0000000000..547f2d96ce --- /dev/null +++ b/classify/cluster.h @@ -0,0 +1,149 @@ +/****************************************************************************** + ** 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" + +/*---------------------------------------------------------------------- + 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 +} + + +CLUSTERCONFIG; + +typedef enum { + normal, uniform, D_random +} + + +DISTRIBUTION; + +typedef union +{ + FLOAT32 Spherical; + FLOAT32 *Elliptical; + +} + + +FLOATUNION; + +typedef struct proto +{ + unsigned Significant:1; // TRUE if prototype is significant + unsigned Style:2; // spherical, elliptical, or mixed + unsigned NumSamples:29; // 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 +} + + +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):(S=push(NIL,(C)))) + +/*-------------------------------------------------------------------------- + Public Function Prototypes +--------------------------------------------------------------------------*/ +CLUSTERER *MakeClusterer (INT16 SampleSize, PARAM_DESC ParamDesc[]); + +SAMPLE *MakeSample (CLUSTERER * Clusterer, 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); + +//--------------Global Data Definitions and Declarations--------------------------- +// define errors that can be trapped +#define ALREADYCLUSTERED 4000 +#endif diff --git a/classify/clusttool.cpp b/classify/clusttool.cpp new file mode 100644 index 0000000000..70e588ef06 --- /dev/null +++ b/classify/clusttool.cpp @@ -0,0 +1,507 @@ +/****************************************************************************** + ** 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) + +/*--------------------------------------------------------------------------- + Public Code +-----------------------------------------------------------------------------*/ +/** ReadSampleSize *********************************************************** +Parameters: File open text file to read sample size from +Globals: None +Operation: This routine reads a single integer from the specified + file and checks to ensure that it is between 0 and + MAXSAMPLESIZE. +Return: Sample size +Exceptions: ILLEGALSAMPLESIZE illegal format or range +History: 6/6/89, DSJ, Created. +******************************************************************************/ +UINT16 ReadSampleSize(FILE *File) { + int SampleSize; + + if ((fscanf (File, "%d", &SampleSize) != 1) || + (SampleSize < 0) || (SampleSize > MAXSAMPLESIZE)) + DoError (ILLEGALSAMPLESIZE, "Illegal sample size"); + return (SampleSize); +} // ReadSampleSize + + +/** ReadParamDesc ************************************************************* +Parameters: File open text file to read N parameter descriptions from + N number of parameter descriptions to read +Globals: None +Operation: This routine reads textual descriptions of sets of parameters + which describe the characteristics of feature dimensions. +Return: Pointer to an array of parameter descriptors. +Exceptions: ILLEGALCIRCULARSPEC + ILLEGALESSENTIALSPEC + ILLEGALMINMAXSPEC +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 (fscanf (File, "%s", Token) != 1) + DoError (ILLEGALCIRCULARSPEC, + "Illegal circular/linear specification"); + if (Token[0] == 'c') + ParamDesc[i].Circular = TRUE; + else + ParamDesc[i].Circular = FALSE; + + if (fscanf (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 (fscanf (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); +} // ReadParamDesc + + +/** ReadPrototype ************************************************************* +Parameters: File open text file to read prototype from + N number of dimensions used in prototype +Globals: None +Operation: This routine reads a textual description of a prototype from + the specified file. +Return: List of prototypes +Exceptions: ILLEGALSIGNIFICANCESPEC + ILLEGALSAMPLECOUNT + ILLEGALMEANSPEC + ILLEGALVARIANCESPEC + ILLEGALDISTRIBUTION +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 = fscanf (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 ((fscanf (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, (double) 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 (fscanf (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; + } + 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); + } +} // ReadPrototype + + +/* ReadProtoStyle ************************************************************* +Parameters: File open text file to read prototype style from +Globals: None +Operation: This routine reads an single token from the specified + text file and interprets it as a prototype specification. +Return: Prototype style read from text file +Exceptions: ILLEGALSTYLESPEC illegal prototype style specification +History: 6/8/89, DSJ, Created. +*******************************************************************************/ +PROTOSTYLE ReadProtoStyle(FILE *File) { + char Token[TOKENSIZE]; + PROTOSTYLE Style; + + if (fscanf (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); +} // ReadProtoStyle + + +/** ReadNFloats ************************************************************* +Parameters: File open text file to read floats from + N number of floats to read + Buffer pointer to buffer to place floats into +Globals: None +Operation: 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. +Return: Pointer to buffer holding floats or NULL if EOF +Exceptions: ILLEGALFLOAT +History: 6/6/89, DSJ, Created. +******************************************************************************/ +FLOAT32 * +ReadNFloats (FILE * File, UINT16 N, FLOAT32 Buffer[]) { + int i; + int NumFloatsRead; + + if (Buffer == NULL) + Buffer = (FLOAT32 *) Emalloc (N * sizeof (FLOAT32)); + + for (i = 0; i < N; i++) { + NumFloatsRead = fscanf (File, "%f", &(Buffer[i])); + if (NumFloatsRead != 1) { + if ((NumFloatsRead == EOF) && (i == 0)) + return (NULL); + else + DoError (ILLEGALFLOAT, "Illegal float specification"); + } + } + return (Buffer); +} // ReadNFloats + + +/** WriteParamDesc ************************************************************ +Parameters: File open text file to write param descriptors to + N number of param descriptors to write + ParamDesc array of param descriptors to write +Globals: None +Operation: This routine writes an array of dimension descriptors to + the specified text file. +Return: None +Exceptions: None +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); + } +} // WriteParamDesc + + +/** WritePrototype ************************************************************ +Parameters: File open text file to write prototype to + N number of dimensions in feature space + Proto prototype to write out +Globals: None +Operation: This routine writes a textual description of a prototype + to the specified text file. +Return: None +Exceptions: None +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; + } + fprintf (File, "\n\t"); + WriteNFloats (File, N, Proto->Variance.Elliptical); + } +} // WritePrototype + + +/** WriteNFloats *********************************************************** +Parameters: File open text file to write N floats to + N number of floats to write + Array array of floats to write +Globals: None +Operation: This routine writes a text representation of N floats from + an array to a file. All of the floats are placed on one line. +Return: None +Exceptions: None +History: 6/6/89, DSJ, Created. +****************************************************************************/ +void +WriteNFloats (FILE * File, UINT16 N, FLOAT32 Array[]) { + int i; + + for (i = 0; i < N; i++) + fprintf (File, " %9.6f", Array[i]); + fprintf (File, "\n"); +} // WriteNFloats + + +/** WriteProtoSyle ********************************************************** +Parameters: File open text file to write prototype style to + ProtoStyle prototype style to write +Globals: None +Operation: This routine writes to the specified text file a word + which represents the ProtoStyle. It does not append + a carriage return to the end. +Return: None +Exceptions: None +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; + } +} // WriteProtoStyle + +/*---------------------------------------------------------------------------*/ +void WriteProtoList( + FILE *File, + UINT16 N, + PARAM_DESC ParamDesc[], + LIST ProtoList, + BOOL8 WriteSigProtos, + BOOL8 WriteInsigProtos) + +/* +** Parameters: +** File open text file to write prototypes to +** N number of dimensions in feature space +** ParamDesc descriptions for each dimension +** ProtoList list of prototypes to be written +** WriteSigProtos TRUE to write out significant prototypes +** WriteInsigProtos TRUE to write out insignificants +** Globals: +** None +** Operation: +** 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. +** Return: +** None +** Exceptions: +** None +** History: +** 6/12/89, DSJ, Created. +*/ + +{ + PROTOTYPE *Proto; + + /* write file header */ + fprintf(File,"%0d\n",N); + WriteParamDesc(File,N,ParamDesc); + + /* write prototypes */ + iterate(ProtoList) + { + Proto = (PROTOTYPE *) first ( ProtoList ); + if (( Proto->Significant && WriteSigProtos ) || + ( ! Proto->Significant && WriteInsigProtos ) ) + WritePrototype( File, N, Proto ); + } +} /* WriteProtoList */ + +/** UniformRandomNumber ******************************************************** +Parameters: MMin lower range of uniform distribution + MMax upper range of uniform distribution +Globals: None +Operation: This routine computes a random number which comes from a + uniform distribution over the range from MMin to MMax. +Return: Uniform random number +Exceptions: None +History: 6/6/89, DSJ, Created. +*******************************************************************************/ +FLOAT32 UniformRandomNumber(FLOAT32 MMin, FLOAT32 MMax) { + double fake_drand48(); + FLOAT32 RandomNumber; + + RandomNumber = fake_drand48 (); + return (MMin + (RandomNumber * (MMax - MMin))); +} // UniformRandomNumber + + +/** drand48 ************************************************************* +Cheap replacement for drand48 which is not available on the PC. +**********************************************************************/ + +double fake_drand48() { + return rand () / (RAND_MAX + 1.0); +} diff --git a/classify/clusttool.h b/classify/clusttool.h new file mode 100644 index 0000000000..aa55d9efe4 --- /dev/null +++ b/classify/clusttool.h @@ -0,0 +1,70 @@ +/****************************************************************************** + ** 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 Funtion 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); + +FLOAT32 UniformRandomNumber(FLOAT32 MMin, FLOAT32 MMax); + +//--------------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 diff --git a/classify/cutoffs.cpp b/classify/cutoffs.cpp new file mode 100644 index 0000000000..b76d55de14 --- /dev/null +++ b/classify/cutoffs.cpp @@ -0,0 +1,67 @@ +/****************************************************************************** + ** 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 "efio.h" +#include "scanutils.h" +#include + +#define MAX_CUTOFF 1000 + +/**---------------------------------------------------------------------------- + Public Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +void ReadNewCutoffs(char *Filename, + CLASS_TO_INDEX ClassMapper, + CLASS_CUTOFF_ARRAY Cutoffs) { +/* + ** Parameters: + ** Filename name of file containing cutoff definitions + ** ClassMapper array which maps class id's to class indexes + ** Cutoffs array to put cutoffs into + ** Globals: none + ** Operation: Open Filename, read in all of the class-id/cutoff pairs + ** and insert them into the Cutoffs array. Cutoffs are + ** inserted in the array so that the array is indexed by + ** class index rather than class id. Unused entries in the + ** array are set to an arbitrarily high cutoff value. + ** Return: none + ** Exceptions: none + ** History: Wed Feb 20 09:38:26 1991, DSJ, Created. + */ + FILE *CutoffFile; + char Class[2]; + CLASS_ID ClassId; + int Cutoff; + int i; + + CutoffFile = Efopen (Filename, "r"); + + for (i = 0; i < MAX_NUM_CLASSES; i++) + Cutoffs[i] = MAX_CUTOFF; + + while (fscanf (CutoffFile, "%1s %d", Class, &Cutoff) == 2) { + ClassId = Class[0]; + Cutoffs[ClassMapper[ClassId]] = Cutoff; + } + fclose(CutoffFile); + +} /* ReadNewCutoffs */ diff --git a/classify/cutoffs.h b/classify/cutoffs.h new file mode 100644 index 0000000000..49b46015e8 --- /dev/null +++ b/classify/cutoffs.h @@ -0,0 +1,49 @@ +/****************************************************************************** + ** 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 +----------------------------------------------------------------------------**/ +void ReadNewCutoffs(char *Filename, + CLASS_TO_INDEX ClassMapper, + CLASS_CUTOFF_ARRAY Cutoffs); + +/* +#if defined(__STDC__) || defined(__cplusplus) +# define _ARGS(s) s +#else +# define _ARGS(s) () +#endif*/ + +/* cutoffs.c +void ReadNewCutoffs + _ARGS((char *Filename, + CLASS_TO_INDEX ClassMapper, + CLASS_CUTOFF_ARRAY Cutoffs)); +#undef _ARGS +*/ +#endif diff --git a/classify/extern.h b/classify/extern.h new file mode 100644 index 0000000000..cfae28c72a --- /dev/null +++ b/classify/extern.h @@ -0,0 +1,35 @@ +#ifndef EXTERN_H +#define EXTERN_H + +/* -*-C-*- + ******************************************************************************** + * + * File: extern.h (Formerly extern.h) + * Description: External definitions for C or C++ + * Author: Mark Seaman, OCR Technology + * Created: Tue Mar 20 14:01:22 1990 + * Modified: Tue Mar 20 14:02:09 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. + * + ******************************************************************************** + */ + +#ifdef __cplusplus +#define EXTERN extern "C" +#else +#define EXTERN extern +#endif +#endif diff --git a/classify/extract.cpp b/classify/extract.cpp new file mode 100644 index 0000000000..74b34eb139 --- /dev/null +++ b/classify/extract.cpp @@ -0,0 +1,100 @@ +/****************************************************************************** + ** Filename: extract.c + ** Purpose: Generic high level feature extractor routines. + ** Author: Dan Johnson + ** History: Sun Jan 21 09:44:08 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 "extract.h" +#include "variables.h" +#include "flexfx.h" +#include "funcdefs.h" +#include "danerror.h" + +typedef CHAR_FEATURES (*CF_FUNC) (); + +/**---------------------------------------------------------------------------- + Private Function Prototypes +----------------------------------------------------------------------------**/ +void ExtractorStub(); + +/**---------------------------------------------------------------------------- + Global Data Definitions and Declarations +----------------------------------------------------------------------------**/ +/* tables to keep track of the different low level feature extractors */ +#define NUM_FX 3 +#define DEFAULT_FX 2 + +int CurrentFx = DEFAULT_FX; + +/**---------------------------------------------------------------------------- + Public Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +CHAR_DESC ExtractBlobFeatures(TBLOB *Blob, LINE_STATS *LineStats) { +/* + ** Parameters: + ** Blob blob to extract features from + ** LineStats statistics about line blob is in + ** Operation: Extract features from Blob by calling the feature + ** extractor which is currently being used. This routine + ** simply provides a high level interface to feature + ** extraction. The caller can extract any type of features + ** from a blob without understanding any lower level details. + ** Return: The character features extracted from Blob. + ** Exceptions: none + ** History: Sun Jan 21 10:07:28 1990, DSJ, Created. + */ + return (ExtractFlexFeatures (Blob, LineStats)); +} /* ExtractBlobFeatures */ + + +/*---------------------------------------------------------------------------*/ +void InitExtractorVars() { +/* + ** Parameters: none + ** Globals: none + ** Operation: Install global extractor variables into the variable + ** system. + ** Return: none + ** Exceptions: none + ** History: Sun Jan 21 10:19:59 1990, DSJ, Created. + */ + // VALUE dummy; + InitFlexFXVars(); + +} /* InitExtractorVars */ + + +/**---------------------------------------------------------------------------- + Private Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +void +ExtractorStub () +/* + ** Parameters: none + ** Globals: none + ** Operation: This routine is used to stub out feature extractors + ** that are no longer used. It simply calls DoError. + ** Return: none + ** Exceptions: none + ** History: Wed Jan 2 14:16:49 1991, DSJ, Created. + */ +#define DUMMY_ERROR 1 +{ + DoError (DUMMY_ERROR, "Selected feature extractor has been stubbed out!"); +} /* ExtractorStub */ diff --git a/classify/extract.h b/classify/extract.h new file mode 100644 index 0000000000..7a16e91796 --- /dev/null +++ b/classify/extract.h @@ -0,0 +1,36 @@ +/****************************************************************************** + ** Filename: extract.h + ** Purpose: Interface to high level generic feature extraction. + ** Author: Dan Johnson + ** History: 1/21/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 EXTRACT_H +#define EXTRACT_H + +#include "fxdefs.h" +#include "featdefs.h" +#include + +/**---------------------------------------------------------------------------- + Public Function Prototypes +----------------------------------------------------------------------------**/ +CHAR_DESC ExtractBlobFeatures(TBLOB *Blob, LINE_STATS *LineStats); + +void InitExtractorVars(); + +/*--------------------------------------------------------------------------- + Private Function Prototypes +----------------------------------------------------------------------------*/ +void ExtractorStub(); +#endif diff --git a/classify/featdefs.cpp b/classify/featdefs.cpp new file mode 100644 index 0000000000..e162c4fa79 --- /dev/null +++ b/classify/featdefs.cpp @@ -0,0 +1,244 @@ +/****************************************************************************** + ** 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 +----------------------------------------------------------------------------**/ +#include "featdefs.h" +#include "emalloc.h" +#include "danerror.h" +#include "scanutils.h" +#include "variables.h" +#include "sigmenu.h" + +#include +#include + +/* define errors triggered by this module */ +#define ILLEGAL_NUM_SETS 3001 + +#define PICO_FEATURE_LENGTH 0.05 +#define MAX_OUTLINE_FEATURES 100 + +/**---------------------------------------------------------------------------- + Global Data Definitions and Declarations +----------------------------------------------------------------------------**/ +/* 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, 0, 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 info about each + parameter).*/ +DefineFeature (MicroFeatureDesc, 5, 1, 4, 50, "Micro", "mf", MicroFeatureParams) + +// 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 info about each + parameter).*/ +DefineFeature (PicoFeatDesc, 2, 1, 1, MAX_UINT8, "Pico", "pf", PicoFeatParams) + +/* define all of the parameters for the NormFeat type*/ +StartParamDesc (CharNormParams) +DefineParam (0, 0, -0.25, 0.75) +DefineParam (0, 0, 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 info about each + parameter).*/ +DefineFeature (CharNormDesc, 4, 0, 1, 1, "CharNorm", "cn", CharNormParams) + +// 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 info about each + parameter).*/ +DefineFeature (OutlineFeatDesc, 3, 1, 1, MAX_OUTLINE_FEATURES, "Outline", + "of", OutlineFeatParams) + +/**---------------------------------------------------------------------------- + Global Data Definitions and Declarations +----------------------------------------------------------------------------**/ +FEATURE_DEFS_STRUCT FeatureDefs = { + NUM_FEATURE_TYPES, + { + &MicroFeatureDesc, + &PicoFeatDesc, + &OutlineFeatDesc, + &CharNormDesc + } +}; + +/**---------------------------------------------------------------------------- + Public Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +void FreeCharDescription(CHAR_DESC CharDesc) { +/* + ** Parameters: + ** CharDesc character description to be deallocated + ** Globals: none + ** Operation: Release the memory consumed by the specified character + ** description and all of the features in that description. + ** Return: none + ** Exceptions: none + ** History: Wed May 23 13:52:19 1990, DSJ, Created. + */ + int i; + + if (CharDesc) { + for (i = 0; i < NumFeatureSetsIn (CharDesc); i++) + FreeFeatureSet (FeaturesOfType (CharDesc, i)); + Efree(CharDesc); + } +} /* FreeCharDescription */ + + +/*---------------------------------------------------------------------------*/ +CHAR_DESC NewCharDescription() { +/* + ** Parameters: none + ** Globals: none + ** Operation: Allocate a new character description, initialize its + ** feature sets to be empty, and return it. + ** Return: New character description structure. + ** Exceptions: none + ** History: Wed May 23 15:27:10 1990, DSJ, Created. + */ + CHAR_DESC CharDesc; + int i; + + CharDesc = (CHAR_DESC) Emalloc (sizeof (CHAR_DESC_STRUCT)); + NumFeatureSetsIn (CharDesc) = NumFeaturesDefined (); + + for (i = 0; i < NumFeatureSetsIn (CharDesc); i++) + FeaturesOfType (CharDesc, i) = NULL; + + return (CharDesc); + +} /* NewCharDescription */ + + +/*---------------------------------------------------------------------------*/ +void WriteCharDescription(FILE *File, CHAR_DESC CharDesc) { +/* + ** Parameters: + ** File open text file to write CharDesc to + ** CharDesc character description to write to File + ** Globals: none + ** Operation: Write a textual representation of CharDesc to File. + ** 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. + ** Return: none + ** Exceptions: none + ** History: Wed May 23 17:21:18 1990, DSJ, Created. + */ + int Type; + int NumSetsToWrite = 0; + + for (Type = 0; Type < NumFeatureSetsIn (CharDesc); Type++) + if (FeaturesOfType (CharDesc, Type)) + NumSetsToWrite++; + + fprintf (File, " %d\n", NumSetsToWrite); + for (Type = 0; Type < NumFeatureSetsIn (CharDesc); Type++) + if (FeaturesOfType (CharDesc, Type)) { + fprintf (File, "%s ", ShortNameOf (DefinitionOf (Type))); + WriteFeatureSet (File, FeaturesOfType (CharDesc, Type)); + } +} /* WriteCharDescription */ + + +/*---------------------------------------------------------------------------*/ +CHAR_DESC ReadCharDescription(FILE *File) { +/* + ** Parameters: + ** File open text file to read character description from + ** Globals: none + ** Operation: Read a character description from File, and return + ** a data structure containing this information. The data + ** is formatted as follows: + ** NumberOfSets + ** ShortNameForSet1 Set1 + ** ShortNameForSet2 Set2 + ** ... + ** Return: Character description read from File. + ** Exceptions: ILLEGAL_NUM_SETS + ** History: Wed May 23 17:32:48 1990, DSJ, Created. + */ + int NumSetsToRead; + char ShortName[FEAT_NAME_SIZE]; + CHAR_DESC CharDesc; + int Type; + + if (fscanf (File, "%d", &NumSetsToRead) != 1 || + NumSetsToRead < 0 || NumSetsToRead > NumFeaturesDefined ()) + DoError (ILLEGAL_NUM_SETS, "Illegal number of feature sets"); + + CharDesc = NewCharDescription (); + for (; NumSetsToRead > 0; NumSetsToRead--) { + fscanf (File, "%s", ShortName); + Type = ShortNameToFeatureType (ShortName); + FeaturesOfType (CharDesc, Type) = + ReadFeatureSet (File, DefinitionOf (Type)); + } + return (CharDesc); + +} // ReadCharDescription + + +/*---------------------------------------------------------------------------*/ +int ShortNameToFeatureType(const char *ShortName) { +/* + ** Parameters: + ** ShortName short name of a feature type + ** Globals: none + ** Operation: Search thru 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. + ** Return: Feature type which corresponds to ShortName. + ** Exceptions: ILLEGAL_SHORT_NAME + ** History: Wed May 23 15:36:05 1990, DSJ, Created. + */ + int i; + + for (i = 0; i < NumFeaturesDefined (); i++) + if (!strcmp (ShortNameOf (DefinitionOf (i)), ShortName)) + return (i); + DoError (ILLEGAL_SHORT_NAME, "Illegal short name for a feature"); + return 0; + +} // ShortNameToFeatureType diff --git a/classify/featdefs.h b/classify/featdefs.h new file mode 100644 index 0000000000..4610ebb315 --- /dev/null +++ b/classify/featdefs.h @@ -0,0 +1,85 @@ +/****************************************************************************** + ** 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 + +/* 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.*/ + +typedef struct +{ + UINT32 NumFeatureSets; + FEATURE_SET FeatureSets[NUM_FEATURE_TYPES]; +} CHAR_DESC_STRUCT; +typedef CHAR_DESC_STRUCT *CHAR_DESC; + +typedef struct +{ + UINT32 NumFeatureTypes; + FEATURE_DESC FeatureDesc[NUM_FEATURE_TYPES]; + FEATURE_EXT_STRUCT* FeatureExtractors[NUM_FEATURE_TYPES]; + int FeatureEnabled[NUM_FEATURE_TYPES]; +} FEATURE_DEFS_STRUCT; +typedef FEATURE_DEFS_STRUCT *FEATURE_DEFS; + +/*---------------------------------------------------------------------- + Macros for finding feature definitions +----------------------------------------------------------------------*/ +#define NumFeaturesDefined() (FeatureDefs.NumFeatureTypes) +#define DefinitionOf(Type) (FeatureDefs.FeatureDesc[Type]) +#define ExtractorOf(Type) (FeatureDefs.FeatureExtractors[Type]) +#define FeatureOn(Type) (FeatureDefs.FeatureEnabled[Type]) + +/*---------------------------------------------------------------------- + Macros for manipulating character descriptions +----------------------------------------------------------------------*/ +#define NumFeatureSetsIn(Char) ((Char)->NumFeatureSets) +#define FeaturesOfType(Char, Type) ((Char)->FeatureSets[Type]) + +/*---------------------------------------------------------------------- + Generic functions for manipulating character descriptions +----------------------------------------------------------------------*/ +void FreeCharDescription(CHAR_DESC CharDesc); + +CHAR_DESC NewCharDescription(); + +void WriteCharDescription(FILE *File, CHAR_DESC CharDesc); + +CHAR_DESC ReadCharDescription(FILE *File); + +int ShortNameToFeatureType(const char *ShortName); + +/**---------------------------------------------------------------------------- + Global Data Definitions and Declarations +----------------------------------------------------------------------------**/ +extern FEATURE_DEFS_STRUCT FeatureDefs; +#endif diff --git a/classify/flexfx.cpp b/classify/flexfx.cpp new file mode 100644 index 0000000000..c9f79c57c5 --- /dev/null +++ b/classify/flexfx.cpp @@ -0,0 +1,86 @@ +/****************************************************************************** + ** Filename: flexfx.c + ** Purpose: Interface to flexible feature extractor. + ** Author: Dan Johnson + ** History: Wed May 23 13:45:10 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 "flexfx.h" +#include "featdefs.h" +#include "variables.h" +#include "sigmenu.h" +#include "emalloc.h" +#include +#include + +/**---------------------------------------------------------------------------- + Public Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +CHAR_DESC ExtractFlexFeatures(TBLOB *Blob, LINE_STATS *LineStats) { +/* + ** Parameters: + ** Blob blob to extract features from + ** LineStats statistics about text line Blob is on + ** Globals: none + ** Operation: Allocate a new character descriptor and fill it in by + ** calling all feature extractors which are enabled. + ** Return: Structure containing features extracted from Blob. + ** Exceptions: none + ** History: Wed May 23 13:46:22 1990, DSJ, Created. + */ + int Type; + CHAR_DESC CharDesc; + + CharDesc = NewCharDescription (); + + for (Type = 0; Type < NumFeatureSetsIn(CharDesc); Type++) + if (ExtractorOf(Type) != NULL && ExtractorOf(Type)->Extractor != NULL) + FeaturesOfType(CharDesc, Type) = + ExtractUsing(ExtractorOf(Type)) (Blob, LineStats); + + return (CharDesc); + +} /* ExtractFlexFeatures */ + + +/*---------------------------------------------------------------------------*/ +void +InitFlexFXVars () +/* + ** Parameters: none + ** Globals: none + ** Operation: Add any control variables used by the feature extractors + ** to the variable system. This includes the enable flag for + ** each individual extractor. This routine needs to create + ** a separate name for the enable for each feature extractor + ** and allocate a string to contain that name. This is + ** necessary since the "variables" routines do not create + ** copies of the string names passed to them. + ** Return: none + ** Exceptions: none + ** History: Wed May 23 15:59:23 1990, DSJ, Created. + */ +#define NamePrefix "Enable" +#define NameSuffix "Features" +{ + int Type; + + SetupExtractors(); + for (Type = 0; Type < NumFeaturesDefined (); Type++) { + InitFXVarsUsing (ExtractorOf (Type)) (); + } +} /* InitFlexFXVars */ diff --git a/classify/flexfx.h b/classify/flexfx.h new file mode 100644 index 0000000000..d519466fb9 --- /dev/null +++ b/classify/flexfx.h @@ -0,0 +1,34 @@ +/****************************************************************************** + ** Filename: flexfx.h + ** Purpose: Interface to flexible feature extractor. + ** Author: Dan Johnson + ** History: Wed May 23 13:36: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. + ******************************************************************************/ +#ifndef FLEXFX_H +#define FLEXFX_H + +/**---------------------------------------------------------------------------- + Include Files and Type Defines +----------------------------------------------------------------------------**/ +#include "fxdefs.h" +#include "featdefs.h" +#include + +/**---------------------------------------------------------------------------- + Public Function Prototypes +----------------------------------------------------------------------------**/ +CHAR_DESC ExtractFlexFeatures(TBLOB *Blob, LINE_STATS *LineStats); + +void InitFlexFXVars(); +#endif diff --git a/classify/float2int.cpp b/classify/float2int.cpp new file mode 100644 index 0000000000..ddeac37ff0 --- /dev/null +++ b/classify/float2int.cpp @@ -0,0 +1,126 @@ +/****************************************************************************** + ** 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 "picofeat.h" + +#define MAX_INT_CHAR_NORM (INT_CHAR_NORM_RANGE - 1) + +/**---------------------------------------------------------------------------- + Public Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +void ClearCharNormArray(INT_TEMPLATES Templates, + CLASS_NORMALIZATION_ARRAY CharNormArray) { +/* + ** Parameters: + ** Templates specifies classes currently defined + ** CharNormArray array to be cleared + ** Globals: none + ** Operation: For each class in Templates, clear the corresponding + ** entry in CharNormArray. CharNormArray is indexed by class + ** indicies (as obtained from Templates) rather than class id's. + ** Return: none + ** Exceptions: none + ** History: Wed Feb 20 11:20:54 1991, DSJ, Created. + */ + int i; + + for (i = 0; i < NumClassesIn (Templates); i++) { + CharNormArray[i] = 0; + } + +} /* ClearCharNormArray */ + + +/*---------------------------------------------------------------------------*/ +void ComputeIntCharNormArray(FEATURE NormFeature, + INT_TEMPLATES Templates, + CLASS_NORMALIZATION_ARRAY CharNormArray) { +/* + ** Parameters: + ** NormFeature character normalization feature + ** Templates specifies classes currently defined + ** CharNormArray place to put results + ** Globals: none + ** Operation: For each class in Templates, compute the match between + ** NormFeature and the normalization protos for that class. + ** Convert this number to the range from 0 - 255 and store it + ** into CharNormArray. CharNormArray is indexed by class + ** indicies (as obtained from Templates) rather than class id's. + ** Return: none (results are returned in CharNormArray) + ** Exceptions: none + ** History: Wed Feb 20 11:20:54 1991, DSJ, Created. + */ + int i; + int NormAdjust; + + for (i = 0; i < NumClassesIn (Templates); i++) { + NormAdjust = (int) (INT_CHAR_NORM_RANGE * + ComputeNormMatch (ClassIdForIndex (Templates, i), + NormFeature, FALSE)); + if (NormAdjust < 0) + NormAdjust = 0; + else if (NormAdjust > MAX_INT_CHAR_NORM) + NormAdjust = MAX_INT_CHAR_NORM; + + CharNormArray[i] = NormAdjust; + } + +} /* ComputeIntCharNormArray */ + + +/*---------------------------------------------------------------------------*/ +void ComputeIntFeatures(FEATURE_SET Features, INT_FEATURE_ARRAY IntFeatures) { +/* + ** Parameters: + ** Features floating point pico-features to be converted + ** IntFeatures array to put converted features into + ** Globals: none + ** Operation: This routine converts each floating point pico-feature + ** in Features into integer format and saves it into + ** IntFeatures. + ** Return: none (results are returned in IntFeatures) + ** Exceptions: none + ** History: Wed Feb 20 10:58:45 1991, DSJ, Created. + */ + int Fid; + FEATURE Feature; + FLOAT32 YShift; + + if (NormMethod == baseline) + YShift = BASELINE_Y_SHIFT; + else + YShift = Y_SHIFT; + + for (Fid = 0; Fid < NumFeaturesIn (Features); Fid++) { + Feature = FeatureIn (Features, Fid); + + IntFeatures[Fid].X = BucketFor (ParamOf (Feature, PicoFeatX), + X_SHIFT, INT_FEAT_RANGE); + IntFeatures[Fid].Y = BucketFor (ParamOf (Feature, PicoFeatY), + YShift, INT_FEAT_RANGE); + IntFeatures[Fid].Theta = CircBucketFor (ParamOf (Feature, PicoFeatDir), + ANGLE_SHIFT, INT_FEAT_RANGE); + IntFeatures[Fid].CP_misses = 0; + } +} /* ComputeIntFeatures */ diff --git a/classify/float2int.h b/classify/float2int.h new file mode 100644 index 0000000000..60d1dead3f --- /dev/null +++ b/classify/float2int.h @@ -0,0 +1,65 @@ +/****************************************************************************** + ** 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) + +/**---------------------------------------------------------------------------- + Public Function Prototypes +----------------------------------------------------------------------------**/ +void ClearCharNormArray(INT_TEMPLATES Templates, + CLASS_NORMALIZATION_ARRAY CharNormArray); + +void ComputeIntCharNormArray(FEATURE NormFeature, + INT_TEMPLATES Templates, + CLASS_NORMALIZATION_ARRAY CharNormArray); + +void ComputeIntFeatures(FEATURE_SET Features, INT_FEATURE_ARRAY IntFeatures); + +/* +#if defined(__STDC__) || defined(__cplusplus) +# define _ARGS(s) s +#else +# define _ARGS(s) () +#endif*/ + +/* float2int.c +void ClearCharNormArray + _ARGS((INT_TEMPLATES Templates, + CLASS_NORMALIZATION_ARRAY CharNormArray)); + +void ComputeIntCharNormArray + _ARGS((FEATURE NormFeature, + INT_TEMPLATES Templates, + CLASS_NORMALIZATION_ARRAY CharNormArray)); + +void ComputeIntFeatures + _ARGS((FEATURE_SET Features, + INT_FEATURE_ARRAY IntFeatures)); + +#undef _ARGS +*/ +#endif diff --git a/classify/fpoint.cpp b/classify/fpoint.cpp new file mode 100644 index 0000000000..763c662608 --- /dev/null +++ b/classify/fpoint.cpp @@ -0,0 +1,56 @@ +/****************************************************************************** + ** 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 NormalizedAngleFrom(FPOINT *Point1, + FPOINT *Point2, + FLOAT32 FullScale) { +/* + ** Parameters: + ** Point1, Point2 points to compute angle between + ** FullScale value to associate with 2*pi + ** Globals: none + ** Operation: 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). + ** Return: none + ** Exceptions: none + ** History: Wed Mar 28 14:27:25 1990, DSJ, Created. + */ + 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); + +} /* NormalizedAngleFrom */ diff --git a/classify/fpoint.h b/classify/fpoint.h new file mode 100644 index 0000000000..74c16e4469 --- /dev/null +++ b/classify/fpoint.h @@ -0,0 +1,62 @@ +/****************************************************************************** + ** 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 "general.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 manipulating simple point data structures */ +#define Xof(P) ( (P).x ) +#define Yof(P) ( (P).y ) +#define XofP(P) ((P)->x) +#define YofP(P) ((P)->y) +#define FillPoint(P,X,Y) ( Xof(P) = (X), Yof(P) = (Y) ) +#define CopyPoint(A,B) ( Xof(B) = Xof(A), Yof(B) = Yof(A) ) + +/* macros for computing miscellaneous functions of 2 points */ +#define XDelta(A,B) ( Xof(B) - Xof(A) ) +#define YDelta(A,B) ( Yof(B) - Yof(A) ) +#define DistanceBetween(A,B) \ +(sqrt ((double) (XDelta(A,B) * XDelta(A,B) + YDelta(A,B) * YDelta(A,B)))) + +#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) - Xof(A)) + Yof(A)) + +/*------------------------------------------------------------------------- + Public Function Prototypes +---------------------------------------------------------------------------*/ +FLOAT32 NormalizedAngleFrom(FPOINT *Point1, FPOINT *Point2, FLOAT32 FullScale); +#endif diff --git a/classify/fxdefs.cpp b/classify/fxdefs.cpp new file mode 100644 index 0000000000..cb1960b2cd --- /dev/null +++ b/classify/fxdefs.cpp @@ -0,0 +1,74 @@ +/****************************************************************************** + ** Filename: fxdefs.c + ** Purpose: Utility functions to be used by feature extractors. + ** Author: Dan Johnson + ** History: Sun Jan 21 15:29:02 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 "fxdefs.h" +#include "featdefs.h" +#include "mf.h" +#include "outfeat.h" +#include "picofeat.h" +#include "normfeat.h" + +/**---------------------------------------------------------------------------- + Global Data Definitions and Declarations +----------------------------------------------------------------------------**/ +/* flag to control learn mode vs. classify mode */ +int ExtractMode; + +// Definitions of extractors separated from feature definitions. +DefineFeatureExt (MicroFeatureExt, ExtractMicros, InitMicroFXVars) +DefineFeatureExt (PicoFeatExt, NULL, DefaultInitFXVars) +DefineFeatureExt (CharNormExt, ExtractCharNormFeatures, DefaultInitFXVars) +DefineFeatureExt (OutlineFeatExt, NULL, DefaultInitFXVars) + +FEATURE_EXT_STRUCT* ExtractorDefs[NUM_FEATURE_TYPES] = { + &MicroFeatureExt, + &PicoFeatExt, + &OutlineFeatExt, + &CharNormExt +}; + + +/**---------------------------------------------------------------------------- + Public Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +void SetupExtractors() { + for (int i = 0; i < NUM_FEATURE_TYPES; ++i) + ExtractorOf(i) = ExtractorDefs[i]; +} + +void GetLineStatsFromRow(TEXTROW *Row, LINE_STATS *LineStats) { +/* + ** Parameters: + ** Row text row to get line statistics from + ** LineStats place to put line statistics + ** Globals: none + ** Operation: This routine copies the relavent fields from the + ** Row struct to the LineStats struct. + ** Return: none (results are returned in LineStats) + ** Exceptions: none + ** History: Mon Mar 11 10:38:43 1991, DSJ, Created. + */ + LineStats->Baseline = &(Row->baseline); + LineStats->XHeightLine = &(Row->xheight); + LineStats->xheight = Row->lineheight; + LineStats->AscRise = Row->ascrise; + LineStats->DescDrop = Row->descdrop; + LineStats->TextRow = Row; /* kludge - only needed by fx for */ + /* fast matcher - remove later */ + +} /* GetLineStatsFromRow */ diff --git a/classify/fxdefs.h b/classify/fxdefs.h new file mode 100644 index 0000000000..5b6fd47d93 --- /dev/null +++ b/classify/fxdefs.h @@ -0,0 +1,93 @@ +/****************************************************************************** + ** Filename: fxdefs.h + ** Purpose: Generic interface definitions for feature extractors + ** Author: Dan Johnson + ** History: Fri Jan 19 09:04:14 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 FXDEFS_H +#define FXDEFS_H + +/**---------------------------------------------------------------------------- + Include Files and Type Defines +----------------------------------------------------------------------------**/ +#include "tessclas.h" +#include "general.h" + +/* define different modes for feature extractor - learning vs. classifying */ +#define LEARN_MODE 0 +#define CLASSIFY_MODE 1 + +/* define a data structure to hold line statistics. These line statistics + are used to normalize character outlines to a standard size and position + relative to the baseline of the text. */ +typedef struct +{ + SPLINE_SPEC *Baseline; /* collection of splines describing baseline */ + SPLINE_SPEC *XHeightLine; /* collection of splines describing x-height */ + FLOAT32 xheight; /* avg. distance from x-height to baseline */ + FLOAT32 AscRise; /* avg. distance from ascenders to x-height */ + FLOAT32 DescDrop; /* avg. distance from baseline to descenders */ + /* always a negative number */ + TEXTROW *TextRow; /* kludge - only needed by fx for fast matcher */ + /* should be removed later */ +} + + +LINE_STATS; + +/* define 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 +----------------------------------------------------------------------------**/ +/* macro to change and monitor the mode of the feature extractor. + In general, learn mode smears features which would otherwise be discrete + in nature; classify mode does not.*/ +#define SetExtractMode(M) (ExtractMode = (M)) +#define EnterLearnMode (SetExtractMode (LEARN_MODE)) +#define EnterClassifyMode (SetExtractMode (CLASSIFY_MODE)) + +/*---------------------------------------------------------------------------- + Public Function Prototypes +-----------------------------------------------------------------------------*/ +void SetupExtractors(); + +void GetLineStatsFromRow(TEXTROW *Row, LINE_STATS *LineStats); + +/* +#if defined(__STDC__) || defined(__cplusplus) +# define _ARGS(s) s +#else +# define _ARGS(s) () +#endif*/ + +/* fxdefs.c +void GetLineStatsFromRow + _ARGS((TEXTROW *Row, + LINE_STATS *LineStats)); + +#undef _ARGS +*/ + +/**---------------------------------------------------------------------------- + Global Data Definitions and Declarations +----------------------------------------------------------------------------**/ +/* flag to control learn mode vs. classify mode */ +extern int ExtractMode; +#endif diff --git a/classify/fxid.h b/classify/fxid.h new file mode 100644 index 0000000000..9db710fa29 --- /dev/null +++ b/classify/fxid.h @@ -0,0 +1,69 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: fxid.h (Formerly fxid.h) + * Description: Feature extractor related includes + * Author: Mark Seaman, OCR Technology + * Created: Thu Oct 19 14:59:51 1989 + * Modified: Thu Jan 31 16:57:07 1991 (Dan Johnson) danj@hpgrlj + * 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. + * + ******************************************************************************** + + *************************************************************************** + ********** A T T E N T I O N ******** + *************************************************************************** + +This module is divided into two sections the declarations for this module +(i.e. the function signatures) are listed in 'fxid.h'. The definitions +(i.e. the actual code and variables) are listed in 'fxid1.c' and 'fxid2.c'. +The appropriate piece of code you need for your application should be +included in your top level program file. + +*/ + +#ifndef FXID_H +#define FXID_H + +#include "extern.h" +#include "const.h" +#include "tessclas.h" +#include "oldlist.h" + +#define FEATURE_MATCHER 1 /* Use micro-features */ + +#define WO_UNSCALED 0 /*first square scaled fx */ +#define STATISTICAL_WO 1 /*new wo */ +#define MICRO_FEATURES 2 /*microfeature extractor */ +#define WO_SCALED 3 /*wiseowl scaled to baseline */ +#define MAX_FX 3 /*no of working fx-ers */ +#define NO_FUNCTION 0 /*special value for nothing */ + +/* This file contains declarations of the top-level feature +extractor functions as used by the Classify process*/ + +typedef LIST (*LISTFUNC) (); + +//extern FUNCPTR word_matchers[MAX_FX]; + +//extern LISTFUNC blob_matchers[MAX_FX]; + +//extern FUNCPTR feature_learners[MAX_FX]; + +extern char fx_ids[MAX_FX]; /*one-char ids */ + +extern char *fx_names[MAX_FX]; +#endif diff --git a/classify/hideedge.cpp b/classify/hideedge.cpp new file mode 100644 index 0000000000..49367dd957 --- /dev/null +++ b/classify/hideedge.cpp @@ -0,0 +1,35 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: hideedge.c (Formerly hideedge.c) + * Description: + * Author: Mark Seaman, OCR Technology + * Created: Fri Oct 16 14:37:00 1987 + * Modified: Tue Apr 30 10:38:29 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 "stdafx.h"*/ +#include "hideedge.h" +#include "debug.h" diff --git a/classify/hideedge.h b/classify/hideedge.h new file mode 100644 index 0000000000..cf9ed6c21d --- /dev/null +++ b/classify/hideedge.h @@ -0,0 +1,76 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: hideedge.h (Formerly hideedge.h) + * Description: + * Author: Mark Seaman, SW Productivity + * Created: Fri Oct 16 14:37:00 1987 + * Modified: Tue Apr 30 12:49:57 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 HIDEEDGE_H +#define HIDEEDGE_H + +/* +---------------------------------------------------------------------- + I n c l u d e s +---------------------------------------------------------------------- +*/ + +#include "general.h" + +/* +---------------------------------------------------------------------- + M a c r o s +---------------------------------------------------------------------- +*/ + +/********************************************************************** + * is_hidden_edge + * + * Check to see if this edge is a hidden edge. This will prohibit + * feature extraction and display functions on this edge. The + * argument should be of type (EDGEPT*). + **********************************************************************/ + +#define is_hidden_edge(edge) \ +/*(hidden_edges &&*/ edge->flags[0] /*) */ + +/********************************************************************** + * hide_edge + * + * Make this edge a hidden edge. This will prohibit feature extraction + * and display functions on this edge. The argument should be of type + * (EDGEPT*). + **********************************************************************/ + +#define hide_edge(edge) \ +/*if (hidden_edges)*/ edge->flags[0] = TRUE + +/********************************************************************** + * reveal_edge + * + * Make this edge a unhidden edge. This will prohibit feature extraction + * and display functions on this edge. The argument should be of type + * (EDGEPT*). + **********************************************************************/ + +#define reveal_edge(edge) \ +/*if (hidden_edges)*/ edge->flags[0] = FALSE +#endif diff --git a/classify/intfx.cpp b/classify/intfx.cpp new file mode 100644 index 0000000000..d54c8c82aa --- /dev/null +++ b/classify/intfx.cpp @@ -0,0 +1,608 @@ +/****************************************************************************** + ** Filename: intfx.c + ** Purpose: Integer character normalization & feature extraction + ** 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. + ******************************************************************************/ +/**---------------------------------------------------------------------------- + Include Files and Type Defines +----------------------------------------------------------------------------**/ +#include "intfx.h" +#include "intmatcher.h" +#include "const.h" +#ifdef __UNIX__ +#include +#endif + +/**---------------------------------------------------------------------------- + Private Function Prototypes +----------------------------------------------------------------------------**/ +int SaveFeature(); +UINT8 TableLookup(); +UINT8 MySqrt2(); +void ClipRadius(); + +make_int_var (RadiusGyrMinMan, 255, MakeRadiusGyrMinMan, +16, 10, SetRadiusGyrMinMan, +"Minimum Radius of Gyration Mantissa 0-255: "); + +make_int_var (RadiusGyrMinExp, 0, MakeRadiusGyrMinExp, +16, 11, SetRadiusGyrMinExp, +"Minimum Radius of Gyration Exponent 0-255: "); + +make_int_var (RadiusGyrMaxMan, 158, MakeRadiusGyrMaxMan, +16, 12, SetRadiusGyrMaxMan, +"Maximum Radius of Gyration Mantissa 0-255: "); + +make_int_var (RadiusGyrMaxExp, 8, MakeRadiusGyrMaxExp, +16, 13, SetRadiusGyrMaxExp, +"Maximum Radius of Gyration Exponent 0-255: "); + +/**---------------------------------------------------------------------------- + Global Data Definitions and Declarations +----------------------------------------------------------------------------**/ +#define ATAN_TABLE_SIZE 64 + +static UINT8 AtanTable[ATAN_TABLE_SIZE]; + +/**---------------------------------------------------------------------------- + Public Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +void InitIntegerFX() { + int i; + + for (i = 0; i < ATAN_TABLE_SIZE; i++) + AtanTable[i] = + (UINT8) (atan ((i / (float) ATAN_TABLE_SIZE)) * 128.0 / PI + 0.5); + +} + + +/*--------------------------------------------------------------------------*/ +int ExtractIntFeat(TBLOB *Blob, + INT_FEATURE_ARRAY BLFeat, + INT_FEATURE_ARRAY CNFeat, + INT_FX_RESULT Results) { + + TESSLINE *OutLine; + EDGEPT *Loop, *LoopStart, *Segment; + INT16 LastX, LastY, Xmean, Ymean; + INT32 NormX, NormY, DeltaX, DeltaY; + INT32 Xsum, Ysum; + UINT32 Ix, Iy, LengthSum; + UINT16 n; + UINT8 Theta; + UINT16 NumBLFeatures, NumCNFeatures; + UINT8 RxInv, RyInv; /* x.xxxxxxx * 2^Exp */ + UINT8 RxExp, RyExp; + /* sxxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxx */ + register INT32 pfX, pfY, dX, dY; + UINT16 Length; + register int i; + + Results->Length = 0; + Results->Xmean = 0; + Results->Ymean = 0; + Results->Rx = 0; + Results->Ry = 0; + Results->NumBL = 0; + Results->NumCN = 0; + + /* find Xmean, Ymean */ + NumBLFeatures = 0; + NumCNFeatures = 0; + OutLine = Blob->outlines; + Xsum = 0; + Ysum = 0; + LengthSum = 0; + while (OutLine != NULL) { + LoopStart = OutLine->loop; + Loop = LoopStart; + LastX = Loop->pos.x; + LastY = Loop->pos.y; + /* Check for bad loops */ + if ((Loop == NULL) || (Loop->next == NULL) || (Loop->next == LoopStart)) + return FALSE; + do { + Segment = Loop; + Loop = Loop->next; + NormX = Loop->pos.x; + NormY = Loop->pos.y; + + n = 1; + if (!is_hidden_edge (Segment)) { + DeltaX = NormX - LastX; + DeltaY = NormY - LastY; + Length = MySqrt (DeltaX, DeltaY); + n = ((Length << 2) + Length + 32) >> 6; + if (n != 0) { + Xsum += ((LastX << 1) + DeltaX) * (int) Length; + Ysum += ((LastY << 1) + DeltaY) * (int) Length; + LengthSum += Length; + } + } + if (n != 0) { /* Throw away a point that is too close */ + LastX = NormX; + LastY = NormY; + } + } + while (Loop != LoopStart); + OutLine = OutLine->next; + } + if (LengthSum == 0) + return FALSE; + Xmean = (Xsum / (INT32) LengthSum) >> 1; + Ymean = (Ysum / (INT32) LengthSum) >> 1; + + Results->Length = LengthSum; + Results->Xmean = Xmean; + Results->Ymean = Ymean; + + /* extract Baseline normalized features, */ + /* and find 2nd moments & radius of gyration */ + Ix = 0; + Iy = 0; + NumBLFeatures = 0; + OutLine = Blob->outlines; + while (OutLine != NULL) { + LoopStart = OutLine->loop; + Loop = LoopStart; + LastX = Loop->pos.x - Xmean; + LastY = Loop->pos.y; + /* Check for bad loops */ + if ((Loop == NULL) || (Loop->next == NULL) || (Loop->next == LoopStart)) + return FALSE; + do { + Segment = Loop; + Loop = Loop->next; + NormX = Loop->pos.x - Xmean; + NormY = Loop->pos.y; + + n = 1; + if (!is_hidden_edge (Segment)) { + DeltaX = NormX - LastX; + DeltaY = NormY - LastY; + Length = MySqrt (DeltaX, DeltaY); + n = ((Length << 2) + Length + 32) >> 6; + if (n != 0) { + Theta = TableLookup (DeltaY, DeltaX); + dX = (DeltaX << 8) / n; + dY = (DeltaY << 8) / n; + pfX = (LastX << 8) + (dX >> 1); + pfY = (LastY << 8) + (dY >> 1); + Ix += ((pfY >> 8) - Ymean) * ((pfY >> 8) - Ymean); + Iy += (pfX >> 8) * (pfX >> 8); + if (SaveFeature (BLFeat, NumBLFeatures, (INT16) (pfX >> 8), + (INT16) ((pfY >> 8) - 128), + Theta) == FALSE) + return FALSE; + NumBLFeatures++; + for (i = 1; i < n; i++) { + pfX += dX; + pfY += dY; + Ix += ((pfY >> 8) - Ymean) * ((pfY >> 8) - Ymean); + Iy += (pfX >> 8) * (pfX >> 8); + if (SaveFeature + (BLFeat, NumBLFeatures, (INT16) (pfX >> 8), + (INT16) ((pfY >> 8) - 128), Theta) == FALSE) + return FALSE; + NumBLFeatures++; + } + } + } + if (n != 0) { /* Throw away a point that is too close */ + LastX = NormX; + LastY = NormY; + } + } + while (Loop != LoopStart); + OutLine = OutLine->next; + } + if (Ix == 0) + Ix = 1; + if (Iy == 0) + Iy = 1; + RxInv = MySqrt2 (NumBLFeatures, Ix, &RxExp); + RyInv = MySqrt2 (NumBLFeatures, Iy, &RyExp); + ClipRadius(&RxInv, &RxExp, &RyInv, &RyExp); + + Results->Rx = (INT16) (51.2 / (double) RxInv * pow (2.0, (double) RxExp)); + Results->Ry = (INT16) (51.2 / (double) RyInv * pow (2.0, (double) RyExp)); + Results->NumBL = NumBLFeatures; + + /* extract character normalized features */ + NumCNFeatures = 0; + OutLine = Blob->outlines; + while (OutLine != NULL) { + LoopStart = OutLine->loop; + Loop = LoopStart; + LastX = (Loop->pos.x - Xmean) * RyInv; + LastY = (Loop->pos.y - Ymean) * RxInv; + LastX >>= (INT8) RyExp; + LastY >>= (INT8) RxExp; + /* Check for bad loops */ + if ((Loop == NULL) || (Loop->next == NULL) || (Loop->next == LoopStart)) + return FALSE; + do { + Segment = Loop; + Loop = Loop->next; + NormX = (Loop->pos.x - Xmean) * RyInv; + NormY = (Loop->pos.y - Ymean) * RxInv; + NormX >>= (INT8) RyExp; + NormY >>= (INT8) RxExp; + + n = 1; + if (!is_hidden_edge (Segment)) { + DeltaX = NormX - LastX; + DeltaY = NormY - LastY; + Length = MySqrt (DeltaX, DeltaY); + n = ((Length << 2) + Length + 32) >> 6; + if (n != 0) { + Theta = TableLookup (DeltaY, DeltaX); + dX = (DeltaX << 8) / n; + dY = (DeltaY << 8) / n; + pfX = (LastX << 8) + (dX >> 1); + pfY = (LastY << 8) + (dY >> 1); + if (SaveFeature (CNFeat, NumCNFeatures, (INT16) (pfX >> 8), + (INT16) ((pfY >> 8)), Theta) == FALSE) + return FALSE; + NumCNFeatures++; + for (i = 1; i < n; i++) { + pfX += dX; + pfY += dY; + if (SaveFeature + (CNFeat, NumCNFeatures, (INT16) (pfX >> 8), + (INT16) ((pfY >> 8)), Theta) == FALSE) + return FALSE; + NumCNFeatures++; + } + } + } + if (n != 0) { /* Throw away a point that is too close */ + LastX = NormX; + LastY = NormY; + } + } + while (Loop != LoopStart); + OutLine = OutLine->next; + } + + Results->NumCN = NumCNFeatures; + return TRUE; +} + + +/*--------------------------------------------------------------------------*/ +UINT8 TableLookup(INT32 Y, INT32 X) { + INT16 Angle; + UINT16 Ratio; + UINT32 AbsX, AbsY; + + assert ((X != 0) || (Y != 0)); + if (X < 0) + AbsX = -X; + else + AbsX = X; + if (Y < 0) + AbsY = -Y; + else + AbsY = Y; + if (AbsX > AbsY) + Ratio = AbsY * ATAN_TABLE_SIZE / AbsX; + else + Ratio = AbsX * ATAN_TABLE_SIZE / AbsY; + if (Ratio >= ATAN_TABLE_SIZE) + Ratio = ATAN_TABLE_SIZE - 1; + Angle = AtanTable[Ratio]; + if (X >= 0) + if (Y >= 0) + if (AbsX > AbsY) + Angle = Angle; + else + Angle = 64 - Angle; + else if (AbsX > AbsY) + Angle = 256 - Angle; + else + Angle = 192 + Angle; + else if (Y >= 0) + if (AbsX > AbsY) + Angle = 128 - Angle; + else + Angle = 64 + Angle; + else if (AbsX > AbsY) + Angle = 128 + Angle; + else + Angle = 192 - Angle; + + /* reverse angles to match old feature extractor: Angle += PI */ + Angle += 128; + Angle &= 255; + return (UINT8) Angle; +} + + +/*--------------------------------------------------------------------------*/ +int SaveFeature(INT_FEATURE_ARRAY FeatureArray, + UINT16 FeatureNum, + INT16 X, + INT16 Y, + UINT8 Theta) { + INT_FEATURE Feature; + + if (FeatureNum >= MAX_NUM_INT_FEATURES) + return FALSE; + + Feature = &(FeatureArray[FeatureNum]); + + X = X + 128; + Y = Y + 128; + + if (X > 255) + Feature->X = 255; + else if (X < 0) + Feature->X = 0; + else + Feature->X = X; + + if (Y > 255) + Feature->Y = 255; + else if (Y < 0) + Feature->Y = 0; + else + Feature->Y = Y; + + Feature->Theta = Theta; + + return TRUE; +} + + +/*---------------------------------------------------------------------------*/ +UINT16 MySqrt(INT32 X, INT32 Y) { + register UINT16 SqRoot; + register UINT32 Square; + register UINT16 BitLocation; + register UINT32 Sum; + + if (X < 0) + X = -X; + if (Y < 0) + Y = -Y; + + if (X > EvidenceMultMask) + X = EvidenceMultMask; + if (Y > EvidenceMultMask) + Y = EvidenceMultMask; + + Sum = X * X + Y * Y; + + BitLocation = 1024; + SqRoot = 0; + do { + Square = (SqRoot | BitLocation) * (SqRoot | BitLocation); + if (Square <= Sum) + SqRoot |= BitLocation; + BitLocation >>= 1; + } + while (BitLocation); + + return SqRoot; +} + + +/*--------------------------------------------------------------------------*/ +UINT8 MySqrt2(UINT16 N, UINT32 I, UINT8 *Exp) { + register INT8 k; + register UINT32 N2; + register UINT8 SqRoot; + register UINT16 Square; + register UINT8 BitLocation; + register UINT16 Ratio; + + N2 = N * 41943; + + k = 9; + while ((N2 & 0xc0000000) == 0) { + N2 <<= 2; + k += 1; + } + + while ((I & 0xc0000000) == 0) { + I <<= 2; + k -= 1; + } + + if (((N2 & 0x80000000) == 0) && ((I & 0x80000000) == 0)) { + N2 <<= 1; + I <<= 1; + } + + N2 &= 0xffff0000; + I >>= 14; + Ratio = N2 / I; + + BitLocation = 128; + SqRoot = 0; + do { + Square = (SqRoot | BitLocation) * (SqRoot | BitLocation); + if (Square <= Ratio) + SqRoot |= BitLocation; + BitLocation >>= 1; + } + while (BitLocation); + + if (k < 0) { + *Exp = 0; + return 255; + } + else { + *Exp = k; + return SqRoot; + } +} + + +/*-------------------------------------------------------------------------*/ +void ClipRadius(UINT8 *RxInv, UINT8 *RxExp, UINT8 *RyInv, UINT8 *RyExp) { + register UINT8 AM, BM, AE, BE; + register UINT8 BitN, LastCarry; + int RxInvLarge, RyInvSmall; + + AM = RadiusGyrMinMan; + AE = RadiusGyrMinExp; + BM = *RxInv; + BE = *RxExp; + LastCarry = 1; + while ((AM != 0) || (BM != 0)) { + if (AE > BE) { + BitN = LastCarry + (AM & 1) + 1; + AM >>= 1; + AE--; + } + else if (AE < BE) { + BitN = LastCarry + (!(BM & 1)); + BM >>= 1; + BE--; + } + else { /* AE == BE */ + BitN = LastCarry + (AM & 1) + (!(BM & 1)); + AM >>= 1; + BM >>= 1; + AE--; + BE--; + } + LastCarry = (BitN & 2) > 1; + BitN = BitN & 1; + } + BitN = LastCarry + 1; + LastCarry = (BitN & 2) > 1; + BitN = BitN & 1; + + if (BitN == 1) { + *RxInv = RadiusGyrMinMan; + *RxExp = RadiusGyrMinExp; + } + + AM = RadiusGyrMinMan; + AE = RadiusGyrMinExp; + BM = *RyInv; + BE = *RyExp; + LastCarry = 1; + while ((AM != 0) || (BM != 0)) { + if (AE > BE) { + BitN = LastCarry + (AM & 1) + 1; + AM >>= 1; + AE--; + } + else if (AE < BE) { + BitN = LastCarry + (!(BM & 1)); + BM >>= 1; + BE--; + } + else { /* AE == BE */ + BitN = LastCarry + (AM & 1) + (!(BM & 1)); + AM >>= 1; + BM >>= 1; + AE--; + BE--; + } + LastCarry = (BitN & 2) > 1; + BitN = BitN & 1; + } + BitN = LastCarry + 1; + LastCarry = (BitN & 2) > 1; + BitN = BitN & 1; + + if (BitN == 1) { + *RyInv = RadiusGyrMinMan; + *RyExp = RadiusGyrMinExp; + } + + AM = RadiusGyrMaxMan; + AE = RadiusGyrMaxExp; + BM = *RxInv; + BE = *RxExp; + LastCarry = 1; + while ((AM != 0) || (BM != 0)) { + if (AE > BE) { + BitN = LastCarry + (AM & 1) + 1; + AM >>= 1; + AE--; + } + else if (AE < BE) { + BitN = LastCarry + (!(BM & 1)); + BM >>= 1; + BE--; + } + else { /* AE == BE */ + BitN = LastCarry + (AM & 1) + (!(BM & 1)); + AM >>= 1; + BM >>= 1; + AE--; + BE--; + } + LastCarry = (BitN & 2) > 1; + BitN = BitN & 1; + } + BitN = LastCarry + 1; + LastCarry = (BitN & 2) > 1; + BitN = BitN & 1; + + if (BitN == 1) + RxInvLarge = 1; + else + RxInvLarge = 0; + + AM = *RyInv; + AE = *RyExp; + BM = RadiusGyrMaxMan; + BE = RadiusGyrMaxExp; + LastCarry = 1; + while ((AM != 0) || (BM != 0)) { + if (AE > BE) { + BitN = LastCarry + (AM & 1) + 1; + AM >>= 1; + AE--; + } + else if (AE < BE) { + BitN = LastCarry + (!(BM & 1)); + BM >>= 1; + BE--; + } + else { /* AE == BE */ + BitN = LastCarry + (AM & 1) + (!(BM & 1)); + AM >>= 1; + BM >>= 1; + AE--; + BE--; + } + LastCarry = (BitN & 2) > 1; + BitN = BitN & 1; + } + BitN = LastCarry + 1; + LastCarry = (BitN & 2) > 1; + BitN = BitN & 1; + + if (BitN == 1) + RyInvSmall = 1; + else + RyInvSmall = 0; + + if (RxInvLarge && RyInvSmall) { + *RyInv = RadiusGyrMaxMan; + *RyExp = RadiusGyrMaxExp; + } + +} diff --git a/classify/intfx.h b/classify/intfx.h new file mode 100644 index 0000000000..0cbab7d5b7 --- /dev/null +++ b/classify/intfx.h @@ -0,0 +1,63 @@ +/****************************************************************************** + ** 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 "tessclas.h" +#include "hideedge.h" +#include "intproto.h" +#include + +typedef 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 */ +} + + +INT_FX_RESULT_STRUCT, *INT_FX_RESULT; + +/**---------------------------------------------------------------------------- + Public Function Prototypes +----------------------------------------------------------------------------**/ +void InitIntegerFX(); + +int ExtractIntFeat(TBLOB *Blob, + INT_FEATURE_ARRAY BLFeat, + INT_FEATURE_ARRAY CNFeat, + INT_FX_RESULT Results); + +UINT8 TableLookup(INT32 Y, INT32 X); + +int SaveFeature(INT_FEATURE_ARRAY FeatureArray, + UINT16 FeatureNum, + INT16 X, + INT16 Y, + UINT8 Theta); + +UINT16 MySqrt(INT32 X, INT32 Y); + +UINT8 MySqrt2(UINT16 N, UINT32 I, UINT8 *Exp); + +void ClipRadius(UINT8 *RxInv, UINT8 *RxExp, UINT8 *RyInv, UINT8 *RyExp); +#endif diff --git a/classify/intmatcher.cpp b/classify/intmatcher.cpp new file mode 100644 index 0000000000..1626892982 --- /dev/null +++ b/classify/intmatcher.cpp @@ -0,0 +1,2321 @@ +/****************************************************************************** + ** 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 Files and Type Defines +----------------------------------------------------------------------------**/ +#include "intmatcher.h" +#include "tordvars.h" +#include "callcpp.h" +#include + +#define CLASS_MASK_SIZE ((MAX_NUM_CLASSES*NUM_BITS_PER_CLASS \ + +BITS_PER_WERD-1)/BITS_PER_WERD) + +/**---------------------------------------------------------------------------- + Global Data Definitions and Declarations +----------------------------------------------------------------------------**/ +#define SE_TABLE_BITS 9 +#define SE_TABLE_SIZE 512 +#define TEMPLATE_CACHE 2 +static UINT8 SimilarityEvidenceTable[SE_TABLE_SIZE]; +static UINT8 offset_table[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 +}; +static UINT8 next_table[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 +}; +static int cp_maxes[128] = { + 100, + 100, 100, 100, 100, 100, 100, 100, 100, + 100, 100, 100, 100, 100, 100, 100, 100, + 100, 100, 100, 100, 100, 100, 100, 100, + 100, 100, 100, 100, 100, 100, 100, 100, + 194, //! + 100, //" + 182, //# + 224, //$ + 203, //% + 242, //& + 245, //' + 226, //( + 190, //) + 244, //* + 195, //+ + 254, //, + 253, //- + 253, //. + 206, /// + 253, //0 + 234, //1 + 252, //2 + 246, //3 + 253, //4 + 160, //5 + 202, //6 + 199, //7 + 171, //8 + 227, //9 + 208, //: + 188, //; + 60, //< + 221, //= + 138, //> + 108, //? + 98, //@ + 251, //A + 214, //B + 230, //C + 252, //D + 237, //E + 217, //F + 233, //G + 174, //H + 216, //I + 210, //J + 252, //K + 253, //L + 233, //M + 243, //N + 240, //O + 230, //P + 167, //Q + 248, //R + 250, //S + 232, //T + 209, //U + 193, //V + 254, //W + 146, //X + 198, //Y + 107, //Z + 167, //[ + 163, // + 73, //] + 16, //^ + 199, //_ + 162, //` + 251, //a + 250, //b + 254, //c + 253, //d + 252, //e + 253, //f + 248, //g + 251, //h + 254, //i + 201, //j + 224, //k + 253, //l + 242, //m + 254, //n + 254, //o + 253, //p + 246, //q + 254, //r + 254, //s + 254, //t + 245, //u + 221, //v + 230, //w + 251, //x + 243, //y + 133, //z + 35, //{ + 100, //| + 143, //} + 100, //~ + 100 +}; + +static float cp_ratios[128] = { + 1.5f, + 1.5f, 1.5f, 1.5f, 1.5f, 1.5f, 1.5f, 1.5f, 1.5f, + 1.5f, 1.5f, 1.5f, 1.5f, 1.5f, 1.5f, 1.5f, 1.5f, + 1.5f, 1.5f, 1.5f, 1.5f, 1.5f, 1.5f, 1.5f, 1.5f, + 1.5f, 1.5f, 1.5f, 1.5f, 1.5f, 1.5f, 1.5f, 1.5f, + 2.24775, //! + 1.5, //" + 1.90376, //# + 1.61443, //$ + 1.87857, //% + 2.29167, //& + 7.4, //' + 4.7, //( + 9.4, //) + 2.13014, //* + 1.53175, //+ + 2.86957, //, + 7.4, //- + 7.4, //. + 9.4, /// + 8.1, //0 + 12.6, //1 + 2.7439, //2 + 4.22222, //3 + 2.57447, //4 + 2.93902, //5 + 4.23684, //6 + 6, //7 + 2.78889, //8 + 3.55, //9 + 8.5, //: + 2.4, //; + 1.5, //< + 1.94737, //= + 1.89394, //> + 1.5, //? + 1.5, //@ + 3.125, //A + 5.5, //B + 6.1, //C + 6, //D + 2.78205, //E + 2.03763, //F + 2.73256, //G + 2.57692, //H + 11.8, //I + 7.1, //J + 1.85227, //K + 7.4, //L + 2.26056, //M + 2.46078, //N + 6.85714, //O + 3.45238, //P + 2.47222, //Q + 3.74, //R + 10.2, //S + 3.08065, //T + 6.1, //U + 9.5, //V + 7.1, //W + 7.9, //X + 2.55714, //Y + 7.7, //Z + 2, //[ + 1.5, // + 2.55714, //] + 1.5, //^ + 1.80065, //_ + 1.69512, //` + 5.34, //a + 7.3, //b + 6.43333, //c + 4.10606, //d + 4.41667, //e + 12.6, //f + 3.7093, //g + 2.38889, //h + 5.5, //i + 4.03125, //j + 2.24561, //k + 11.5, //l + 3.5, //m + 5.63333, //n + 11, //o + 2.52667, //p + 2.1129, //q + 6.56667, //r + 6.42857, //s + 11.4, //t + 3.62, //u + 2.77273, //v + 2.90909, //w + 6.5, //x + 4.98387, //y + 2.92857, //z + 1.5, //{ + 1.5, //| + 2.02128, //} + 1.5, //~ + 1.5f +}; +static INT8 miss_table[256] = { + 4, 4, 4, 3, 4, 4, 4, 3, 4, 4, 4, 3, 3, 3, 3, 2, + 4, 4, 4, 3, 4, 4, 4, 3, 4, 4, 4, 3, 3, 3, 3, 2, + 4, 4, 4, 3, 4, 4, 4, 3, 4, 4, 4, 3, 3, 3, 3, 2, + 3, 3, 3, 2, 3, 3, 3, 2, 3, 3, 3, 2, 2, 2, 2, 1, + 4, 4, 4, 3, 4, 4, 4, 3, 4, 4, 4, 3, 3, 3, 3, 2, + 4, 4, 4, 3, 4, 4, 4, 3, 4, 4, 4, 3, 3, 3, 3, 2, + 4, 4, 4, 3, 4, 4, 4, 3, 4, 4, 4, 3, 3, 3, 3, 2, + 3, 3, 3, 2, 3, 3, 3, 2, 3, 3, 3, 2, 2, 2, 2, 1, + 4, 4, 4, 3, 4, 4, 4, 3, 4, 4, 4, 3, 3, 3, 3, 2, + 4, 4, 4, 3, 4, 4, 4, 3, 4, 4, 4, 3, 3, 3, 3, 2, + 4, 4, 4, 3, 4, 4, 4, 3, 4, 4, 4, 3, 3, 3, 3, 2, + 3, 3, 3, 2, 3, 3, 3, 2, 3, 3, 3, 2, 2, 2, 2, 1, + 3, 3, 3, 2, 3, 3, 3, 2, 3, 3, 3, 2, 2, 2, 2, 1, + 3, 3, 3, 2, 3, 3, 3, 2, 3, 3, 3, 2, 2, 2, 2, 1, + 3, 3, 3, 2, 3, 3, 3, 2, 3, 3, 3, 2, 2, 2, 2, 1, + 2, 2, 2, 1, 2, 2, 2, 1, 2, 2, 2, 1, 1, 1, 1, 0 +}; + +static UINT32 EvidenceTableMask; + +static UINT32 MultTruncShiftBits; + +static UINT32 TableTruncShiftBits; + +UINT32 EvidenceMultMask; + +static INT16 LocalMatcherMultiplier; + +make_int_var (ClassPrunerThreshold, 229, MakeClassPrunerThreshold, +16, 20, SetClassPrunerThreshold, +"Class Pruner Threshold 0-255: "); + +make_int_var (ClassPrunerMultiplier, 15, MakeClassPrunerMultiplier, +16, 21, SetClassPrunerMultiplier, +"Class Pruner Multiplier 0-255: "); + +make_int_var (IntegerMatcherMultiplier, 14, MakeIntegerMatcherMultiplier, +16, 22, SetIntegerMatcherMultiplier, +"Integer Matcher Multiplier 0-255: "); + +make_int_var (IntThetaFudge, 128, MakeIntThetaFudge, +16, 23, SetIntThetaFudge, +"Integer Matcher Theta Fudge 0-255: "); + +make_float_var (CPCutoffStrength, 0.15, MakeCPCutoffStrength, +16, 24, SetCPCutoffStrength, +"Class Pruner CutoffStrength: "); + +make_int_var (EvidenceTableBits, 9, MakeEvidenceTableBits, +16, 25, SetEvidenceTableBits, +"Bits in Similarity to Evidence Lookup 8-9: "); + +make_int_var (IntEvidenceTruncBits, 14, MakeIntEvidenceTruncBits, +16, 26, SetIntEvidenceTruncBits, +"Integer Evidence Truncation Bits (Distance) 8-14: "); + +make_float_var (SEExponentialMultiplier, 0, MakeSEExponentialMultiplier, +16, 27, SetSEExponentialMultiplier, +"Similarity to Evidence Table Exponential Multiplier: "); + +make_float_var (SimilarityCenter, 0.0075, MakeSimilarityCenter, +16, 28, SetSimilarityCenter, "Center of Similarity Curve: "); + +make_int_var (AdaptProtoThresh, 230, MakeAdaptProtoThresh, +16, 29, SetAdaptProtoThresh, +"Threshold for good protos during adaptive 0-255: "); + +make_int_var (AdaptFeatureThresh, 230, MakeAdaptFeatureThresh, +16, 30, SetAdaptFeatureThresh, +"Threshold for good features during adaptive 0-255: "); +//extern int display_ratings; +//extern "C" int newcp_ratings_on; +//extern "C" double newcp_prune_threshold; +//extern "C" double tessedit_cp_ratio; +//extern "C" int feature_prune_percentile; +//extern INT32 cp_maps[4]; + +int protoword_lookups; +int zero_protowords; +int proto_shifts; +int set_proto_bits; +int config_shifts; +int set_config_bits; + +/**---------------------------------------------------------------------------- + Public Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +int ClassPruner(INT_TEMPLATES IntTemplates, + INT16 NumFeatures, + INT_FEATURE_ARRAY Features, + CLASS_NORMALIZATION_ARRAY NormalizationFactors, + CLASS_CUTOFF_ARRAY ExpectedNumFeatures, + CLASS_PRUNER_RESULTS Results, + int Debug) { +/* + ** Parameters: + ** IntTemplates Class pruner tables + ** NumFeatures Number of features in blob + ** Features Array of features + ** NormalizationFactors Array of fudge factors from blob + ** normalization process + ** (by CLASS_INDEX) + ** ExpectedNumFeatures Array of expected number of features + ** for each class + ** (by CLASS_INDEX) + ** Results Sorted Array of pruned classes + ** (by CLASS_ID) + ** Debug Debugger flag: 1=debugger on + ** Globals: + ** ClassPrunerThreshold Cutoff threshold + ** ClassPrunerMultiplier Normalization factor multiplier + ** Operation: + ** Prune the classes using a modified fast match table. + ** Return a sorted list of classes along with the number + ** of pruned classes in that list. + ** Return: Number of pruned classes. + ** Exceptions: none + ** History: Tue Feb 19 10:24:24 MST 1991, RWM, Created. + */ + UINT32 PrunerWord; + INT32 class_index; //index to class + int Word; + UINT32 *BasePrunerAddress; + UINT32 feature_address; //current feature index + INT_FEATURE feature; //current feature + CLASS_PRUNER *ClassPruner; + int PrunerSet; + int NumPruners; + INT32 feature_index; //current feature + + static INT32 ClassCount[MAX_NUM_CLASSES - 1]; + static INT16 NormCount[MAX_NUM_CLASSES - 1]; + static INT16 SortKey[MAX_NUM_CLASSES]; + static UINT8 SortIndex[MAX_NUM_CLASSES]; + CLASS_INDEX Class; + int out_class; + int MaxNumClasses; + int MaxCount; + int NumClasses; + FLOAT32 max_rating; //max allowed rating + INT32 *ClassCountPtr; + INT8 classch; + + MaxNumClasses = NumClassesIn (IntTemplates); + + /* Clear Class Counts */ + ClassCountPtr = &(ClassCount[0]); + for (Class = 0; Class < MaxNumClasses; Class++) { + *ClassCountPtr++ = 0; + } + + /* Update Class Counts */ + NumPruners = NumClassPrunersIn (IntTemplates); + for (feature_index = 0; feature_index < NumFeatures; feature_index++) { + feature = &Features[feature_index]; + feature->CP_misses = 0; + feature_address = (((feature->X * NUM_CP_BUCKETS >> 8) * NUM_CP_BUCKETS + + + (feature->Y * NUM_CP_BUCKETS >> 8)) * + NUM_CP_BUCKETS + + (feature->Theta * NUM_CP_BUCKETS >> 8)) << 1; + ClassPruner = ClassPrunersFor (IntTemplates); + class_index = 0; + for (PrunerSet = 0; PrunerSet < NumPruners; PrunerSet++, ClassPruner++) { + BasePrunerAddress = (UINT32 *) (*ClassPruner) + feature_address; + + for (Word = 0; Word < WERDS_PER_CP_VECTOR; Word++) { + PrunerWord = *BasePrunerAddress++; + ClassCount[class_index++] += cp_maps[PrunerWord & 3]; + PrunerWord >>= 2; + ClassCount[class_index++] += cp_maps[PrunerWord & 3]; + PrunerWord >>= 2; + ClassCount[class_index++] += cp_maps[PrunerWord & 3]; + PrunerWord >>= 2; + ClassCount[class_index++] += cp_maps[PrunerWord & 3]; + PrunerWord >>= 2; + ClassCount[class_index++] += cp_maps[PrunerWord & 3]; + PrunerWord >>= 2; + ClassCount[class_index++] += cp_maps[PrunerWord & 3]; + PrunerWord >>= 2; + ClassCount[class_index++] += cp_maps[PrunerWord & 3]; + PrunerWord >>= 2; + ClassCount[class_index++] += cp_maps[PrunerWord & 3]; + PrunerWord >>= 2; + ClassCount[class_index++] += cp_maps[PrunerWord & 3]; + PrunerWord >>= 2; + ClassCount[class_index++] += cp_maps[PrunerWord & 3]; + PrunerWord >>= 2; + ClassCount[class_index++] += cp_maps[PrunerWord & 3]; + PrunerWord >>= 2; + ClassCount[class_index++] += cp_maps[PrunerWord & 3]; + PrunerWord >>= 2; + ClassCount[class_index++] += cp_maps[PrunerWord & 3]; + PrunerWord >>= 2; + ClassCount[class_index++] += cp_maps[PrunerWord & 3]; + PrunerWord >>= 2; + ClassCount[class_index++] += cp_maps[PrunerWord & 3]; + PrunerWord >>= 2; + ClassCount[class_index++] += cp_maps[PrunerWord & 3]; + } + } + } + + /* Adjust Class Counts for Number of Expected Features */ + for (Class = 0; Class < MaxNumClasses; Class++) + if (NumFeatures < ExpectedNumFeatures[Class]) + ClassCount[Class] = + (int) (((FLOAT32) (ClassCount[Class] * NumFeatures)) / + (NumFeatures + + CPCutoffStrength * (ExpectedNumFeatures[Class] - + NumFeatures))); + + /* Adjust Class Counts for Normalization Factors */ + MaxCount = 0; + for (Class = 0; Class < MaxNumClasses; Class++) { + NormCount[Class] = ClassCount[Class] + - ((ClassPrunerMultiplier * NormalizationFactors[Class]) >> 8) + * cp_maps[3] / 3; + if (NormCount[Class] > MaxCount) + MaxCount = NormCount[Class]; + } + + /* Prune Classes */ + MaxCount *= ClassPrunerThreshold; + MaxCount >>= 8; + /* Select Classes */ + if (MaxCount < 1) + MaxCount = 1; + NumClasses = 0; + for (Class = 0; Class < MaxNumClasses; Class++) + if (NormCount[Class] >= MaxCount) { + NumClasses++; + SortIndex[NumClasses] = Class; + SortKey[NumClasses] = NormCount[Class]; + } + + /* Sort Classes using Heapsort Algorithm */ + if (NumClasses > 1) + HeapSort(NumClasses, SortKey, SortIndex); + + if (display_ratings > 1) { + cprintf ("CP:%d classes, %d features:\n", NumClasses, NumFeatures); + for (Class = 0; Class < NumClasses; Class++) { + classch = + ClassIdForIndex (IntTemplates, SortIndex[NumClasses - Class]); + cprintf ("%c:C=%d, E=%d, N=%d, Rat=%d\n", classch, + ClassCount[SortIndex[NumClasses - Class]], + ExpectedNumFeatures[SortIndex[NumClasses - Class]], + SortKey[NumClasses - Class], + (int) (10 + + 1000 * (1.0f - + SortKey[NumClasses - + Class] / ((float) cp_maps[3] * + NumFeatures)))); + } + if (display_ratings > 2) { + NumPruners = NumClassPrunersIn (IntTemplates); + for (feature_index = 0; feature_index < NumFeatures; + feature_index++) { + cprintf ("F=%3d,", feature_index); + feature = &Features[feature_index]; + feature->CP_misses = 0; + feature_address = + (((feature->X * NUM_CP_BUCKETS >> 8) * NUM_CP_BUCKETS + + (feature->Y * NUM_CP_BUCKETS >> 8)) * NUM_CP_BUCKETS + + (feature->Theta * NUM_CP_BUCKETS >> 8)) << 1; + ClassPruner = ClassPrunersFor (IntTemplates); + class_index = 0; + for (PrunerSet = 0; PrunerSet < NumPruners; + PrunerSet++, ClassPruner++) { + BasePrunerAddress = (UINT32 *) (*ClassPruner) + + feature_address; + + for (Word = 0; Word < WERDS_PER_CP_VECTOR; Word++) { + PrunerWord = *BasePrunerAddress++; + for (Class = 0; Class < 16; Class++, class_index++) { + if (NormCount[class_index] >= MaxCount) + cprintf (" %c=%d,", + ClassIdForIndex (IntTemplates, + class_index), + PrunerWord & 3); + PrunerWord >>= 2; + } + } + } + cprintf ("\n"); + } + cprintf ("Adjustments:"); + for (Class = 0; Class < MaxNumClasses; Class++) { + if (NormCount[Class] > MaxCount) + cprintf (" %c=%d,", + ClassIdForIndex (IntTemplates, Class), + -((ClassPrunerMultiplier * + NormalizationFactors[Class]) >> 8) * cp_maps[3] / + 3); + } + cprintf ("\n"); + } + } + + /* Set Up Results */ + max_rating = 0.0f; + for (Class = 0, out_class = 0; Class < NumClasses; Class++) { + Results[out_class].Class = + ClassIdForIndex (IntTemplates, SortIndex[NumClasses - Class]); + Results[out_class].config_mask = (UINT32) - 1; + Results[out_class].Rating = + 1.0 - SortKey[NumClasses - + Class] / ((float) cp_maps[3] * NumFeatures); + /**/ Results[out_class].Rating2 = + 1.0 - SortKey[NumClasses - + Class] / ((float) cp_maps[3] * NumFeatures); + if (tessedit_cp_ratio == 0.0 || Class == 0 + || Results[out_class].Rating * 1000 + 10 < + cp_maxes[Results[out_class].Class] + && Results[out_class].Rating * 1000 + 10 < + (Results[0].Rating * 1000 + + 10) * cp_ratios[Results[out_class].Class]) + out_class++; + } + NumClasses = out_class; + if (blob_type != 0) { + cp_classes = NumClasses; + if (NumClasses > 0) { + cp_chars[0] = Results[0].Class; + cp_ratings[0] = (int) (1000 * Results[0].Rating + 10); + cp_confs[0] = (int) (1000 * Results[0].Rating2 + 10); + if (NumClasses > 1) { + cp_chars[1] = Results[1].Class; + cp_ratings[1] = (int) (1000 * Results[1].Rating + 10); + cp_confs[1] = (int) (1000 * Results[1].Rating2 + 10); + } + else { + cp_chars[1] = '~'; + cp_ratings[1] = -1; + cp_confs[1] = -1; + } + } + else { + cp_chars[0] = '~'; + cp_ratings[0] = -1; + cp_confs[0] = -1; + } + cp_bestindex = -1; + cp_bestrating = -1; + cp_bestconf = -1; + for (Class = 0; Class < NumClasses; Class++) { + classch = Results[Class].Class; + if (classch == blob_answer) { + cp_bestindex = Class; + cp_bestrating = (int) (1000 * Results[Class].Rating + 10); + cp_bestconf = (int) (1000 * Results[Class].Rating2 + 10); + } + } + } + return NumClasses; + +} + + +/*---------------------------------------------------------------------------*/ +int feature_pruner(INT_TEMPLATES IntTemplates, + INT16 NumFeatures, + INT_FEATURE_ARRAY Features, + INT32 NumClasses, + CLASS_PRUNER_RESULTS Results) { +/* + ** Parameters: + ** IntTemplates Class pruner tables + ** NumFeatures Number of features in blob + ** Features Array of features + ** NormalizationFactors Array of fudge factors from blob + ** normalization process + ** (by CLASS_INDEX) + ** ExpectedNumFeatures Array of expected number of features + ** for each class + ** (by CLASS_INDEX) + ** Results Sorted Array of pruned classes + ** (by CLASS_ID) + ** Debug Debugger flag: 1=debugger on + ** Globals: + ** ClassPrunerThreshold Cutoff threshold + ** ClassPrunerMultiplier Normalization factor multiplier + ** Operation: + ** Prune the classes using a modified fast match table. + ** Return a sorted list of classes along with the number + ** of pruned classes in that list. + ** Return: Number of pruned classes. + ** Exceptions: none + ** History: Tue Feb 19 10:24:24 MST 1991, RWM, Created. + */ + UINT32 PrunerWord; + CLASS_PRUNER *ClassPruner; + INT32 class_index; //index to class + INT32 result_index; //CP results index + int PrunerSet; + int NumPruners; + int Word; + INT_FEATURE feature; //current feature + INT32 feature_index; //current feature + INT32 CP_misses; //missed features + UINT32 feature_address; //current feature index + UINT32 *BasePrunerAddress; + int MaxNumClasses; + UINT32 class_mask[CLASS_MASK_SIZE]; + INT32 miss_histogram[MAX_NUM_CLASSES]; + + MaxNumClasses = NumClassesIn (IntTemplates); + for (class_index = 0; class_index < MaxNumClasses; class_index++) + miss_histogram[class_index] = 0; + + /* Create class mask */ + for (class_index = 0; class_index < CLASS_MASK_SIZE; class_index++) + class_mask[class_index] = (UINT32) - 1; + for (result_index = 0; result_index < NumClasses; result_index++) { + class_index = + IndexForClassId (IntTemplates, Results[result_index].Class); + class_mask[class_index / CLASSES_PER_CP_WERD] &= + ~(3 << (class_index % CLASSES_PER_CP_WERD) * 2); + } + + /* Update Class Counts */ + NumPruners = NumClassPrunersIn (IntTemplates); + for (feature_index = 0; feature_index < NumFeatures; feature_index++) { + feature = &Features[feature_index]; + feature_address = (((feature->X * NUM_CP_BUCKETS >> 8) * NUM_CP_BUCKETS + + + (feature->Y * NUM_CP_BUCKETS >> 8)) * + NUM_CP_BUCKETS + + (feature->Theta * NUM_CP_BUCKETS >> 8)) << 1; + CP_misses = 0; + ClassPruner = ClassPrunersFor (IntTemplates); + class_index = 0; + for (PrunerSet = 0; PrunerSet < NumPruners; PrunerSet++, ClassPruner++) { + BasePrunerAddress = (UINT32 *) (*ClassPruner) + feature_address; + + for (Word = 0; Word < WERDS_PER_CP_VECTOR; Word++) { + PrunerWord = *BasePrunerAddress++; + PrunerWord |= class_mask[class_index++]; + CP_misses += miss_table[PrunerWord & 255]; + PrunerWord >>= 8; + CP_misses += miss_table[PrunerWord & 255]; + PrunerWord >>= 8; + CP_misses += miss_table[PrunerWord & 255]; + PrunerWord >>= 8; + CP_misses += miss_table[PrunerWord & 255]; + } + } + feature->CP_misses = CP_misses; + if (display_ratings > 1) { + cprintf ("F=%d: misses=%d\n", feature_index, CP_misses); + } + miss_histogram[CP_misses]++; + } + + CP_misses = 0; + feature_index = NumFeatures * feature_prune_percentile / 100; + for (class_index = MaxNumClasses - 1; class_index >= 0; class_index--) { + CP_misses += miss_histogram[class_index]; + if (CP_misses >= feature_index) + break; + } + + if (display_ratings > 1) { + cprintf ("FP:Selected miss factor of %d for %d features (%g%%)\n", + class_index, CP_misses, 100.0 * CP_misses / NumFeatures); + } + return class_index; +} + + +/*---------------------------------------------------------------------------*/ +int prune_configs(INT_TEMPLATES IntTemplates, + INT32 min_misses, + INT16 NumFeatures, + INT_FEATURE_ARRAY Features, + CLASS_NORMALIZATION_ARRAY NormalizationFactors, + INT32 class_count, + UINT16 BlobLength, + CLASS_PRUNER_RESULTS Results, + int Debug) { +/* + ** Parameters: + ** IntTemplates Class pruner tables + ** NumFeatures Number of features in blob + ** Features Array of features + ** NormalizationFactors Array of fudge factors from blob + ** normalization process + ** (by CLASS_INDEX) + ** ExpectedNumFeatures Array of expected number of features + ** for each class + ** (by CLASS_INDEX) + ** Results Sorted Array of pruned classes + ** (by CLASS_ID) + ** Debug Debugger flag: 1=debugger on + ** Globals: + ** ClassPrunerThreshold Cutoff threshold + ** ClassPrunerMultiplier Normalization factor multiplier + ** Operation: + ** Prune the classes using a modified fast match table. + ** Return a sorted list of classes along with the number + ** of pruned classes in that list. + ** Return: Number of pruned classes. + ** Exceptions: none + ** History: Tue Feb 19 10:24:24 MST 1991, RWM, Created. + */ + INT32 classindex; //current Results index + CLASS_INDEX Class; //current class + INT_CLASS ClassTemplate; //info on current class + FLOAT32 best_rating; //best over all classes + FLOAT32 best_class_rating; //best over all classes + INT32 output_count; //number of classes out + INT32 best_index; //for sorting + INT_RESULT_STRUCT IntResult; + //results of pruning + CLASS_PRUNER_RESULTS new_results; + + best_class_rating = 9999.0f; + for (classindex = 0; classindex < class_count; classindex++) { + Class = IndexForClassId (IntTemplates, Results[classindex].Class); + ClassTemplate = ClassForIndex (IntTemplates, Class); + PruningMatcher (ClassTemplate, BlobLength, NumFeatures, Features, + min_misses, NormalizationFactors[Class], + &IntResult, Debug); + + /* Prune configs */ + //save old rating + new_results[classindex].Rating2 = Results[classindex].Rating; + //save new rating + new_results[classindex].Rating = IntResult.Rating; + //save new rating + new_results[classindex].config_mask = (1 << IntResult.Config) | (1 << IntResult.Config2); + //save old class + new_results[classindex].Class = Results[classindex].Class; + + if (display_ratings > 1) { + cprintf ("PC:%c: old=%g, best_rating=%g, config1=%d, config2=%d\n", + Results[classindex].Class, + Results[classindex].Rating2, + IntResult.Rating, IntResult.Config, IntResult.Config2); + } + + if (IntResult.Rating < best_class_rating) + best_class_rating = IntResult.Rating; + } + /* Select Classes */ + best_class_rating *= newcp_prune_threshold; + + output_count = 0; + do { + best_rating = best_class_rating; + best_index = -1; + for (classindex = 0; classindex < class_count; classindex++) { + if (new_results[classindex].Rating <= best_rating) { + best_rating = new_results[classindex].Rating; + best_index = classindex; + } + } + if (best_index >= 0) { + Results[output_count].Class = new_results[best_index].Class; + Results[output_count].Rating = best_rating; + Results[output_count].Rating2 = new_results[best_index].Rating2; + Results[output_count].config_mask = + new_results[best_index].config_mask; + new_results[best_index].Rating = 9999.0f; + output_count++; + } + } + while (best_index >= 0); + + if (display_ratings > 1) { + cprintf ("%d classes reduced to %d\n", class_count, output_count); + for (classindex = 0; classindex < output_count; classindex++) { + cprintf ("%c=%g/%g/0x%x, ", + Results[classindex].Class, + Results[classindex].Rating, + Results[classindex].Rating2, + Results[classindex].config_mask); + } + cprintf ("\n"); + } + return output_count; +} + + +/*---------------------------------------------------------------------------*/ +void PruningMatcher(INT_CLASS ClassTemplate, + UINT16 BlobLength, + INT16 NumFeatures, + INT_FEATURE_ARRAY Features, + INT32 min_misses, + UINT8 NormalizationFactor, + INT_RESULT Result, + int Debug) { +/* + ** Parameters: + ** ClassTemplate Prototypes & tables for a class + ** BlobLength Length of unormalized blob + ** NumFeatures Number of features in blob + ** Features Array of features + ** NormalizationFactor Fudge factor from blob + ** normalization process + ** Result Class rating & configuration: + ** (0.0 -> 1.0), 0=good, 1=bad + ** Debug Debugger flag: 1=debugger on + ** Globals: + ** LocalMatcherMultiplier Normalization factor multiplier + ** IntThetaFudge Theta fudge factor used for + ** evidence calculation + ** Operation: + ** 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. + ** Return: + ** Exceptions: none + ** History: Tue Feb 19 16:36:23 MST 1991, RWM, Created. + */ + static UINT8 FeatureEvidence[MAX_NUM_CONFIGS]; + static int SumOfFeatureEvidence[MAX_NUM_CONFIGS]; + int *IntPointer; + int Feature; + int BestMatch; + int used_features; + int NumConfigs; + + if (MatchDebuggingOn (Debug)) + cprintf ("Pruning Matcher -------------------------------------------\n"); + + IntPointer = SumOfFeatureEvidence; + for (NumConfigs = NumIntConfigsIn (ClassTemplate); NumConfigs > 0; + NumConfigs--) + *IntPointer++ = 0; + + for (Feature = 0, used_features = 0; Feature < NumFeatures; Feature++) { + if (Features[Feature].CP_misses >= min_misses) { + PMUpdateTablesForFeature (ClassTemplate, Feature, + &(Features[Feature]), FeatureEvidence, + SumOfFeatureEvidence, Debug); + used_features++; + } + } + + IMNormalizeSumOfEvidences(ClassTemplate, + SumOfFeatureEvidence, + NumFeatures, + used_features); + + BestMatch = + IMFindBestMatch(ClassTemplate, + SumOfFeatureEvidence, + BlobLength, + NormalizationFactor, + Result); + +#ifndef GRAPHICS_DISABLED + if (PrintMatchSummaryOn (Debug)) + IMDebugBestMatch(BestMatch, Result, BlobLength, NormalizationFactor); +#endif + + if (MatchDebuggingOn (Debug)) + cprintf ("Match Complete --------------------------------------------\n"); + +} + + +/*---------------------------------------------------------------------------*/ +void config_mask_to_proto_mask(INT_CLASS ClassTemplate, + BIT_VECTOR config_mask, + BIT_VECTOR proto_mask) { + UINT32 ConfigWord; + int ProtoSetIndex; + UINT32 ProtoNum; + PROTO_SET ProtoSet; + int NumProtos; + UINT32 ActualProtoNum; + + NumProtos = NumIntProtosIn (ClassTemplate); + + zero_all_bits (proto_mask, WordsInVectorOfSize (MAX_NUM_PROTOS)); + for (ProtoSetIndex = 0; ProtoSetIndex < NumProtoSetsIn (ClassTemplate); + ProtoSetIndex++) { + ProtoSet = ProtoSetIn (ClassTemplate, ProtoSetIndex); + ActualProtoNum = (ProtoSetIndex * PROTOS_PER_PROTO_SET); + for (ProtoNum = 0; + ((ProtoNum < PROTOS_PER_PROTO_SET) + && (ActualProtoNum < NumProtos)); ProtoNum++, ActualProtoNum++) { + ConfigWord = (ProtoSet->Protos[ProtoNum]).Configs[0]; + ConfigWord &= *config_mask; + if (ConfigWord != 0) { + proto_mask[ActualProtoNum / 32] |= 1 << (ActualProtoNum % 32); + } + } + } +} + + +/*---------------------------------------------------------------------------*/ +void IntegerMatcher(INT_CLASS ClassTemplate, + BIT_VECTOR ProtoMask, + BIT_VECTOR ConfigMask, + UINT16 BlobLength, + INT16 NumFeatures, + INT_FEATURE_ARRAY Features, + INT32 min_misses, + UINT8 NormalizationFactor, + INT_RESULT Result, + int Debug) { +/* + ** Parameters: + ** ClassTemplate Prototypes & tables for a class + ** BlobLength Length of unormalized blob + ** NumFeatures Number of features in blob + ** Features Array of features + ** NormalizationFactor Fudge factor from blob + ** normalization process + ** Result Class rating & configuration: + ** (0.0 -> 1.0), 0=good, 1=bad + ** Debug Debugger flag: 1=debugger on + ** Globals: + ** LocalMatcherMultiplier Normalization factor multiplier + ** IntThetaFudge Theta fudge factor used for + ** evidence calculation + ** Operation: + ** 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. + ** Return: + ** Exceptions: none + ** History: Tue Feb 19 16:36:23 MST 1991, RWM, Created. + */ + static UINT8 FeatureEvidence[MAX_NUM_CONFIGS]; + static int SumOfFeatureEvidence[MAX_NUM_CONFIGS]; + static UINT8 ProtoEvidence[MAX_NUM_PROTOS][MAX_PROTO_INDEX]; + int Feature; + int BestMatch; + int used_features; + + if (MatchDebuggingOn (Debug)) + cprintf ("Integer Matcher -------------------------------------------\n"); + + IMClearTables(ClassTemplate, SumOfFeatureEvidence, ProtoEvidence); + + for (Feature = 0, used_features = 0; Feature < NumFeatures; Feature++) { + if (Features[Feature].CP_misses >= min_misses) { + IMUpdateTablesForFeature (ClassTemplate, ProtoMask, ConfigMask, + Feature, &(Features[Feature]), + FeatureEvidence, SumOfFeatureEvidence, + ProtoEvidence, Debug); + used_features++; + } + } + +#ifndef GRAPHICS_DISABLED + if (PrintProtoMatchesOn (Debug) || PrintMatchSummaryOn (Debug)) + IMDebugFeatureProtoError(ClassTemplate, + ProtoMask, + ConfigMask, + SumOfFeatureEvidence, + ProtoEvidence, + NumFeatures, + Debug); + + if (DisplayProtoMatchesOn (Debug)) + IMDisplayProtoDebugInfo(ClassTemplate, + ProtoMask, + ConfigMask, + ProtoEvidence, + Debug); + + if (DisplayFeatureMatchesOn (Debug)) + IMDisplayFeatureDebugInfo(ClassTemplate, + ProtoMask, + ConfigMask, + NumFeatures, + Features, + Debug); +#endif + + IMUpdateSumOfProtoEvidences(ClassTemplate, + ConfigMask, + SumOfFeatureEvidence, + ProtoEvidence, + NumFeatures); + + IMNormalizeSumOfEvidences(ClassTemplate, + SumOfFeatureEvidence, + NumFeatures, + used_features); + + BestMatch = + IMFindBestMatch(ClassTemplate, + SumOfFeatureEvidence, + BlobLength, + NormalizationFactor, + Result); + +#ifndef GRAPHICS_DISABLED + if (PrintMatchSummaryOn (Debug)) + IMDebugBestMatch(BestMatch, Result, BlobLength, NormalizationFactor); + + if (MatchDebuggingOn (Debug)) + cprintf ("Match Complete --------------------------------------------\n"); +#endif + +} + + +/*---------------------------------------------------------------------------*/ +int FindGoodProtos(INT_CLASS ClassTemplate, + BIT_VECTOR ProtoMask, + BIT_VECTOR ConfigMask, + UINT16 BlobLength, + INT16 NumFeatures, + INT_FEATURE_ARRAY Features, + PROTO_ID *ProtoArray, + int Debug) { +/* + ** Parameters: + ** ClassTemplate Prototypes & tables for a class + ** ProtoMask AND Mask for proto word + ** ConfigMask AND Mask for config word + ** BlobLength Length of unormalized blob + ** NumFeatures Number of features in blob + ** Features Array of features + ** ProtoArray Array of good protos + ** Debug Debugger flag: 1=debugger on + ** Globals: + ** LocalMatcherMultiplier Normalization factor multiplier + ** IntThetaFudge Theta fudge factor used for + ** evidence calculation + ** AdaptProtoThresh Threshold for good protos + ** Operation: + ** FindGoodProtos finds all protos whose normalized proto-evidence + ** exceed AdaptProtoThresh. The list is ordered by increasing + ** proto id number. + ** Return: + ** Number of good protos in ProtoArray. + ** Exceptions: none + ** History: Tue Mar 12 17:09:26 MST 1991, RWM, Created + */ + static UINT8 FeatureEvidence[MAX_NUM_CONFIGS]; + static int SumOfFeatureEvidence[MAX_NUM_CONFIGS]; + static UINT8 ProtoEvidence[MAX_NUM_PROTOS][MAX_PROTO_INDEX]; + int Feature; + register UINT8 *UINT8Pointer; + register int ProtoIndex; + int NumProtos; + int NumGoodProtos; + UINT16 ActualProtoNum; + register int Temp; + + /* DEBUG opening heading */ + if (MatchDebuggingOn (Debug)) + cprintf + ("Find Good Protos -------------------------------------------\n"); + + IMClearTables(ClassTemplate, SumOfFeatureEvidence, ProtoEvidence); + + for (Feature = 0; Feature < NumFeatures; Feature++) + IMUpdateTablesForFeature (ClassTemplate, ProtoMask, ConfigMask, Feature, + &(Features[Feature]), FeatureEvidence, + SumOfFeatureEvidence, ProtoEvidence, Debug); + +#ifndef GRAPHICS_DISABLED + if (PrintProtoMatchesOn (Debug) || PrintMatchSummaryOn (Debug)) + IMDebugFeatureProtoError(ClassTemplate, + ProtoMask, + ConfigMask, + SumOfFeatureEvidence, + ProtoEvidence, + NumFeatures, + Debug); +#endif + + /* Average Proto Evidences & Find Good Protos */ + NumProtos = NumIntProtosIn (ClassTemplate); + NumGoodProtos = 0; + for (ActualProtoNum = 0; ActualProtoNum < NumProtos; ActualProtoNum++) { + /* Compute Average for Actual Proto */ + Temp = 0; + UINT8Pointer = &(ProtoEvidence[ActualProtoNum][0]); + for (ProtoIndex = LengthForProtoId (ClassTemplate, ActualProtoNum); + ProtoIndex > 0; ProtoIndex--, UINT8Pointer++) + Temp += *UINT8Pointer; + + Temp /= LengthForProtoId (ClassTemplate, ActualProtoNum); + + /* Find Good Protos */ + if (Temp >= AdaptProtoThresh) { + *ProtoArray = ActualProtoNum; + ProtoArray++; + NumGoodProtos++; + } + } + + if (MatchDebuggingOn (Debug)) + cprintf ("Match Complete --------------------------------------------\n"); + + return NumGoodProtos; + +} + + +/*---------------------------------------------------------------------------*/ +int FindBadFeatures(INT_CLASS ClassTemplate, + BIT_VECTOR ProtoMask, + BIT_VECTOR ConfigMask, + UINT16 BlobLength, + INT16 NumFeatures, + INT_FEATURE_ARRAY Features, + FEATURE_ID *FeatureArray, + int Debug) { +/* + ** Parameters: + ** ClassTemplate Prototypes & tables for a class + ** ProtoMask AND Mask for proto word + ** ConfigMask AND Mask for config word + ** BlobLength Length of unormalized blob + ** NumFeatures Number of features in blob + ** Features Array of features + ** FeatureArray Array of bad features + ** Debug Debugger flag: 1=debugger on + ** Globals: + ** LocalMatcherMultiplier Normalization factor multiplier + ** IntThetaFudge Theta fudge factor used for + ** evidence calculation + ** AdaptFeatureThresh Threshold for bad features + ** Operation: + ** FindBadFeatures finds all features whose maximum feature-evidence + ** was less than AdaptFeatureThresh. The list is ordered by increasing + ** feature number. + ** Return: + ** Number of bad features in FeatureArray. + ** Exceptions: none + ** History: Tue Mar 12 17:09:26 MST 1991, RWM, Created + */ + static UINT8 FeatureEvidence[MAX_NUM_CONFIGS]; + static int SumOfFeatureEvidence[MAX_NUM_CONFIGS]; + static UINT8 ProtoEvidence[MAX_NUM_PROTOS][MAX_PROTO_INDEX]; + int Feature; + register UINT8 *UINT8Pointer; + register int ConfigNum; + int NumConfigs; + int NumBadFeatures; + register int Temp; + + /* DEBUG opening heading */ + if (MatchDebuggingOn (Debug)) + cprintf + ("Find Bad Features -------------------------------------------\n"); + + IMClearTables(ClassTemplate, SumOfFeatureEvidence, ProtoEvidence); + + NumBadFeatures = 0; + NumConfigs = NumIntConfigsIn (ClassTemplate); + for (Feature = 0; Feature < NumFeatures; Feature++) { + IMUpdateTablesForFeature (ClassTemplate, ProtoMask, ConfigMask, Feature, + &(Features[Feature]), FeatureEvidence, + SumOfFeatureEvidence, ProtoEvidence, Debug); + + /* Find Best Evidence for Current Feature */ + Temp = 0; + UINT8Pointer = FeatureEvidence; + for (ConfigNum = 0; ConfigNum < NumConfigs; ConfigNum++, UINT8Pointer++) + if (*UINT8Pointer > Temp) + Temp = *UINT8Pointer; + + /* Find Bad Features */ + if (Temp < AdaptFeatureThresh) { + *FeatureArray = Feature; + FeatureArray++; + NumBadFeatures++; + } + } + +#ifndef GRAPHICS_DISABLED + if (PrintProtoMatchesOn (Debug) || PrintMatchSummaryOn (Debug)) + IMDebugFeatureProtoError(ClassTemplate, + ProtoMask, + ConfigMask, + SumOfFeatureEvidence, + ProtoEvidence, + NumFeatures, + Debug); +#endif + + if (MatchDebuggingOn (Debug)) + cprintf ("Match Complete --------------------------------------------\n"); + + return NumBadFeatures; + +} + + +/*---------------------------------------------------------------------------*/ +void InitIntegerMatcher() { + int i; + UINT32 IntSimilarity; + double Similarity; + double Evidence; + double ScaleFactor; + + /* Set default mode of operation of IntegerMatcher */ + SetCharNormMatch(); + + /* Initialize table for evidence to similarity lookup */ + for (i = 0; i < SE_TABLE_SIZE; i++) { + IntSimilarity = i << (27 - SE_TABLE_BITS); + Similarity = ((double) IntSimilarity) / 65536.0 / 65536.0; + Evidence = Similarity / SimilarityCenter; + Evidence *= Evidence; + Evidence += 1.0; + Evidence = 1.0 / Evidence; + Evidence *= 255.0; + + if (SEExponentialMultiplier > 0.0) { + ScaleFactor = 1.0 - exp (-SEExponentialMultiplier) * + exp (SEExponentialMultiplier * ((double) i / SE_TABLE_SIZE)); + if (ScaleFactor > 1.0) + ScaleFactor = 1.0; + if (ScaleFactor < 0.0) + ScaleFactor = 0.0; + Evidence *= ScaleFactor; + } + + SimilarityEvidenceTable[i] = (UINT8) (Evidence + 0.5); + } + + /* Initialize evidence computation variables */ + EvidenceTableMask = + ((1 << EvidenceTableBits) - 1) << (9 - EvidenceTableBits); + MultTruncShiftBits = (14 - IntEvidenceTruncBits); + TableTruncShiftBits = (27 - SE_TABLE_BITS - (MultTruncShiftBits << 1)); + EvidenceMultMask = ((1 << IntEvidenceTruncBits) - 1); + +} + + +/*---------------------------------------------------------------------------*/ +void InitIntegerMatcherVars() { + MakeClassPrunerThreshold(); + MakeClassPrunerMultiplier(); + MakeIntegerMatcherMultiplier(); + MakeIntThetaFudge(); + MakeCPCutoffStrength(); + MakeEvidenceTableBits(); + MakeIntEvidenceTruncBits(); + MakeSEExponentialMultiplier(); + MakeSimilarityCenter(); +} + + +/*-------------------------------------------------------------------------*/ +void PrintIntMatcherStats(FILE *f) { + fprintf (f, "protoword_lookups=%d, zero_protowords=%d, proto_shifts=%d\n", + protoword_lookups, zero_protowords, proto_shifts); + fprintf (f, "set_proto_bits=%d, config_shifts=%d, set_config_bits=%d\n", + set_proto_bits, config_shifts, set_config_bits); +} + + +/*-------------------------------------------------------------------------*/ +void SetProtoThresh(FLOAT32 Threshold) { + AdaptProtoThresh = (int) (255 * Threshold); + if (AdaptProtoThresh < 0) + AdaptProtoThresh = 0; + if (AdaptProtoThresh > 255) + AdaptProtoThresh = 255; +} + + +/*---------------------------------------------------------------------------*/ +void SetFeatureThresh(FLOAT32 Threshold) { + AdaptFeatureThresh = (int) (255 * Threshold); + if (AdaptFeatureThresh < 0) + AdaptFeatureThresh = 0; + if (AdaptFeatureThresh > 255) + AdaptFeatureThresh = 255; +} + + +/*--------------------------------------------------------------------------*/ +void SetBaseLineMatch() { + LocalMatcherMultiplier = 0; +} + + +/*--------------------------------------------------------------------------*/ +void SetCharNormMatch() { + LocalMatcherMultiplier = IntegerMatcherMultiplier; +} + + +/**---------------------------------------------------------------------------- + Private Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +void +IMClearTables (INT_CLASS ClassTemplate, +int SumOfFeatureEvidence[MAX_NUM_CONFIGS], +UINT8 ProtoEvidence[MAX_NUM_PROTOS][MAX_PROTO_INDEX]) { +/* + ** Parameters: + ** SumOfFeatureEvidence Sum of Feature Evidence Table + ** NumConfigs Number of Configurations + ** ProtoEvidence Prototype Evidence Table + ** NumProtos Number of Prototypes + ** Globals: + ** Operation: + ** Clear SumOfFeatureEvidence and ProtoEvidence tables. + ** Return: + ** Exceptions: none + ** History: Wed Feb 27 14:12:28 MST 1991, RWM, Created. + */ + register UINT8 *UINT8Pointer; + register int *IntPointer; + register int ConfigNum; + int NumConfigs; + register UINT16 ProtoNum; + int NumProtos; + register int ProtoIndex; + + NumProtos = NumIntProtosIn (ClassTemplate); + NumConfigs = NumIntConfigsIn (ClassTemplate); + + IntPointer = SumOfFeatureEvidence; + for (ConfigNum = 0; ConfigNum < NumConfigs; ConfigNum++, IntPointer++) + *IntPointer = 0; + UINT8Pointer = (UINT8 *) ProtoEvidence; + for (ProtoNum = 0; ProtoNum < NumProtos; ProtoNum++) + for (ProtoIndex = 0; ProtoIndex < MAX_PROTO_INDEX; + ProtoIndex++, UINT8Pointer++) + *UINT8Pointer = 0; + +} + + +/*---------------------------------------------------------------------------*/ +void +IMClearFeatureEvidenceTable (UINT8 FeatureEvidence[MAX_NUM_CONFIGS], +int NumConfigs) { +/* + ** Parameters: + ** FeatureEvidence Feature Evidence Table + ** NumConfigs Number of Configurations + ** Globals: + ** Operation: + ** Clear FeatureEvidence table. + ** Return: + ** Exceptions: none + ** History: Wed Feb 27 14:12:28 MST 1991, RWM, Created. + */ + register UINT8 *UINT8Pointer; + register int ConfigNum; + + UINT8Pointer = FeatureEvidence; + for (ConfigNum = 0; ConfigNum < NumConfigs; ConfigNum++, UINT8Pointer++) + *UINT8Pointer = 0; + +} + + +/*---------------------------------------------------------------------------*/ +void IMDebugConfiguration(int FeatureNum, + UINT16 ActualProtoNum, + UINT8 Evidence, + BIT_VECTOR ConfigMask, + UINT32 ConfigWord) { +/* + ** Parameters: + ** Globals: + ** Operation: + ** Print debugging information for Configuations + ** Return: + ** Exceptions: none + ** History: Wed Feb 27 14:12:28 MST 1991, RWM, Created. + */ + 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"); +} + + +/*---------------------------------------------------------------------------*/ +void IMDebugConfigurationSum(int FeatureNum, + UINT8 *FeatureEvidence, + INT32 ConfigCount) { +/* + ** Parameters: + ** Globals: + ** Operation: + ** Print debugging information for Configuations + ** Return: + ** Exceptions: none + ** History: Wed Feb 27 14:12:28 MST 1991, RWM, Created. + */ + int ConfigNum; + + cprintf ("F=%3d, C=", (int) FeatureNum); + + for (ConfigNum = 0; ConfigNum < ConfigCount; ConfigNum++) { + cprintf ("%4d", FeatureEvidence[ConfigNum]); + } + cprintf ("\n"); + +} + + +/*---------------------------------------------------------------------------*/ +void +PMUpdateTablesForFeature (INT_CLASS ClassTemplate, +int FeatureNum, +INT_FEATURE Feature, +UINT8 FeatureEvidence[MAX_NUM_CONFIGS], +int SumOfFeatureEvidence[MAX_NUM_CONFIGS], +int Debug) { +/* + ** Parameters: + ** ClassTemplate Prototypes & tables for a class + ** FeatureNum Current feature number (for DEBUG only) + ** Feature Pointer to a feature struct + ** FeatureEvidence Feature Evidence Table + ** SumOfFeatureEvidence Sum of Feature Evidence Table + ** ProtoEvidence Prototype Evidence Table + ** Debug Debugger flag: 1=debugger on + ** Globals: + ** Operation: + ** For the given feature: prune protos, compute evidence, update Feature Evidence, + ** Proto Evidence, and Sum of Feature Evidence tables. + ** Return: + ** Exceptions: none + ** History: Wed Feb 27 14:12:28 MST 1991, RWM, Created. + */ + UINT8 config_byte; + UINT8 proto_byte; + UINT8 Evidence; + INT32 config_offset; + UINT8 *UINT8Pointer; + UINT32 ConfigWord; + UINT32 ProtoWord; + INT32 M3; + INT32 A3; + UINT32 A4; + INT32 proto_word_offset; + INT32 proto_offset; + UINT32 ProtoNum; + UINT32 ActualProtoNum; + PROTO_SET ProtoSet; + UINT32 *ProtoPrunerPtr; + INT_PROTO Proto; + int ProtoSetIndex; + UINT32 XFeatureAddress; + UINT32 YFeatureAddress; + UINT32 ThetaFeatureAddress; + int *IntPointer; + int ConfigNum; + + IMClearFeatureEvidenceTable (FeatureEvidence, + NumIntConfigsIn (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 < NumProtoSetsIn (ClassTemplate); ProtoSetIndex++) { + ProtoSet = ProtoSetIn (ClassTemplate, 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), ProtoPrunerPtr++) { + /* Prune Protos of current Proto Set */ + ProtoWord = *(ProtoPrunerPtr + XFeatureAddress); + ProtoWord &= *(ProtoPrunerPtr + YFeatureAddress); + ProtoWord &= *(ProtoPrunerPtr + ThetaFeatureAddress); + + 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]; + /* Compute Evidence */ + 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)) * + IntThetaFudge) << 1; + + if (A3 < 0) + A3 = ~A3; + if (M3 < 0) + M3 = ~M3; + A3 >>= MultTruncShiftBits; + M3 >>= MultTruncShiftBits; + if (A3 > EvidenceMultMask) + A3 = EvidenceMultMask; + if (M3 > EvidenceMultMask) + M3 = EvidenceMultMask; + + A4 = (A3 * A3) + (M3 * M3); + A4 >>= TableTruncShiftBits; + if (A4 > EvidenceTableMask) + Evidence = 0; + else + Evidence = SimilarityEvidenceTable[A4]; + + UINT8Pointer = FeatureEvidence - 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; + } + } + } + } + } + + if (PrintFeatureMatchesOn (Debug)) + IMDebugConfigurationSum (FeatureNum, FeatureEvidence, + NumIntConfigsIn (ClassTemplate)); + IntPointer = SumOfFeatureEvidence; + UINT8Pointer = FeatureEvidence; + for (ConfigNum = NumIntConfigsIn (ClassTemplate); ConfigNum > 0; + ConfigNum--) + *IntPointer++ += (*UINT8Pointer++); +} + + +/*---------------------------------------------------------------------------*/ +void +IMUpdateTablesForFeature (INT_CLASS ClassTemplate, +BIT_VECTOR ProtoMask, +BIT_VECTOR ConfigMask, +int FeatureNum, +INT_FEATURE Feature, +UINT8 FeatureEvidence[MAX_NUM_CONFIGS], +int SumOfFeatureEvidence[MAX_NUM_CONFIGS], +UINT8 +ProtoEvidence[MAX_NUM_PROTOS][MAX_PROTO_INDEX], +int Debug) { +/* + ** Parameters: + ** ClassTemplate Prototypes & tables for a class + ** FeatureNum Current feature number (for DEBUG only) + ** Feature Pointer to a feature struct + ** FeatureEvidence Feature Evidence Table + ** SumOfFeatureEvidence Sum of Feature Evidence Table + ** ProtoEvidence Prototype Evidence Table + ** Debug Debugger flag: 1=debugger on + ** Globals: + ** Operation: + ** For the given feature: prune protos, compute evidence, update Feature Evidence, + ** Proto Evidence, and Sum of Feature Evidence tables. + ** Return: + ** Exceptions: none + ** History: Wed Feb 27 14:12:28 MST 1991, RWM, Created. + */ + register UINT32 ConfigWord; + register UINT32 ProtoWord; + register UINT32 ProtoNum; + register 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; + register UINT8 *UINT8Pointer; + register int ProtoIndex; + UINT8 Temp; + register int *IntPointer; + int ConfigNum; + register INT32 M3; + register INT32 A3; + register UINT32 A4; + + IMClearFeatureEvidenceTable (FeatureEvidence, + NumIntConfigsIn (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 < NumProtoSetsIn (ClassTemplate); ProtoSetIndex++) { + ProtoSet = ProtoSetIn (ClassTemplate, 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)) * + IntThetaFudge) << 1; + + if (A3 < 0) + A3 = ~A3; + if (M3 < 0) + M3 = ~M3; + A3 >>= MultTruncShiftBits; + M3 >>= MultTruncShiftBits; + if (A3 > EvidenceMultMask) + A3 = EvidenceMultMask; + if (M3 > EvidenceMultMask) + M3 = EvidenceMultMask; + + A4 = (A3 * A3) + (M3 * M3); + A4 >>= TableTruncShiftBits; + if (A4 > EvidenceTableMask) + Evidence = 0; + else + Evidence = SimilarityEvidenceTable[A4]; + + if (PrintFeatureMatchesOn (Debug)) + IMDebugConfiguration (FeatureNum, + ActualProtoNum + proto_offset, + Evidence, ConfigMask, ConfigWord); + + ConfigWord &= *ConfigMask; + + UINT8Pointer = FeatureEvidence - 8; + config_byte = 0; + while (ConfigWord != 0 || config_byte != 0) { + while (config_byte == 0) { + config_byte = ConfigWord & 0xff; + ConfigWord >>= 8; + UINT8Pointer += 8; + // config_shifts++; + } + config_offset = offset_table[config_byte]; + config_byte = next_table[config_byte]; + if (Evidence > UINT8Pointer[config_offset]) + UINT8Pointer[config_offset] = Evidence; + } + + UINT8Pointer = + &(ProtoEvidence[ActualProtoNum + proto_offset][0]); + for (ProtoIndex = + LengthForProtoId (ClassTemplate, + 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, FeatureEvidence, + NumIntConfigsIn (ClassTemplate)); + IntPointer = SumOfFeatureEvidence; + UINT8Pointer = FeatureEvidence; + for (ConfigNum = NumIntConfigsIn (ClassTemplate); ConfigNum > 0; + ConfigNum--) + *IntPointer++ += (*UINT8Pointer++); + +} + + +/*---------------------------------------------------------------------------*/ +#ifndef GRAPHICS_DISABLED +void +IMDebugFeatureProtoError (INT_CLASS ClassTemplate, +BIT_VECTOR ProtoMask, +BIT_VECTOR ConfigMask, +int SumOfFeatureEvidence[MAX_NUM_CONFIGS], +UINT8 +ProtoEvidence[MAX_NUM_PROTOS][MAX_PROTO_INDEX], +INT16 NumFeatures, int Debug) { +/* + ** Parameters: + ** Globals: + ** Operation: + ** Print debugging information for Configuations + ** Return: + ** Exceptions: none + ** History: Wed Feb 27 14:12:28 MST 1991, RWM, Created. + */ + UINT8 *UINT8Pointer; + int *IntPointer; + FLOAT32 ProtoConfigs[MAX_NUM_CONFIGS]; + int ConfigNum; + UINT32 ConfigWord; + int ProtoSetIndex; + UINT16 ProtoNum; + UINT8 ProtoWordNum; + PROTO_SET ProtoSet; + int ProtoIndex; + int NumProtos; + UINT16 ActualProtoNum; + int Temp; + int NumConfigs; + + NumProtos = NumIntProtosIn (ClassTemplate); + NumConfigs = NumIntConfigsIn (ClassTemplate); + + if (PrintMatchSummaryOn (Debug)) { + cprintf ("Configuration Mask:\n"); + for (ConfigNum = 0; ConfigNum < NumConfigs; ConfigNum++) + cprintf ("%1d", (((*ConfigMask) >> ConfigNum) & 1)); + cprintf ("\n"); + + cprintf ("Feature Error for Configurations:\n"); + for (ConfigNum = 0; ConfigNum < NumConfigs; ConfigNum++) + cprintf (" %5.1f", + 100.0 * (1.0 - + (FLOAT32) SumOfFeatureEvidence[ConfigNum] / + NumFeatures / 256.0)); + cprintf ("\n\n\n"); + } + + if (PrintMatchSummaryOn (Debug)) { + cprintf ("Proto Mask:\n"); + for (ProtoSetIndex = 0; ProtoSetIndex < NumProtoSetsIn (ClassTemplate); + 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 < NumProtos)); + ProtoNum++, ActualProtoNum++) + cprintf ("%1d", (((*ProtoMask) >> ProtoNum) & 1)); + cprintf ("\n"); + } + } + cprintf ("\n"); + } + + for (ConfigNum = 0; ConfigNum < NumConfigs; ConfigNum++) + ProtoConfigs[ConfigNum] = 0; + + if (PrintProtoMatchesOn (Debug)) { + cprintf ("Proto Evidence:\n"); + for (ProtoSetIndex = 0; ProtoSetIndex < NumProtoSetsIn (ClassTemplate); + ProtoSetIndex++) { + ProtoSet = ProtoSetIn (ClassTemplate, ProtoSetIndex); + ActualProtoNum = (ProtoSetIndex * PROTOS_PER_PROTO_SET); + for (ProtoNum = 0; + ((ProtoNum < PROTOS_PER_PROTO_SET) + && (ActualProtoNum < NumProtos)); + ProtoNum++, ActualProtoNum++) { + cprintf ("P %3d =", ActualProtoNum); + Temp = 0; + UINT8Pointer = &(ProtoEvidence[ActualProtoNum][0]); + for (ProtoIndex = 0; + ProtoIndex < LengthForProtoId (ClassTemplate, + ActualProtoNum); + ProtoIndex++, UINT8Pointer++) { + cprintf (" %d", *UINT8Pointer); + Temp += *UINT8Pointer; + } + + cprintf (" = %6.4f%%\n", Temp / + 256.0 / LengthForProtoId (ClassTemplate, + ActualProtoNum)); + + ConfigWord = (ProtoSet->Protos[ProtoNum]).Configs[0]; + IntPointer = SumOfFeatureEvidence; + ConfigNum = 0; + while (ConfigWord) { + cprintf ("%5d", ConfigWord & 1 ? Temp : 0); + if (ConfigWord & 1) + ProtoConfigs[ConfigNum] += Temp; + IntPointer++; + ConfigNum++; + ConfigWord >>= 1; + } + cprintf ("\n"); + } + } + } + + if (PrintMatchSummaryOn (Debug)) { + cprintf ("Proto Error for Configurations:\n"); + for (ConfigNum = 0; ConfigNum < NumConfigs; ConfigNum++) + cprintf (" %5.1f", + 100.0 * (1.0 - + ProtoConfigs[ConfigNum] / + LengthForConfigId (ClassTemplate, + ConfigNum) / 256.0)); + cprintf ("\n\n"); + } + + if (PrintProtoMatchesOn (Debug)) { + cprintf ("Proto Sum for Configurations:\n"); + for (ConfigNum = 0; ConfigNum < NumConfigs; ConfigNum++) + cprintf (" %4.1f", ProtoConfigs[ConfigNum] / 256.0); + cprintf ("\n\n"); + + cprintf ("Proto Length for Configurations:\n"); + for (ConfigNum = 0; ConfigNum < NumConfigs; ConfigNum++) + cprintf (" %4.1f", + (float) LengthForConfigId (ClassTemplate, ConfigNum)); + cprintf ("\n\n"); + } + +} + + +/*---------------------------------------------------------------------------*/ +void +IMDisplayProtoDebugInfo (INT_CLASS ClassTemplate, +BIT_VECTOR ProtoMask, +BIT_VECTOR ConfigMask, +UINT8 ProtoEvidence[MAX_NUM_PROTOS][MAX_PROTO_INDEX], +int Debug) { + register UINT8 *UINT8Pointer; + register UINT32 ConfigWord; + register UINT16 ProtoNum; + register UINT16 ActualProtoNum; + PROTO_SET ProtoSet; + int ProtoSetIndex; + int ProtoIndex; + int NumProtos; + register int Temp; + + extern void *IntMatchWindow; + + if (IntMatchWindow == NULL) { + IntMatchWindow = c_create_window ("IntMatchWindow", 50, 200, + 520, 520, + -130.0, 130.0, -130.0, 130.0); + } + NumProtos = NumIntProtosIn (ClassTemplate); + + for (ProtoSetIndex = 0; ProtoSetIndex < NumProtoSetsIn (ClassTemplate); + ProtoSetIndex++) { + ProtoSet = ProtoSetIn (ClassTemplate, ProtoSetIndex); + ActualProtoNum = (ProtoSetIndex * PROTOS_PER_PROTO_SET); + for (ProtoNum = 0; + ((ProtoNum < PROTOS_PER_PROTO_SET) + && (ActualProtoNum < NumProtos)); ProtoNum++, ActualProtoNum++) { + /* Compute Average for Actual Proto */ + Temp = 0; + UINT8Pointer = &(ProtoEvidence[ActualProtoNum][0]); + for (ProtoIndex = LengthForProtoId (ClassTemplate, ActualProtoNum); + ProtoIndex > 0; ProtoIndex--, UINT8Pointer++) + Temp += *UINT8Pointer; + + Temp /= LengthForProtoId (ClassTemplate, ActualProtoNum); + + ConfigWord = (ProtoSet->Protos[ProtoNum]).Configs[0]; + ConfigWord &= *ConfigMask; + if (ConfigWord) + /* Update display for current proto */ + if (ClipMatchEvidenceOn (Debug)) { + if (Temp < AdaptProtoThresh) + DisplayIntProto (ClassTemplate, ActualProtoNum, + (Temp / 255.0)); + else + DisplayIntProto (ClassTemplate, ActualProtoNum, + (Temp / 255.0)); + } + else { + DisplayIntProto (ClassTemplate, ActualProtoNum, + (Temp / 255.0)); + } + } + } +} + + +/*---------------------------------------------------------------------------*/ +void IMDisplayFeatureDebugInfo(INT_CLASS ClassTemplate, + BIT_VECTOR ProtoMask, + BIT_VECTOR ConfigMask, + INT16 NumFeatures, + INT_FEATURE_ARRAY Features, + int Debug) { + static UINT8 FeatureEvidence[MAX_NUM_CONFIGS]; + static int SumOfFeatureEvidence[MAX_NUM_CONFIGS]; + static UINT8 ProtoEvidence[MAX_NUM_PROTOS][MAX_PROTO_INDEX]; + int Feature; + register UINT8 *UINT8Pointer; + register int ConfigNum; + int NumConfigs; + register int Temp; + + IMClearTables(ClassTemplate, SumOfFeatureEvidence, ProtoEvidence); + + NumConfigs = NumIntConfigsIn (ClassTemplate); + for (Feature = 0; Feature < NumFeatures; Feature++) { + IMUpdateTablesForFeature (ClassTemplate, ProtoMask, ConfigMask, Feature, + &(Features[Feature]), FeatureEvidence, + SumOfFeatureEvidence, ProtoEvidence, 0); + + /* Find Best Evidence for Current Feature */ + Temp = 0; + UINT8Pointer = FeatureEvidence; + for (ConfigNum = 0; ConfigNum < NumConfigs; ConfigNum++, UINT8Pointer++) + if (*UINT8Pointer > Temp) + Temp = *UINT8Pointer; + + /* Update display for current feature */ + if (ClipMatchEvidenceOn (Debug)) { + if (Temp < AdaptFeatureThresh) + DisplayIntFeature (&(Features[Feature]), 0.0); + else + DisplayIntFeature (&(Features[Feature]), 1.0); + } + else { + DisplayIntFeature (&(Features[Feature]), (Temp / 255.0)); + } + } +} +#endif + +/*---------------------------------------------------------------------------*/ +void +IMUpdateSumOfProtoEvidences (INT_CLASS ClassTemplate, +BIT_VECTOR ConfigMask, +int SumOfFeatureEvidence[MAX_NUM_CONFIGS], +UINT8 +ProtoEvidence[MAX_NUM_PROTOS][MAX_PROTO_INDEX], +INT16 NumFeatures) { +/* + ** Parameters: + ** Globals: + ** Operation: + ** Add sum of Proto Evidences into Sum Of Feature Evidence Array + ** Return: + ** Exceptions: none + ** History: Wed Feb 27 14:12:28 MST 1991, RWM, Created. + */ + register UINT8 *UINT8Pointer; + register int *IntPointer; + register UINT32 ConfigWord; + int ProtoSetIndex; + register UINT16 ProtoNum; + PROTO_SET ProtoSet; + register int ProtoIndex; + int NumProtos; + UINT16 ActualProtoNum; + int Temp; + + NumProtos = NumIntProtosIn (ClassTemplate); + + for (ProtoSetIndex = 0; ProtoSetIndex < NumProtoSetsIn (ClassTemplate); + ProtoSetIndex++) { + ProtoSet = ProtoSetIn (ClassTemplate, ProtoSetIndex); + ActualProtoNum = (ProtoSetIndex * PROTOS_PER_PROTO_SET); + for (ProtoNum = 0; + ((ProtoNum < PROTOS_PER_PROTO_SET) + && (ActualProtoNum < NumProtos)); ProtoNum++, ActualProtoNum++) { + Temp = 0; + UINT8Pointer = &(ProtoEvidence[ActualProtoNum][0]); + for (ProtoIndex = LengthForProtoId (ClassTemplate, ActualProtoNum); + ProtoIndex > 0; ProtoIndex--, UINT8Pointer++) + Temp += *UINT8Pointer; + + ConfigWord = (ProtoSet->Protos[ProtoNum]).Configs[0]; + ConfigWord &= *ConfigMask; + IntPointer = SumOfFeatureEvidence; + while (ConfigWord) { + if (ConfigWord & 1) + *IntPointer += Temp; + IntPointer++; + ConfigWord >>= 1; + } + } + } +} + + +/*---------------------------------------------------------------------------*/ +void +PMNormalizeSumOfEvidences (INT_CLASS ClassTemplate, +int SumOfFeatureEvidence[MAX_NUM_CONFIGS], +INT16 NumFeatures, INT32 used_features) { +/* + ** Parameters: + ** Globals: + ** Operation: + ** Normalize Sum of Proto and Feature Evidence by dividing by + ** the sum of the Feature Lengths and the Proto Lengths for each + ** configuration. + ** Return: + ** Exceptions: none + ** History: Wed Feb 27 14:12:28 MST 1991, RWM, Created. + */ + register int *IntPointer; + register int ConfigNum; + int NumConfigs; + + NumConfigs = NumIntConfigsIn (ClassTemplate); + if (used_features <= 0) + used_features = 1; + + IntPointer = SumOfFeatureEvidence; + for (ConfigNum = 0; ConfigNum < NumConfigs; ConfigNum++, IntPointer++) + *IntPointer = (*IntPointer << 8) / used_features; +} + + +/*---------------------------------------------------------------------------*/ +void +IMNormalizeSumOfEvidences (INT_CLASS ClassTemplate, +int SumOfFeatureEvidence[MAX_NUM_CONFIGS], +INT16 NumFeatures, INT32 used_features) { +/* + ** Parameters: + ** Globals: + ** Operation: + ** Normalize Sum of Proto and Feature Evidence by dividing by + ** the sum of the Feature Lengths and the Proto Lengths for each + ** configuration. + ** Return: + ** Exceptions: none + ** History: Wed Feb 27 14:12:28 MST 1991, RWM, Created. + */ + register int *IntPointer; + register int ConfigNum; + int NumConfigs; + + NumConfigs = NumIntConfigsIn (ClassTemplate); + + IntPointer = SumOfFeatureEvidence; + for (ConfigNum = 0; ConfigNum < NumConfigs; ConfigNum++, IntPointer++) + *IntPointer = (*IntPointer << 8) / + (NumFeatures + LengthForConfigId (ClassTemplate, ConfigNum)); +} + + +/*---------------------------------------------------------------------------*/ +int +IMFindBestMatch (INT_CLASS ClassTemplate, +int SumOfFeatureEvidence[MAX_NUM_CONFIGS], +UINT16 BlobLength, +UINT8 NormalizationFactor, INT_RESULT Result) { +/* + ** Parameters: + ** Globals: + ** Operation: + ** 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 + ** Exceptions: none + ** History: Wed Feb 27 14:12:28 MST 1991, RWM, Created. + */ + register int *IntPointer; + register int ConfigNum; + register int NumConfigs; + register int BestMatch; + register int Best2Match; + + NumConfigs = NumIntConfigsIn (ClassTemplate); + + /* Find best match */ + BestMatch = 0; + Best2Match = 0; + IntPointer = SumOfFeatureEvidence; + for (ConfigNum = 0; ConfigNum < NumConfigs; ConfigNum++, IntPointer++) { + if (display_ratings > 1) + cprintf ("Config %d, rating=%d\n", ConfigNum, *IntPointer); + if (*IntPointer > BestMatch) { + if (BestMatch > 0) { + Result->Config2 = Result->Config; + Best2Match = BestMatch; + } + else + Result->Config2 = ConfigNum; + Result->Config = ConfigNum; + BestMatch = *IntPointer; + } + else if (*IntPointer > Best2Match) { + Result->Config2 = ConfigNum; + Best2Match = *IntPointer; + } + } + + /* Compute Certainty Rating */ + (*Result).Rating = ((65536.0 - BestMatch) / 65536.0 * BlobLength + + LocalMatcherMultiplier * NormalizationFactor / 256.0) / + (BlobLength + LocalMatcherMultiplier); + + return BestMatch; +} + + +/*---------------------------------------------------------------------------*/ +#ifndef GRAPHICS_DISABLED +void IMDebugBestMatch(int BestMatch, + INT_RESULT Result, + UINT16 BlobLength, + UINT8 NormalizationFactor) { +/* + ** Parameters: + ** Globals: + ** Operation: + ** Find the best match for the current class and update the Result + ** Return: + ** Exceptions: none + ** History: Wed Feb 27 14:12:28 MST 1991, RWM, Created. + */ + cprintf ("Rating = %5.1f%% Best Config = %3d\n", + 100.0 * ((*Result).Rating), (int) ((*Result).Config)); + cprintf + ("Matcher Error = %5.1f%% Blob Length = %3d Weight = %4.1f%%\n", + 100.0 * (65536.0 - BestMatch) / 65536.0, (int) BlobLength, + 100.0 * BlobLength / (BlobLength + LocalMatcherMultiplier)); + cprintf + ("Char Norm Error = %5.1f%% Norm Strength = %3d Weight = %4.1f%%\n", + 100.0 * NormalizationFactor / 256.0, LocalMatcherMultiplier, + 100.0 * LocalMatcherMultiplier / (BlobLength + LocalMatcherMultiplier)); +} +#endif + +/*---------------------------------------------------------------------------*/ +void +HeapSort (int n, register INT16 ra[], register UINT8 rb[]) { +/* + ** Parameters: + ** n Number of elements to sort + ** ra Key array [1..n] + ** rb Index array [1..n] + ** Globals: + ** Operation: + ** Sort Key array in ascending order using heap sort + ** algorithm. Also sort Index array that is tied to + ** the key array. + ** Return: + ** Exceptions: none + ** History: Tue Feb 19 10:24:24 MST 1991, RWM, Created. + */ + register 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; + } +} diff --git a/classify/intmatcher.h b/classify/intmatcher.h new file mode 100644 index 0000000000..710ce57fda --- /dev/null +++ b/classify/intmatcher.h @@ -0,0 +1,240 @@ +/****************************************************************************** + ** 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 Files and Type Defines +----------------------------------------------------------------------------**/ +#include "debug.h" +#include "intproto.h" +#include "cutoffs.h" + +typedef struct +{ + FLOAT32 Rating; + UINT8 Config; + UINT8 Config2; +} + + +INT_RESULT_STRUCT, *INT_RESULT; + +typedef struct +{ + FLOAT32 Rating; + FLOAT32 Rating2; + UINT32 config_mask; + CLASS_ID Class; +} + + +CP_RESULT_STRUCT; + +/*typedef CLASS_ID CLASS_PRUNER_RESULTS [MAX_NUM_CLASSES]; */ +typedef CP_RESULT_STRUCT CLASS_PRUNER_RESULTS[MAX_NUM_CLASSES]; + +typedef UINT8 CLASS_NORMALIZATION_ARRAY[MAX_NUM_CLASSES]; + +/*---------------------------------------------------------------------------- + Variables +-----------------------------------------------------------------------------*/ +extern int AdaptProtoThresh; +extern int AdaptFeatureThresh; + +/**---------------------------------------------------------------------------- + Public Function Prototypes +----------------------------------------------------------------------------**/ +int ClassPruner(INT_TEMPLATES IntTemplates, + INT16 NumFeatures, + INT_FEATURE_ARRAY Features, + CLASS_NORMALIZATION_ARRAY NormalizationFactors, + CLASS_CUTOFF_ARRAY ExpectedNumFeatures, + CLASS_PRUNER_RESULTS Results, + int Debug); + +int feature_pruner(INT_TEMPLATES IntTemplates, + INT16 NumFeatures, + INT_FEATURE_ARRAY Features, + INT32 NumClasses, + CLASS_PRUNER_RESULTS Results); + +int prune_configs(INT_TEMPLATES IntTemplates, + INT32 min_misses, + INT16 NumFeatures, + INT_FEATURE_ARRAY Features, + CLASS_NORMALIZATION_ARRAY NormalizationFactors, + INT32 class_count, + UINT16 BlobLength, + CLASS_PRUNER_RESULTS Results, + int Debug); + +void PruningMatcher(INT_CLASS ClassTemplate, + UINT16 BlobLength, + INT16 NumFeatures, + INT_FEATURE_ARRAY Features, + INT32 min_misses, + UINT8 NormalizationFactor, + INT_RESULT Result, + int Debug); + +void config_mask_to_proto_mask(INT_CLASS ClassTemplate, + BIT_VECTOR config_mask, + BIT_VECTOR proto_mask); + +void IntegerMatcher(INT_CLASS ClassTemplate, + BIT_VECTOR ProtoMask, + BIT_VECTOR ConfigMask, + UINT16 BlobLength, + INT16 NumFeatures, + INT_FEATURE_ARRAY Features, + INT32 min_misses, + UINT8 NormalizationFactor, + INT_RESULT Result, + int Debug); + +int FindGoodProtos(INT_CLASS ClassTemplate, + BIT_VECTOR ProtoMask, + BIT_VECTOR ConfigMask, + UINT16 BlobLength, + INT16 NumFeatures, + INT_FEATURE_ARRAY Features, + PROTO_ID *ProtoArray, + 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 Debug); + +void InitIntegerMatcher(); + +void InitIntegerMatcherVars(); + +void PrintIntMatcherStats(FILE *f); + +void SetProtoThresh(FLOAT32 Threshold); + +void SetFeatureThresh(FLOAT32 Threshold); + +void SetBaseLineMatch(); + +void SetCharNormMatch(); + +/**---------------------------------------------------------------------------- + Private Function Prototypes +----------------------------------------------------------------------------**/ +void IMClearTables (INT_CLASS ClassTemplate, +int SumOfFeatureEvidence[MAX_NUM_CONFIGS], +UINT8 ProtoEvidence[MAX_NUM_PROTOS][MAX_PROTO_INDEX]); + +void IMClearFeatureEvidenceTable (UINT8 FeatureEvidence[MAX_NUM_CONFIGS], +int NumConfigs); + +void IMDebugConfiguration(INT_FEATURE FeatureNum, + UINT16 ActualProtoNum, + UINT8 Evidence, + BIT_VECTOR ConfigMask, + UINT32 ConfigWord); + +void IMDebugConfigurationSum(INT_FEATURE FeatureNum, + UINT8 *FeatureEvidence, + INT32 ConfigCount); + +void PMUpdateTablesForFeature (INT_CLASS ClassTemplate, +int FeatureNum, +INT_FEATURE Feature, +UINT8 FeatureEvidence[MAX_NUM_CONFIGS], +int SumOfFeatureEvidence[MAX_NUM_CONFIGS], +int Debug); + +void IMUpdateTablesForFeature (INT_CLASS ClassTemplate, +BIT_VECTOR ProtoMask, +BIT_VECTOR ConfigMask, +int FeatureNum, +INT_FEATURE Feature, +UINT8 FeatureEvidence[MAX_NUM_CONFIGS], +int SumOfFeatureEvidence[MAX_NUM_CONFIGS], +UINT8 +ProtoEvidence[MAX_NUM_PROTOS][MAX_PROTO_INDEX], +int Debug); + +#ifndef GRAPHICS_DISABLED +void IMDebugFeatureProtoError (INT_CLASS ClassTemplate, +BIT_VECTOR ProtoMask, +BIT_VECTOR ConfigMask, +int SumOfFeatureEvidence[MAX_NUM_CONFIGS], +UINT8 +ProtoEvidence[MAX_NUM_PROTOS][MAX_PROTO_INDEX], +INT16 NumFeatures, int Debug); + +void IMDisplayProtoDebugInfo (INT_CLASS ClassTemplate, +BIT_VECTOR ProtoMask, +BIT_VECTOR ConfigMask, +UINT8 +ProtoEvidence[MAX_NUM_PROTOS][MAX_PROTO_INDEX], +int Debug); + +void IMDisplayFeatureDebugInfo(INT_CLASS ClassTemplate, + BIT_VECTOR ProtoMask, + BIT_VECTOR ConfigMask, + INT16 NumFeatures, + INT_FEATURE_ARRAY Features, + int Debug); +#endif + +void IMUpdateSumOfProtoEvidences (INT_CLASS ClassTemplate, +BIT_VECTOR ConfigMask, +int SumOfFeatureEvidence[MAX_NUM_CONFIGS], +UINT8 +ProtoEvidence[MAX_NUM_PROTOS] +[MAX_PROTO_INDEX], INT16 NumFeatures); + +void PMNormalizeSumOfEvidences (INT_CLASS ClassTemplate, +int SumOfFeatureEvidence[MAX_NUM_CONFIGS], +INT16 NumFeatures, INT32 used_features); + +void IMNormalizeSumOfEvidences (INT_CLASS ClassTemplate, +int SumOfFeatureEvidence[MAX_NUM_CONFIGS], +INT16 NumFeatures, INT32 used_features); + +int IMFindBestMatch (INT_CLASS ClassTemplate, +int SumOfFeatureEvidence[MAX_NUM_CONFIGS], +UINT16 BlobLength, +UINT8 NormalizationFactor, INT_RESULT Result); + +#ifndef GRAPHICS_DISABLED +void IMDebugBestMatch(int BestMatch, + INT_RESULT Result, + UINT16 BlobLength, + UINT8 NormalizationFactor); +#endif + +void HeapSort (int n, register INT16 ra[], register UINT8 rb[]); + +/**---------------------------------------------------------------------------- + Global Data Definitions and Declarations +----------------------------------------------------------------------------**/ +extern int IntegerMatcherMultiplier; + +extern UINT32 EvidenceMultMask; +#endif diff --git a/classify/intproto.cpp b/classify/intproto.cpp new file mode 100644 index 0000000000..b922f76049 --- /dev/null +++ b/classify/intproto.cpp @@ -0,0 +1,1746 @@ +/****************************************************************************** + ** 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 "intproto.h" +#include "picofeat.h" +#include "debug.h" +#include "mfoutline.h" +#include "emalloc.h" +#include "const.h" +#include "ndminx.h" +#include "adaptmatch.h" + +//extern GetPicoFeatureLength(); + +#include +#include +#include + +/* match debug display constants*/ +#define DISPLAY_OFFSET (0.5 * INT_CHAR_NORM_RANGE) +#define PROTO_PRUNER_SCALE (4.0) + +#define INT_DESCENDER (0.0 * INT_CHAR_NORM_RANGE - DISPLAY_OFFSET) +#define INT_BASELINE (0.25 * INT_CHAR_NORM_RANGE - DISPLAY_OFFSET) +#define INT_XHEIGHT (0.75 * INT_CHAR_NORM_RANGE - DISPLAY_OFFSET) +#define INT_CAPHEIGHT (1.0 * INT_CHAR_NORM_RANGE - DISPLAY_OFFSET) + +#define INT_XCENTER (0.5 * INT_CHAR_NORM_RANGE - DISPLAY_OFFSET) +#define INT_YCENTER (0.5 * INT_CHAR_NORM_RANGE - DISPLAY_OFFSET) +#define INT_XRADIUS (0.2 * INT_CHAR_NORM_RANGE) +#define INT_YRADIUS (0.2 * INT_CHAR_NORM_RANGE) +#define INT_MIN_X (- DISPLAY_OFFSET) +#define INT_MIN_Y (- DISPLAY_OFFSET) +#define INT_MAX_X ( DISPLAY_OFFSET) +#define INT_MAX_Y ( DISPLAY_OFFSET) +#define DOUBLE_OFFSET 0.095 + +/* 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; + +/**---------------------------------------------------------------------------- + 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 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); + +void FillPPLinearBits (UINT32 ParamTable[NUM_PP_BUCKETS][WERDS_PER_PP_VECTOR], +int Bit, FLOAT32 Center, FLOAT32 Spread); + +#ifndef GRAPHICS_DISABLED +CLASS_ID GetClassToDebug(const char *Prompt); +#endif + +void GetCPPadsForLevel(int Level, + FLOAT32 *EndPad, + FLOAT32 *SidePad, + FLOAT32 *AnglePad); + +C_COL 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(void *window, INT_FEATURE Feature, C_COL Color); + +void RenderIntProto(void *window, + INT_CLASS Class, + PROTO_ID ProtoId, + C_COL Color); +#endif + +int TruncateParam(FLOAT32 Param, int Min, int Max, char *Id); + +/* +#if defined(__STDC__) || defined(__cplusplus) +# define _ARGS(s) s +#else +# define _ARGS(s) () +#endif*/ + +/* /users/danj/wiseowl/src/danj/microfeatures/intproto.c +FLOAT32 BucketStart + _ARGS((int Bucket, + FLOAT32 Offset, + int NumBuckets)); + +FLOAT32 BucketEnd + _ARGS((int Bucket, + FLOAT32 Offset, + int NumBuckets)); + +void DoFill + _ARGS((FILL_SPEC *FillSpec, + CLASS_PRUNER Pruner, + UINT32 ClassMask, + UINT32 ClassCount, + UINT32 WordIndex)); + +BOOL8 FillerDone + _ARGS((TABLE_FILLER *Filler)); + +void FillPPCircularBits + _ARGS((UINT32 ParamTable [NUM_PP_BUCKETS ][WERDS_PER_PP_VECTOR ], + int Bit, + FLOAT32 Center, + FLOAT32 Spread)); + +void FillPPLinearBits + _ARGS((UINT32 ParamTable [NUM_PP_BUCKETS ][WERDS_PER_PP_VECTOR ], + int Bit, + FLOAT32 Center, + FLOAT32 Spread)); + +void GetCPPadsForLevel + _ARGS((int Level, + FLOAT32 *EndPad, + FLOAT32 *SidePad, + FLOAT32 *AnglePad)); + +C_COL GetMatchColorFor + _ARGS((FLOAT32 Evidence)); + +void GetNextFill + _ARGS((TABLE_FILLER *Filler, + FILL_SPEC *Fill)); + +void InitTableFiller + _ARGS((FLOAT32 EndPad, + FLOAT32 SidePad, + FLOAT32 AnglePad, + PROTO Proto, + TABLE_FILLER *Filler)); + +void RenderIntFeature + _ARGS((SHAPE_LIST ShapeList, + INT_FEATURE Feature, + char *Color)); + +void RenderIntProto + _ARGS((SHAPE_LIST ShapeList, + INT_CLASS Class, + PROTO_ID ProtoId, + char *Color)); + +int TruncateParam + _ARGS((FLOAT32 Param, + int Min, + int Max, + char *Id)); + +#undef _ARGS +*/ + +/**---------------------------------------------------------------------------- + Global Data Definitions and Declarations +----------------------------------------------------------------------------**/ +/* control knobs */ +make_int_const (NumCPLevels, 3, MakeNumCPLevels); +make_float_const (CPAnglePadLoose, 45.0, MakeCPAnglePadLoose); +make_float_const (CPAnglePadMedium, 20.0, MakeCPAnglePadMedium); +make_float_const (CPAnglePadTight, 10.0, MakeCPAnglePadTight); +make_float_const (CPEndPadLoose, 0.5, MakeCPEndPadLoose); +make_float_const (CPEndPadMedium, 0.5, MakeCPEndPadMedium); +make_float_const (CPEndPadTight, 0.5, MakeCPEndPadTight); +make_float_const (CPSidePadLoose, 2.5, MakeCPSidePadLoose); +make_float_const (CPSidePadMedium, 1.2, MakeCPSidePadMedium); +make_float_const (CPSidePadTight, 0.6, MakeCPSidePadTight); +make_float_const (PPAnglePad, 45.0, MakePPAnglePad); +make_float_const (PPEndPad, 0.5, MakePPEndPad); +make_float_const (PPSidePad, 2.5, MakePPSidePad); + +/* global display lists used to display proto and feature match information*/ +void *IntMatchWindow = NULL; +//extern int LearningDebugLevel; + +/**---------------------------------------------------------------------------- + Public Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +int AddIntClass(INT_TEMPLATES Templates, CLASS_ID ClassId, INT_CLASS Class) { +/* + ** Parameters: + ** Templates templates to add new class to + ** ClassId class id to associate new class with + ** Class class data structure to add to templates + ** Globals: none + ** Operation: This routine adds a new class structure to a set of + ** templates. + ** Return: The class index of the new class. + ** Exceptions: none + ** History: Mon Feb 11 11:52:08 1991, DSJ, Created. + */ + int Index; + int Pruner; + UINT32 *Word; + + assert (LegalClassId (ClassId)); + assert (UnusedClassIdIn (Templates, ClassId)); + + Index = NumClassesIn (Templates); + IndexForClassId (Templates, ClassId) = Index; + ClassIdForIndex (Templates, Index) = ClassId; + + NumClassesIn (Templates)++; + ClassForIndex (Templates, Index) = Class; + + if (NumClassesIn (Templates) > MaxNumClassesIn (Templates)) { + Pruner = NumClassPrunersIn (Templates); + NumClassPrunersIn (Templates)++; + Templates->ClassPruner[Pruner] = + (CLASS_PRUNER) Emalloc (sizeof (CLASS_PRUNER_STRUCT)); + + for (Word = (UINT32 *) (Templates->ClassPruner[Pruner]); + Word < (UINT32 *) (Templates->ClassPruner[Pruner]) + WERDS_PER_CP; + *Word++ = 0); + } + + return (Index); + +} /* AddIntClass */ + + +/*---------------------------------------------------------------------------*/ +int AddIntConfig(INT_CLASS Class) { +/* + ** Parameters: + ** Class class to add new configuration to + ** Globals: none + ** Operation: This routine returns the index of the next free config + ** in Class. + ** Return: Index of next free config. + ** Exceptions: none + ** History: Mon Feb 11 14:44:40 1991, DSJ, Created. + */ + int Index; + + assert (NumIntConfigsIn (Class) < MAX_NUM_CONFIGS); + + Index = NumIntConfigsIn (Class); + NumIntConfigsIn (Class)++; + LengthForConfigId (Class, Index) = 0; + return (Index); +} /* AddIntConfig */ + + +/*---------------------------------------------------------------------------*/ +int AddIntProto(INT_CLASS Class) { +/* + ** Parameters: + ** Class class to add new proto to + ** Globals: none + ** Operation: This routine allocates the next free proto in Class and + ** returns its index. + ** Return: Proto index of new proto. + ** Exceptions: none + ** History: Mon Feb 11 13:26:41 1991, DSJ, Created. + */ + int Index; + int ProtoSetId; + PROTO_SET ProtoSet; + INT_PROTO Proto; + register UINT32 *Word; + + if (NumIntProtosIn (Class) >= MAX_NUM_PROTOS) + return (NO_PROTO); + + Index = NumIntProtosIn (Class); + NumIntProtosIn (Class)++; + + if (NumIntProtosIn (Class) > MaxNumIntProtosIn (Class)) { + ProtoSetId = NumProtoSetsIn (Class); + NumProtoSetsIn (Class)++; + + ProtoSet = (PROTO_SET) Emalloc (sizeof (PROTO_SET_STRUCT)); + ProtoSetIn (Class, ProtoSetId) = ProtoSet; + for (Word = (UINT32 *) (ProtoPrunerFor (ProtoSet)); + Word < (UINT32 *) (ProtoPrunerFor (ProtoSet)) + WERDS_PER_PP; + *Word++ = 0); + + /* reallocate space for the proto lengths and install in class */ + Class->ProtoLengths = (UINT8 *) Erealloc (Class->ProtoLengths, + MaxNumIntProtosIn (Class) * + sizeof (UINT8)); + } + + /* initialize proto so its length is zero and it isn't in any configs */ + LengthForProtoId (Class, Index) = 0; + Proto = ProtoForProtoId (Class, Index); + for (Word = Proto->Configs; + Word < Proto->Configs + WERDS_PER_CONFIG_VEC; *Word++ = 0); + + return (Index); + +} /* AddIntProto */ + + +/*---------------------------------------------------------------------------*/ +void +AddProtoToClassPruner (PROTO Proto, CLASS_ID ClassId, INT_TEMPLATES Templates) +/* + ** Parameters: + ** Proto floating-pt proto to add to class pruner + ** ClassId class id corresponding to Proto + ** Templates set of templates containing class pruner + ** Globals: + ** NumCPLevels number of levels used in the class pruner + ** Operation: This routine adds Proto to the class pruning tables + ** for the specified class in Templates. + ** Return: none + ** Exceptions: none + ** History: Wed Feb 13 08:49:54 1991, DSJ, Created. + */ +#define MAX_LEVEL 2 +{ + CLASS_PRUNER Pruner; + UINT32 ClassMask; + UINT32 ClassCount; + CLASS_INDEX ClassIndex; + UINT32 WordIndex; + int Level; + FLOAT32 EndPad, SidePad, AnglePad; + TABLE_FILLER TableFiller; + FILL_SPEC FillSpec; + + ClassIndex = IndexForClassId (Templates, ClassId); + Pruner = CPrunerFor (Templates, ClassIndex); + WordIndex = CPrunerWordIndexFor (ClassIndex); + ClassMask = CPrunerMaskFor (MAX_LEVEL, ClassIndex); + + for (Level = NumCPLevels - 1; Level >= 0; Level--) { + GetCPPadsForLevel(Level, &EndPad, &SidePad, &AnglePad); + ClassCount = CPrunerMaskFor (Level, ClassIndex); + InitTableFiller(EndPad, SidePad, AnglePad, Proto, &TableFiller); + + while (!FillerDone (&TableFiller)) { + GetNextFill(&TableFiller, &FillSpec); + DoFill(&FillSpec, Pruner, ClassMask, ClassCount, WordIndex); + } + } +} /* AddProtoToClassPruner */ + + +/*---------------------------------------------------------------------------*/ +void AddProtoToProtoPruner(PROTO Proto, int ProtoId, INT_CLASS Class) { +/* + ** Parameters: + ** Proto floating-pt proto to be added to proto pruner + ** ProtoId id of proto + ** Class integer class that contains desired proto pruner + ** Globals: none + ** Operation: This routine updates the proto pruner lookup tables + ** for Class to include a new proto identified by ProtoId + ** and described by Proto. + ** Return: none + ** Exceptions: none + ** History: Fri Feb 8 13:07:19 1991, DSJ, Created. + */ + FLOAT32 Angle, X, Y, Length; + FLOAT32 Pad; + int Index; + PROTO_SET ProtoSet; + + if (ProtoId >= NumIntProtosIn (Class)) + cprintf ("AddProtoToProtoPruner:assert failed: %d < %d", + ProtoId, NumIntProtosIn (Class)); + assert (ProtoId < NumIntProtosIn (Class)); + + Index = IndexForProto (ProtoId); + ProtoSet = ProtoSetIn (Class, SetForProto (ProtoId)); + + Angle = ProtoAngle (Proto); + FillPPCircularBits (ProtoSet->ProtoPruner[PRUNER_ANGLE], Index, + Angle + ANGLE_SHIFT, PPAnglePad / 360.0); + + Angle *= 2.0 * PI; + Length = ProtoLength (Proto); + + X = ProtoX (Proto) + X_SHIFT; + Pad = max (fabs (cos (Angle)) * (Length / 2.0 + + PPEndPad * GetPicoFeatureLength ()), + fabs (sin (Angle)) * (PPSidePad * GetPicoFeatureLength ())); + + FillPPLinearBits (ProtoSet->ProtoPruner[PRUNER_X], Index, X, Pad); + + Y = ProtoY (Proto) + Y_SHIFT; + Pad = max (fabs (sin (Angle)) * (Length / 2.0 + + PPEndPad * GetPicoFeatureLength ()), + fabs (cos (Angle)) * (PPSidePad * GetPicoFeatureLength ())); + + FillPPLinearBits (ProtoSet->ProtoPruner[PRUNER_Y], Index, Y, Pad); + +} /* AddProtoToProtoPruner */ + + +/*---------------------------------------------------------------------------*/ +int BucketFor(FLOAT32 Param, FLOAT32 Offset, int NumBuckets) { +/* + ** Parameters: + ** Param parameter value to map into a bucket number + ** Offset amount to shift param before mapping it + ** NumBuckets number of buckets to map param into + ** Globals: none + ** Operation: This routine maps a parameter value into a bucket between + ** 0 and NumBuckets-1. Offset is added to the parameter + ** before mapping it. Values which map to buckets outside + ** the range are truncated to fit within the range. Mapping + ** is done by truncating rather than rounding. + ** Return: Bucket number corresponding to Param + Offset. + ** Exceptions: none + ** History: Thu Feb 14 13:24:33 1991, DSJ, Created. + */ + int Bucket; + + Bucket = (int) MapParam (Param, Offset, NumBuckets); + if (Bucket < 0) + Bucket = 0; + else if (Bucket >= NumBuckets) + Bucket = NumBuckets - 1; + return (Bucket); + +} /* BucketFor */ + + +/*---------------------------------------------------------------------------*/ +int CircBucketFor(FLOAT32 Param, FLOAT32 Offset, int NumBuckets) { +/* + ** Parameters: + ** Param parameter value to map into a circular bucket + ** Offset amount to shift param before mapping it + ** NumBuckets number of buckets to map param into + ** Globals: none + ** Operation: This routine maps a parameter value into a bucket between + ** 0 and NumBuckets-1. Offset is added to the parameter + ** before mapping it. Values which map to buckets outside + ** the range are wrapped to a new value in a circular fashion. + ** Mapping is done by truncating rather than rounding. + ** Return: Bucket number corresponding to Param + Offset. + ** Exceptions: none + ** History: Thu Feb 14 13:24:33 1991, DSJ, Created. + */ + int Bucket; + + Bucket = (int) MapParam (Param, Offset, NumBuckets); + if (Bucket < 0) + Bucket += NumBuckets; + else if (Bucket >= NumBuckets) + Bucket -= NumBuckets; + return (Bucket); + +} /* CircBucketFor */ + + +/*---------------------------------------------------------------------------*/ +#ifndef GRAPHICS_DISABLED +void UpdateMatchDisplay() { +/* + ** Parameters: none + ** Globals: + ** FeatureShapes display list for features + ** ProtoShapes display list for protos + ** Operation: This routine clears the global feature and proto + ** display lists. + ** Return: none + ** Exceptions: none + ** History: Thu Mar 21 15:40:19 1991, DSJ, Created. + */ + if (IntMatchWindow != NULL) + c_make_current(IntMatchWindow); +} /* ClearMatchDisplay */ +#endif + +/*---------------------------------------------------------------------------*/ +void ConvertConfig(BIT_VECTOR Config, int ConfigId, INT_CLASS Class) { +/* + ** Parameters: + ** Config config to be added to class + ** ConfigId id to be used for new config + ** Class class to add new config to + ** Globals: none + ** Operation: 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. + ** Return: none + ** Exceptions: none + ** History: Mon Feb 11 14:57:31 1991, DSJ, Created. + */ + int ProtoId; + INT_PROTO Proto; + int TotalLength; + + for (ProtoId = 0, TotalLength = 0; + ProtoId < NumIntProtosIn (Class); ProtoId++) + if (test_bit (Config, ProtoId)) { + Proto = ProtoForProtoId (Class, ProtoId); + SET_BIT (Proto->Configs, ConfigId); + TotalLength += LengthForProtoId (Class, ProtoId); + } + LengthForConfigId (Class, ConfigId) = TotalLength; +} /* ConvertConfig */ + + +/*---------------------------------------------------------------------------*/ +void ConvertProto(PROTO Proto, int ProtoId, INT_CLASS Class) { +/* + ** Parameters: + ** Proto floating-pt proto to be converted to integer format + ** ProtoId id of proto + ** Class integer class to add converted proto to + ** Globals: none + ** Operation: This routine converts Proto to integer format and + ** installs it as ProtoId in Class. + ** Return: none + ** Exceptions: none + ** History: Fri Feb 8 11:22:43 1991, DSJ, Created. + */ + INT_PROTO P; + FLOAT32 Param; + + assert (ProtoId < NumIntProtosIn (Class)); + + P = ProtoForProtoId (Class, ProtoId); + + Param = CoefficientA (Proto) * 128; + P->A = TruncateParam (Param, -128, 127, NULL); + + Param = -CoefficientB (Proto) * 256; + P->B = TruncateParam (Param, 0, 255, NULL); + + Param = CoefficientC (Proto) * 128; + P->C = TruncateParam (Param, -128, 127, NULL); + + Param = ProtoAngle (Proto) * 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 = (ProtoLength (Proto) / GetPicoFeatureLength ()) + 0.5; + LengthForProtoId (Class, ProtoId) = TruncateParam (Param, 1, 255, NULL); + if (LearningDebugLevel >= 2) + cprintf ("Converted ffeat to (A=%d,B=%d,C=%d,L=%d)", + P->A, P->B, P->C, LengthForProtoId (Class, ProtoId)); +} /* ConvertProto */ + + +/*---------------------------------------------------------------------------*/ +INT_TEMPLATES CreateIntTemplates(CLASSES FloatProtos) { +/* + ** Parameters: + ** FloatProtos prototypes in old floating pt format + ** Globals: none + ** Operation: This routine converts from the old floating point format + ** to the new integer format. + ** Return: New set of training templates in integer format. + ** Exceptions: none + ** History: Thu Feb 7 14:40:42 1991, DSJ, Created. + */ + INT_TEMPLATES IntTemplates; + CLASS_TYPE FClass; + INT_CLASS IClass; + int ClassId; + int ProtoId; + int ConfigId; + + IntTemplates = NewIntTemplates (); + + for (ClassId = 0; ClassId < NUMBER_OF_CLASSES; ClassId++) { + FClass = &(FloatProtos[ClassId]); + if (NumProtosIn (FClass) > 0) { + assert (UnusedClassIdIn (IntTemplates, ClassId)); + + IClass = NewIntClass (NumProtosIn (FClass), NumConfigsIn (FClass)); + AddIntClass(IntTemplates, ClassId, IClass); + + for (ProtoId = 0; ProtoId < NumProtosIn (FClass); ProtoId++) { + AddIntProto(IClass); + ConvertProto (ProtoIn (FClass, ProtoId), ProtoId, IClass); + AddProtoToProtoPruner (ProtoIn (FClass, ProtoId), ProtoId, + IClass); + AddProtoToClassPruner (ProtoIn (FClass, ProtoId), ClassId, + IntTemplates); + } + + for (ConfigId = 0; ConfigId < NumConfigsIn (FClass); ConfigId++) { + AddIntConfig(IClass); + ConvertConfig (ConfigIn (FClass, ConfigId), ConfigId, IClass); + } + } + } + return (IntTemplates); +} /* CreateIntTemplates */ + + +/*---------------------------------------------------------------------------*/ +#ifndef GRAPHICS_DISABLED +void DisplayIntFeature(INT_FEATURE Feature, FLOAT32 Evidence) { +/* + ** Parameters: + ** Feature pico-feature to be displayed + ** Evidence best evidence for this feature (0-1) + ** Globals: + ** FeatureShapes global display list for features + ** Operation: This routine renders the specified feature into a + ** global display list. + ** Return: none + ** Exceptions: none + ** History: Thu Mar 21 14:45:04 1991, DSJ, Created. + */ + C_COL Color; + + Color = GetMatchColorFor (Evidence); + RenderIntFeature(IntMatchWindow, Feature, Color); +} /* DisplayIntFeature */ + + +/*---------------------------------------------------------------------------*/ +void DisplayIntProto(INT_CLASS Class, PROTO_ID ProtoId, FLOAT32 Evidence) { +/* + ** Parameters: + ** Class class to take proto from + ** ProtoId id of proto in Class to be displayed + ** Evidence total evidence for proto (0-1) + ** Globals: + ** ProtoShapes global display list for protos + ** Operation: This routine renders the specified proto into a + ** global display list. + ** Return: none + ** Exceptions: none + ** History: Thu Mar 21 14:45:04 1991, DSJ, Created. + */ + C_COL Color; + + Color = GetMatchColorFor (Evidence); + RenderIntProto(IntMatchWindow, Class, ProtoId, Color); + +} /* DisplayIntProto */ +#endif + +/*---------------------------------------------------------------------------*/ +void InitIntProtoVars() { +/* + ** Parameters: none + ** Globals: none + ** Operation: Initialize the control variables for the integer proto + ** routines. + ** Return: none + ** Exceptions: none + ** History: Tue Feb 12 08:04:34 1991, DSJ, Created. + */ + MakeNumCPLevels(); + MakeCPAnglePadLoose(); + MakeCPAnglePadMedium(); + MakeCPAnglePadTight(); + MakeCPEndPadLoose(); + MakeCPEndPadMedium(); + MakeCPEndPadTight(); + MakeCPSidePadLoose(); + MakeCPSidePadMedium(); + MakeCPSidePadTight(); + MakePPAnglePad(); + MakePPEndPad(); + MakePPSidePad(); +} /* InitIntProtoVars */ + + +/*---------------------------------------------------------------------------*/ +INT_CLASS NewIntClass(int MaxNumProtos, int MaxNumConfigs) { +/* + ** Parameters: + ** MaxNumProtos number of protos to allocate space for + ** MaxNumConfigs number of configs to allocate space for + ** Globals: none + ** Operation: 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. + ** Return: New class created. + ** Exceptions: none + ** History: Fri Feb 8 10:51:23 1991, DSJ, Created. + */ + INT_CLASS Class; + PROTO_SET ProtoSet; + int i; + register UINT32 *Word; + + assert (MaxNumConfigs <= MAX_NUM_CONFIGS); + + Class = (INT_CLASS) Emalloc (sizeof (INT_CLASS_STRUCT)); + NumProtoSetsIn (Class) = ((MaxNumProtos + PROTOS_PER_PROTO_SET - 1) / + PROTOS_PER_PROTO_SET); + + assert (NumProtoSetsIn (Class) <= MAX_NUM_PROTO_SETS); + + NumIntProtosIn (Class) = 0; + NumIntConfigsIn (Class) = 0; + + for (i = 0; i < NumProtoSetsIn (Class); i++) { + /* allocate space for a proto set, install in class, and initialize */ + ProtoSet = (PROTO_SET) Emalloc (sizeof (PROTO_SET_STRUCT)); + ProtoSetIn (Class, i) = ProtoSet; + for (Word = (UINT32 *) (ProtoPrunerFor (ProtoSet)); + Word < (UINT32 *) (ProtoPrunerFor (ProtoSet)) + WERDS_PER_PP; + *Word++ = 0); + + /* allocate space for the proto lengths and install in class */ + } + Class->ProtoLengths = (UINT8 *) Emalloc (MaxNumIntProtosIn (Class) * + sizeof (UINT8)); + + return (Class); + +} /* NewIntClass */ + + +/*-------------------------------------------------------------------------*/ +void free_int_class( /*class to free */ + INT_CLASS int_class) { + int i; + + for (i = 0; i < NumProtoSetsIn (int_class); i++) { + Efree (ProtoSetIn (int_class, i)); + } + Efree (int_class->ProtoLengths); + Efree(int_class); +} + + +/*---------------------------------------------------------------------------*/ +INT_TEMPLATES NewIntTemplates() { +/* + ** Parameters: none + ** Globals: none + ** Operation: This routine allocates a new set of integer templates + ** initialized to hold 0 classes. + ** Return: The integer templates created. + ** Exceptions: none + ** History: Fri Feb 8 08:38:51 1991, DSJ, Created. + */ + INT_TEMPLATES T; + int i; + + T = (INT_TEMPLATES) Emalloc (sizeof (INT_TEMPLATES_STRUCT)); + NumClassesIn (T) = 0; + NumClassPrunersIn (T) = 0; + + /* initialize mapping tables */ + for (i = 0; i <= MAX_CLASS_ID; i++) + IndexForClassId (T, i) = ILLEGAL_CLASS; + for (i = 0; i < MAX_NUM_CLASSES; i++) + ClassIdForIndex (T, i) = NO_CLASS; + + return (T); + +} /* NewIntTemplates */ + + +/*---------------------------------------------------------------------------*/ +void free_int_templates(INT_TEMPLATES templates) { + int i; + + for (i = 0; i < NumClassesIn (templates); i++) + free_int_class (ClassForIndex (templates, i)); + for (i = 0; i < NumClassPrunersIn (templates); i++) + Efree (templates->ClassPruner[i]); + Efree(templates); +} + + +/*---------------------------------------------------------------------------*/ +INT_TEMPLATES ReadIntTemplates(FILE *File, BOOL8 swap) { +/* + ** Parameters: + ** File open file to read templates from + ** Globals: none + ** Operation: This routine reads a set of integer templates from + ** File. File must already be open and must be in the + ** correct binary format. + ** Return: Pointer to integer templates read from File. + ** Exceptions: none + ** History: Wed Feb 27 11:48:46 1991, DSJ, Created. + */ + int i, j, x, y, z; + int nread; + INT_TEMPLATES Templates; + CLASS_PRUNER Pruner; + INT_CLASS Class; + UINT8 *Lengths; + PROTO_SET ProtoSet; + + /* first read the high level template struct */ + Templates = NewIntTemplates (); + // Read Templates in parts for 64 bit compatibility. + if (fread(&Templates->NumClasses, sizeof(int), 1, File) != 1 || + fread(&Templates->NumClassPruners, sizeof(int), 1, File) != 1) + cprintf ("Bad read of inttemp!\n"); + for (i = 0; i <= MAX_CLASS_ID; ++i) { + if (fread(&Templates->IndexFor[i], sizeof(CLASS_INDEX), 1, File) != 1) + cprintf("Bad read of inttemp!\n"); + } + for (i = 0; i < MAX_NUM_CLASSES; ++i) { + if (fread(&Templates->ClassIdFor[i], sizeof(CLASS_ID), 1, File) != 1) + cprintf("Bad read of inttemp!\n"); + } + for (i = 0; i < MAX_NUM_CLASSES + MAX_NUM_CLASS_PRUNERS; ++i) { + int junk; + if (fread(&junk, sizeof(junk), 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); + for (i = 0; i < MAX_CLASS_ID + 1; i++) + reverse16 (&Templates->IndexFor[i]); + } + + /* then read in the class pruners */ + for (i = 0; i < NumClassPrunersIn (Templates); i++) { + Pruner = (CLASS_PRUNER) Emalloc (sizeof (CLASS_PRUNER_STRUCT)); + if ((nread = + fread ((char *) Pruner, 1, sizeof (CLASS_PRUNER_STRUCT), + File)) != sizeof (CLASS_PRUNER_STRUCT)) + cprintf ("Bad read of inttemp!\n"); + if (swap) { + for (j = 0; j < NUM_CP_BUCKETS; j++) { + for (x = 0; x < NUM_CP_BUCKETS; x++) { + for (y = 0; y < NUM_CP_BUCKETS; y++) { + for (z = 0; z < WERDS_PER_CP_VECTOR; z++) { + reverse32 (&Pruner[j][x][y][z]); + } + } + } + } + } + Templates->ClassPruner[i] = Pruner; + } + + /* then read in each class */ + for (i = 0; i < NumClassesIn (Templates); 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"); + for (j = 0; j <= MAX_NUM_PROTO_SETS; ++j) { + int junk; + if (fread(&junk, sizeof(junk), 1, File) != 1) + cprintf ("Bad read of inttemp!\n"); + } + for (j = 0; j < MAX_NUM_CONFIGS; ++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 < MAX_NUM_CONFIGS; j++) + reverse16 (&Class->ConfigLengths[j]); + } + ClassForIndex (Templates, i) = Class; + + /* then read in the proto lengths */ + 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 < NumProtoSetsIn (Class); j++) { + ProtoSet = (PROTO_SET) Emalloc (sizeof (PROTO_SET_STRUCT)); + 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 < WERDS_PER_CONFIG_VEC; y++) + reverse32 (&ProtoSet->Protos[x].Configs[y]); + } + ProtoSetIn (Class, j) = ProtoSet; + } + } + return (Templates); +} /* ReadIntTemplates */ + + +/*---------------------------------------------------------------------------*/ +#ifndef GRAPHICS_DISABLED +void ShowMatchDisplay() { +/* + ** Parameters: none + ** Globals: + ** FeatureShapes display list containing feature matches + ** ProtoShapes display list containing proto matches + ** Operation: This routine sends the shapes in the global display + ** lists to the match debugger window. + ** Return: none + ** Exceptions: none + ** History: Thu Mar 21 15:47:33 1991, DSJ, Created. + */ + void *window; + /* Size of drawable */ + if (IntMatchWindow == NULL) { + IntMatchWindow = c_create_window ("IntMatchWindow", 50, 200, + 520, 520, + -130.0, 130.0, -130.0, 130.0); + } + else + c_clear_window(IntMatchWindow); + + window = IntMatchWindow; + c_line_color_index(window, Grey); + /* Default size of drawing */ + if (NormMethod == baseline) { + c_move (window, -1000.0, INT_BASELINE); + c_draw (window, 1000.0, INT_BASELINE); + c_move (window, -1000.0, INT_DESCENDER); + c_draw (window, 1000.0, INT_DESCENDER); + c_move (window, -1000.0, INT_XHEIGHT); + c_draw (window, 1000.0, INT_XHEIGHT); + c_move (window, -1000.0, INT_CAPHEIGHT); + c_draw (window, 1000.0, INT_CAPHEIGHT); + c_move (window, INT_MIN_X, -1000.0); + c_draw (window, INT_MIN_X, 1000.0); + c_move (window, INT_MAX_X, -1000.0); + c_draw (window, INT_MAX_X, 1000.0); + } + else { + c_move (window, INT_XCENTER - INT_XRADIUS, INT_YCENTER - INT_YRADIUS); + c_draw (window, INT_XCENTER + INT_XRADIUS, INT_YCENTER - INT_YRADIUS); + c_move (window, INT_XCENTER - INT_XRADIUS, INT_YCENTER + INT_YRADIUS); + c_draw (window, INT_XCENTER + INT_XRADIUS, INT_YCENTER + INT_YRADIUS); + c_move (window, INT_XCENTER - INT_XRADIUS, INT_YCENTER - INT_YRADIUS); + c_draw (window, INT_XCENTER - INT_XRADIUS, INT_YCENTER + INT_YRADIUS); + c_move (window, INT_XCENTER + INT_XRADIUS, INT_YCENTER - INT_YRADIUS); + c_draw (window, INT_XCENTER + INT_XRADIUS, INT_YCENTER + INT_YRADIUS); + c_move(window, INT_MIN_X, INT_MIN_Y); + c_draw(window, INT_MIN_X, INT_MAX_Y); + c_move(window, INT_MIN_X, INT_MIN_Y); + c_draw(window, INT_MAX_X, INT_MIN_Y); + c_move(window, INT_MAX_X, INT_MAX_Y); + c_draw(window, INT_MIN_X, INT_MAX_Y); + c_move(window, INT_MAX_X, INT_MAX_Y); + c_draw(window, INT_MAX_X, INT_MIN_Y); + } +} /* ShowMatchDisplay */ +#endif + +/*---------------------------------------------------------------------------*/ +void WriteIntTemplates(FILE *File, INT_TEMPLATES Templates) { +/* + ** Parameters: + ** File open file to write templates to + ** Templates templates to save into File + ** Globals: none + ** Operation: This routine writes Templates to File. The format + ** is an efficient binary format. File must already be open + ** for writing. + ** Return: none + ** Exceptions: none + ** History: Wed Feb 27 11:48:46 1991, DSJ, Created. + */ + int i, j; + INT_CLASS Class; + + /* first write the high level template struct */ + fwrite ((char *) Templates, sizeof (INT_TEMPLATES_STRUCT), 1, File); + + /* then write out the class pruners */ + for (i = 0; i < NumClassPrunersIn (Templates); i++) + fwrite ((char *) (Templates->ClassPruner[i]), + sizeof (CLASS_PRUNER_STRUCT), 1, File); + + /* then write out each class */ + for (i = 0; i < NumClassesIn (Templates); i++) { + Class = ClassForIndex (Templates, i); + + /* first write out the high level struct for the class */ + fwrite ((char *) Class, sizeof (INT_CLASS_STRUCT), 1, File); + + /* then write out the proto lengths */ + fwrite ((char *) (Class->ProtoLengths), sizeof (UINT8), + MaxNumIntProtosIn (Class), File); + + /* then write out the proto sets */ + for (j = 0; j < NumProtoSetsIn (Class); j++) + fwrite ((char *) ProtoSetIn (Class, j), + sizeof (PROTO_SET_STRUCT), 1, File); + } +} /* WriteIntTemplates */ + + +/**---------------------------------------------------------------------------- + Private Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +FLOAT32 BucketStart(int Bucket, FLOAT32 Offset, int NumBuckets) { +/* + ** Parameters: + ** Bucket bucket whose start is to be computed + ** Offset offset used to map params to buckets + ** NumBuckets total number of buckets + ** Globals: none + ** Operation: 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. + ** Return: Param value corresponding to start position of Bucket. + ** Exceptions: none + ** History: Thu Feb 14 13:24:33 1991, DSJ, Created. + */ + return (((FLOAT32) Bucket / NumBuckets) - Offset); + +} /* BucketStart */ + + +/*---------------------------------------------------------------------------*/ +FLOAT32 BucketEnd(int Bucket, FLOAT32 Offset, int NumBuckets) { +/* + ** Parameters: + ** Bucket bucket whose end is to be computed + ** Offset offset used to map params to buckets + ** NumBuckets total number of buckets + ** Globals: none + ** Operation: 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. + ** Return: Param value corresponding to end position of Bucket. + ** Exceptions: none + ** History: Thu Feb 14 13:24:33 1991, DSJ, Created. + */ + return (((FLOAT32) (Bucket + 1) / NumBuckets) - Offset); +} /* BucketEnd */ + + +/*---------------------------------------------------------------------------*/ +void DoFill(FILL_SPEC *FillSpec, + CLASS_PRUNER Pruner, + register UINT32 ClassMask, + register UINT32 ClassCount, + register UINT32 WordIndex) { +/* + ** Parameters: + ** FillSpec specifies which bits to fill in pruner + ** Pruner class pruner to be filled + ** ClassMask indicates which bits to change in each word + ** ClassCount indicates what to change bits to + ** WordIndex indicates which word to change + ** Globals: none + ** Operation: This routine fills in the section of a class pruner + ** corresponding to a single x value for a single proto of + ** a class. + ** Return: none + ** Exceptions: none + ** History: Tue Feb 19 11:11:29 1991, DSJ, Created. + */ + register int X, Y, Angle; + register 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[X][Y][Angle][WordIndex]; + if (ClassCount > (OldWord & ClassMask)) { + OldWord &= ~ClassMask; + OldWord |= ClassCount; + Pruner[X][Y][Angle][WordIndex] = OldWord; + } + if (Angle == FillSpec->AngleEnd) + break; + } +} /* DoFill */ + + +/*---------------------------------------------------------------------------*/ +BOOL8 FillerDone(TABLE_FILLER *Filler) { +/* + ** Parameters: + ** Filler table filler to check if done + ** Globals: none + ** Operation: Return TRUE if the specified table filler is done, i.e. + ** if it has no more lines to fill. + ** Return: TRUE if no more lines to fill, FALSE otherwise. + ** Exceptions: none + ** History: Tue Feb 19 10:08:05 1991, DSJ, Created. + */ + FILL_SWITCH *Next; + + Next = &(Filler->Switch[Filler->NextSwitch]); + + if (Filler->X > Next->X && Next->Type == LastSwitch) + return (TRUE); + else + return (FALSE); + +} /* FillerDone */ + + +/*---------------------------------------------------------------------------*/ +void +FillPPCircularBits (UINT32 ParamTable[NUM_PP_BUCKETS][WERDS_PER_PP_VECTOR], +int Bit, FLOAT32 Center, FLOAT32 Spread) { +/* + ** Parameters: + ** ParamTable table of bit vectors, one per param bucket + ** Bit bit position in vectors to be filled + ** Center center of filled area + ** Spread spread of filled area + ** Globals: none + ** Operation: 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. + ** Return: none + ** Exceptions: none + ** History: Tue Oct 16 09:26:54 1990, DSJ, Created. + */ + 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 (LearningDebugLevel >= 2) + cprintf ("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 */ + + +/*---------------------------------------------------------------------------*/ +void +FillPPLinearBits (UINT32 ParamTable[NUM_PP_BUCKETS][WERDS_PER_PP_VECTOR], +int Bit, FLOAT32 Center, FLOAT32 Spread) { +/* + ** Parameters: + ** ParamTable table of bit vectors, one per param bucket + ** Bit bit number being filled + ** Center center of filled area + ** Spread spread of filled area + ** Globals: none + ** Operation: 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. + ** Return: none + ** Exceptions: none + ** History: Tue Oct 16 09:26:54 1990, DSJ, Created. + */ + 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 (LearningDebugLevel >= 2) + cprintf ("Linear fill from %d to %d", FirstBucket, LastBucket); + for (i = FirstBucket; i <= LastBucket; i++) + SET_BIT (ParamTable[i], Bit); + +} /* FillPPLinearBits */ + + +/*---------------------------------------------------------------------------*/ +#ifndef GRAPHICS_DISABLED +CLASS_ID GetClassToDebug(const char *Prompt) { +/* + ** Parameters: + ** Prompt prompt to print while waiting for input from window + ** Globals: none + ** Operation: This routine prompts the user with Prompt and waits + ** for the user to enter something in the debug window. + ** Return: Character entered in the debug window. + ** Exceptions: none + ** History: Thu Mar 21 16:55:13 1991, DSJ, Created. + */ + return window_wait (IntMatchWindow); + +} /* GetClassToDebug */ +#endif + +/*---------------------------------------------------------------------------*/ +void GetCPPadsForLevel(int Level, + FLOAT32 *EndPad, + FLOAT32 *SidePad, + FLOAT32 *AnglePad) { +/* + ** Parameters: + ** Level "tightness" level to return pads for + ** EndPad place to put end pad for Level + ** SidePad place to put side pad for Level + ** AnglePad place to put angle pad for Level + ** Globals: none + ** Operation: 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. + ** Return: none (results are returned in EndPad, SidePad, and AnglePad. + ** Exceptions: none + ** History: Thu Feb 14 08:26:49 1991, DSJ, Created. + */ + switch (Level) { + case 0: + *EndPad = CPEndPadLoose * GetPicoFeatureLength (); + *SidePad = CPSidePadLoose * GetPicoFeatureLength (); + *AnglePad = CPAnglePadLoose / 360.0; + break; + + case 1: + *EndPad = CPEndPadMedium * GetPicoFeatureLength (); + *SidePad = CPSidePadMedium * GetPicoFeatureLength (); + *AnglePad = CPAnglePadMedium / 360.0; + break; + + case 2: + *EndPad = CPEndPadTight * GetPicoFeatureLength (); + *SidePad = CPSidePadTight * GetPicoFeatureLength (); + *AnglePad = CPAnglePadTight / 360.0; + break; + + default: + *EndPad = CPEndPadTight * GetPicoFeatureLength (); + *SidePad = CPSidePadTight * GetPicoFeatureLength (); + *AnglePad = CPAnglePadTight / 360.0; + break; + } + if (*AnglePad > 0.5) + *AnglePad = 0.5; + +} /* GetCPPadsForLevel */ + + +/*---------------------------------------------------------------------------*/ +C_COL GetMatchColorFor(FLOAT32 Evidence) { +/* + ** Parameters: + ** Evidence evidence value to return color for + ** Globals: none + ** Operation: + ** Return: Color which corresponds to specified Evidence value. + ** Exceptions: none + ** History: Thu Mar 21 15:24:52 1991, DSJ, Created. + */ + + assert (Evidence >= 0.0); + assert (Evidence <= 1.0); + + if (Evidence >= 0.90) + return White; + else if (Evidence >= 0.75) + return Green; + else if (Evidence >= 0.50) + return Red; + else + return Blue; +} /* GetMatchColorFor */ + + +/*---------------------------------------------------------------------------*/ +void GetNextFill(TABLE_FILLER *Filler, FILL_SPEC *Fill) { +/* + ** Parameters: + ** Filler filler to get next fill spec from + ** Fill place to put spec for next fill + ** Globals: none + ** Operation: 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. + ** Return: none (results are returned in Fill) + ** Exceptions: none + ** History: Tue Feb 19 10:17:42 1991, DSJ, Created. + */ + 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 */ + + +/*---------------------------------------------------------------------------*/ +void +InitTableFiller (FLOAT32 EndPad, +FLOAT32 SidePad, +FLOAT32 AnglePad, PROTO Proto, TABLE_FILLER * Filler) +/* + ** Parameters: + ** EndPad, SidePad, AnglePad padding to add to proto + ** Proto proto to create a filler for + ** Filler place to put table filler + ** Globals: none + ** Operation: This routine computes a data structure (Filler) + ** which can be used to fill in a rectangle surrounding + ** the specified Proto. + ** Return: none (results are returned in Filler) + ** Exceptions: none + ** History: Thu Feb 14 09:27:05 1991, DSJ, Created. + */ +#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 = ProtoAngle (Proto); + X = ProtoX (Proto); + Y = ProtoY (Proto); + HalfLength = ProtoLength (Proto) / 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 = BucketFor (X - HalfLength - EndPad, XS, NB); + Filler->YStart = BucketFor (Y - SidePad, YS, NB * 256); + Filler->YEnd = BucketFor (Y + SidePad, YS, NB * 256); + Filler->StartDelta = 0; + Filler->EndDelta = 0; + Filler->Switch[0].Type = LastSwitch; + Filler->Switch[0].X = BucketFor (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 = BucketFor (X - SidePad, XS, NB); + Filler->YStart = BucketFor (Y - HalfLength - EndPad, YS, NB * 256); + Filler->YEnd = BucketFor (Y + HalfLength + EndPad, YS, NB * 256); + Filler->StartDelta = 0; + Filler->EndDelta = 0; + Filler->Switch[0].Type = LastSwitch; + Filler->Switch[0].X = BucketFor (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 = (INT8) MapParam (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 = (INT16) MapParam (Start.y - YAdjust, YS, NB * 256); + YAdjust = XAdjust * Sin / Cos; + Filler->YEnd = (INT16) MapParam (Start.y + YAdjust, YS, NB * 256); + + Filler->Switch[S1].Type = StartSwitch; + Filler->Switch[S1].X = (INT8) MapParam (Switch1.x, XS, NB); + Filler->Switch[S1].Y = (INT8) MapParam (Switch1.y, YS, NB); + XAdjust = Switch1.x - BucketStart (Filler->Switch[S1].X, XS, NB); + YAdjust = XAdjust * Sin / Cos; + Filler->Switch[S1].YInit = + (INT16) MapParam (Switch1.y - YAdjust, YS, NB * 256); + Filler->Switch[S1].Delta = Filler->EndDelta; + + Filler->Switch[S2].Type = EndSwitch; + Filler->Switch[S2].X = (INT8) MapParam (Switch2.x, XS, NB); + Filler->Switch[S2].Y = (INT8) MapParam (Switch2.y, YS, NB); + XAdjust = Switch2.x - BucketStart (Filler->Switch[S2].X, XS, NB); + YAdjust = XAdjust * Cos / Sin; + Filler->Switch[S2].YInit = + (INT16) MapParam (Switch2.y + YAdjust, YS, NB * 256); + Filler->Switch[S2].Delta = Filler->StartDelta; + + Filler->Switch[2].Type = LastSwitch; + Filler->Switch[2].X = (INT8) MapParam (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 = (INT8) MapParam (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 = (INT16) MapParam (Start.y - YAdjust, YS, NB * 256); + YAdjust = XAdjust * Cos / Sin; + Filler->YEnd = (INT16) MapParam (Start.y + YAdjust, YS, NB * 256); + + Filler->Switch[S1].Type = EndSwitch; + Filler->Switch[S1].X = (INT8) MapParam (Switch1.x, XS, NB); + Filler->Switch[S1].Y = (INT8) MapParam (Switch1.y, YS, NB); + XAdjust = Switch1.x - BucketStart (Filler->Switch[S1].X, XS, NB); + YAdjust = XAdjust * Sin / Cos; + Filler->Switch[S1].YInit = + (INT16) MapParam (Switch1.y + YAdjust, YS, NB * 256); + Filler->Switch[S1].Delta = Filler->StartDelta; + + Filler->Switch[S2].Type = StartSwitch; + Filler->Switch[S2].X = (INT8) MapParam (Switch2.x, XS, NB); + Filler->Switch[S2].Y = (INT8) MapParam (Switch2.y, YS, NB); + XAdjust = Switch2.x - BucketStart (Filler->Switch[S2].X, XS, NB); + YAdjust = XAdjust * Cos / Sin; + Filler->Switch[S2].YInit = + (INT16) MapParam (Switch2.y - YAdjust, YS, NB * 256); + Filler->Switch[S2].Delta = Filler->EndDelta; + + Filler->Switch[2].Type = LastSwitch; + Filler->Switch[2].X = (INT8) MapParam (End.x, XS, NB); + } + } +} /* InitTableFiller */ + + +/*---------------------------------------------------------------------------*/ +#ifndef GRAPHICS_DISABLED +void RenderIntFeature(void *window, INT_FEATURE Feature, C_COL Color) { +/* + ** Parameters: + ** ShapeList shape list to add feature rendering to + ** Feature feature to be rendered + ** Color color to use for feature rendering + ** Globals: none + ** Operation: This routine renders the specified feature into ShapeList. + ** Return: New shape list with rendering of Feature added. + ** Exceptions: none + ** History: Thu Mar 21 14:57:41 1991, DSJ, Created. + */ + FLOAT32 X, Y, Dx, Dy, Length; + + c_line_color_index(window, Color); + assert (Feature != NULL); + assert (Color != 0); + + X = Feature->X - DISPLAY_OFFSET; + Y = Feature->Y - DISPLAY_OFFSET; + Length = GetPicoFeatureLength () * 0.7 * INT_CHAR_NORM_RANGE; + Dx = (Length / 2.0) * cos ((Feature->Theta / 256.0) * 2.0 * PI); + Dy = (Length / 2.0) * sin ((Feature->Theta / 256.0) * 2.0 * PI); + + c_move (window, X - Dx, Y - Dy); + c_draw (window, X + Dx, Y + Dy); + c_move (window, X - Dx - Dy * DOUBLE_OFFSET, Y - Dy + Dx * DOUBLE_OFFSET); + c_draw (window, X + Dx - Dy * DOUBLE_OFFSET, Y + Dy + Dx * DOUBLE_OFFSET); +} /* RenderIntFeature */ + + +/*---------------------------------------------------------------------------*/ +void RenderIntProto(void *window, + INT_CLASS Class, + PROTO_ID ProtoId, + C_COL Color) { +/* + ** Parameters: + ** ShapeList shape list to append proto rendering onto + ** Class class that proto is contained in + ** ProtoId id of proto to be rendered + ** Color color to render proto in + ** Globals: none + ** Operation: This routine extracts the parameters of the specified + ** proto from the class description and adds a rendering of + ** the proto onto the ShapeList. + ** Return: New shape list with a rendering of one proto added. + ** Exceptions: none + ** History: Thu Mar 21 10:21:09 1991, DSJ, Created. + */ + 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 < NumIntProtosIn (Class)); + assert (Color != 0); + c_line_color_index(window, Color); + + ProtoSet = ProtoSetIn (Class, SetForProto (ProtoId)); + ProtoSetIndex = IndexForProto (ProtoId); + Proto = &(ProtoSet->Protos[ProtoSetIndex]); + Length = (LengthForProtoId (Class, 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]) + if (Bucket < Xmin) + Xmin = Bucket; + else if (Bucket > Xmax) + Xmax = Bucket; + + if (ProtoMask & ProtoSet->ProtoPruner[PRUNER_Y][Bucket][ProtoWordIndex]) + if (Bucket < Ymin) + Ymin = Bucket; + else if (Bucket > Ymax) + Ymax = Bucket; + } + X = (Xmin + Xmax + 1) / 2.0 * PROTO_PRUNER_SCALE - DISPLAY_OFFSET; + Y = (Ymin + Ymax + 1) / 2.0 * PROTO_PRUNER_SCALE - DISPLAY_OFFSET; + Dx = (Length / 2.0) * cos ((Proto->Angle / 256.0) * 2.0 * PI); + Dy = (Length / 2.0) * sin ((Proto->Angle / 256.0) * 2.0 * PI); + + c_move (window, X - Dx, Y - Dy); + c_draw (window, X + Dx, Y + Dy); +} /* RenderIntProto */ +#endif + +/*---------------------------------------------------------------------------*/ +int TruncateParam(FLOAT32 Param, int Min, int Max, char *Id) { +/* + ** Parameters: + ** Param parameter value to be truncated + ** Min, Max parameter limits (inclusive) + ** Id string id of parameter for error messages + ** Globals: none + ** Operation: 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. + ** Return: Truncated parameter. + ** Exceptions: none + ** History: Fri Feb 8 11:54:28 1991, DSJ, Created. + */ + 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 (int) floor (Param); + +} /* TruncateParam */ diff --git a/classify/intproto.h b/classify/intproto.h new file mode 100644 index 0000000000..1d8e7516e6 --- /dev/null +++ b/classify/intproto.h @@ -0,0 +1,335 @@ +/****************************************************************************** + ** 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 "matchdefs.h" +#include "protos.h" +#include "callcpp.h" + +/* 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 (8 * sizeof (UINT32)) +#define MAX_NUM_CONFIGS 32 +#define MAX_NUM_PROTOS 256 +#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 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) + +typedef UINT32 CLASS_PRUNER_STRUCT +[NUM_CP_BUCKETS][NUM_CP_BUCKETS][NUM_CP_BUCKETS][WERDS_PER_CP_VECTOR]; + +typedef +UINT32 (*CLASS_PRUNER)[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 struct +{ + UINT32 ProtoPruner[NUM_PP_PARAMS][NUM_PP_BUCKETS][WERDS_PER_PP_VECTOR]; + 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_CLASS_STRUCT, *INT_CLASS; + +typedef struct +{ + int NumClasses; + int NumClassPruners; + CLASS_TO_INDEX IndexFor; /*int16[256] */ + INDEX_TO_CLASS ClassIdFor; /*unit8[100 */ + INT_CLASS Class[MAX_NUM_CLASSES]; + CLASS_PRUNER ClassPruner[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 + +typedef struct +{ + UINT8 X; + UINT8 Y; + UINT8 Theta; + INT8 CP_misses; +} + + +INT_FEATURE_STRUCT; +typedef INT_FEATURE_STRUCT *INT_FEATURE; + +typedef INT_FEATURE_STRUCT INT_FEATURE_ARRAY[MAX_NUM_INT_FEATURES]; + +/**---------------------------------------------------------------------------- + Macros +----------------------------------------------------------------------------**/ +/* PROTO_SET access macros*/ +#define ProtoPrunerFor(S) (S->ProtoPruner) + +/* INT_CLASS access macros*/ +#define NumIntProtosIn(C) ((C)->NumProtos) +#define NumProtoSetsIn(C) ((C)->NumProtoSets) +#define MaxNumIntProtosIn(C) (NumProtoSetsIn (C) * PROTOS_PER_PROTO_SET) +#define NumIntConfigsIn(C) ((C)->NumConfigs) +#define ProtoSetIn(C,I) ((C)->ProtoSets[I]) +#define SetForProto(P) (P / PROTOS_PER_PROTO_SET) +#define IndexForProto(P) (P % PROTOS_PER_PROTO_SET) +//#define IllegalProto(C,P) (P >= MaxNumIntProtosIn (C)) +#define ProtoForProtoId(C,P) (&((ProtoSetIn (C, SetForProto (P)))-> \ + Protos [IndexForProto (P)])) +#define LengthForProtoId(C,P) ((C)->ProtoLengths[P]) +#define LengthForConfigId(C,c) ((C)->ConfigLengths[c]) +#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)) + +/* INT_TEMPLATE access macros*/ +#define NumClassesIn(T) ((T)->NumClasses) +#define NumClassPrunersIn(T) ((T)->NumClassPruners) +#define MaxNumClassesIn(T) (NumClassPrunersIn (T) * CLASSES_PER_CP) +#define ClassIdForIndex(T,I) ((T)->ClassIdFor[I]) +#define IndexForClassId(T,C) ((T)->IndexFor[C]) +#define LegalClassId(C) ((C) > 0 && (C) < MAX_CLASS_ID) +#define UnusedClassIdIn(T,C) (IndexForClassId (T,C) == ILLEGAL_CLASS) +#define ClassForIndex(T,I) ((T)->Class[I]) +#define ClassForClassId(T,C) (ClassForIndex (T, IndexForClassId (T, C))) +#define ClassPrunersFor(T) ((T)->ClassPruner) +#define CPrunerIdFor(I) ((I) / CLASSES_PER_CP) +#define CPrunerFor(T,I) ((T)->ClassPruner [CPrunerIdFor (I)]) +#define CPrunerWordIndexFor(I) (((I) % CLASSES_PER_CP) / CLASSES_PER_CP_WERD) +#define CPrunerBitIndexFor(I) (((I) % CLASSES_PER_CP) % CLASSES_PER_CP_WERD) +#define CPrunerMaskFor(L,I) (((L)+1) << CPrunerBitIndexFor (I) * 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 +----------------------------------------------------------------------------**/ +int 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); + +int BucketFor(FLOAT32 Param, FLOAT32 Offset, int NumBuckets); + +int CircBucketFor(FLOAT32 Param, FLOAT32 Offset, int NumBuckets); + +void UpdateMatchDisplay(); + +void ConvertConfig(BIT_VECTOR Config, int ConfigId, INT_CLASS Class); + +void ConvertProto(PROTO Proto, int ProtoId, INT_CLASS Class); + +INT_TEMPLATES CreateIntTemplates(CLASSES FloatProtos); + +void DisplayIntFeature(INT_FEATURE Feature, FLOAT32 Evidence); + +void DisplayIntProto(INT_CLASS Class, PROTO_ID ProtoId, FLOAT32 Evidence); + +void InitIntProtoVars(); + +INT_CLASS NewIntClass(int MaxNumProtos, int MaxNumConfigs); + +void free_int_class(INT_CLASS int_class); + +INT_TEMPLATES NewIntTemplates(); + +void free_int_templates(INT_TEMPLATES templates); + +INT_TEMPLATES ReadIntTemplates(FILE *File, BOOL8 swap); + +void ShowMatchDisplay(); + +CLASS_ID GetClassToDebug(const char *Prompt); + +void WriteIntTemplates(FILE *File, INT_TEMPLATES Templates); + +/* +#if defined(__STDC__) || defined(__cplusplus) +# define _ARGS(s) s +#else +# define _ARGS(s) () +#endif*/ + +/* intproto.c +int AddIntClass + _ARGS((INT_TEMPLATES Templates, + CLASS_ID ClassId, + INT_CLASS Class)); + +int AddIntConfig + _ARGS((INT_CLASS Class)); + +int AddIntProto + _ARGS((INT_CLASS Class)); + +void AddProtoToClassPruner + _ARGS((PROTO Proto, + CLASS_ID ClassId, + INT_TEMPLATES Templates)); + +void AddProtoToProtoPruner + _ARGS((PROTO Proto, + int ProtoId, + INT_CLASS Class)); + +int BucketFor + _ARGS((FLOAT32 Param, + FLOAT32 Offset, + int NumBuckets)); + +int CircBucketFor + _ARGS((FLOAT32 Param, + FLOAT32 Offset, + int NumBuckets)); + +void UpdateMatchDisplay + _ARGS((void)); + +void ConvertConfig + _ARGS((BIT_VECTOR Config, + int ConfigId, + INT_CLASS Class)); + +void ConvertProto + _ARGS((PROTO Proto, + int ProtoId, + INT_CLASS Class)); + +INT_TEMPLATES CreateIntTemplates + _ARGS((CLASSES FloatProtos)); + +void DisplayIntFeature + _ARGS((INT_FEATURE Feature, + FLOAT32 Evidence)); + +void DisplayIntProto + _ARGS((INT_CLASS Class, + PROTO_ID ProtoId, + FLOAT32 Evidence)); + +void InitIntProtoVars + _ARGS((void)); + +INT_CLASS NewIntClass + _ARGS((int MaxNumProtos, + int MaxNumConfigs)); + +INT_TEMPLATES NewIntTemplates + _ARGS((void)); + +INT_TEMPLATES ReadIntTemplates + _ARGS((FILE *File)); + +void ShowMatchDisplay + _ARGS((void)); + +void WriteIntTemplates + _ARGS((FILE *File, + INT_TEMPLATES Templates)); + +CLASS_ID GetClassToDebug + _ARGS((char *Prompt)); + +C_COL GetMatchColorFor + _ARGS((FLOAT32 Evidence)); + +#undef _ARGS +*/ +#endif diff --git a/classify/kdtree.cpp b/classify/kdtree.cpp new file mode 100644 index 0000000000..609fe81bde --- /dev/null +++ b/classify/kdtree.cpp @@ -0,0 +1,872 @@ +/****************************************************************************** + ** Filename: kdtree.c + ** 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 +#include + +#define Magnitude(X) ((X) < 0 ? -(X) : (X)) +#define MIN(A,B) ((A) < (B) ? (A) : (B)) +#define NodeFound(N,K,D) (( (N)->Key == (K) ) && ( (N)->Data == (D) )) + +/**---------------------------------------------------------------------------- + Global Data Definitions and Declarations +----------------------------------------------------------------------------**/ +#define MINSEARCH -MAX_FLOAT32 +#define MAXSEARCH MAX_FLOAT32 + +static int NumberOfNeighbors; +static INT16 N; /* number of dimensions in the kd tree */ + +static FLOAT32 *QueryPoint; +static int MaxNeighbors; +static FLOAT32 Radius; +static int Furthest; +static char **Neighbor; +static FLOAT32 *Distance; + +static int MaxDimension = 0; +static FLOAT32 *SBMin; +static FLOAT32 *SBMax; +static FLOAT32 *LBMin; +static FLOAT32 *LBMax; + +static PARAM_DESC *KeyDesc; + +static jmp_buf QuickExit; + +static void_proc WalkAction; + +/**---------------------------------------------------------------------------- + Public Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +KDTREE * +MakeKDTree (INT16 KeySize, PARAM_DESC KeyDesc[]) { +/* + ** Parameters: + ** KeySize # of dimensions in the K-D tree + ** KeyDesc array of params to describe key dimensions + ** Globals: + ** MaxDimension largest # of dimensions in any K-D tree + ** SBMin small search region box + ** SBMax + ** LBMin large search region box + ** LBMax + ** Key description of key dimensions + ** Operation: + ** This routine allocates and returns a new K-D tree data + ** structure. It also reallocates the small and large + ** search region boxes if they are not large enough to + ** accomodate the size of the new K-D tree. KeyDesc is + ** an array of key descriptors that indicate which dimensions + ** are circular and, if they are circular, what the range is. + ** Return: + ** Pointer to new K-D tree + ** Exceptions: + ** None + ** History: + ** 3/13/89, DSJ, Created. + */ + int i; + void *NewMemory; + KDTREE *KDTree; + + if (KeySize > MaxDimension) { + NewMemory = Emalloc (KeySize * 4 * sizeof (FLOAT32)); + if (MaxDimension > 0) { + memfree ((char *) SBMin); + memfree ((char *) SBMax); + memfree ((char *) LBMin); + memfree ((char *) LBMax); + } + SBMin = (FLOAT32 *) NewMemory; + SBMax = SBMin + KeySize; + LBMin = SBMax + KeySize; + LBMax = LBMin + KeySize; + } + + KDTree = + (KDTREE *) Emalloc (sizeof (KDTREE) + + (KeySize - 1) * sizeof (PARAM_DESC)); + for (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); +} /* MakeKDTree */ + + +/*---------------------------------------------------------------------------*/ +void KDStore(KDTREE *Tree, FLOAT32 *Key, void *Data) { +/* + ** Parameters: + ** Tree K-D tree in which data is to be stored + ** Key ptr to key by which data can be retrieved + ** Data ptr to data to be stored in the tree + ** Globals: + ** N dimension of the K-D tree + ** KeyDesc descriptions of tree dimensions + ** StoreCount debug variables for performance tests + ** StoreUniqueCount + ** StoreProbeCount + ** Operation: + ** This routine stores Data in the K-D tree specified by Tree + ** using Key as an access key. + ** Return: none + ** Exceptions: none + ** History: 3/10/89, DSJ, Created. + ** 7/13/89, DSJ, Changed return to void. + */ + int Level; + KDNODE *Node; + KDNODE **PtrToNode; + + N = Tree->KeySize; + KeyDesc = &(Tree->KeyDesc[0]); + PtrToNode = &(Tree->Root.Left); + Node = *PtrToNode; + Level = 0; + 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++; + if (Level >= N) + Level = 0; + Node = *PtrToNode; + } + + *PtrToNode = MakeKDNode (Key, (char *) Data, Level); +} /* KDStore */ + + +/*---------------------------------------------------------------------------*/ +void +KDDelete (KDTREE * Tree, FLOAT32 Key[], void *Data) { +/* + ** Parameters: + ** Tree K-D tree to delete node from + ** Key key of node to be deleted + ** Data data contents of node to be deleted + ** Globals: + ** N dimension of the K-D tree + ** KeyDesc description of each dimension + ** DeleteCount debug variables for performance tests + ** DeleteProbeCount + ** Operation: + ** 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 empty space left in the tree + ** is filled by pulling a leaf up from the bottom of one of + ** the subtrees of the node being deleted. The leaf node will + ** be pulled from left subtrees whenever possible (this was + ** an arbitrary decision). No attempt is made to pull the leaf + ** from the deepest subtree (to minimize length). The branch + ** point for the replacement node is changed to be the same as + ** the branch point of the deleted node. This keeps us from + ** having to rearrange the tree every time we delete a node. + ** Also, the LeftBranch and RightBranch numbers of the + ** replacement node are set to be the same as the deleted node. + ** The makes the delete easier and more efficient, but it may + ** make searches in the tree less efficient after many nodes are + ** deleted. If the node specified by Key and Data does not + ** exist in the tree, then nothing is done. + ** Return: none + ** None + ** Exceptions: none + ** None + ** History: 3/13/89, DSJ, Created. + ** 7/13/89, DSJ, Specify node indirectly by key and data. + */ + int Level; + KDNODE *Current; + KDNODE *Father; + KDNODE *Replacement; + KDNODE *FatherReplacement; + + /* initialize search at root of tree */ + N = Tree->KeySize; + KeyDesc = &(Tree->KeyDesc[0]); + Father = &(Tree->Root); + Current = Father->Left; + Level = 0; + + /* 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++; + if (Level >= N) + Level = 0; + } + + if (Current != NULL) { /* if node to be deleted was found */ + Replacement = Current; + FatherReplacement = Father; + + /* search for replacement node (a leaf under node to be deleted */ + while (TRUE) { + if (Replacement->Left != NULL) { + FatherReplacement = Replacement; + Replacement = Replacement->Left; + } + else if (Replacement->Right != NULL) { + FatherReplacement = Replacement; + Replacement = Replacement->Right; + } + else + break; + + Level++; + if (Level >= N) + Level = 0; + } + + /* compute level of replacement node's father */ + Level--; + if (Level < 0) + Level = N - 1; + + /* disconnect replacement node from it's father */ + if (FatherReplacement->Left == Replacement) { + FatherReplacement->Left = NULL; + FatherReplacement->LeftBranch = KeyDesc[Level].Min; + } + else { + FatherReplacement->Right = NULL; + FatherReplacement->RightBranch = KeyDesc[Level].Max; + } + + /* replace deleted node with replacement (unless they are the same) */ + if (Replacement != Current) { + Replacement->BranchPoint = Current->BranchPoint; + Replacement->LeftBranch = Current->LeftBranch; + Replacement->RightBranch = Current->RightBranch; + Replacement->Left = Current->Left; + Replacement->Right = Current->Right; + + if (Father->Left == Current) + Father->Left = Replacement; + else + Father->Right = Replacement; + } + FreeKDNode(Current); + } +} /* KDDelete */ + + +/*---------------------------------------------------------------------------*/ +int +KDNearestNeighborSearch (KDTREE * Tree, +FLOAT32 Query[], +int QuerySize, +FLOAT32 MaxDistance, +void *NBuffer, FLOAT32 DBuffer[]) { +/* + ** Parameters: + ** Tree ptr to K-D tree to be searched + ** Query ptr to query key (point in D-space) + ** QuerySize number of nearest neighbors to be found + ** MaxDistance all neighbors must be within this distance + ** NBuffer ptr to QuerySize buffer to hold nearest neighbors + ** DBuffer ptr to QuerySize buffer to hold distances + ** from nearest neighbor to query point + ** Globals: + ** NumberOfNeighbors # of neighbors found so far + ** N # of features in each key + ** KeyDesc description of tree dimensions + ** QueryPoint point in D-space to find neighbors of + ** MaxNeighbors maximum # of neighbors to find + ** Radius current distance of furthest neighbor + ** Furthest index of furthest neighbor + ** Neighbor buffer of current neighbors + ** Distance buffer of neighbor distances + ** SBMin lower extent of small search region + ** SBMax upper extent of small search region + ** LBMin lower extent of large search region + ** LBMax upper extent of large search region + ** QuickExit quick exit from recursive search + ** Operation: + ** 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. + ** Return: Number of nearest neighbors actually found + ** Exceptions: none + ** History: + ** 3/10/89, DSJ, Created. + ** 7/13/89, DSJ, Return contents of node instead of node itself. + */ + int i; + + NumberOfNeighbors = 0; + N = Tree->KeySize; + KeyDesc = &(Tree->KeyDesc[0]); + QueryPoint = Query; + MaxNeighbors = QuerySize; + Radius = MaxDistance; + Furthest = 0; + Neighbor = (char **) NBuffer; + Distance = DBuffer; + + for (i = 0; i < N; i++) { + SBMin[i] = KeyDesc[i].Min; + SBMax[i] = KeyDesc[i].Max; + LBMin[i] = KeyDesc[i].Min; + LBMax[i] = KeyDesc[i].Max; + } + + if (Tree->Root.Left != NULL) { + if (setjmp (QuickExit) == 0) + Search (0, Tree->Root.Left); + } + return (NumberOfNeighbors); +} /* KDNearestNeighborSearch */ + + +/*---------------------------------------------------------------------------*/ +void KDWalk(KDTREE *Tree, void_proc Action) { +/* + ** Parameters: + ** Tree ptr to K-D tree to be walked + ** Action ptr to function to be executed at each node + ** Globals: + ** WalkAction action to be performed at every node + ** Operation: + ** This routine stores the desired action in a global + ** variable and starts a recursive walk of Tree. The walk + ** is started at the root node. + ** Return: + ** None + ** Exceptions: + ** None + ** History: + ** 3/13/89, DSJ, Created. + */ + WalkAction = Action; + if (Tree->Root.Left != NULL) + Walk (Tree->Root.Left, 0); +} /* KDWalk */ + + +/*---------------------------------------------------------------------------*/ +void FreeKDTree(KDTREE *Tree) { +/* + ** Parameters: + ** Tree tree data structure to be released + ** Globals: none + ** Operation: + ** 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. + ** Return: none + ** Exceptions: none + ** History: + ** 5/26/89, DSJ, Created. + */ + FreeSubTree (Tree->Root.Left); + memfree(Tree); +} /* FreeKDTree */ + + +/**---------------------------------------------------------------------------- + Private Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +int +Equal (FLOAT32 Key1[], FLOAT32 Key2[]) { +/* + ** Parameters: + ** Key1,Key2 search keys to be compared for equality + ** Globals: + ** N number of parameters per key + ** Operation: + ** This routine returns TRUE if Key1 = Key2. + ** Return: + ** TRUE if Key1 = Key2, else FALSE. + ** Exceptions: + ** None + ** History: + ** 3/11/89, DSJ, Created. + */ + int i; + + for (i = N; i > 0; i--, Key1++, Key2++) + if (*Key1 != *Key2) + return (FALSE); + return (TRUE); +} /* Equal */ + + +/*---------------------------------------------------------------------------*/ +KDNODE * +MakeKDNode (FLOAT32 Key[], char *Data, int Index) { +/* + ** Parameters: + ** Key Access key for new node in KD tree + ** Data ptr to data to be stored in new node + ** Index index of Key to branch on + ** Globals: + ** KeyDesc descriptions of key dimensions + ** Operation: + ** 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. + ** Return: + ** pointer to new K-D tree node + ** Exceptions: + ** None + ** History: + ** 3/11/89, DSJ, Created. + */ + KDNODE *NewNode; + + NewNode = (KDNODE *) Emalloc (sizeof (KDNODE)); + + NewNode->Key = Key; + NewNode->Data = Data; + NewNode->BranchPoint = Key[Index]; + NewNode->LeftBranch = KeyDesc[Index].Min; + NewNode->RightBranch = KeyDesc[Index].Max; + NewNode->Left = NULL; + NewNode->Right = NULL; + + return (NewNode); +} /* MakeKDNode */ + + +/*---------------------------------------------------------------------------*/ +void FreeKDNode(KDNODE *Node) { +/* + ** Parameters: + ** Node ptr to node data structure to be freed + ** Globals: + ** None + ** Operation: + ** This routine frees up the memory allocated to Node. + ** Return: + ** None + ** Exceptions: + ** None + ** History: + ** 3/13/89, DSJ, Created. + */ + memfree ((char *) Node); +} /* FreeKDNode */ + + +/*---------------------------------------------------------------------------*/ +void Search(int Level, KDNODE *SubTree) { +/* + ** Parameters: + ** Level level in tree of sub-tree to be searched + ** SubTree sub-tree to be searched + ** Globals: + ** NumberOfNeighbors # of neighbors found so far + ** N # of features in each key + ** KeyDesc description of key dimensions + ** QueryPoint point in D-space to find neighbors of + ** MaxNeighbors maximum # of neighbors to find + ** Radius current distance of furthest neighbor + ** Furthest index of furthest neighbor + ** Neighbor buffer of current neighbors + ** Distance buffer of neighbor distances + ** SBMin lower extent of small search region + ** SBMax upper extent of small search region + ** LBMin lower extent of large search region + ** LBMax upper extent of large search region + ** QuickExit quick exit from recursive search + ** Operation: + ** This routine searches SubTree for those entries which are + ** possibly among the MaxNeighbors nearest neighbors of the + ** QueryPoint and places their data in the Neighbor buffer and + ** their distances from QueryPoint in the Distance buffer. + ** Return: none + ** Exceptions: none + ** History: + ** 3/11/89, DSJ, Created. + ** 7/13/89, DSJ, Save node contents, not node, in neighbor buffer + */ + FLOAT32 d; + FLOAT32 OldSBoxEdge; + FLOAT32 OldLBoxEdge; + + if (Level >= N) + Level = 0; + + d = ComputeDistance (N, KeyDesc, QueryPoint, SubTree->Key); + if (d < Radius) { + if (NumberOfNeighbors < MaxNeighbors) { + Neighbor[NumberOfNeighbors] = SubTree->Data; + Distance[NumberOfNeighbors] = d; + NumberOfNeighbors++; + if (NumberOfNeighbors == MaxNeighbors) + FindMaxDistance(); + } + else { + Neighbor[Furthest] = SubTree->Data; + Distance[Furthest] = d; + FindMaxDistance(); + } + } + if (QueryPoint[Level] < SubTree->BranchPoint) { + OldSBoxEdge = SBMax[Level]; + SBMax[Level] = SubTree->LeftBranch; + OldLBoxEdge = LBMax[Level]; + LBMax[Level] = SubTree->RightBranch; + if (SubTree->Left != NULL) + Search (Level + 1, SubTree->Left); + SBMax[Level] = OldSBoxEdge; + LBMax[Level] = OldLBoxEdge; + OldSBoxEdge = SBMin[Level]; + SBMin[Level] = SubTree->RightBranch; + OldLBoxEdge = LBMin[Level]; + LBMin[Level] = SubTree->LeftBranch; + if ((SubTree->Right != NULL) && QueryIntersectsSearch ()) + Search (Level + 1, SubTree->Right); + SBMin[Level] = OldSBoxEdge; + LBMin[Level] = OldLBoxEdge; + } + else { + OldSBoxEdge = SBMin[Level]; + SBMin[Level] = SubTree->RightBranch; + OldLBoxEdge = LBMin[Level]; + LBMin[Level] = SubTree->LeftBranch; + if (SubTree->Right != NULL) + Search (Level + 1, SubTree->Right); + SBMin[Level] = OldSBoxEdge; + LBMin[Level] = OldLBoxEdge; + OldSBoxEdge = SBMax[Level]; + SBMax[Level] = SubTree->LeftBranch; + OldLBoxEdge = LBMax[Level]; + LBMax[Level] = SubTree->RightBranch; + if ((SubTree->Left != NULL) && QueryIntersectsSearch ()) + Search (Level + 1, SubTree->Left); + SBMax[Level] = OldSBoxEdge; + LBMax[Level] = OldLBoxEdge; + } + if (QueryInSearch ()) + longjmp (QuickExit, 1); +} /* Search */ + + +/*---------------------------------------------------------------------------*/ +FLOAT32 +ComputeDistance (register int N, +register PARAM_DESC Dim[], +register FLOAT32 p1[], register FLOAT32 p2[]) { +/* + ** Parameters: + ** N number of dimensions in K-D space + ** Dim descriptions of each dimension + ** p1,p2 two different points in K-D space + ** Globals: + ** None + ** Operation: + ** This routine computes the euclidian distance + ** between p1 and p2 in K-D space (an N dimensional space). + ** Return: + ** Distance between p1 and p2. + ** Exceptions: + ** None + ** History: + ** 3/11/89, DSJ, Created. + */ + register FLOAT32 TotalDistance; + register FLOAT32 DimensionDistance; + FLOAT32 WrapDistance; + + TotalDistance = 0; + for (; N > 0; N--, p1++, p2++, Dim++) { + if (Dim->NonEssential) + continue; + + DimensionDistance = *p1 - *p2; + + /* if this dimension is circular - check wraparound distance */ + if (Dim->Circular) { + DimensionDistance = Magnitude (DimensionDistance); + WrapDistance = Dim->Max - Dim->Min - DimensionDistance; + DimensionDistance = MIN (DimensionDistance, WrapDistance); + } + + TotalDistance += DimensionDistance * DimensionDistance; + } + return ((FLOAT32) sqrt ((FLOAT64) TotalDistance)); +} /* ComputeDistance */ + + +/*---------------------------------------------------------------------------*/ +void FindMaxDistance() { +/* + ** Parameters: + ** None + ** Globals: + ** MaxNeighbors maximum # of neighbors to find + ** Radius current distance of furthest neighbor + ** Furthest index of furthest neighbor + ** Distance buffer of neighbor distances + ** Operation: + ** This routine searches the Distance buffer for the maximum + ** distance, places this distance in Radius, and places the + ** index of this distance in Furthest. + ** Return: + ** None + ** Exceptions: + ** None + ** History: + ** 3/11/89, DSJ, Created. + */ + int i; + + Radius = Distance[Furthest]; + for (i = 0; i < MaxNeighbors; i++) { + if (Distance[i] > Radius) { + Radius = Distance[i]; + Furthest = i; + } + } +} /* FindMaxDistance */ + + +/*---------------------------------------------------------------------------*/ +int QueryIntersectsSearch() { +/* + ** Parameters: + ** None + ** Globals: + ** N # of features in each key + ** KeyDesc descriptions of each dimension + ** QueryPoint point in D-space to find neighbors of + ** Radius current distance of furthest neighbor + ** SBMin lower extent of small search region + ** SBMax upper extent of small search region + ** Operation: + ** This routine returns TRUE if the query region intersects + ** the current smallest search region. The query region is + ** the circle of radius Radius centered at QueryPoint. + ** The smallest search region is the box (in N dimensions) + ** whose edges in each dimension are specified by SBMin and SBMax. + ** In the case of circular dimensions, we must also check the + ** point which is one wrap-distance away from the query to + ** see if it would intersect the search region. + ** Return: + ** TRUE if query region intersects search region, else FALSE + ** Exceptions: + ** None + ** History: + ** 3/11/89, DSJ, Created. + */ + register int i; + register FLOAT32 *Query; + register FLOAT32 *Lower; + register FLOAT32 *Upper; + register FLOAT64 TotalDistance; + register FLOAT32 DimensionDistance; + register FLOAT64 RadiusSquared; + register PARAM_DESC *Dim; + register FLOAT32 WrapDistance; + + RadiusSquared = Radius * Radius; + Query = QueryPoint; + Lower = SBMin; + Upper = SBMax; + TotalDistance = 0.0; + Dim = KeyDesc; + for (i = N; i > 0; i--, Dim++, Query++, Lower++, Upper++) { + if (Dim->NonEssential) + continue; + + if (*Query < *Lower) + DimensionDistance = *Lower - *Query; + else if (*Query > *Upper) + DimensionDistance = *Query - *Upper; + else + DimensionDistance = 0; + + /* if this dimension is circular - check wraparound distance */ + if (Dim->Circular) { + if (*Query < *Lower) + WrapDistance = *Query + Dim->Max - Dim->Min - *Upper; + else if (*Query > *Upper) + WrapDistance = *Lower - (*Query - (Dim->Max - Dim->Min)); + else + WrapDistance = MAX_FLOAT32; + + DimensionDistance = MIN (DimensionDistance, WrapDistance); + } + + TotalDistance += DimensionDistance * DimensionDistance; + if (TotalDistance >= RadiusSquared) + return (FALSE); + } + return (TRUE); +} /* QueryIntersectsSearch */ + + +/*---------------------------------------------------------------------------*/ +int QueryInSearch() { +/* + ** Parameters: + ** None + ** Globals: + ** N # of features in each key + ** KeyDesc descriptions of each dimension + ** QueryPoint point in D-space to find neighbors of + ** Radius current distance of furthest neighbor + ** LBMin lower extent of large search region + ** LBMax upper extent of large search region + ** Operation: + ** This routine returns TRUE if the current query region is + ** totally contained in the current largest search region. + ** The query region is the circle of + ** radius Radius centered at QueryPoint. The search region is + ** the box (in N dimensions) whose edges in each + ** dimension are specified by LBMin and LBMax. + ** Return: + ** TRUE if query region is inside search region, else FALSE + ** Exceptions: + ** None + ** History: + ** 3/11/89, DSJ, Created. + */ + register int i; + register FLOAT32 *Query; + register FLOAT32 *Lower; + register FLOAT32 *Upper; + register PARAM_DESC *Dim; + + Query = QueryPoint; + Lower = LBMin; + Upper = LBMax; + Dim = KeyDesc; + + for (i = N - 1; i >= 0; i--, Dim++, Query++, Lower++, Upper++) { + if (Dim->NonEssential) + continue; + + if ((*Query < *Lower + Radius) || (*Query > *Upper - Radius)) + return (FALSE); + } + return (TRUE); +} /* QueryInSearch */ + + +/*---------------------------------------------------------------------------*/ +void Walk(KDNODE *SubTree, INT32 Level) { +/* + ** Parameters: + ** SubTree ptr to root of subtree to be walked + ** Level current level in the tree for this node + ** Globals: + ** WalkAction action to be performed at every node + ** Operation: + ** This routine walks thru the specified SubTree and invokes + ** WalkAction at each node. WalkAction is invoked with three + ** arguments as follows: + ** WalkAction( NodeData, Order, Level ) + ** Data is the data contents of the node being visited, + ** Order is either preorder, + ** postorder, endorder, or leaf depending on whether this is + ** the 1st, 2nd, or 3rd time a node has been visited, or + ** whether the node is a leaf. Level is the level of the node in + ** the tree with the root being level 0. + ** Return: none + ** Exceptions: none + ** History: + ** 3/13/89, DSJ, Created. + ** 7/13/89, DSJ, Pass node contents, not node, to WalkAction(). + */ + if ((SubTree->Left == NULL) && (SubTree->Right == NULL)) + (*WalkAction) (SubTree->Data, leaf, Level); + else { + (*WalkAction) (SubTree->Data, preorder, Level); + if (SubTree->Left != NULL) + Walk (SubTree->Left, Level + 1); + (*WalkAction) (SubTree->Data, postorder, Level); + if (SubTree->Right != NULL) + Walk (SubTree->Right, Level + 1); + (*WalkAction) (SubTree->Data, endorder, Level); + } +} /* Walk */ + + +/*---------------------------------------------------------------------------*/ +void FreeSubTree(KDNODE *SubTree) { +/* + ** Parameters: + ** SubTree ptr to root node of sub-tree to be freed + ** Globals: none + ** Operation: + ** This routine recursively frees the memory allocated to + ** to the specified subtree. + ** Return: none + ** Exceptions: none + ** History: 7/13/89, DSJ, Created. + */ + if (SubTree != NULL) { + FreeSubTree (SubTree->Left); + FreeSubTree (SubTree->Right); + memfree(SubTree); + } +} /* FreeSubTree */ diff --git a/classify/kdtree.h b/classify/kdtree.h new file mode 100644 index 0000000000..9a4c4ef8a4 --- /dev/null +++ b/classify/kdtree.h @@ -0,0 +1,118 @@ +/****************************************************************************** + ** 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 "general.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. +*/ + +typedef struct kdnode +{ + FLOAT32 *Key; /* search key */ + char *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; +} + + +KDNODE; + +typedef struct +{ + 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 */ +} + + +KDTREE; + +typedef enum { /* used for walking thru KD trees */ + preorder, postorder, endorder, leaf +} + + +VISIT; + +/*---------------------------------------------------------------------------- + Macros +-----------------------------------------------------------------------------*/ +#define RootOf(T) ((T)->Root.Left->Data) + +/**---------------------------------------------------------------------------- + Public Function Prototypes +----------------------------------------------------------------------------**/ +KDTREE *MakeKDTree (INT16 KeySize, PARAM_DESC KeyDesc[]); + +void KDStore(KDTREE *Tree, FLOAT32 *Key, void *Data); + +void KDDelete (KDTREE * Tree, FLOAT32 Key[], void *Data); + +int KDNearestNeighborSearch (KDTREE * Tree, +FLOAT32 Query[], +int QuerySize, +FLOAT32 MaxDistance, +void *NBuffer, FLOAT32 DBuffer[]); + +void KDWalk(KDTREE *Tree, void_proc Action); + +void FreeKDTree(KDTREE *Tree); + +/**---------------------------------------------------------------------------- + Private Function Prototypes +----------------------------------------------------------------------------**/ +int Equal (FLOAT32 Key1[], FLOAT32 Key2[]); + +KDNODE *MakeKDNode (FLOAT32 Key[], char *Data, int Index); + +void FreeKDNode(KDNODE *Node); + +void Search(int Level, KDNODE *SubTree); + +FLOAT32 ComputeDistance (register int N, +register PARAM_DESC Dim[], +register FLOAT32 p1[], register FLOAT32 p2[]); + +void FindMaxDistance(); + +int QueryIntersectsSearch(); + +int QueryInSearch(); + +void Walk(KDNODE *SubTree, INT32 Level); + +void FreeSubTree(KDNODE *SubTree); +#endif diff --git a/classify/mf.cpp b/classify/mf.cpp new file mode 100644 index 0000000000..3cfdd66170 --- /dev/null +++ b/classify/mf.cpp @@ -0,0 +1,106 @@ +/****************************************************************************** + ** 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 "mfdefs.h" +#include "variables.h" +#include "mf.h" +#include "fxdefs.h" +#include "mfx.h" +#include + +/**---------------------------------------------------------------------------- + Global Data Definitions and Declarations +----------------------------------------------------------------------------**/ +/**---------------------------------------------------------------------------- + Private Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +FEATURE_SET ExtractMicros(TBLOB *Blob, LINE_STATS *LineStats) { +/* + ** Parameters: + ** Blob blob to extract micro-features from + ** LineStats statistics on text row blob is in + ** Globals: none + ** Operation: Call the old micro-feature extractor and then copy + ** the features into the new format. Then deallocate the + ** old micro-features. + ** Return: Micro-features for Blob. + ** Exceptions: none + ** History: Wed May 23 18:06:38 1990, DSJ, Created. + */ + int NumFeatures; + MICROFEATURES Features, OldFeatures; + FEATURE_SET FeatureSet; + FEATURE Feature; + MICROFEATURE OldFeature; + + OldFeatures = (MICROFEATURES) BlobMicroFeatures (Blob, LineStats); + NumFeatures = count (OldFeatures); + FeatureSet = NewFeatureSet (NumFeatures); + + Features = OldFeatures; + iterate(Features) { + OldFeature = (MICROFEATURE) first (Features); + Feature = NewFeature (&MicroFeatureDesc); + ParamOf (Feature, MFDirection) = OrientationOf (OldFeature); + ParamOf (Feature, MFXPosition) = CenterX (OldFeature); + ParamOf (Feature, MFYPosition) = CenterY (OldFeature); + ParamOf (Feature, MFLength) = LengthOf (OldFeature); + + // Bulge features should not be used + // anymore and are therefore set to 0. +// ParamOf (Feature, MFBulge1) = FirstBulgeOf (OldFeature); +// ParamOf (Feature, MFBulge2) = SecondBulgeOf (OldFeature); + ParamOf (Feature, MFBulge1) = 0.0f; + ParamOf (Feature, MFBulge2) = 0.0f; + + AddFeature(FeatureSet, Feature); + } + FreeMicroFeatures(OldFeatures); + return (FeatureSet); + +} /* ExtractMicros */ + + +/*---------------------------------------------------------------------------*/ +void InitMicroFXVars() { +/* + ** Parameters: none + ** Globals: + ** ExtraPenaltyMagnitude controls for adjusting extra penalty + ** ExtraPenaltyWeight + ** ExtraPenaltyOrder + ** Operation: Initialize the microfeature extractor variables that can + ** be tuned without recompiling. + ** Return: none + ** Exceptions: none + ** History: Thu May 24 10:50:46 1990, DSJ, Created. + */ + /* + float_variable (ExtraPenaltyMagnitude, "MFExtraPenaltyMag", + EXTRA_PENALTY_MAGNITUDE); + float_variable (ExtraPenaltyWeight, "MFExtraPenaltyWeight", + EXTRA_PENALTY_WEIGHT); + float_variable (ExtraPenaltyOrder, "MFExtraPenaltyOrder", + EXTRA_PENALTY_ORDER); + */ + InitMicroFxVars(); + +} /* InitMicroFXVars */ diff --git a/classify/mf.h b/classify/mf.h new file mode 100644 index 0000000000..79f1c8ef5f --- /dev/null +++ b/classify/mf.h @@ -0,0 +1,43 @@ +/****************************************************************************** + ** 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 "tessclas.h" +#include "fxdefs.h" + +typedef enum { + MFXPosition, MFYPosition, + MFLength, MFDirection, MFBulge1, MFBulge2 +} MF_PARAM_NAME; +/*---------------------------------------------------------------------------- + Private Function Prototypes +-----------------------------------------------------------------------------*/ +FEATURE_SET ExtractMicros(TBLOB *Blob, LINE_STATS *LineStats); + +void InitMicroFXVars(); + +/**---------------------------------------------------------------------------- + Global Data Definitions and Declarations +----------------------------------------------------------------------------**/ +extern FEATURE_DESC_STRUCT MicroFeatureDesc; +#endif diff --git a/classify/mfdefs.cpp b/classify/mfdefs.cpp new file mode 100644 index 0000000000..34e1ea6e5b --- /dev/null +++ b/classify/mfdefs.cpp @@ -0,0 +1,58 @@ +/****************************************************************************** + ** 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 +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +MICROFEATURE NewMicroFeature() { +/* + ** Parameters: none + ** Globals: none + ** Operation: + ** This routine allocates and returns a new micro-feature + ** data structure. + ** Return: New micro-feature. + ** Exceptions: none + ** History: 7/27/89, DSJ, Created. + */ + return ((MICROFEATURE) Emalloc (sizeof (MFBLOCK))); +} /* NewMicroFeature */ + + +/*---------------------------------------------------------------------------*/ +void FreeMicroFeatures(MICROFEATURES MicroFeatures) { +/* + ** Parameters: + ** MicroFeatures list of micro-features to be freed + ** Globals: none + ** Operation: + ** This routine deallocates all of the memory consumed by + ** a list of micro-features. + ** Return: none + ** Exceptions: none + ** History: 7/27/89, DSJ, Created. + */ + destroy_nodes(MicroFeatures, Efree); +} /* FreeMicroFeatures */ diff --git a/classify/mfdefs.h b/classify/mfdefs.h new file mode 100644 index 0000000000..b0873b34bb --- /dev/null +++ b/classify/mfdefs.h @@ -0,0 +1,67 @@ +/****************************************************************************** + ** 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" +#include "xform2d.h" + +/* maximum size of a bulge for length=1 is sqrt(2)/3 */ +#define BULGENORMALIZER 0.942809041 + +/* 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 parameters */ +#define CenterX(M) ( (M)[XPOSITION] ) +#define CenterY(M) ( (M)[YPOSITION] ) +#define LengthOf(M) ( (M)[MFLENGTH] ) +#define OrientationOf(M) ( (M)[ORIENTATION] ) +#define FirstBulgeOf(M) ( (M)[FIRSTBULGE] ) +#define SecondBulgeOf(M) ( (M)[SECONDBULGE] ) + +/* macros for accessing micro-feature lists */ +#define NextFeatureOf(L) ( (MICROFEATURE) first( L ) ) + +/**---------------------------------------------------------------------------- + Public Function Prototypes +----------------------------------------------------------------------------**/ +MICROFEATURE NewMicroFeature(); + +void FreeMicroFeatures(MICROFEATURES MicroFeatures); +#endif diff --git a/classify/mfoutline.cpp b/classify/mfoutline.cpp new file mode 100644 index 0000000000..8ea44bf161 --- /dev/null +++ b/classify/mfoutline.cpp @@ -0,0 +1,1086 @@ +/****************************************************************************** + ** 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 "debug.h" +#include "hideedge.h" +#include "blobs.h" +#include "const.h" +#include "mfx.h" + +#include +#include + +#define MIN_INERTIA (0.00001) + +/**---------------------------------------------------------------------------- + Private Function Prototypes +----------------------------------------------------------------------------**/ +/* +#if defined(__STDC__) || defined(__cplusplus) +# define _ARGS(s) s +#else +# define _ARGS(s) () +#endif*/ + +/* /users/danj/wiseowl/src/danj/microfeatures/mfoutline.c +void ChangeDirection + _ARGS((MFOUTLINE Start, + MFOUTLINE End, + DIRECTION Direction)); + +void CharNormalizeOutline + _ARGS((MFOUTLINE Outline, + OUTLINE_STATS *OutlineStats)); + +void ComputeDirection + _ARGS((MFEDGEPT *Start, + MFEDGEPT *Finish, + FLOAT32 MinSlope, + FLOAT32 MaxSlope)); + +void FinishOutlineStats + _ARGS((OUTLINE_STATS *OutlineStats)); + +void InitOutlineStats + _ARGS((OUTLINE_STATS *OutlineStats)); + +MFOUTLINE NextDirectionChange + _ARGS((MFOUTLINE EdgePoint)); + +void UpdateOutlineStats + _ARGS((OUTLINE_STATS *OutlineStats, + FLOAT32 x1, + FLOAT32 y1, + FLOAT32 x2, + FLOAT32 y2)); + +#undef _ARGS +*/ +/**---------------------------------------------------------------------------- + Global Data Definitions and Declarations +----------------------------------------------------------------------------**/ +/* center of current blob being processed - used when "unexpanding" + expanded blobs */ +static TPOINT BlobCenter; + +/* control knobs used to control normalization of outlines */ +make_int_var (NormMethod, character, MakeNormMethod, +15, 10, SetNormMethod, "Normalization Method ...") +/* PREV DEFAULT "baseline" */ +make_float_var (CharNormRange, 0.2, MakeCharNormRange, +15, 11, SetCharNormRange, "Character Normalization Range ...") +make_float_var (MinNormScaleX, 0.0, MakeMinNormScaleX, +15, 12, SetMinNormScaleX, "Min char x-norm scale ...") +/* PREV DEFAULT 0.1 */ +make_float_var (MaxNormScaleX, 0.325, MakeMaxNormScaleX, +15, 13, SetMaxNormScaleX, "Max char x-norm scale ...") +/* PREV DEFAULT 0.3 */ +make_float_var (MinNormScaleY, 0.0, MakeMinNormScaleY, +15, 14, SetMinNormScaleY, "Min char y-norm scale ...") +/* PREV DEFAULT 0.1 */ +make_float_var (MaxNormScaleY, 0.325, MakeMaxNormScaleY, +15, 15, SetMaxNormScaleY, "Max char y-norm scale ...") +/* PREV DEFAULT 0.3 */ +/**---------------------------------------------------------------------------- + Public Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +void ComputeBlobCenter(TBLOB *Blob, TPOINT *BlobCenter) { +/* + ** Parameters: + ** Blob blob to compute centerpoint of + ** BlobCenter data struct to place results in + ** Globals: none + ** Operation: + ** This routine computes the center point of the specified + ** blob using the bounding box of all top level outlines in the + ** blob. The center point is computed in a coordinate system + ** which is scaled up by VECSCALE from the page coordinate + ** system. + ** Return: none + ** Exceptions: none + ** History: Fri Sep 8 10:45:39 1989, DSJ, Created. + */ + TPOINT TopLeft; + TPOINT BottomRight; + + blob_bounding_box(Blob, &TopLeft, &BottomRight); + + BlobCenter->x = ((TopLeft.x << VECSCALE) + (BottomRight.x << VECSCALE)) / 2; + BlobCenter->y = ((TopLeft.y << VECSCALE) + (BottomRight.y << VECSCALE)) / 2; + +} /* ComputeBlobCenter */ + + +/*---------------------------------------------------------------------------*/ +LIST ConvertBlob(TBLOB *Blob) { +/* + ** Parameters: + ** Blob blob to be converted + ** Globals: none + ** Operation: Convert Blob into a list of outlines. + ** Return: List of outlines representing blob. + ** Exceptions: none + ** History: Thu Dec 13 15:40:17 1990, DSJ, Created. + */ + LIST ConvertedOutlines = NIL; + + if (Blob != NULL) { + SettupBlobConversion(Blob); //ComputeBlobCenter (Blob, &BlobCenter); + ConvertedOutlines = ConvertOutlines (Blob->outlines, + ConvertedOutlines, outer); + } + + return (ConvertedOutlines); +} /* ConvertBlob */ + + +/*---------------------------------------------------------------------------*/ +MFOUTLINE ConvertOutline(TESSLINE *Outline) { +/* + ** Parameters: + ** Outline outline to be converted + ** Globals: + ** BlobCenter pre-computed center of current blob + ** Operation: + ** This routine converts the specified outline into a special + ** data structure which is used for extracting micro-features. + ** If the outline has been pre-normalized by the splitter, + ** then it is assumed to be in expanded form and all we must + ** do is copy the points. Otherwise, + ** if the outline is expanded, then the expanded form is used + ** and the coordinates of the points are returned to page + ** coordinates using the global variable BlobCenter and the + ** scaling factor REALSCALE. If the outline is not expanded, + ** then the compressed form is used. + ** Return: Outline converted into special micro-features format. + ** Exceptions: none + ** History: 8/2/89, DSJ, Created. + ** 9/8/89, DSJ, Added ability to convert expanded blobs. + ** 1/11/90, DSJ, Changed to use REALSCALE instead of VECSCALE + ** to eliminate round-off problems. + ** 2/21/91, DSJ, Added ability to work with pre-normalized + ** blobs. + ** 4/30/91, DSJ, Added concept of "hidden" segments. + */ + register BYTEVEC *Vector; + TPOINT Position; + TPOINT StartPosition; + MFEDGEPT *NewPoint; + MFOUTLINE MFOutline = NIL; + EDGEPT *EdgePoint; + EDGEPT *StartPoint; + EDGEPT *NextPoint; + + if (Outline == NULL || + (Outline->compactloop == NULL && Outline->loop == NULL)) + return (MFOutline); + + /* have outlines been prenormalized */ + if (is_baseline_normalized ()) { + 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); + IsHidden (NewPoint) = is_hidden_edge (EdgePoint) ? TRUE : FALSE; + XPositionOf (NewPoint) = EdgePoint->pos.x; + YPositionOf (NewPoint) = EdgePoint->pos.y; + MFOutline = push (MFOutline, NewPoint); + } + EdgePoint = NextPoint; + } + while (EdgePoint != StartPoint); + } + /* use compressed version of outline */ + else if (Outline->loop == NULL) { + Xof (Position) = Xof (StartPosition) = Outline->start.x; + Yof (Position) = Yof (StartPosition) = Outline->start.y; + Vector = Outline->compactloop; + do { + if (Vector->dx != 0 || Vector->dy != 0) { + NewPoint = NewEdgePoint (); + ClearMark(NewPoint); + /* all edges are visible */ + IsHidden (NewPoint) = FALSE; + CopyPoint (Position, PositionOf (NewPoint)); + MFOutline = push (MFOutline, NewPoint); + } + Xof (Position) += Vector->dx; + Yof (Position) += Vector->dy; + Vector++; + } + while ((Xof (Position) != Xof (StartPosition)) || + (Yof (Position) != Yof (StartPosition))); + } + else { /* use expanded version of outline */ + 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); + IsHidden (NewPoint) = is_hidden_edge (EdgePoint) ? TRUE : FALSE; + XPositionOf (NewPoint) = + (EdgePoint->pos.x + BlobCenter.x) / REALSCALE; + YPositionOf (NewPoint) = + (EdgePoint->pos.y + BlobCenter.y) / REALSCALE; + MFOutline = push (MFOutline, NewPoint); + } + EdgePoint = NextPoint; + } + while (EdgePoint != StartPoint); + } + + MakeOutlineCircular(MFOutline); + return (MFOutline); + +} /* ConvertOutline */ + + +/*---------------------------------------------------------------------------*/ +LIST ConvertOutlines(TESSLINE *Outline, + LIST ConvertedOutlines, + OUTLINETYPE OutlineType) { +/* + ** Parameters: + ** Outline first outline to be converted + ** ConvertedOutlines list to add converted outlines to + ** OutlineType are the outlines outer or holes? + ** Globals: none + ** Operation: + ** This routine converts all given outlines into a new format. + ** of outlines. Outline points to a list of the top level + ** outlines to be converted. The children of these outlines + ** are also recursively converted. All converted outlines + ** are added to ConvertedOutlines. This is a list of outlines, + ** one for each outline that was converted. + ** Return: Updated list of converted outlines. + ** Exceptions: none + ** History: Thu Dec 13 15:57:38 1990, DSJ, Created. + */ + MFOUTLINE MFOutline; + + while (Outline != NULL) { + if (Outline->child != NULL) + if (OutlineType == outer) + ConvertedOutlines = ConvertOutlines (Outline->child, + ConvertedOutlines, hole); + else + ConvertedOutlines = ConvertOutlines (Outline->child, + ConvertedOutlines, outer); + + MFOutline = ConvertOutline (Outline); + ConvertedOutlines = push (ConvertedOutlines, MFOutline); + Outline = Outline->next; + } + return (ConvertedOutlines); +} /* ConvertOutlines */ + + +/*---------------------------------------------------------------------------*/ +void ComputeOutlineStats(LIST Outlines, OUTLINE_STATS *OutlineStats) { +/* + ** Parameters: + ** Outlines list of outlines to compute stats for + ** OutlineStats place to put results + ** Globals: none + ** Operation: This routine computes several statistics about the outlines + ** in Outlines. These statistics are usually used to perform + ** anistropic normalization of all of the outlines. The + ** statistics generated are: + ** first moments about x and y axes + ** total length of all outlines + ** center of mass of all outlines + ** second moments about center of mass axes + ** radius of gyration about center of mass axes + ** Return: none (results are returned in OutlineStats) + ** Exceptions: none + ** History: Fri Dec 14 08:32:03 1990, DSJ, Created. + */ + MFOUTLINE Outline; + MFOUTLINE EdgePoint; + MFEDGEPT *Current; + MFEDGEPT *Last; + + InitOutlineStats(OutlineStats); + iterate(Outlines) { + Outline = (MFOUTLINE) first (Outlines); + + Last = PointAt (Outline); + Outline = NextPointAfter (Outline); + EdgePoint = Outline; + do { + Current = PointAt (EdgePoint); + + UpdateOutlineStats (OutlineStats, + XPositionOf (Last), YPositionOf (Last), + XPositionOf (Current), YPositionOf (Current)); + + Last = Current; + EdgePoint = NextPointAfter (EdgePoint); + } + while (EdgePoint != Outline); + } + FinishOutlineStats(OutlineStats); + +} /* ComputeOutlineStats */ + + +/*---------------------------------------------------------------------------*/ +void FilterEdgeNoise(MFOUTLINE Outline, FLOAT32 NoiseSegmentLength) { +/* + ** Parameters: + ** Outline outline to be filtered + ** NoiseSegmentLength maximum length of a "noise" segment + ** Globals: none + ** Operation: Filter out noise from the specified outline. This is + ** done by changing the direction of short segments of the + ** outline to the same direction as the preceding outline + ** segment. + ** Return: none + ** Exceptions: none + ** History: Fri May 4 10:23:45 1990, DSJ, Created. + */ + MFOUTLINE Current; + MFOUTLINE Last; + MFOUTLINE First; + FLOAT32 Length; + int NumFound = 0; + DIRECTION DirectionOfFirst = north; + + if (DegenerateOutline (Outline)) + return; + + /* find 2 segments of different orientation which are long enough to + not be filtered. If two cannot be found, leave the outline unchanged. */ + First = NextDirectionChange (Outline); + Last = First; + do { + Current = NextDirectionChange (Last); + Length = DistanceBetween (PositionOf (PointAt (Current)), + PositionOf (PointAt (Last))); + if (Length >= NoiseSegmentLength) { + if (NumFound == 0) { + NumFound = 1; + DirectionOfFirst = DirectionOf (PointAt (Last)); + } + else if (DirectionOfFirst != DirectionOf (PointAt (Last))) + break; + } + Last = Current; + } + while (Last != First); + if (Current == Last) + return; + + /* find each segment and filter it out if it is too short. Note that + the above code guarantees that the initial direction change will + not be removed, therefore the loop will terminate. */ + First = Last; + do { + Current = NextDirectionChange (Last); + Length = DistanceBetween (PositionOf (PointAt (Current)), + PositionOf (PointAt (Last))); + if (Length < NoiseSegmentLength) + ChangeDirection (Last, Current, PreviousDirectionOf (PointAt (Last))); + + Last = Current; + } + while (Last != First); + +} /* FilterEdgeNoise */ + + +/*---------------------------------------------------------------------------*/ +void FindDirectionChanges(MFOUTLINE Outline, + FLOAT32 MinSlope, + FLOAT32 MaxSlope) { +/* + ** Parameters: + ** Outline micro-feature outline to analyze + ** MinSlope controls "snapping" of segments to horizontal + ** MaxSlope controls "snapping" of segments to vertical + ** Globals: none + ** Operation: + ** This routine searches thru 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. + ** Return: none + ** Exceptions: none + ** History: 7/21/89, DSJ, Created. + */ + 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 */ + + +/*---------------------------------------------------------------------------*/ +void FreeMFOutline(void *arg) { //MFOUTLINE Outline) +/* + ** Parameters: + ** Outline micro-feature outline to be freed + ** Globals: none + ** Operation: + ** This routine deallocates all of the memory consumed by + ** a micro-feature outline. + ** Return: none + ** Exceptions: none + ** History: 7/27/89, DSJ, Created. + */ + MFOUTLINE Start; + MFOUTLINE Outline = (MFOUTLINE) arg; + + /* break the circular outline so we can use std. techniques to deallocate */ + Start = rest (Outline); + set_rest(Outline, NIL); + while (Start != NULL) { + c_free_struct (first (Start), sizeof (MFEDGEPT), "MFEDGEPT"); + Start = pop (Start); + } + +} /* FreeMFOutline */ + + +/*---------------------------------------------------------------------------*/ +void FreeOutlines(LIST Outlines) { +/* + ** Parameters: + ** Outlines list of mf-outlines to be freed + ** Globals: none + ** Operation: Release all memory consumed by the specified list + ** of outlines. + ** Return: none + ** Exceptions: none + ** History: Thu Dec 13 16:14:50 1990, DSJ, Created. + */ + destroy_nodes(Outlines, FreeMFOutline); +} /* FreeOutlines */ + + +/*---------------------------------------------------------------------------*/ +void InitMFOutlineVars() { +/* + ** Parameters: none + ** Globals: none + ** Operation: This routine initializes the global control knobs for + ** all routines in this file. + ** Return: none + ** Exceptions: none + ** History: Fri Dec 14 10:50:12 1990, DSJ, Created. + */ + MakeNormMethod(); + MakeCharNormRange(); + MakeMinNormScaleX(); + MakeMaxNormScaleX(); + MakeMinNormScaleY(); + MakeMaxNormScaleY(); +} /* InitMFOutlineVars */ + + +/*---------------------------------------------------------------------------*/ +void MarkDirectionChanges(MFOUTLINE Outline) { +/* + ** Parameters: + ** Outline micro-feature outline to analyze + ** Globals: none + ** Operation: + ** This routine searches thru 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. + ** Return: none + ** Exceptions: none + ** History: 6/29/90, DSJ, Created. + */ + 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 */ + + +/*---------------------------------------------------------------------------*/ +MFEDGEPT *NewEdgePoint() { +/* + ** Parameters: none + ** Globals: none + ** Operation: + ** This routine allocates and returns a new edge point for + ** a micro-feature outline. + ** Return: New edge point. + ** Exceptions: none + ** History: 7/21/89, DSJ, Created. + */ + return ((MFEDGEPT *) c_alloc_struct (sizeof (MFEDGEPT), "MFEDGEPT")); + +} /* NewEdgePoint */ + + +/*---------------------------------------------------------------------------*/ +MFOUTLINE NextExtremity(MFOUTLINE EdgePoint) { +/* + ** Parameters: + ** EdgePoint start search from this point + ** Globals: none + ** Operation: + ** 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). + ** Return: Next extremity in the outline after EdgePoint. + ** Exceptions: none + ** History: 7/26/89, DSJ, Created. + */ + EdgePoint = NextPointAfter (EdgePoint); + while (NotExtremity (PointAt (EdgePoint))) + EdgePoint = NextPointAfter (EdgePoint); + + return (EdgePoint); + +} /* NextExtremity */ + + +/*---------------------------------------------------------------------------*/ +void NormalizeOutline(MFOUTLINE Outline, + LINE_STATS *LineStats, + FLOAT32 XOrigin) { +/* + ** Parameters: + ** Outline outline to be normalized + ** LineStats statistics for text line normalization + ** XOrigin x-origin of text + ** Globals: none + ** Operation: + ** 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. + ** Return: none + ** Exceptions: none + ** History: 8/2/89, DSJ, Created. + ** 10/23/89, DSJ, Added ascender/descender stretching. + ** 11/89, DSJ, Removed ascender/descender stretching. + */ + MFEDGEPT *Current; + MFOUTLINE EdgePoint; + FLOAT32 ScaleFactor; + FLOAT32 AscStretch; + FLOAT32 DescStretch; + + if (Outline != NIL) { + ScaleFactor = ComputeScaleFactor (LineStats); + AscStretch = 1.0; + DescStretch = 1.0; + + EdgePoint = Outline; + do { + Current = PointAt (EdgePoint); + + YPositionOf (Current) = ScaleFactor * + (YPositionOf (Current) - + BaselineAt (LineStats, XPositionOf (Current))); + + if (YPositionOf (Current) > NORMAL_X_HEIGHT) + YPositionOf (Current) = NORMAL_X_HEIGHT + + (YPositionOf (Current) - NORMAL_X_HEIGHT) / AscStretch; + + else if (YPositionOf (Current) < NORMAL_BASELINE) + YPositionOf (Current) = NORMAL_BASELINE + + (YPositionOf (Current) - NORMAL_BASELINE) / DescStretch; + + XPositionOf (Current) = ScaleFactor * + (XPositionOf (Current) - XOrigin); + + EdgePoint = NextPointAfter (EdgePoint); + } + while (EdgePoint != Outline); + } +} /* NormalizeOutline */ + + +/*---------------------------------------------------------------------------*/ +void NormalizeOutlines(LIST Outlines, + LINE_STATS *LineStats, + FLOAT32 *XScale, + FLOAT32 *YScale) { +/* + ** Parameters: + ** Outlines list of outlines to be normalized + ** LineStats statistics for text line normalization + ** XScale x-direction scale factor used by routine + ** YScale y-direction scale factor used by routine + ** Globals: + ** NormMethod method being used for normalization + ** CharNormRange map radius of gyration to this value + ** Operation: 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. + ** Return: none (Outlines are changed and XScale and YScale are updated) + ** Exceptions: none + ** History: Fri Dec 14 08:14:55 1990, DSJ, Created. + */ + MFOUTLINE Outline; + OUTLINE_STATS OutlineStats; + FLOAT32 BaselineScale; + + switch (NormMethod) { + case character: + ComputeOutlineStats(Outlines, &OutlineStats); + + /* limit scale factor to avoid overscaling small blobs (.,`'), + thin blobs (l1ift), and merged blobs */ + *XScale = *YScale = BaselineScale = ComputeScaleFactor (LineStats); + *XScale *= OutlineStats.Ry; + *YScale *= OutlineStats.Rx; + if (*XScale < MinNormScaleX) + *XScale = MinNormScaleX; + if (*YScale < MinNormScaleY) + *YScale = MinNormScaleY; + if (*XScale > MaxNormScaleX && *YScale <= MaxNormScaleY) + *XScale = MaxNormScaleX; + *XScale = CharNormRange * BaselineScale / *XScale; + *YScale = CharNormRange * BaselineScale / *YScale; + + iterate(Outlines) { + Outline = (MFOUTLINE) first (Outlines); + CharNormalizeOutline (Outline, + OutlineStats.x, OutlineStats.y, + *XScale, *YScale); + } + break; + + case baseline: + iterate(Outlines) { + Outline = (MFOUTLINE) first (Outlines); + NormalizeOutline (Outline, LineStats, 0.0); + } + *XScale = *YScale = ComputeScaleFactor (LineStats); + break; + } +} /* NormalizeOutlines */ + + +/*---------------------------------------------------------------------------*/ +void SettupBlobConversion(TBLOB *Blob) { +/* + ** Parameters: + ** Blob blob that is to be converted + ** Globals: + ** BlobCenter center of blob to be converted + ** Operation: Compute the center of the blob's bounding box and save + ** it in a global variable. This routine must be called before + ** any calls to ConvertOutline. It must be called once per + ** blob. + ** Return: none + ** Exceptions: none + ** History: Thu May 17 11:06:17 1990, DSJ, Created. + */ + ComputeBlobCenter(Blob, &BlobCenter); + +} /* SettupBlobConversion */ + + +/*---------------------------------------------------------------------------*/ +void SmearExtremities(MFOUTLINE Outline, FLOAT32 XScale, FLOAT32 YScale) { +/* + ** Parameters: + ** Outline outline whose extremities are to be smeared + ** XScale factor used to normalize outline in x dir + ** YScale factor used to normalize outline in y dir + ** Globals: none + ** Operation: + ** This routine smears the extremities of the specified outline. + ** It does this by adding a random number between + ** -0.5 and 0.5 pixels (that is why X/YScale are needed) to + ** the x and y position of the point. This is done so that + ** the discrete nature of the original scanned image does not + ** affect the statistical clustering used during training. + ** Return: none + ** Exceptions: none + ** History: 1/11/90, DSJ, Created. + */ + MFEDGEPT *Current; + MFOUTLINE EdgePoint; + FLOAT32 MinXSmear; + FLOAT32 MaxXSmear; + FLOAT32 MinYSmear; + FLOAT32 MaxYSmear; + + if (Outline != NIL) { + MinXSmear = -0.5 * XScale; + MaxXSmear = 0.5 * XScale; + MinYSmear = -0.5 * YScale; + MaxYSmear = 0.5 * YScale; + EdgePoint = Outline; + do { + Current = PointAt (EdgePoint); + if (IsExtremity (Current)) { + XPositionOf (Current) += + UniformRandomNumber(MinXSmear, MaxXSmear); + YPositionOf (Current) += + UniformRandomNumber(MinYSmear, MaxYSmear); + } + + EdgePoint = NextPointAfter (EdgePoint); + } + while (EdgePoint != Outline); + } +} /* SmearExtremities */ + + +/**---------------------------------------------------------------------------- + Private Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +void ChangeDirection(MFOUTLINE Start, MFOUTLINE End, DIRECTION Direction) { +/* + ** Parameters: + ** Start, End defines segment of outline to be modified + ** Direction new direction to assign to segment + ** Globals: none + ** Operation: 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. + ** Return: none + ** Exceptions: none + ** History: Fri May 4 10:42:04 1990, DSJ, Created. + */ + MFOUTLINE Current; + + for (Current = Start; Current != End; Current = NextPointAfter (Current)) + DirectionOf (PointAt (Current)) = Direction; + + PreviousDirectionOf (PointAt (End)) = Direction; + +} /* ChangeDirection */ + + +/*---------------------------------------------------------------------------*/ +void CharNormalizeOutline(MFOUTLINE Outline, + FLOAT32 XCenter, + FLOAT32 YCenter, + FLOAT32 XScale, + FLOAT32 YScale) { +/* + ** Parameters: + ** Outline outline to be character normalized + ** XCenter, YCenter center point for normalization + ** XScale, YScale scale factors for normalization + ** Globals: none + ** Operation: This routine normalizes each point in Outline by + ** translating it to the specified center and scaling it + ** anisotropically according to the given scale factors. + ** Return: none + ** Exceptions: none + ** History: Fri Dec 14 10:27:11 1990, DSJ, Created. + */ + MFOUTLINE First, Current; + MFEDGEPT *CurrentPoint; + + if (Outline == NIL) + return; + + First = Outline; + Current = First; + do { + CurrentPoint = PointAt (Current); + XPositionOf (CurrentPoint) = + (XPositionOf (CurrentPoint) - XCenter) * XScale; + YPositionOf (CurrentPoint) = + (YPositionOf (CurrentPoint) - YCenter) * YScale; + + Current = NextPointAfter (Current); + } + while (Current != First); + +} /* CharNormalizeOutline */ + + +/*---------------------------------------------------------------------------*/ +void ComputeDirection(MFEDGEPT *Start, + MFEDGEPT *Finish, + FLOAT32 MinSlope, + FLOAT32 MaxSlope) { +/* + ** Parameters: + ** Start starting point to compute direction from + ** Finish finishing point to compute direction to + ** MinSlope slope below which lines are horizontal + ** MaxSlope slope above which lines are vertical + ** Globals: none + ** Operation: + ** 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. + ** Return: none + ** Exceptions: none + ** History: 7/25/89, DSJ, Created. + */ + 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; +} /* ComputeDirection */ + + +/*---------------------------------------------------------------------------*/ +void FinishOutlineStats(register OUTLINE_STATS *OutlineStats) { +/* + ** Parameters: + ** OutlineStats statistics about a set of outlines + ** Globals: none + ** Operation: Use the preliminary statistics accumulated in OutlineStats + ** to compute the final statistics. + ** (see Dan Johnson's Tesseract lab + ** notebook #2, pgs. 74-78). + ** Return: none + ** Exceptions: none + ** History: Fri Dec 14 10:13:36 1990, DSJ, Created. + */ + OutlineStats->x = 0.5 * OutlineStats->My / OutlineStats->L; + OutlineStats->y = 0.5 * OutlineStats->Mx / OutlineStats->L; + + OutlineStats->Ix = (OutlineStats->Ix / 3.0 - + OutlineStats->y * OutlineStats->Mx + + OutlineStats->y * OutlineStats->y * OutlineStats->L); + + OutlineStats->Iy = (OutlineStats->Iy / 3.0 - + OutlineStats->x * OutlineStats->My + + OutlineStats->x * OutlineStats->x * OutlineStats->L); + + /* Ix and/or Iy could possibly be negative due to roundoff error */ + if (OutlineStats->Ix < 0.0) + OutlineStats->Ix = MIN_INERTIA; + if (OutlineStats->Iy < 0.0) + OutlineStats->Iy = MIN_INERTIA; + + OutlineStats->Rx = sqrt (OutlineStats->Ix / OutlineStats->L); + OutlineStats->Ry = sqrt (OutlineStats->Iy / OutlineStats->L); + + OutlineStats->Mx *= 0.5; + OutlineStats->My *= 0.5; + +} /* FinishOutlineStats */ + + +/*---------------------------------------------------------------------------*/ +void InitOutlineStats(OUTLINE_STATS *OutlineStats) { +/* + ** Parameters: + ** OutlineStats stats data structure to be initialized + ** Globals: none + ** Operation: Initialize the outline statistics data structure so + ** that it is ready to start accumulating statistics. + ** Return: none + ** Exceptions: none + ** History: Fri Dec 14 08:55:22 1990, DSJ, Created. + */ + OutlineStats->Mx = 0.0; + OutlineStats->My = 0.0; + OutlineStats->L = 0.0; + OutlineStats->x = 0.0; + OutlineStats->y = 0.0; + OutlineStats->Ix = 0.0; + OutlineStats->Iy = 0.0; + OutlineStats->Rx = 0.0; + OutlineStats->Ry = 0.0; +} /* InitOutlineStats */ + + +/*---------------------------------------------------------------------------*/ +MFOUTLINE NextDirectionChange(MFOUTLINE EdgePoint) { +/* + ** Parameters: + ** EdgePoint start search from this point + ** Globals: none + ** Operation: + ** 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). + ** Return: Point of next direction change in micro-feature outline. + ** Exceptions: none + ** History: 7/25/89, DSJ, Created. + */ + DIRECTION InitialDirection; + + InitialDirection = DirectionOf (PointAt (EdgePoint)); + + do + EdgePoint = NextPointAfter (EdgePoint); + while (DirectionOf (PointAt (EdgePoint)) == InitialDirection); + + return (EdgePoint); +} /* NextDirectionChange */ + + +/*---------------------------------------------------------------------------*/ +void UpdateOutlineStats(register OUTLINE_STATS *OutlineStats, + register FLOAT32 x1, + register FLOAT32 x2, + register FLOAT32 y1, + register FLOAT32 y2) { +/* + ** Parameters: + ** OutlineStats statistics to add this segment to + ** x1, y1, x2, y2 segment to be added to statistics + ** Globals: none + ** Operation: This routine adds the statistics for the specified + ** line segment to OutlineStats. The statistics that are + ** kept are: + ** sum of length of all segments + ** sum of 2*Mx for all segments + ** sum of 2*My for all segments + ** sum of 2*Mx*(y1+y2) - L*y1*y2 for all segments + ** sum of 2*My*(x1+x2) - L*x1*x2 for all segments + ** These numbers, once collected can later be used to easily + ** compute the center of mass, first and second moments, + ** and radii of gyration. (see Dan Johnson's Tesseract lab + ** notebook #2, pgs. 74-78). + ** Return: none + ** Exceptions: none + ** History: Fri Dec 14 08:59:17 1990, DSJ, Created. + */ + register FLOAT64 L; + register FLOAT64 Mx2; + register FLOAT64 My2; + + /* compute length of segment */ + L = sqrt ((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1)); + OutlineStats->L += L; + + /* compute 2Mx and 2My components */ + Mx2 = L * (y1 + y2); + My2 = L * (x1 + x2); + OutlineStats->Mx += Mx2; + OutlineStats->My += My2; + + /* compute second moment component */ + OutlineStats->Ix += Mx2 * (y1 + y2) - L * y1 * y2; + OutlineStats->Iy += My2 * (x1 + x2) - L * x1 * x2; + +} /* UpdateOutlineStats */ diff --git a/classify/mfoutline.h b/classify/mfoutline.h new file mode 100644 index 0000000000..d70667ca87 --- /dev/null +++ b/classify/mfoutline.h @@ -0,0 +1,277 @@ +/****************************************************************************** + ** 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 "general.h" +#include "oldlist.h" +#include "fpoint.h" +#include "fxdefs.h" +#include "baseline.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 enum +{ +False, True +} +BOOLEAN; +*/ +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 struct +{ + FLOAT64 Mx, My; /* first moment of all outlines */ + FLOAT64 L; /* total length of all outlines */ + FLOAT64 x, y; /* center of mass of all outlines */ + FLOAT64 Ix, Iy; /* second moments about center of mass axes */ + FLOAT64 Rx, Ry; /* radius of gyration about center of mass axes */ +} + + +OUTLINE_STATS; + +typedef enum { + baseline, character +} + + +NORM_METHOD; + +/*---------------------------------------------------------------------------- + Variables +------------------------------------------------------------------------------*/ +extern int NormMethod; + +/**---------------------------------------------------------------------------- + Macros +----------------------------------------------------------------------------**/ +#define AverageOf(A,B) (((A) + (B)) / 2) + +/* macro for computing the baseline of a row of text at an x position */ +#define BaselineAt(L,X) (BASELINE_OFFSET) + +/* macro for computing the scale factor to use to normalize characters */ +#define ComputeScaleFactor(L) \ +(NORMAL_X_HEIGHT / ((is_baseline_normalized ())? \ + (BASELINE_SCALE): \ + ((L)->xheight))) + +/* macros for manipulating micro-feature outlines */ +#define DegenerateOutline(O) (((O) == NIL) || ((O) == rest(O))) +#define PointAt(O) ((MFEDGEPT *) first (O)) +#define NextPointAfter(E) (rest (E)) +#define MakeOutlineCircular(O) (set_rest (last (O), (O))) + +/* macros for manipulating micro-feature outline edge points */ +#define PositionOf(P) ((P)->Point) +#define XPositionOf(P) (PositionOf(P).x) +#define YPositionOf(P) (PositionOf(P).y) +#define DirectionOf(P) ((P)->Direction) +#define PreviousDirectionOf(P) ((P)->PreviousDirection) +#define ClearMark(P) ((P)->ExtremityMark = FALSE) +#define MarkPoint(P) ((P)->ExtremityMark = TRUE) +#define IsExtremity(P) ((P)->ExtremityMark) +#define NotExtremity(P) (!IsExtremity(P)) +#define IsVisible(E) (! IsHidden(E)) +#define IsHidden(E) ((E)->Hidden) + +/**---------------------------------------------------------------------------- + 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 ComputeOutlineStats(LIST Outlines, OUTLINE_STATS *OutlineStats); + +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 InitMFOutlineVars(); + +void MarkDirectionChanges(MFOUTLINE Outline); + +MFEDGEPT *NewEdgePoint(); + +MFOUTLINE NextExtremity(MFOUTLINE EdgePoint); + +void NormalizeOutline(MFOUTLINE Outline, + LINE_STATS *LineStats, + FLOAT32 XOrigin); + +void NormalizeOutlines(LIST Outlines, + LINE_STATS *LineStats, + FLOAT32 *XScale, + FLOAT32 *YScale); + +void SettupBlobConversion(TBLOB *Blob); + +void SmearExtremities(MFOUTLINE Outline, FLOAT32 XScale, FLOAT32 YScale); + +/*---------------------------------------------------------------------------- + Private Function Prototypes +-----------------------------------------------------------------------------*/ +void ChangeDirection(MFOUTLINE Start, MFOUTLINE End, DIRECTION Direction); + +void CharNormalizeOutline(MFOUTLINE Outline, + FLOAT32 XCenter, + FLOAT32 YCenter, + FLOAT32 XScale, + FLOAT32 YScale); + +void ComputeDirection(MFEDGEPT *Start, + MFEDGEPT *Finish, + FLOAT32 MinSlope, + FLOAT32 MaxSlope); + +void FinishOutlineStats(register OUTLINE_STATS *OutlineStats); + +void InitOutlineStats(OUTLINE_STATS *OutlineStats); + +MFOUTLINE NextDirectionChange(MFOUTLINE EdgePoint); + +void UpdateOutlineStats(register OUTLINE_STATS *OutlineStats, + register FLOAT32 x1, + register FLOAT32 x2, + register FLOAT32 y1, + register FLOAT32 y2); + +/* +#if defined(__STDC__) || defined(__cplusplus) +# define _ARGS(s) s +#else +# define _ARGS(s) () +#endif*/ + +/* mfoutline.c +void ComputeBlobCenter + _ARGS((BLOB *Blob, + TPOINT *BlobCenter)); + +LIST ConvertBlob + _ARGS((BLOB *Blob)); + +MFOUTLINE ConvertOutline + _ARGS((TESSLINE *Outline)); + +LIST ConvertOutlines + _ARGS((TESSLINE *Outline, + LIST ConvertedOutlines, + OUTLINETYPE OutlineType)); + +void ComputeOutlineStats + _ARGS((LIST Outlines, + OUTLINE_STATS *OutlineStats)); + +void FilterEdgeNoise + _ARGS((MFOUTLINE Outline, + FLOAT32 NoiseSegmentLength)); + +void FindDirectionChanges + _ARGS((MFOUTLINE Outline, + FLOAT32 MinSlope, + FLOAT32 MaxSlope)); + +void FreeMFOutline + _ARGS((MFOUTLINE Outline)); + +void FreeOutlines + _ARGS((LIST Outlines)); + +void InitMFOutlineVars + _ARGS((void)); + +void MarkDirectionChanges + _ARGS((MFOUTLINE Outline)); + +MFEDGEPT *NewEdgePoint + _ARGS((void)); + +MFOUTLINE NextExtremity + _ARGS((MFOUTLINE EdgePoint)); + +void NormalizeOutline + _ARGS((MFOUTLINE Outline, + LINE_STATS *LineStats, + FLOAT32 XOrigin)); + +void NormalizeOutlines + _ARGS((LIST Outlines, + LINE_STATS *LineStats)); + +void SettupBlobConversion + _ARGS((BLOB *Blob)); + +void SmearExtremities + _ARGS((MFOUTLINE Outline, + FLOAT32 XScale, + FLOAT32 YScale)); + +#undef _ARGS +*/ +/**---------------------------------------------------------------------------- + Global Data Definitions and Declarations +----------------------------------------------------------------------------**/ +extern int NormMethod; /* normalized method currently selected */ +#endif diff --git a/classify/mfx.cpp b/classify/mfx.cpp new file mode 100644 index 0000000000..4174555844 --- /dev/null +++ b/classify/mfx.cpp @@ -0,0 +1,437 @@ +/****************************************************************************** + ** 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 "variables.h" +#include "sigmenu.h" +#include "mfoutline.h" +#include "clusttool.h" //NEEDED +#include "const.h" +#include "intfx.h" +#include + +/* default values for tunable knobs */ +/* old numbers corresponded to 10.0 degrees and 80.0 degrees */ + /* PREV DEFAULT 0.176326981 approx. 10.0 degrees */ +#define MIN_SLOPE 0.414213562 + /* PREV DEFAULT 5.671281820 approx. 80.0 degrees */ +#define MAX_SLOPE 2.414213562 + /* no noise filtering */ +#define NOISE_SEGMENT_LENGTH (0.00) + /* no feature splitting */ +#define MAX_FEATURE_LENGTH (MAXFLOAT) + +/**---------------------------------------------------------------------------- + Macros +----------------------------------------------------------------------------**/ +/* miscellaneous macros */ +#define NormalizeAngle(A) ( (((A)<0)?((A)+2*PI):(A)) / (2*PI) ) + +/*---------------------------------------------------------------------------- + Private Function Prototypes +-----------------------------------------------------------------------------*/ +void ComputeBulges(MFOUTLINE Start, MFOUTLINE End, MICROFEATURE MicroFeature); + +FLOAT32 ComputeOrientation(MFEDGEPT *Start, MFEDGEPT *End); + +MICROFEATURES ConvertToMicroFeatures(MFOUTLINE Outline, + MICROFEATURES MicroFeatures); + +MICROFEATURE ExtractMicroFeature(MFOUTLINE Start, MFOUTLINE End); + +void SmearBulges(MICROFEATURES MicroFeatures, FLOAT32 XScale, FLOAT32 YScale); + +/* +#if defined(__STDC__) || defined(__cplusplus) +# define _ARGS(s) s +#else +# define _ARGS(s) () +#endif*/ + +/* /users/danj/wiseowl/src/danj/microfeatures/mfx.c +void ComputeBulges + _ARGS((MFOUTLINE Start, + MFOUTLINE End, + MICROFEATURE MicroFeature)); + +FLOAT32 ComputeOrientation + _ARGS((MFEDGEPT *Start, + MFEDGEPT *End)); + +MICROFEATURES ConvertToMicroFeatures + _ARGS((MFOUTLINE Outline, + MICROFEATURES MicroFeatures)); + +MICROFEATURE ExtractMicroFeature + _ARGS((MFOUTLINE Start, + MFOUTLINE End)); + +void SmearBulges + _ARGS((MICROFEATURES MicroFeatures, + FLOAT32 XScale, + FLOAT32 YScale)); + +#undef _ARGS +*/ + +/**---------------------------------------------------------------------------- + Global Data Definitions and Declarations +----------------------------------------------------------------------------**/ +/* tuning knobs that can be adjusted without recompilation */ +static FLOAT32 MinSlope; +static FLOAT32 MaxSlope; +static FLOAT32 NoiseSegmentLength; + +/**---------------------------------------------------------------------------- + Public Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +void InitMicroFxVars() { +/* + ** Parameters: none + ** Globals: + ** MinSlope slope below which lines are called horizontal + ** MaxSlope slope above which lines are called vertical + ** NoiseSegmentLength length below which outline segments + ** are treated as noise + ** MaxFeatureLength length above which a feature will + ** be split into 2 equal pieces + ** ExtremityMode controls how extremities are defined + ** XHeightAdjust allows xheight of line to be adjusted + ** Operation: Initialize the micro-feature extractor variables (knobs) + ** that can be tuned without recompiling. + ** Return: none + ** Exceptions: none + ** History: Mon May 14 11:24:40 1990, DSJ, Created. + */ + VALUE dummy; + + float_variable (MinSlope, "MinSlope", MIN_SLOPE); + float_variable (MaxSlope, "MaxSlope", MAX_SLOPE); + float_variable (NoiseSegmentLength, "NoiseSegmentLength", + NOISE_SEGMENT_LENGTH); +} /* InitMicroFxVars */ + + +/*---------------------------------------------------------------------------*/ +CHAR_FEATURES BlobMicroFeatures(TBLOB *Blob, LINE_STATS *LineStats) { +/* + ** Parameters: + ** Blob blob to extract micro-features from + ** LineStats statistics for text line normalization + ** Globals: + ** XHeightAdjust used for manually adjusting xheight + ** Operation: + ** 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. + ** Return: List of micro-features extracted from the blob. + ** Exceptions: none + ** History: 7/21/89, DSJ, Created. + */ + MICROFEATURES MicroFeatures = NIL; + FLOAT32 XScale, YScale; + LIST Outlines; + LIST RemainingOutlines; + MFOUTLINE Outline; + INT_FEATURE_ARRAY blfeatures; + INT_FEATURE_ARRAY cnfeatures; + INT_FX_RESULT_STRUCT results; + + if (Blob != NULL) { + Outlines = ConvertBlob (Blob); +// NormalizeOutlines(Outlines, LineStats, &XScale, &YScale); + ExtractIntFeat(Blob, blfeatures, cnfeatures, &results); + XScale = 0.2f / results.Ry; + YScale = 0.2f / results.Rx; + + RemainingOutlines = Outlines; + iterate(RemainingOutlines) { + Outline = (MFOUTLINE) first (RemainingOutlines); + CharNormalizeOutline (Outline, + results.Xmean, results.Ymean, + XScale, YScale); + } + + RemainingOutlines = Outlines; + iterate(RemainingOutlines) { + Outline = (MFOUTLINE) first (RemainingOutlines); + FindDirectionChanges(Outline, MinSlope, MaxSlope); + FilterEdgeNoise(Outline, NoiseSegmentLength); + MarkDirectionChanges(Outline); + SmearExtremities(Outline, XScale, YScale); + MicroFeatures = ConvertToMicroFeatures (Outline, MicroFeatures); + } + SmearBulges(MicroFeatures, XScale, YScale); + FreeOutlines(Outlines); + } + return ((CHAR_FEATURES) MicroFeatures); +} /* BlobMicroFeatures */ + + +/**---------------------------------------------------------------------------- + Private Macros +----------------------------------------------------------------------------**/ +/********************************************************************** + * angle_of + * + * Return the angle of the line between two points. + **********************************************************************/ +#define angle_of(x1,y1,x2,y2) \ +((x2-x1) ? \ + (atan2 (y2-y1, x2-x1)) : \ + ((y2= 1)) + Orientation = 0; + return (Orientation); +} /* ComputeOrientation */ + + +/*---------------------------------------------------------------------------*/ +MICROFEATURES ConvertToMicroFeatures(MFOUTLINE Outline, + MICROFEATURES MicroFeatures) { +/* + ** Parameters: + ** Outline outline to extract micro-features from + ** MicroFeatures list of micro-features to add to + ** Globals: none + ** Operation: + ** This routine + ** Return: List of micro-features with new features added to front. + ** Exceptions: none + ** History: 7/26/89, DSJ, Created. + */ + MFOUTLINE Current; + MFOUTLINE Last; + MFOUTLINE First; + MICROFEATURE NewFeature; + + if (DegenerateOutline (Outline)) + return (MicroFeatures); + + First = NextExtremity (Outline); + Last = First; + do { + Current = NextExtremity (Last); + NewFeature = ExtractMicroFeature (Last, Current); + if (NewFeature != NULL) + MicroFeatures = push (MicroFeatures, NewFeature); + Last = Current; + } + while (Last != First); + + return (MicroFeatures); +} /* ConvertToMicroFeatures */ + + +/*---------------------------------------------------------------------------*/ +MICROFEATURE ExtractMicroFeature(MFOUTLINE Start, MFOUTLINE End) { +/* + ** Parameters: + ** Start starting point of micro-feature + ** End ending point of micro-feature + ** Globals: none + ** Operation: + ** 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. + ** Return: New micro-feature or NULL if the feature was rejected. + ** Exceptions: none + ** History: 7/26/89, DSJ, Created. + ** 11/17/89, DSJ, Added handling for Start and End same point. + */ + MICROFEATURE NewFeature; + MFEDGEPT *P1, *P2; + + P1 = PointAt (Start); + P2 = PointAt (End); + + NewFeature = NewMicroFeature (); + CenterX (NewFeature) = AverageOf (XPositionOf (P1), XPositionOf (P2)); + CenterY (NewFeature) = AverageOf (YPositionOf (P1), YPositionOf (P2)); + LengthOf (NewFeature) = DistanceBetween (PositionOf (P1), PositionOf (P2)); + OrientationOf (NewFeature) = + NormalizedAngleFrom (&(PositionOf (P1)), &(PositionOf (P2)), 1.0); + ComputeBulges(Start, End, NewFeature); + return (NewFeature); +} /* ExtractMicroFeature */ + + +/*---------------------------------------------------------------------------*/ +void SmearBulges(MICROFEATURES MicroFeatures, FLOAT32 XScale, FLOAT32 YScale) { +/* + ** Parameters: + ** MicroFeatures features to be smeared + ** XScale # of normalized units per pixel in x dir + ** YScale # of normalized units per pixel in y dir + ** Globals: none + ** Operation: Add a random amount to each bulge parameter of each + ** feature. The amount added is between -0.5 pixels and + ** 0.5 pixels. This is done to prevent the prototypes + ** generated in training from being unrealistically tight. + ** Return: none + ** Exceptions: none + ** History: Thu Jun 28 18:03:38 1990, DSJ, Created. + */ + MICROFEATURE MicroFeature; + FLOAT32 MinSmear; + FLOAT32 MaxSmear; + FLOAT32 Cos, Sin; + FLOAT32 Scale; + + iterate(MicroFeatures) { + MicroFeature = NextFeatureOf (MicroFeatures); + + Cos = fabs (cos (2.0 * PI * OrientationOf (MicroFeature))); + Sin = fabs (sin (2.0 * PI * OrientationOf (MicroFeature))); + Scale = YScale * Cos + XScale * Sin; + + MinSmear = -0.5 * Scale / (BULGENORMALIZER * LengthOf (MicroFeature)); + MaxSmear = 0.5 * Scale / (BULGENORMALIZER * LengthOf (MicroFeature)); + + FirstBulgeOf (MicroFeature) += UniformRandomNumber (MinSmear, MaxSmear); + SecondBulgeOf (MicroFeature) += UniformRandomNumber (MinSmear, MaxSmear); + } +} /* SmearBulges */ diff --git a/classify/mfx.h b/classify/mfx.h new file mode 100644 index 0000000000..eabfc40b27 --- /dev/null +++ b/classify/mfx.h @@ -0,0 +1,52 @@ +/****************************************************************************** + ** 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 "fxdefs.h" + +extern FLOAT32 MinSlope; +extern FLOAT32 MaxSlope; +/**---------------------------------------------------------------------------- + Public Function Prototypes +----------------------------------------------------------------------------**/ +void InitMicroFxVars(); + +CHAR_FEATURES BlobMicroFeatures(TBLOB *Blob, LINE_STATS *LineStats); + +/* +#if defined(__STDC__) || defined(__cplusplus) +# define _ARGS(s) s +#else +# define _ARGS(s) () +#endif*/ + +/* mfx.c +void InitMicroFxVars + _ARGS((void)); + +CHAR_FEATURES BlobMicroFeatures + _ARGS((BLOB *Blob, + LINE_STATS *LineStats)); + +#undef _ARGS +*/ +#endif diff --git a/classify/normfeat.cpp b/classify/normfeat.cpp new file mode 100644 index 0000000000..a090843ab8 --- /dev/null +++ b/classify/normfeat.cpp @@ -0,0 +1,132 @@ +/****************************************************************************** + ** 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 "mfoutline.h" +#include "intfx.h" + +#include "ocrfeatures.h" //Debug +#include //Debug +#include "efio.h" //Debug +//#include "christydbg.h" + +/**---------------------------------------------------------------------------- + Global Data Definitions and Declarations +----------------------------------------------------------------------------**/ +/**---------------------------------------------------------------------------- + Public Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +FLOAT32 ActualOutlineLength(FEATURE Feature) { +/* + ** Parameters: + ** Feature normalization feature + ** Globals: none + ** Operation: This routine returns the length that the outline + ** would have been if it were baseline normalized instead + ** of character normalized. + ** Return: Baseline normalized length of outline. + ** Exceptions: none + ** History: Thu Dec 20 14:50:57 1990, DSJ, Created. + */ + return (ParamOf (Feature, CharNormLength) * LENGTH_COMPRESSION); + +} /* ActualOutlineLength */ + + +/*---------------------------------------------------------------------------*/ +FEATURE_SET ExtractCharNormFeatures(TBLOB *Blob, LINE_STATS *LineStats) { +/* + ** Parameters: + ** Blob blob to extract char norm feature from + ** LineStats statistics on text row blob is in + ** Globals: none + ** Operation: Compute a feature whose parameters describe how a + ** character will be affected by the character normalization + ** algorithm. The feature parameters are: + ** y position of center of mass in baseline coordinates + ** total length of outlines in baseline coordinates + ** divided by a scale factor + ** radii of gyration about the center of mass in + ** baseline coordinates + ** Return: Character normalization feature for Blob. + ** Exceptions: none + ** History: Wed May 23 18:06:38 1990, DSJ, Created. + */ + FEATURE_SET FeatureSet; + FEATURE Feature; + FLOAT32 Scale; + FLOAT32 Baseline; + LIST Outlines; + INT_FEATURE_ARRAY blfeatures; + INT_FEATURE_ARRAY cnfeatures; + INT_FX_RESULT_STRUCT FXInfo; + + /* allocate the feature and feature set - note that there is always one + and only one char normalization feature for any blob */ + FeatureSet = NewFeatureSet (1); + Feature = NewFeature (&CharNormDesc); + AddFeature(FeatureSet, Feature); + + /* compute the normalization statistics for this blob */ + Outlines = ConvertBlob (Blob); + /*---------Debug--------------------------------------------------* + OFile = fopen ("f:/ims/debug/nfOutline.logCPP", "r"); + if (OFile == NULL) + { + OFile = Efopen ("f:/ims/debug/nfOutline.logCPP", "w"); + WriteOutlines(OFile, Outlines); + } + else + { + fclose (OFile); + OFile = Efopen ("f:/ims/debug/nfOutline.logCPP", "a"); + } + WriteOutlines(OFile, Outlines); + fclose (OFile); + *--------------------------------------------------------------------*/ + + ExtractIntFeat(Blob, blfeatures, cnfeatures, &FXInfo); + Baseline = BaselineAt (LineStats, FXInfo.Xmean); + Scale = ComputeScaleFactor (LineStats); + ParamOf (Feature, CharNormY) = (FXInfo.Ymean - Baseline) * Scale; + ParamOf (Feature, CharNormLength) = + FXInfo.Length * Scale / LENGTH_COMPRESSION; + ParamOf (Feature, CharNormRx) = FXInfo.Rx * Scale; + ParamOf (Feature, CharNormRy) = FXInfo.Ry * Scale; + + /*---------Debug--------------------------------------------------* + File = fopen ("f:/ims/debug/nfFeatSet.logCPP", "r"); + if (File == NULL) + { + File = Efopen ("f:/ims/debug/nfFeatSet.logCPP", "w"); + WriteFeatureSet(File, FeatureSet); + } + else + { + fclose (File); + File = Efopen ("f:/ims/debug/nfFeatSet.logCPP", "a"); + } + WriteFeatureSet(File, FeatureSet); + fclose (File); + *--------------------------------------------------------------------*/ + FreeOutlines(Outlines); + return (FeatureSet); +} /* ExtractCharNormFeatures */ diff --git a/classify/normfeat.h b/classify/normfeat.h new file mode 100644 index 0000000000..ce2ec70322 --- /dev/null +++ b/classify/normfeat.h @@ -0,0 +1,63 @@ +/****************************************************************************** + ** 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" +#include "tessclas.h" +#include "fxdefs.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(TBLOB *Blob, LINE_STATS *LineStats); + +/* +#if defined(__STDC__) || defined(__cplusplus) +# define _ARGS(s) s +#else +# define _ARGS(s) () +#endif*/ + +/* normfeat.c +FLOAT32 ActualOutlineLength + _ARGS((FEATURE Feature)); + +FEATURE_SET ExtractCharNormFeatures + _ARGS((BLOB *Blob, + LINE_STATS *LineStats)); + +#undef _ARGS +*/ + +/**---------------------------------------------------------------------------- + Global Data Definitions and Declarations +----------------------------------------------------------------------------**/ +extern FEATURE_DESC_STRUCT CharNormDesc; +#endif diff --git a/classify/normmatch.cpp b/classify/normmatch.cpp new file mode 100644 index 0000000000..8af6ed41af --- /dev/null +++ b/classify/normmatch.cpp @@ -0,0 +1,300 @@ +/****************************************************************************** + ** 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 "clusttool.h" +#include "normfeat.h" +#include "debug.h" +#include "const.h" +#include "efio.h" +#include "emalloc.h" +#include "globals.h" +#include "scanutils.h" + +#include +#include + +/* define default filenames for training data */ +#define NORM_PROTO_FILE "tessdata/normproto" + +typedef struct +{ + int NumParams; + PARAM_DESC *ParamDesc; + LIST Protos[MAX_CLASS_ID + 1]; +} + + +NORM_PROTOS; + +/**---------------------------------------------------------------------------- + Private Function Prototypes +----------------------------------------------------------------------------**/ +FLOAT32 NormEvidenceOf(register FLOAT32 NormAdj); + +void PrintNormMatch(FILE *File, + int NumParams, + PROTOTYPE *Proto, + FEATURE Feature); + +NORM_PROTOS *ReadNormProtos(FILE *File); + +/**---------------------------------------------------------------------------- + Global Data Definitions and Declarations +----------------------------------------------------------------------------**/ +/* global data structure to hold char normalization protos */ +static NORM_PROTOS *NormProtos; + +/* name of file containing char normalization protos */ +static const char *NormProtoFile = NORM_PROTO_FILE; + +/* control knobs used to control the normalization adjustment process */ +make_float_var (NormAdjMidpoint, 32.0, MakeNormAdjMidpoint, +15, 16, SetNormAdjMidpoint, "Norm adjust midpoint ...") +make_float_var (NormAdjCurl, 2.0, MakeNormAdjCurl, +15, 17, SetNormAdjCurl, "Norm adjust curl ...") +//extern char *demodir; +/**---------------------------------------------------------------------------- + Public Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +FLOAT32 ComputeNormMatch(CLASS_ID ClassId, FEATURE Feature, BOOL8 DebugMatch) { +/* + ** Parameters: + ** ClassId id of class to match against + ** Feature character normalization feature + ** DebugMatch controls dump of debug info + ** Globals: + ** NormProtos character normalization prototypes + ** Operation: This routine compares Features against each character + ** normalization proto for ClassId and returns the match + ** rating of the best match. + ** Return: Best match rating for Feature against protos of ClassId. + ** Exceptions: none + ** History: Wed Dec 19 16:56:12 1990, DSJ, Created. + */ + LIST Protos; + FLOAT32 BestMatch; + FLOAT32 Match; + FLOAT32 Delta; + PROTOTYPE *Proto; + int ProtoId; + + /* handle requests for classification as noise */ + if (ClassId == NO_CLASS) { + /* kludge - clean up constants and make into control knobs later */ + Match = (ParamOf (Feature, CharNormLength) * + ParamOf (Feature, CharNormLength) * 500.0 + + ParamOf (Feature, CharNormRx) * + ParamOf (Feature, CharNormRx) * 8000.0 + + ParamOf (Feature, CharNormRy) * + ParamOf (Feature, CharNormRy) * 8000.0); + return (1.0 - NormEvidenceOf (Match)); + } + + BestMatch = MAX_FLOAT32; + Protos = NormProtos->Protos[ClassId]; + + if (DebugMatch) { + cprintf ("\nFeature = "); + WriteFeature(stdout, Feature); + } + + ProtoId = 0; + iterate(Protos) { + Proto = (PROTOTYPE *) first (Protos); + Delta = ParamOf (Feature, CharNormY) - Proto->Mean[CharNormY]; + Match = Delta * Delta * Proto->Weight.Elliptical[CharNormY]; + Delta = ParamOf (Feature, CharNormRx) - Proto->Mean[CharNormRx]; + Match += Delta * Delta * Proto->Weight.Elliptical[CharNormRx]; + + if (Match < BestMatch) + BestMatch = Match; + + if (DebugMatch) { + cprintf ("Proto %1d = ", ProtoId); + WriteNFloats (stdout, NormProtos->NumParams, Proto->Mean); + cprintf (" var = "); + WriteNFloats (stdout, NormProtos->NumParams, + Proto->Variance.Elliptical); + cprintf (" match = "); + PrintNormMatch (stdout, NormProtos->NumParams, Proto, Feature); + } + ProtoId++; + } + return (1.0 - NormEvidenceOf (BestMatch)); +} /* ComputeNormMatch */ + + +/*---------------------------------------------------------------------------*/ +void GetNormProtos() { +/* + ** Parameters: none + ** Globals: + ** NormProtoFile name of file containing normalization protos + ** NormProtos global data structure to hold protos + ** Operation: This routine reads in a set of character normalization + ** protos from NormProtoFile and places them into NormProtos. + ** Return: none + ** Exceptions: none + ** History: Wed Dec 19 16:24:25 1990, DSJ, Created. + */ + FILE *File; + char name[1024]; + + strcpy(name, demodir); + strcat(name, NormProtoFile); + File = Efopen (name, "r"); + NormProtos = ReadNormProtos (File); + fclose(File); + +} /* GetNormProtos */ + +void FreeNormProtos() { + if (NormProtos != NULL) { + for (int i = 0; i <= MAX_CLASS_ID; i++) + FreeProtoList(&NormProtos->Protos[i]); + Efree(NormProtos->ParamDesc); + Efree(NormProtos); + NormProtos = NULL; + } +} + +/*---------------------------------------------------------------------------*/ +void InitNormProtoVars() { +/* + ** Parameters: none + ** Globals: + ** NormProtoFile filename for normalization protos + ** Operation: Initialize the control variables for the normalization + ** matcher. + ** Return: none + ** Exceptions: none + ** History: Mon Nov 5 17:22:10 1990, DSJ, Created. + */ + VALUE dummy; + + string_variable (NormProtoFile, "NormProtoFile", NORM_PROTO_FILE); + + MakeNormAdjMidpoint(); + MakeNormAdjCurl(); + +} /* InitNormProtoVars */ + + +/**---------------------------------------------------------------------------- + Private Code +----------------------------------------------------------------------------**/ +/********************************************************************** + * 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) + **********************************************************************/ +FLOAT32 NormEvidenceOf(register FLOAT32 NormAdj) { + NormAdj /= NormAdjMidpoint; + + if (NormAdjCurl == 3) + NormAdj = NormAdj * NormAdj * NormAdj; + else if (NormAdjCurl == 2) + NormAdj = NormAdj * NormAdj; + else + NormAdj = pow (NormAdj, NormAdjCurl); + return (1.0 / (1.0 + NormAdj)); +} + + +/*---------------------------------------------------------------------------*/ +void PrintNormMatch(FILE *File, + int NumParams, + PROTOTYPE *Proto, + FEATURE Feature) { +/* + ** Parameters: + ** File open text file to dump match debug info to + ** NumParams # of parameters in proto and feature + ** Proto[] array of prototype parameters + ** Feature[] array of feature parameters + ** Globals: none + ** Operation: This routine dumps out detailed normalization match info. + ** Return: none + ** Exceptions: none + ** History: Wed Jan 2 09:49:35 1991, DSJ, Created. + */ + int i; + FLOAT32 ParamMatch; + FLOAT32 TotalMatch; + + for (i = 0, TotalMatch = 0.0; i < NumParams; i++) { + ParamMatch = ((ParamOf (Feature, 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 */ + + +/*---------------------------------------------------------------------------*/ +NORM_PROTOS *ReadNormProtos(FILE *File) { +/* + ** Parameters: + ** File open text file to read normalization protos from + ** Globals: none + ** Operation: 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. + ** Return: Character normalization protos. + ** Exceptions: none + ** History: Wed Dec 19 16:38:49 1990, DSJ, Created. + */ + NORM_PROTOS *NormProtos; + int i; + char ClassId[2]; + LIST Protos; + int NumProtos; + + /* allocate and initialization data structure */ + NormProtos = (NORM_PROTOS *) Emalloc (sizeof (NORM_PROTOS)); + for (i = 0; i <= MAX_CLASS_ID; i++) + NormProtos->Protos[i] = NIL; + + /* 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 (fscanf (File, "%1s %d", ClassId, &NumProtos) == 2) { + Protos = NormProtos->Protos[ClassId[0]]; + for (i = 0; i < NumProtos; i++) + Protos = + push_last (Protos, ReadPrototype (File, NormProtos->NumParams)); + NormProtos->Protos[ClassId[0]] = Protos; + } + + return (NormProtos); + +} /* ReadNormProtos */ diff --git a/classify/normmatch.h b/classify/normmatch.h new file mode 100644 index 0000000000..a6798e4858 --- /dev/null +++ b/classify/normmatch.h @@ -0,0 +1,59 @@ +/****************************************************************************** + ** 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 "cluster.h" +#include "ocrfeatures.h" + +/**---------------------------------------------------------------------------- + Public Function Prototypes +----------------------------------------------------------------------------**/ +FLOAT32 ComputeNormMatch(CLASS_ID ClassId, FEATURE Feature, BOOL8 DebugMatch); + +void GetNormProtos(); +void FreeNormProtos(); + +void InitNormProtoVars(); + +/* +#if defined(__STDC__) || defined(__cplusplus) +# define _ARGS(s) s +#else +# define _ARGS(s) () +#endif*/ + +/* normmatch.c * +FLOAT32 ComputeNormMatch + _ARGS((CLASS_ID ClassId, + FEATURE Feature, + BOOL8 DebugMatch)); + +void GetNormProtos + _ARGS((void)); + +void InitNormProtoVars + _ARGS((void)); + +#undef _ARGS +*/ +#endif diff --git a/classify/ocrfeatures.cpp b/classify/ocrfeatures.cpp new file mode 100644 index 0000000000..85a807bb41 --- /dev/null +++ b/classify/ocrfeatures.cpp @@ -0,0 +1,310 @@ +/****************************************************************************** + ** 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" + +/**---------------------------------------------------------------------------- + Public Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +BOOL8 AddFeature(FEATURE_SET FeatureSet, FEATURE Feature) { +/* + ** Parameters: + ** FeatureSet set of features to add Feature to + ** Feature feature to be added to FeatureSet + ** Globals: none + ** Operation: 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. + ** Return: TRUE if feature added to set, FALSE if set is already full. + ** Exceptions: none + ** History: Tue May 22 17:22:23 1990, DSJ, Created. + */ + if (NumFeaturesIn (FeatureSet) >= MaxNumFeaturesIn (FeatureSet)) { + FreeFeature(Feature); + return (FALSE); + } + + FeatureIn (FeatureSet, NumFeaturesIn (FeatureSet)) = Feature; + NumFeaturesIn (FeatureSet)++; + return (TRUE); + +} /* AddFeature */ + + +/*---------------------------------------------------------------------------*/ +void DefaultInitFXVars() { +/* + ** Parameters: none + ** Globals: none + ** Operation: This routine can be used by any feature extractor which + ** does not use adjustable controls. + ** It does nothing. + ** Return: none + ** Exceptions: none + ** History: Wed May 23 16:37:45 1990, DSJ, Created. + */ +} /* DefaultInitFXVars */ + + +/*---------------------------------------------------------------------------*/ +void FreeFeature(FEATURE Feature) { +/* + ** Parameters: + ** Feature feature to be deallocated. + ** Globals: none + ** Operation: Release the memory consumed by the specified feature. + ** Return: none + ** Exceptions: none + ** History: Mon May 21 13:33:27 1990, DSJ, Created. + */ + if (Feature) { + c_free_struct (Feature, sizeof (FEATURE_STRUCT) + + sizeof (FLOAT32) * (NumParamsIn (Feature) - 1), + "sizeof(FEATURE_STRUCT)+sizeof(FLOAT32)*(NumParamsIn(Feature)-1)"); + } + +} /* FreeFeature */ + + +/*---------------------------------------------------------------------------*/ +void FreeFeatureSet(FEATURE_SET FeatureSet) { +/* + ** Parameters: + ** FeatureSet set of features to be freed + ** Globals: none + ** Operation: Release the memory consumed by the specified feature + ** set. This routine also frees the memory consumed by the + ** features contained in the set. + ** Return: none + ** Exceptions: none + ** History: Mon May 21 13:59:46 1990, DSJ, Created. + */ + int i; + + if (FeatureSet) { + for (i = 0; i < NumFeaturesIn (FeatureSet); i++) + FreeFeature (FeatureIn (FeatureSet, i)); + memfree(FeatureSet); + } +} /* FreeFeatureSet */ + + +/*---------------------------------------------------------------------------*/ +FEATURE NewFeature(FEATURE_DESC FeatureDesc) { +/* + ** Parameters: + ** FeatureDesc description of feature to be created. + ** Globals: none + ** Operation: Allocate and return a new feature of the specified + ** type. + ** Return: New feature. + ** Exceptions: none + ** History: Mon May 21 14:06:42 1990, DSJ, Created. + */ + FEATURE Feature; + + Feature = (FEATURE) c_alloc_struct (sizeof (FEATURE_STRUCT) + + (FeatureDesc->NumParams - 1) * + sizeof (FLOAT32), + "sizeof(FEATURE_STRUCT)+sizeof(FLOAT32)*(NumParamsIn(Feature)-1)"); + TypeOf (Feature) = FeatureDesc; + return (Feature); + +} /* NewFeature */ + + +/*---------------------------------------------------------------------------*/ +FEATURE_SET NewFeatureSet(int NumFeatures) { +/* + ** Parameters: + ** NumFeatures maximum # of features to be put in feature set + ** Globals: none + ** Operation: Allocate and return a new feature set large enough to + ** hold the specified number of features. + ** Return: New feature set. + ** Exceptions: none + ** History: Mon May 21 14:22:40 1990, DSJ, Created. + */ + FEATURE_SET FeatureSet; + + FeatureSet = (FEATURE_SET) Emalloc (sizeof (FEATURE_SET_STRUCT) + + (NumFeatures - 1) * sizeof (FEATURE)); + MaxNumFeaturesIn (FeatureSet) = NumFeatures; + NumFeaturesIn (FeatureSet) = 0; + return (FeatureSet); + +} /* NewFeatureSet */ + + +/*---------------------------------------------------------------------------*/ +FEATURE ReadFeature(FILE *File, FEATURE_DESC FeatureDesc) { +/* + ** Parameters: + ** File open text file to read feature from + ** FeatureDesc specifies type of feature to read from File + ** Globals: none + ** Operation: 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. + ** Return: New feature read from File. + ** Exceptions: ILLEGAL_FEATURE_PARAM if text file doesn't match expected format + ** History: Wed May 23 08:53:16 1990, DSJ, Created. + */ + FEATURE Feature; + int i; + + Feature = NewFeature (FeatureDesc); + for (i = 0; i < NumParamsIn (Feature); i++) { + if (fscanf (File, "%f", &(ParamOf (Feature, i))) != 1) + DoError (ILLEGAL_FEATURE_PARAM, "Illegal feature parameter spec"); + } + return (Feature); + +} /* ReadFeature */ + + +/*---------------------------------------------------------------------------*/ +FEATURE_SET ReadFeatureSet(FILE *File, FEATURE_DESC FeatureDesc) { +/* + ** Parameters: + ** File open text file to read new feature set from + ** FeatureDesc specifies type of feature to read from File + ** Globals: none + ** Operation: 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. + ** Return: New feature set read from File. + ** Exceptions: none + ** History: Wed May 23 09:17:31 1990, DSJ, Created. + */ + FEATURE_SET FeatureSet; + int NumFeatures; + int i; + + if (fscanf (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 */ + + +/*---------------------------------------------------------------------------*/ +void WriteFeature(FILE *File, FEATURE Feature) { +/* + ** Parameters: + ** File open text file to write Feature to + ** Feature feature to write out to File + ** Globals: none + ** Operation: Write a textual representation of Feature to File. + ** 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. + ** Return: none + ** Exceptions: none + ** History: Wed May 23 09:28:18 1990, DSJ, Created. + */ + int i; + + for (i = 0; i < NumParamsIn (Feature); i++) + fprintf (File, " %12g", ParamOf (Feature, i)); + fprintf (File, "\n"); + +} /* WriteFeature */ + + +/*---------------------------------------------------------------------------*/ +void WriteFeatureSet(FILE *File, FEATURE_SET FeatureSet) { +/* + ** Parameters: + ** File open text file to write FeatureSet to + ** FeatureSet feature set to write to File + ** Globals: none + ** Operation: 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. + ** Return: none + ** Exceptions: none + ** History: Wed May 23 10:06:03 1990, DSJ, Created. + */ + int i; + + if (FeatureSet) { + fprintf (File, "%d\n", NumFeaturesIn (FeatureSet)); + for (i = 0; i < NumFeaturesIn (FeatureSet); i++) + WriteFeature (File, FeatureIn (FeatureSet, i)); + } +} /* WriteFeatureSet */ + + +/*---------------------------------------------------------------------------*/ +void WriteOldParamDesc(FILE *File, FEATURE_DESC FeatureDesc) { +/* + ** Parameters: + ** File open text file to write FeatureDesc to + ** FeatureDesc feature descriptor to write to File + ** Globals: none + ** Operation: Write a textual representation of FeatureDesc to File + ** in the old format (i.e. the format used by the clusterer). + ** This format is: + ** Number of Params + ** Description of Param 1 + ** ... + ** Return: none + ** Exceptions: none + ** History: Fri May 25 15:27:18 1990, DSJ, Created. + */ + 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 */ diff --git a/classify/ocrfeatures.h b/classify/ocrfeatures.h new file mode 100644 index 0000000000..6c9e4c9a4b --- /dev/null +++ b/classify/ocrfeatures.h @@ -0,0 +1,170 @@ +/****************************************************************************** + ** 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 "funcdefs.h" +#include "tessclas.h" +#include "fxdefs.h" + +#include + +#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.*/ + +typedef struct +{ + struct fds *Type; /* points to description of feature type */ + FLOAT32 Params[1]; /* variable size array - params for feature */ +} FEATURE_STRUCT; +typedef FEATURE_STRUCT *FEATURE; + +typedef struct +{ + UINT16 NumFeatures; /* number of features in set */ + UINT16 MaxNumFeatures; /* maximum size of feature set */ + FEATURE Features[1]; /* variable size array of features */ +} FEATURE_SET_STRUCT; +typedef FEATURE_SET_STRUCT *FEATURE_SET; + +/* Define various function types which will be needed for "class methods"*/ +typedef FEATURE (*FEAT_FUNC) (); +typedef FEATURE_SET (*FX_FUNC) (TBLOB *, LINE_STATS *); +typedef FLOAT32 (*PENALTY_FUNC) (); + +typedef struct +{ + 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 */ +} PARAM_DESC; + +typedef struct fds +{ + UINT16 NumParams; /* total # of params */ + UINT8 NumLinearParams; /* # of linear params */ + UINT8 NumCircularParams; /* # of linear params */ + UINT8 MinFeatPerChar; /* min # of feats allowed */ + UINT8 MaxFeatPerChar; /* max # of feats allowed */ + char LongName[FEAT_NAME_SIZE]; /* long name for feature */ + char ShortName[FEAT_NAME_SIZE];/* short name for feature */ + PARAM_DESC *ParamDesc; /* array - one per param */ +} FEATURE_DESC_STRUCT; + /* one per feature type */ +typedef FEATURE_DESC_STRUCT *FEATURE_DESC; + +typedef struct fxs +{ + FX_FUNC Extractor; /* func to extract features */ + VOID_FUNC InitExtractorVars; /* func to init fx controls */ +} FEATURE_EXT_STRUCT; + +/*---------------------------------------------------------------------- + Macros for defining the parameters of a new features +----------------------------------------------------------------------*/ +#define StartParamDesc(Name) \ +static 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, + MinFeatPerChar, MaxFeatPerChar, + LongName, ShortName, ParamName, + Extractor, Displayer, + ComputeExtraPenalty, + InitExtractor, InitExtractorVars, TweekExtractorVars) +----------------------------------------------------------------------*/ +#define DefineFeature(Name, NL, NC, Min, Max, LN, SN, PN) \ +FEATURE_DESC_STRUCT Name = { \ + ((NL) + (NC)), NL, NC, Min, Max, LN, SN, PN}; +#define DefineFeatureExt(Name, E, IEV) FEATURE_EXT_STRUCT Name = {E, IEV}; + +/*---------------------------------------------------------------------- + Macros for accessing features +----------------------------------------------------------------------*/ +#define TypeOf(Feature) ((Feature)->Type) +#define ParamOf(Feature, N) ((Feature)->Params[N]) +#define NumParamsIn(Feature) (TypeOf (Feature) -> NumParams) + +/*---------------------------------------------------------------------- + Macros for accessing feature sets +----------------------------------------------------------------------*/ +#define NumFeaturesIn(Set) ((Set)->NumFeatures) +#define MaxNumFeaturesIn(Set) ((Set)->MaxNumFeatures) +#define FeatureIn(Set, N) ((Set)->Features[N]) + +/*---------------------------------------------------------------------- + Macros for accessing feature descriptions +----------------------------------------------------------------------*/ +#define ShortNameOf(FeatDesc) ((FeatDesc)->ShortName) +#define LongNameOf(FeatDesc) ((FeatDesc)->LongName) +#define ExtractUsing(FeatDesc) (*(FeatDesc)->Extractor) +#define InitFXVarsUsing(FD) (*(FD)->InitExtractorVars) + +/*---------------------------------------------------------------------- + Generic routines that work for all feature types +----------------------------------------------------------------------*/ +BOOL8 AddFeature(FEATURE_SET FeatureSet, FEATURE Feature); + +void DefaultInitFXVars(); + +void FreeFeature(FEATURE Feature); + +void FreeFeatureSet(FEATURE_SET FeatureSet); + +FEATURE NewFeature(FEATURE_DESC FeatureDesc); + +FEATURE_SET NewFeatureSet(int NumFeatures); + +FEATURE ReadFeature(FILE *File, FEATURE_DESC FeatureDesc); + +FEATURE_SET ReadFeatureSet(FILE *File, FEATURE_DESC FeatureDesc); + +void WriteFeature(FILE *File, FEATURE Feature); + +void WriteFeatureSet(FILE *File, FEATURE_SET FeatureSet); + +void WriteOldParamDesc(FILE *File, FEATURE_DESC FeatureDesc); +#endif diff --git a/classify/outfeat.cpp b/classify/outfeat.cpp new file mode 100644 index 0000000000..1200f306a7 --- /dev/null +++ b/classify/outfeat.cpp @@ -0,0 +1,262 @@ +/****************************************************************************** + ** 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 "mfoutline.h" +#include "variables.h" +#include "sigmenu.h" + +#include "ocrfeatures.h" //Debug +#include //Debug +#include "efio.h" //Debug +//#include "christydbg.h" + +/**---------------------------------------------------------------------------- + Private Function Prototypes +----------------------------------------------------------------------------**/ +/* +#if defined(__STDC__) || defined(__cplusplus) +# define _ARGS(s) s +#else +# define _ARGS(s) () +#endif*/ + +/* /users/danj/wiseowl/src/danj/microfeatures/outfeat.c +void AddOutlineFeatureToSet + _ARGS((FPOINT *Start, + FPOINT *End, + FEATURE_SET FeatureSet)); + +void ConvertToOutlineFeatures + _ARGS((MFOUTLINE Outline, + FEATURE_SET FeatureSet)); + +void NormalizeOutlineX + _ARGS((FEATURE_SET FeatureSet)); + +#undef _ARGS +*/ +/**---------------------------------------------------------------------------- + Global Data Definitions and Declarations +----------------------------------------------------------------------------**/ +/**---------------------------------------------------------------------------- + Public Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +FEATURE_SET ExtractOutlineFeatures(TBLOB *Blob, LINE_STATS *LineStats) { +/* + ** Parameters: + ** Blob blob to extract pico-features from + ** LineStats statistics on text row blob is in + ** Globals: none + ** Operation: Convert each segment in the outline to a feature + ** and return the features. + ** Return: Outline-features for Blob. + ** Exceptions: none + ** History: 11/13/90, DSJ, Created. + ** 05/24/91, DSJ, Updated for either char or baseline normalize. + */ + 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, LineStats, &XScale, &YScale); + RemainingOutlines = Outlines; + iterate(RemainingOutlines) { + Outline = (MFOUTLINE) first (RemainingOutlines); + /*---------Debug--------------------------------------------------* + OFile = fopen ("f:/ims/debug/ofOutline.logCPP", "r"); + if (OFile == NULL) + { + OFile = Efopen ("f:/ims/debug/ofOutline.logCPP", "w"); + WriteOutline(OFile, Outline); + } + else + { + fclose (OFile); + OFile = Efopen ("f:/ims/debug/ofOutline.logCPP", "a"); + } + WriteOutline(OFile, Outline); + fclose (OFile); + *--------------------------------------------------------------------*/ + ConvertToOutlineFeatures(Outline, FeatureSet); + } + if (NormMethod == baseline) + NormalizeOutlineX(FeatureSet); + /*---------Debug--------------------------------------------------* + File = fopen ("f:/ims/debug/ofFeatSet.logCPP", "r"); + if (File == NULL) + { + File = Efopen ("f:/ims/debug/ofFeatSet.logCPP", "w"); + WriteFeatureSet(File, FeatureSet); + } + else + { + fclose (File); + File = Efopen ("f:/ims/debug/ofFeatSet.logCPP", "a"); + } + WriteFeatureSet(File, FeatureSet); + fclose (File); + *--------------------------------------------------------------------*/ + FreeOutlines(Outlines); + return (FeatureSet); +} /* ExtractOutlineFeatures */ + + +/*---------------------------------------------------------------------------*/ +void InitOutlineFXVars() { + //once contained a dummy +/* + ** Parameters: none + ** Globals: none + ** Operation: Initialize the outline-feature extractor variables that can + ** be tuned without recompiling. + ** Return: none + ** Exceptions: none + ** History: 11/13/90, DSJ, Created. + */ +} /* InitOutlineFXVars */ + + +/**---------------------------------------------------------------------------- + Private Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +void AddOutlineFeatureToSet(FPOINT *Start, + FPOINT *End, + FEATURE_SET FeatureSet) { +/* + ** Parameters: + ** Start starting point of outline-feature + ** End ending point of outline-feature + ** FeatureSet set to add outline-feature to + ** Globals: none + ** Operation: 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. + ** Return: none (results are placed in FeatureSet) + ** Exceptions: none + ** History: 11/13/90, DSJ, Created. + */ + FEATURE Feature; + + Feature = NewFeature (&OutlineFeatDesc); + ParamOf (Feature, OutlineFeatDir) = NormalizedAngleFrom (Start, End, 1.0); + ParamOf (Feature, OutlineFeatX) = AverageOf (Xof (*Start), Xof (*End)); + ParamOf (Feature, OutlineFeatY) = AverageOf (Yof (*Start), Yof (*End)); + ParamOf (Feature, OutlineFeatLength) = DistanceBetween (*Start, *End); + AddFeature(FeatureSet, Feature); + +} /* AddOutlineFeatureToSet */ + + +/*---------------------------------------------------------------------------*/ +void ConvertToOutlineFeatures(MFOUTLINE Outline, FEATURE_SET FeatureSet) { +/* + ** Parameters: + ** Outline outline to extract outline-features from + ** FeatureSet set of features to add outline-features to + ** Globals: none + ** Operation: + ** This routine steps converts each section in the specified + ** outline to a feature described by its x,y position, length + ** and angle. + ** Return: none (results are returned in FeatureSet) + ** Exceptions: none + ** History: 11/13/90, DSJ, Created. + ** 5/24/91, DSJ, Added hidden edge capability. + */ + MFOUTLINE Next; + MFOUTLINE First; + FPOINT FeatureStart; + FPOINT FeatureEnd; + + if (DegenerateOutline (Outline)) + return; + + First = Outline; + Next = First; + do { + CopyPoint (PositionOf (PointAt (Next)), FeatureStart); + 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 (IsVisible (PointAt (Next))) { + CopyPoint (PositionOf (PointAt (Next)), FeatureEnd); + AddOutlineFeatureToSet(&FeatureStart, &FeatureEnd, FeatureSet); + } + } + while (Next != First); +} /* ConvertToOutlineFeatures */ + + +/*---------------------------------------------------------------------------*/ +void NormalizeOutlineX(FEATURE_SET FeatureSet) { +/* + ** Parameters: + ** FeatureSet outline-features to be normalized + ** Globals: none + ** Operation: 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). + ** Return: none (FeatureSet is changed) + ** Exceptions: none + ** History: 11/13/90, DSJ, Created. + */ + int i; + FEATURE Feature; + FLOAT32 Length; + FLOAT32 TotalX = 0.0; + FLOAT32 TotalWeight = 0.0; + FLOAT32 Origin; + + if (NumFeaturesIn (FeatureSet) <= 0) + return; + + for (i = 0; i < NumFeaturesIn (FeatureSet); i++) { + Feature = FeatureIn (FeatureSet, i); + Length = ParamOf (Feature, OutlineFeatLength); + TotalX += ParamOf (Feature, OutlineFeatX) * Length; + TotalWeight += Length; + } + Origin = TotalX / TotalWeight; + + for (i = 0; i < NumFeaturesIn (FeatureSet); i++) { + Feature = FeatureIn (FeatureSet, i); + ParamOf (Feature, OutlineFeatX) -= Origin; + } +} /* NormalizeOutlineX */ diff --git a/classify/outfeat.h b/classify/outfeat.h new file mode 100644 index 0000000000..818a0c3d25 --- /dev/null +++ b/classify/outfeat.h @@ -0,0 +1,76 @@ +/****************************************************************************** + ** 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) +/**---------------------------------------------------------------------------- + Public Function Prototypes +----------------------------------------------------------------------------**/ +FEATURE_SET ExtractOutlineFeatures(TBLOB *Blob, LINE_STATS *LineStats); + +void InitOutlineFXVars(); + +/*--------------------------------------------------------------------------- + Privat Function Prototypes +----------------------------------------------------------------------------*/ +void AddOutlineFeatureToSet(FPOINT *Start, + FPOINT *End, + FEATURE_SET FeatureSet); + +void ConvertToOutlineFeatures(MFOUTLINE Outline, FEATURE_SET FeatureSet); + +void NormalizeOutlineX(FEATURE_SET FeatureSet); + +/* +#if defined(__STDC__) || defined(__cplusplus) +# define _ARGS(s) s +#else +# define _ARGS(s) () +#endif*/ + +/* outfeat.c * +FEATURE_SET ExtractOutlineFeatures + _ARGS((BLOB *Blob, + LINE_STATS *LineStats)); + +void InitOutlineFXVars + _ARGS((void)); + +#undef _ARGS +*/ + +/**---------------------------------------------------------------------------- + Global Data Definitions and Declarations +----------------------------------------------------------------------------**/ +extern FEATURE_DESC_STRUCT OutlineFeatDesc; +#endif diff --git a/classify/picofeat.cpp b/classify/picofeat.cpp new file mode 100644 index 0000000000..37d22ed48b --- /dev/null +++ b/classify/picofeat.cpp @@ -0,0 +1,297 @@ +/****************************************************************************** + ** 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 "mfoutline.h" +#include "variables.h" +#include "sigmenu.h" +#include "hideedge.h" +#include "fpoint.h" + +#include + +#include "ocrfeatures.h" //Debug +#include //Debug +#include "efio.h" //Debug +//#include "christydbg.h" + +#define PICO_FEATURE_LENGTH 0.05 + +/*--------------------------------------------------------------------------- + Private Function Prototypes +----------------------------------------------------------------------------*/ +void ConvertSegmentToPicoFeat(FPOINT *Start, + FPOINT *End, + FEATURE_SET FeatureSet); + +void ConvertToPicoFeatures2(MFOUTLINE Outline, FEATURE_SET FeatureSet); + +void NormalizePicoX(FEATURE_SET FeatureSet); + +/* +#if defined(__STDC__) || defined(__cplusplus) +# define _ARGS(s) s +#else +# define _ARGS(s) () +#endif*/ + +/* /users/danj/wiseowl/src/danj/microfeatures/picofeat.c +void ConvertSegmentToPicoFeat + _ARGS((FPOINT *Start, + FPOINT *End, + FEATURE_SET FeatureSet)); + +void ConvertToPicoFeatures2 + _ARGS((MFOUTLINE Outline, + FEATURE_SET FeatureSet)); + +void NormalizePicoX + _ARGS((FEATURE_SET FeatureSet)); + +#undef _ARGS +*/ + +/**---------------------------------------------------------------------------- + Global Data Definitions and Declarations +----------------------------------------------------------------------------**/ + +/**---------------------------------------------------------------------------- + Public Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +FEATURE_SET ExtractPicoFeatures(TBLOB *Blob, LINE_STATS *LineStats) { +/* + ** Parameters: + ** Blob blob to extract pico-features from + ** LineStats statistics on text row blob is in + ** Globals: + ** NormMethod normalization method currently specified + ** Operation: Dummy for now. + ** Return: Pico-features for Blob. + ** Exceptions: none + ** History: 9/4/90, DSJ, Created. + */ + LIST Outlines; + LIST RemainingOutlines; + MFOUTLINE Outline; + FEATURE_SET FeatureSet; + FLOAT32 XScale, YScale; + + FeatureSet = NewFeatureSet (MAX_PICO_FEATURES); + + Outlines = ConvertBlob (Blob); + + NormalizeOutlines(Outlines, LineStats, &XScale, &YScale); + RemainingOutlines = Outlines; + iterate(RemainingOutlines) { + Outline = (MFOUTLINE) first (RemainingOutlines); + /*---------Debug--------------------------------------------------* + OFile = fopen ("f:/ims/debug/pfOutline.logCPP", "r"); + if (OFile == NULL) + { + OFile = Efopen ("f:/ims/debug/pfOutline.logCPP", "w"); + WriteOutline(OFile, Outline); + } + else + { + fclose (OFile); + OFile = Efopen ("f:/ims/debug/pfOutline.logCPP", "a"); + } + WriteOutline(OFile, Outline); + fclose (OFile); + *--------------------------------------------------------------------*/ + ConvertToPicoFeatures2(Outline, FeatureSet); + } + if (NormMethod == baseline) + NormalizePicoX(FeatureSet); + /*---------Debug--------------------------------------------------* + File = fopen ("f:/ims/debug/pfFeatSet.logCPP", "r"); + if (File == NULL) + { + File = Efopen ("f:/ims/debug/pfFeatSet.logCPP", "w"); + WriteFeatureSet(File, FeatureSet); + } + else + { + fclose (File); + File = Efopen ("f:/ims/debug/pfFeatSet.logCPP", "a"); + } + WriteFeatureSet(File, FeatureSet); + fclose (File); + *--------------------------------------------------------------------*/ + FreeOutlines(Outlines); + return (FeatureSet); + +} /* ExtractPicoFeatures */ + + +/*---------------------------------------------------------------------------*/ +void InitPicoFXVars() { +/* + ** Parameters: none + ** Globals: + ** PicoFeatureLength controls length of pico-features + ** Operation: Initialize the pico-feature extractor variables that can + ** be tuned without recompiling. + ** Return: none + ** Exceptions: none + ** History: 9/4/90, DSJ, Created. + */ + + VALUE dummy; + + float_variable (PicoFeatureLength, "PicoFeatureLength", + PICO_FEATURE_LENGTH); + +} /* InitPicoFXVars */ + +/**---------------------------------------------------------------------------- + Private Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +void ConvertSegmentToPicoFeat(FPOINT *Start, + FPOINT *End, + FEATURE_SET FeatureSet) { +/* + ** Parameters: + ** Start starting point of pico-feature + ** End ending point of pico-feature + ** FeatureSet set to add pico-feature to + ** Globals: + ** PicoFeatureLength length of a single pico-feature + ** Operation: 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. + ** Return: none (results are placed in FeatureSet) + ** Exceptions: none + ** History: Tue Apr 30 15:44:34 1991, DSJ, Created. + */ + 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 / PicoFeatureLength + 0.5); + if (NumFeatures < 1) + NumFeatures = 1; + + /* compute vector for one pico feature */ + Xof (Delta) = XDelta (*Start, *End) / NumFeatures; + Yof (Delta) = YDelta (*Start, *End) / NumFeatures; + + /* compute position of first pico feature */ + Xof (Center) = Xof (*Start) + Xof (Delta) / 2.0; + Yof (Center) = Yof (*Start) + Yof (Delta) / 2.0; + + /* compute each pico feature in segment and add to feature set */ + for (i = 0; i < NumFeatures; i++) { + Feature = NewFeature (&PicoFeatDesc); + ParamOf (Feature, PicoFeatDir) = Angle; + ParamOf (Feature, PicoFeatX) = Xof (Center); + ParamOf (Feature, PicoFeatY) = Yof (Center); + AddFeature(FeatureSet, Feature); + + Xof (Center) += Xof (Delta); + Yof (Center) += Yof (Delta); + } +} /* ConvertSegmentToPicoFeat */ + + +/*---------------------------------------------------------------------------*/ +void ConvertToPicoFeatures2(MFOUTLINE Outline, FEATURE_SET FeatureSet) { +/* + ** Parameters: + ** Outline outline to extract micro-features from + ** FeatureSet set of features to add pico-features to + ** Globals: + ** PicoFeatureLength length of features to be extracted + ** Operation: + ** This routine steps thru 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. + ** Return: none (results are returned in FeatureSet) + ** Exceptions: none + ** History: 4/30/91, DSJ, Adapted from ConvertToPicoFeatures(). + */ + 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 (IsVisible (PointAt (Next))) + ConvertSegmentToPicoFeat (&(PositionOf (PointAt (Current))), + &(PositionOf (PointAt (Next))), FeatureSet); + + Current = Next; + Next = NextPointAfter (Current); + } + while (Current != First); + +} /* ConvertToPicoFeatures2 */ + + +/*---------------------------------------------------------------------------*/ +void NormalizePicoX(FEATURE_SET FeatureSet) { +/* + ** Parameters: + ** FeatureSet pico-features to be normalized + ** Globals: none + ** Operation: 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). + ** Return: none (FeatureSet is changed) + ** Exceptions: none + ** History: Tue Sep 4 16:50:08 1990, DSJ, Created. + */ + int i; + FEATURE Feature; + FLOAT32 Origin = 0.0; + + for (i = 0; i < NumFeaturesIn (FeatureSet); i++) { + Feature = FeatureIn (FeatureSet, i); + Origin += ParamOf (Feature, PicoFeatX); + } + Origin /= NumFeaturesIn (FeatureSet); + + for (i = 0; i < NumFeaturesIn (FeatureSet); i++) { + Feature = FeatureIn (FeatureSet, i); + ParamOf (Feature, PicoFeatX) -= Origin; + } +} /* NormalizePicoX */ diff --git a/classify/picofeat.h b/classify/picofeat.h new file mode 100644 index 0000000000..8c08ee6b42 --- /dev/null +++ b/classify/picofeat.h @@ -0,0 +1,65 @@ +/****************************************************************************** + ** 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 "tessclas.h" +#include "fxdefs.h" + +typedef enum +{ PicoFeatY, PicoFeatDir, PicoFeatX } +PICO_FEAT_PARAM_NAME; + +#define MAX_PICO_FEATURES (1000) + +/**---------------------------------------------------------------------------- + Public Function Prototypes +----------------------------------------------------------------------------**/ +#define GetPicoFeatureLength() (PicoFeatureLength) + +FEATURE_SET ExtractPicoFeatures(TBLOB *Blob, LINE_STATS *LineStats); + +void InitPicoFXVars(); + +/* +#if defined(__STDC__) || defined(__cplusplus) +# define _ARGS(s) s +#else +# define _ARGS(s) () +#endif*/ + +/* picofeat.c +FEATURE_SET ExtractPicoFeatures + _ARGS((BLOB *Blob, + LINE_STATS *LineStats)); + +void InitPicoFXVars + _ARGS((void)); + +#undef _ARGS +*/ +/**---------------------------------------------------------------------------- + Global Data Definitions and Declarations +----------------------------------------------------------------------------**/ +extern FEATURE_DESC_STRUCT PicoFeatDesc; +extern FLOAT32 PicoFeatureLength; +#endif diff --git a/classify/protos.cpp b/classify/protos.cpp new file mode 100644 index 0000000000..752d84523b --- /dev/null +++ b/classify/protos.cpp @@ -0,0 +1,468 @@ +/* -*-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 "debug.h" +#include "const.h" +#include "emalloc.h" +#include "freelist.h" +#include "callcpp.h" +#include "adaptmatch.h" +#include "scanutils.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]; + +char *TrainingFile; + +//extern int LearningDebugLevel; + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +/********************************************************************** + * 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. + **********************************************************************/ +int AddConfigToClass(CLASS_TYPE Class) { + int NewNumConfigs; + int NewConfig; + int MaxNumProtos; + BIT_VECTOR Config; + + MaxNumProtos = Class->MaxNumProtos; + + if (NumConfigsIn (Class) >= 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 = NumConfigsIn (Class); + NumConfigsIn (Class)++; + Config = NewBitVector (MaxNumProtos); + ConfigIn (Class, NewConfig) = Config; + zero_all_bits (Config, WordsInVectorOfSize (MaxNumProtos)); + + return (NewConfig); +} + + +/********************************************************************** + * 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. + **********************************************************************/ +int AddProtoToClass(CLASS_TYPE Class) { + int i; + int Bit; + int NewNumProtos; + int NewProto; + BIT_VECTOR Config; + + if (NumProtosIn (Class) >= 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 < NumConfigsIn (Class); i++) { + Config = ConfigIn (Class, i); + ConfigIn (Class, i) = ExpandBitVector (Config, NewNumProtos); + + for (Bit = NumProtosIn (Class); Bit < NewNumProtos; Bit++) + reset_bit(Config, Bit); + } + } + NewProto = NumProtosIn (Class); + NumProtosIn (Class)++; + return (NewProto); +} + + +/********************************************************************** + * ClassConfigLength + * + * Return the length of all the protos in this class. + **********************************************************************/ +FLOAT32 ClassConfigLength(CLASS_TYPE Class, BIT_VECTOR Config) { + INT16 Pid; + FLOAT32 TotalLength = 0; + + for (Pid = 0; Pid < NumProtosIn (Class); Pid++) { + if (test_bit (Config, Pid)) { + + TotalLength += ProtoLength (ProtoIn (Class, Pid)); + } + } + return (TotalLength); +} + + +/********************************************************************** + * ClassProtoLength + * + * Return the length of all the protos in this class. + **********************************************************************/ +FLOAT32 ClassProtoLength(CLASS_TYPE Class) { + INT16 Pid; + FLOAT32 TotalLength = 0; + + for (Pid = 0; Pid < NumProtosIn (Class); Pid++) { + TotalLength += ProtoLength (ProtoIn (Class, Pid)); + } + return (TotalLength); +} + + +/********************************************************************** + * CopyProto + * + * Copy the first proto into the second. + **********************************************************************/ +void CopyProto(PROTO Src, PROTO Dest) { + ProtoX (Dest) = ProtoX (Src); + ProtoY (Dest) = ProtoY (Src); + ProtoLength (Dest) = ProtoLength (Src); + ProtoAngle (Dest) = ProtoAngle (Src); + CoefficientA (Dest) = CoefficientA (Src); + CoefficientB (Dest) = CoefficientB (Src); + CoefficientC (Dest) = CoefficientC (Src); +} + + +/********************************************************************** + * 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); + memfree(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 < NumConfigsIn (Class); i++) + FreeBitVector (ConfigIn (Class, i)); + memfree (Class->Configurations); + } + } +} + + +/********************************************************************** + * InitPrototypes + * + * Initialize anything that needs to be initialized to work with the + * functions in this file. + **********************************************************************/ +void InitPrototypes() { + string_variable (TrainingFile, "TrainingFile", "MicroFeatures"); +} + + +/********************************************************************** + * 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 = (CLASS_TYPE) Emalloc (sizeof (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 < NumProtosIn (Class); Pid++) { + cprintf ("Proto %d:\t", Pid); + PrintProto (ProtoIn (Class, Pid)); + cprintf ("\t"); + PrintProtoLine (ProtoIn (Class, Pid)); + new_line(); + } +} + + +/********************************************************************** + * ReadClassFile + * + * Read in the training data from a file. All of the classes are read + * in. The results are stored in the global variable, 'TrainingData'. + **********************************************************************/ +void ReadClassFile() { + FILE *File; + char TextLine[CHARS_PER_LINE]; + + cprintf ("Reading training data from '%s' ...", TrainingFile); + fflush(stdout); + + File = open_file (TrainingFile, "r"); + while (fgets (TextLine, CHARS_PER_LINE, File) != NULL) { + + ReadClassFromFile (File, TextLine[0]); + fgets(TextLine, CHARS_PER_LINE, File); + fgets(TextLine, CHARS_PER_LINE, File); + } + fclose(File); + + new_line(); +} + + +/********************************************************************** + * ReadClassFromFile + * + * Read in a class description (protos and configs) from a file. Update + * the class structure record. + **********************************************************************/ +void ReadClassFromFile(FILE *File, char ClassChar) { + CLASS_TYPE Class; + + Class = &TrainingData[ClassChar]; + + ReadProtos(File, Class); + + ReadConfigs(File, Class); +} + + +/********************************************************************** + * ReadConfigs + * + * Read the prototype configurations for this class from a file. Read + * the requested number of lines. + **********************************************************************/ +void ReadConfigs(register FILE *File, CLASS_TYPE Class) { + INT16 Cid; + register INT16 Wid; + register BIT_VECTOR ThisConfig; + int NumWords; + int NumConfigs; + + fscanf (File, "%d %d\n", &NumConfigs, &NumWords); + NumConfigsIn (Class) = NumConfigs; + Class->MaxNumConfigs = NumConfigs; + Class->Configurations = + (CONFIGS) Emalloc (sizeof (BIT_VECTOR) * NumConfigs); + NumWords = WordsInVectorOfSize (NumProtosIn (Class)); + + for (Cid = 0; Cid < NumConfigs; Cid++) { + + ThisConfig = NewBitVector (NumProtosIn (Class)); + for (Wid = 0; Wid < NumWords; Wid++) + fscanf (File, "%x", &ThisConfig[Wid]); + ConfigIn (Class, Cid) = ThisConfig; + } +} + + +/********************************************************************** + * ReadProtos + * + * Read in all the prototype information from a file. Read the number + * of lines requested. + **********************************************************************/ +void ReadProtos(register FILE *File, CLASS_TYPE Class) { + register INT16 Pid; + register PROTO Proto; + int NumProtos; + + fscanf (File, "%d\n", &NumProtos); + NumProtosIn (Class) = NumProtos; + Class->MaxNumProtos = NumProtos; + Class->Prototypes = (PROTO) Emalloc (sizeof (PROTO_STRUCT) * NumProtos); + + for (Pid = 0; Pid < NumProtos; Pid++) { + Proto = ProtoIn (Class, Pid); + fscanf (File, "%f %f %f %f %f %f %f\n", + &ProtoX (Proto), + &ProtoY (Proto), + &ProtoLength (Proto), + &ProtoAngle (Proto), + &CoefficientA (Proto), + &CoefficientB (Proto), &CoefficientC (Proto)); + } +} + + +/********************************************************************** + * SplitProto + * + * 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. + * Update all configurations so that each config which contained the + * specified old proto will also contain the new proto. The caller + * is responsible for actually filling in the appropriate proto params. + **********************************************************************/ +int SplitProto(CLASS_TYPE Class, int OldPid) { + int i; + int NewPid; + BIT_VECTOR Config; + + NewPid = AddProtoToClass (Class); + + for (i = 0; i < NumConfigsIn (Class); i++) { + Config = ConfigIn (Class, i); + if (test_bit (Config, OldPid)) + SET_BIT(Config, NewPid); + } + return (NewPid); +} + + +/********************************************************************** + * WriteOldConfigFile + * + * Write the configs in the given class to the specified file in the + * old config format. + **********************************************************************/ +void WriteOldConfigFile(FILE *File, CLASS_TYPE Class) { + int Cid, Pid; + BIT_VECTOR Config; + + fprintf (File, "%d %d\n", NumConfigsIn (Class), NumProtosIn (Class)); + + for (Cid = 0; Cid < NumConfigsIn (Class); Cid++) { + fprintf (File, "1 "); + + Config = ConfigIn (Class, Cid); + + for (Pid = 0; Pid < NumProtosIn (Class); Pid++) { + if (test_bit (Config, Pid)) + fprintf (File, "1"); + else + fprintf (File, "0"); + } + fprintf (File, "\n"); + } +} + + +/********************************************************************** + * WriteOldProtoFile + * + * Write the protos in the given class to the specified file in the + * old proto format. + **********************************************************************/ +void WriteOldProtoFile(FILE *File, CLASS_TYPE Class) { + int Pid; + PROTO Proto; + + /* print old header */ + fprintf (File, "6\n"); + fprintf (File, "linear essential -0.500000 0.500000\n"); + fprintf (File, "linear essential -0.250000 0.750000\n"); + fprintf (File, "linear essential 0.000000 1.000000\n"); + fprintf (File, "circular essential 0.000000 1.000000\n"); + fprintf (File, "linear non-essential -0.500000 0.500000\n"); + fprintf (File, "linear non-essential -0.500000 0.500000\n"); + + for (Pid = 0; Pid < NumProtosIn (Class); Pid++) { + Proto = ProtoIn (Class, Pid); + + fprintf (File, "significant elliptical 1\n"); + fprintf (File, " %9.6f %9.6f %9.6f %9.6f %9.6f %9.6f\n", + ProtoX (Proto), ProtoY (Proto), + ProtoLength (Proto), ProtoAngle (Proto), 0.0, 0.0); + fprintf (File, " %9.6f %9.6f %9.6f %9.6f %9.6f %9.6f\n", + 0.0001, 0.0001, 0.0001, 0.0001, 0.0001, 0.0001); + } +} diff --git a/classify/protos.h b/classify/protos.h new file mode 100644 index 0000000000..b669cd7fbc --- /dev/null +++ b/classify/protos.h @@ -0,0 +1,360 @@ +/* -*-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" + +/*---------------------------------------------------------------------- + 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; + +typedef struct +{ + INT16 NumProtos; + INT16 MaxNumProtos; + PROTO Prototypes; + INT16 NumConfigs; + INT16 MaxNumConfigs; + CONFIGS Configurations; +} CLASS_STRUCT; +typedef CLASS_STRUCT *CLASS_TYPE; +typedef CLASS_STRUCT *CLASSES; + +/*---------------------------------------------------------------------- + C o n s t a n t s +----------------------------------------------------------------------*/ +#define NUMBER_OF_CLASSES 256 +#define Y_OFFSET -40.0 +#define FEATURE_SCALE 100.0 + +/*---------------------------------------------------------------------- + V a r i a b l e s +----------------------------------------------------------------------*/ +extern CLASS_STRUCT TrainingData[]; + +/*---------------------------------------------------------------------- + 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]) + +/********************************************************************** + * ConfigIn + * + * Choose the selected prototype configuration in this class record. + * Return it as type 'BIT_VECTOR'. + **********************************************************************/ + +#define ConfigIn(Class,Cid) \ +((Class)->Configurations [Cid]) + +/********************************************************************** + * NumProtosIn + * + * Return the number of prototypes in this class. The 'Class' argument + * is of type 'CLASS_TYPE'. + **********************************************************************/ + +#define NumProtosIn(Class) \ +((Class)->NumProtos) + +/********************************************************************** + * NumConfigsIn + * + * Return the number of configurations in this class. The 'Class' argument + * is of type 'CLASS_TYPE'. + **********************************************************************/ + +#define NumConfigsIn(Class) \ +((Class)->NumConfigs) + +/********************************************************************** + * CoefficientA + * + * Return the first parameter of the prototype structure. This is the + * A coefficient in the line representation of "Ax + By + C = 0". The + * 'Proto' argument is of type 'PROTO'. + **********************************************************************/ + +#define CoefficientA(Proto) \ +((Proto)->A) + +/********************************************************************** + * CoefficientB + * + * Return the second parameter of the prototype structure. This is the + * B coefficient in the line representation of "Ax + By + C = 0". The + * 'Proto' argument is of type 'PROTO'. + **********************************************************************/ + +#define CoefficientB(Proto) \ +((Proto)->B) + +/********************************************************************** + * CoefficientC + * + * Return the third parameter of the prototype structure. This is the + * C coefficient in the line representation of "Ax + By + C = 0". The + * 'Proto' argument is of type 'PROTO'. + **********************************************************************/ + +#define CoefficientC(Proto) \ +((Proto)->C) + +/********************************************************************** + * ProtoAngle + * + * Return the angle parameter of the prototype structure. The + * 'Proto' argument is of type 'PROTO'. + **********************************************************************/ + +#define ProtoAngle(Proto) \ +((Proto)->Angle) + +/********************************************************************** + * ProtoX + * + * Return the X parameter of the prototype structure. The 'Proto' + * argument is of type 'PROTO'. + **********************************************************************/ + +#define ProtoX(Proto) \ +((Proto)->X) + +/********************************************************************** + * ProtoY + * + * Return the angle parameter of the prototype structure. The 'Proto' + * argument is of type 'PROTO'. + **********************************************************************/ + +#define ProtoY(Proto) \ +((Proto)->Y) + +/********************************************************************** + * ProtoLength + * + * Return the length parameter of the prototype structure. The + * 'Proto' argument is of type 'PROTO'. + **********************************************************************/ + +#define ProtoLength(Proto) \ +((Proto)->Length) + +/********************************************************************** + * PrintProto + * + * Print out the contents of a prototype. The 'Proto' argument is of + * type 'PROTO'. + **********************************************************************/ + +#define PrintProto(Proto) \ +(cprintf ("X=%4.2f, Y=%4.2f, Angle=%4.2f", \ + ProtoX (Proto), \ + ProtoY (Proto), \ + ProtoLength (Proto), \ + ProtoAngle (Proto))) \ + + +/********************************************************************** + * 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", \ + CoefficientA (Proto), \ + CoefficientB (Proto), \ + CoefficientC (Proto))) \ + +/*---------------------------------------------------------------------- + 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); + +void ReadClassFile(); + +void ReadClassFromFile(FILE *File, char ClassChar); + +void ReadConfigs(register FILE *File, CLASS_TYPE Class); + +void ReadProtos(register FILE *File, CLASS_TYPE Class); + +int SplitProto(CLASS_TYPE Class, int OldPid); + +void WriteOldConfigFile(FILE *File, CLASS_TYPE Class); + +void WriteOldProtoFile(FILE *File, CLASS_TYPE Class); + +/* +#if defined(__STDC__) || defined(__cplusplus) +# define _ARGS(s) s +#else +# define _ARGS(s) () +#endif*/ + +/* protos.c * +int AddConfigToClass + _ARGS((CLASS_TYPE Class)); + +int AddProtoToClass + _ARGS((CLASS_TYPE Class)); + +FLOAT32 ClassConfigLength + _ARGS((CLASS_TYPE Class, + BIT_VECTOR Config)); + +FLOAT32 ClassProtoLength + _ARGS((CLASS_TYPE Class)); + +void CopyProto + _ARGS((PROTO Src, + PROTO Dest)); + +void FillABC + _ARGS((PROTO Proto)); + +void FreeClass + _ARGS((CLASS_TYPE Class)); + +void FreeClassFields + _ARGS((CLASS_TYPE Class)); + +void InitPrototypes + _ARGS((void)); + +CLASS_TYPE NewClass + _ARGS((int NumProtos, + int NumConfigs)); + +void PrintProtos + _ARGS((CLASS_TYPE Class)); + +void ReadClassFile + _ARGS((void)); + +void ReadClassFromFile + _ARGS((FILE *File, + int ClassChar)); + +void ReadConfigs + _ARGS((FILE *File, + CLASS_TYPE Class)); + +void ReadProtos + _ARGS((FILE *File, + CLASS_TYPE Class)); + +int SplitProto + _ARGS((CLASS_TYPE Class, + int OldPid)); + +void WriteOldConfigFile + _ARGS((FILE *File, + CLASS_TYPE Class)); + +void WriteOldProtoFile + _ARGS((FILE *File, + CLASS_TYPE Class)); + +#undef _ARGS +*/ +#endif diff --git a/classify/sigmenu.cpp b/classify/sigmenu.cpp new file mode 100644 index 0000000000..783b104d96 --- /dev/null +++ b/classify/sigmenu.cpp @@ -0,0 +1,225 @@ +/****************************************************************************** + ** Filename: sigmenu.c + ** Purpose: General purpose, menu-oriented signal handling routines + ** Author: Dan Johnson + ** History: Mon Oct 2 07:25:50 1989, 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 "sigmenu.h" +#include "oldlist.h" +#include "emalloc.h" +#include "secname.h" + +#include +#include +#include + +#define MAX_COMMAND_LENGTH 128 + +typedef struct +{ + int ItemNum; + char *ItemLabel; + int_void ItemFunc; +} SIG_MENU_ITEM; + +/**---------------------------------------------------------------------------- + Global Data Definitions and Declarations +----------------------------------------------------------------------------**/ +static LIST SignalMenus[NSIG]; /* must be initialized to NIL */ + +/**---------------------------------------------------------------------------- + Private Function Prototypes +----------------------------------------------------------------------------**/ +void MainSignalHandler(int Signal); + +SIG_MENU_ITEM *NewSignalMenuItem (int ItemNum, +const char ItemLabel[], int_void ItemFunc); + +int ItemCompare(void *arg1, //SIG_MENU_ITEM *Item1, + void *arg2); //SIG_MENU_ITEM *Item2); + +/**---------------------------------------------------------------------------- + Public Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +void +AddSignalMenuItem (int Signal, +int ItemNum, const char ItemLabel[], int_void ItemFunc) { +/* + ** Parameters: + ** Signal signal to be trapped for this menu + ** ItemNum menu number for this item + ** ItemLabel menu label for this item + ** ItemFunc function to be called when item is selected + ** Globals: + ** SignalMenus list of menu items for each possible signal + ** Operation: + ** Add a new menu item to the list of menu items for Signal. + ** Whenever Signal is encountered, the user will be given + ** a list of options to choose from. This list is the list + ** of all of the menu items that have been specified for that + ** Signal. + ** Return: none + ** Exceptions: none + ** History: Mon Oct 2 07:42:19 1989, DSJ, Created. + */ + #if 0 + #ifndef SECURE_NAMES + SIG_MENU_ITEM *NewItem; + + /* check for a valid Signal */ + if (Signal >= NSIG || Signal <= 0) { + cprintf ("Illegal signal (%d) specified for menu item!\n", Signal); + return; + } + + /* if this is the first item for this signal, indicate that the + appropriate signal handler has been enabled */ + if (SignalMenus[Signal] == NIL) + cprintf ("Signal handler enabled for signal %d.\n", Signal); + + /* add the new menu item to the appropriate list of menu items */ + NewItem = NewSignalMenuItem (ItemNum, ItemLabel, ItemFunc); + SignalMenus[Signal] = s_adjoin (SignalMenus[Signal], NewItem, ItemCompare); + + /* set up the trap for the appropriate signal */ + signal(Signal, MainSignalHandler); + #endif + #endif +} /* AddSignalMenuItem */ + + +/**---------------------------------------------------------------------------- + Private Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +void MainSignalHandler(int Signal) { +/* + ** Parameters: + ** Signal signal that caused this function to be called + ** Globals: + ** SignalMenus list of menu items for each possible signal + ** Operation: Provide the user with a menu of actions for the trapped + ** signal. Execute the appropriate function. If the function + ** returns SIG_RESUME, then terminate the signal handler and + ** resume normal processing. If the function does not return + ** SIG_RESUME, remain in the main signal handler menu. + ** Return: none + ** Exceptions: none + ** History: Mon Oct 2 08:18:52 1989, DSJ, Created. + */ + #ifndef SECURE_NAMES + int Command; + char CommandLine[MAX_COMMAND_LENGTH]; + char *Params; + LIST Items; + SIG_MENU_ITEM *MenuItem; + + while (TRUE) { + Command = -1; + cprintf ("\nMAIN SIGNAL HANDLER FOR SIGNAL %d\n", Signal); + cprintf ("0. Resume normal operation\n"); + + Items = SignalMenus[Signal]; + iterate(Items) { + MenuItem = (SIG_MENU_ITEM *) first (Items); + cprintf ("%d. %s\n", MenuItem->ItemNum, MenuItem->ItemLabel); + } + cprintf ("\nEnter Selection: "); + + while (fgets (CommandLine, MAX_COMMAND_LENGTH, stdin) == NULL + || strlen (CommandLine) <= 0); + + Command = strtol (CommandLine, &Params, 10); + if (CommandLine == Params) { + cprintf ("\nIllegal command! - Try again.\n"); + continue; + } + + if (Command == 0) + signal(Signal, MainSignalHandler); + + Items = SignalMenus[Signal]; + iterate(Items) { + MenuItem = (SIG_MENU_ITEM *) first (Items); + if (Command == MenuItem->ItemNum) { + if ((*MenuItem->ItemFunc) ( /*Params */ ) == SIG_RESUME) + signal(Signal, MainSignalHandler); + break; + } + } + if (Items == NIL) + cprintf ("\nIllegal command! - Try again.\n"); + } + #endif +} /* MainSignalHandler */ + + +/*---------------------------------------------------------------------------*/ +SIG_MENU_ITEM * +NewSignalMenuItem (int ItemNum, const char ItemLabel[], int_void ItemFunc) { +/* + ** Parameters: + ** ItemNum menu number for this item + ** ItemLabel menu label for this item + ** ItemFunc function to be called when item is selected + ** Globals: none + ** Operation: Allocate, initialize, and return a new signal menu item. + ** Return: Ptr to new signal menu item data structure. + ** Exceptions: none + ** History: Mon Oct 2 08:04:20 1989, DSJ, Created. + */ + SIG_MENU_ITEM *NewItem; + + NewItem = (SIG_MENU_ITEM *) Emalloc (sizeof (SIG_MENU_ITEM)); + NewItem->ItemNum = ItemNum; + NewItem->ItemFunc = ItemFunc; + NewItem->ItemLabel = (char *) Emalloc (strlen (ItemLabel) + 1); + strcpy (NewItem->ItemLabel, ItemLabel); + return (NewItem); + +} /* NewSignalMenuItem */ + + +/*---------------------------------------------------------------------------*/ +int ItemCompare(void *arg1, //SIG_MENU_ITEM *Item1, + void *arg2) { //SIG_MENU_ITEM *Item2) +/* + ** Parameters: + ** Item1, Item2 two menu items to be compared + ** Globals: none + ** Operation: Return -1 if the ItemNum of Item1 is less than the + ** ItemNum of Item2. Return 0 if they are equal. Return +1 + ** if the ItemNum of Item1 is greater than the ItemNum of + ** Item2. This routine is used by the list sorter to sort + ** lists of menu items according to their item number. + ** Return: -1, 0, or 1 + ** Exceptions: none + ** History: Mon Oct 2 08:11:59 1989, DSJ, Created. + */ + SIG_MENU_ITEM *Item1 = (SIG_MENU_ITEM *) arg1; + SIG_MENU_ITEM *Item2 = (SIG_MENU_ITEM *) arg2; + + if (Item1->ItemNum < Item2->ItemNum) + return (-1); + else if (Item1->ItemNum == Item2->ItemNum) + return (0); + else if (Item1->ItemNum > Item2->ItemNum) + return (1); + else + return 0; +} /* ItemCompare */ diff --git a/classify/sigmenu.h b/classify/sigmenu.h new file mode 100644 index 0000000000..7eab09f404 --- /dev/null +++ b/classify/sigmenu.h @@ -0,0 +1,39 @@ +/****************************************************************************** + ** Filename: sigmenu.h + ** Purpose: Definition of signal handler routines + ** Author: Dan Johnson + ** History: 10/2/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 SIGMENU_H +#define SIGMENU_H + +/**---------------------------------------------------------------------------- + Include Files and Type Defines +----------------------------------------------------------------------------**/ +#include "cutil.h" +#include + +/* functions to be placed in the signal menu look like: */ +//typedef int (*SIG_MENU_FUNC)(...); +/* the value returned from a SIG_MENU_FUNC must be one of the following */ +#define SIG_RESUME 1 +#define SIG_MENU 0 + +/**---------------------------------------------------------------------------- + Public Function Prototypes +----------------------------------------------------------------------------**/ +void AddSignalMenuItem (int Signal, +int ItemNum, +const char ItemLabel[], int_void ItemFunc); +#endif diff --git a/classify/speckle.cpp b/classify/speckle.cpp new file mode 100644 index 0000000000..69106df15b --- /dev/null +++ b/classify/speckle.cpp @@ -0,0 +1,126 @@ +/****************************************************************************** + ** Filename: speckle.c + ** Purpose: Routines used by classifier to filter out speckle. + ** Author: Dan Johnson + ** History: Mon Mar 11 10:06:14 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 "speckle.h" +#include "debug.h" +#include "blobs.h" + +/**---------------------------------------------------------------------------- + Global Data Definitions and Declarations +----------------------------------------------------------------------------**/ +/* define control knobs for adjusting definition of speckle*/ +make_float_var (MaxLargeSpeckleSize, 0.30, MakeMaxLargeSpeckleSize, +16, 2, SetMaxLargeSpeckleSize, "Max Large Speckle Size ..."); + +make_float_var (SmallSpecklePenalty, 10.0, MakeSmallSpecklePenalty, +16, 3, SetSmallSpecklePenalty, "Small Speckle Penalty ..."); + +make_float_var (LargeSpecklePenalty, 10.0, MakeLargeSpecklePenalty, +16, 4, SetLargeSpecklePenalty, "Large Speckle Penalty ..."); + +make_float_var (SmallSpeckleCertainty, -1.0, MakeSmallSpeckleCertainty, +16, 5, SetSmallSpeckleCertainty, +"Small Speckle Certainty ..."); + +/**---------------------------------------------------------------------------- + Public Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +LIST AddLargeSpeckleTo(LIST Choices) { +/* + ** Parameters: + ** Choices choices to add a speckle choice to + ** Globals: + ** SmallSpecklePenalty rating for a small speckle + ** LargeSpecklePenalty rating penalty for a large speckle + ** SmallSpeckleCertainty certainty for a small speckle + ** Operation: This routine adds a null choice to Choices with a + ** rating equal to the worst rating in Choices plus a pad. + ** The certainty of the new choice is the same as the + ** certainty of the worst choice in Choices. The new choice + ** is added to the end of Choices. + ** Return: New Choices list with null choice added to end. + ** Exceptions: none + ** History: Mon Mar 11 11:08:11 1991, DSJ, Created. + */ + LIST WorstChoice; + + /* if there are no other choices, use the small speckle penalty plus + the large speckle penalty */ + if (Choices == NIL) + return (append_choice (NIL, "", SmallSpecklePenalty + LargeSpecklePenalty, + SmallSpeckleCertainty, -1)); + + /* if there are other choices, add a null choice that is slightly worse + than the worst choice so far */ + WorstChoice = last (Choices); + return (append_choice (Choices, "", + best_probability (WorstChoice) + LargeSpecklePenalty, + best_certainty (WorstChoice), -1)); + +} /* AddLargeSpeckleTo */ + + +/*---------------------------------------------------------------------------*/ +void InitSpeckleVars() { +/* + ** Parameters: none + ** Globals: none + ** Operation: Install the control variables needed for the speckle + ** filters. + ** Return: none + ** Exceptions: none + ** History: Mon Mar 11 12:04:59 1991, DSJ, Created. + */ + MakeMaxLargeSpeckleSize(); + MakeSmallSpecklePenalty(); + MakeLargeSpecklePenalty(); + MakeSmallSpeckleCertainty(); +} /* InitSpeckleVars */ + + +/*---------------------------------------------------------------------------*/ +BOOL8 LargeSpeckle(TBLOB *Blob, TEXTROW *Row) { +/* + ** Parameters: + ** Blob blob to test against speckle criteria + ** Row text row that blob is in + ** Globals: + ** MaxLargeSpeckleSize largest allowed speckle + ** Operation: This routine returns TRUE if both the width of height + ** of Blob are less than the MaxLargeSpeckleSize. + ** Return: TRUE if Blob is speckle, FALSE otherwise. + ** Exceptions: none + ** History: Mon Mar 11 10:06:49 1991, DSJ, Created. + */ + FLOAT32 SpeckleSize; + TPOINT TopLeft; + TPOINT BottomRight; + + SpeckleSize = RowHeight (Row) * MaxLargeSpeckleSize; + blob_bounding_box(Blob, &TopLeft, &BottomRight); + + if (TopLeft.y - BottomRight.y < SpeckleSize && + BottomRight.x - TopLeft.x < SpeckleSize) + return (TRUE); + else + return (FALSE); + +} /* LargeSpeckle */ diff --git a/classify/speckle.h b/classify/speckle.h new file mode 100644 index 0000000000..1f6de31a99 --- /dev/null +++ b/classify/speckle.h @@ -0,0 +1,69 @@ +/****************************************************************************** + ** Filename: speckle.h + ** Purpose: Interface to classifier speckle filtering routines. + ** Author: Dan Johnson + ** History: Mon Mar 11 10:14: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. + ******************************************************************************/ +#ifndef SPECKLE_H +#define SPECKLE_H + +/**---------------------------------------------------------------------------- + Include Files and Type Defines +----------------------------------------------------------------------------**/ +#include "baseline.h" +#include "choices.h" + +/**---------------------------------------------------------------------------- + Macros +----------------------------------------------------------------------------**/ +/* macro for getting the height of a row of text */ +#define RowHeight(R) ((is_baseline_normalized ())? \ + (BASELINE_SCALE): \ + ((R)->lineheight)) + +/**---------------------------------------------------------------------------- + Public Function Prototypes +----------------------------------------------------------------------------**/ +LIST AddLargeSpeckleTo(LIST Choices); + +void InitSpeckleVars(); + +BOOL8 LargeSpeckle(TBLOB *Blob, TEXTROW *Row); + +/* +#if defined(__STDC__) || defined(__cplusplus) +# define _ARGS(s) s +#else +# define _ARGS(s) () +#endif*/ + +/* speckle.c +LIST AddLargeSpeckleTo + _ARGS((LIST Choices)); + +void InitSpeckleVars + _ARGS((void)); + +BOOL8 LargeSpeckle + _ARGS((BLOB *Blob, + TEXTROW *Row)); + +#undef _ARGS +*/ +/**---------------------------------------------------------------------------- + Global Data Definitions and Declarations +----------------------------------------------------------------------------**/ +extern float SmallSpecklePenalty; +extern float SmallSpeckleCertainty; +#endif diff --git a/classify/xform2d.cpp b/classify/xform2d.cpp new file mode 100644 index 0000000000..90a172824e --- /dev/null +++ b/classify/xform2d.cpp @@ -0,0 +1,63 @@ +/****************************************************************************** + ** Filename: xform2d.c + ** Purpose: Library routines for performing 2D point transformations + ** Author: Dan Johnson + ** History: Fri Sep 22 09:54:17 1989, 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 "xform2d.h" +#include + +/**---------------------------------------------------------------------------- + Public Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +void RotateMatrix(MATRIX_2D_PTR Matrix, FLOAT32 Angle) { +/* + ** Parameters: + ** Matrix transformation matrix to rotate + ** Angle angle to rotate matrix + ** Globals: none + ** Operation: + ** Rotate the coordinate system (as specified by Matrix) about + ** its origin by Angle radians. In matrix notation the + ** effect is as follows: + ** + ** Matrix = R X Matrix + ** + ** where R is the following matrix + ** + ** cos Angle sin Angle 0 + ** -sin Angle cos Angle 0 + ** 0 0 1 + ** Return: none + ** Exceptions: none + ** History: 7/27/89, DSJ, Create. + */ + FLOAT32 Cos, Sin; + FLOAT32 NewA, NewB; + + Cos = cos ((double) Angle); + Sin = sin ((double) Angle); + + NewA = Matrix->a * Cos + Matrix->c * Sin; + NewB = Matrix->b * Cos + Matrix->d * Sin; + Matrix->c = Matrix->a * -Sin + Matrix->c * Cos; + Matrix->d = Matrix->b * -Sin + Matrix->d * Cos; + Matrix->a = NewA; + Matrix->b = NewB; + +} /* RotateMatrix */ diff --git a/classify/xform2d.h b/classify/xform2d.h new file mode 100644 index 0000000000..088a68158f --- /dev/null +++ b/classify/xform2d.h @@ -0,0 +1,69 @@ +/****************************************************************************** + ** Filename: xform2d.h + ** Purpose: Definitions for using 2D point transformation library + ** Author: Dan Johnson + ** History: Fri Sep 22 09:57:08 1989, 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 XFORM2D_H +#define XFORM2D_H + +/**---------------------------------------------------------------------------- + Include Files and Type Defines +----------------------------------------------------------------------------**/ +#include "fpoint.h" + +typedef struct +{ + FLOAT32 a, b, c, d, tx, ty; +} + + +MATRIX_2D, *MATRIX_2D_PTR; + +/**---------------------------------------------------------------------------- + Macros +----------------------------------------------------------------------------**/ +/* macros for initializing transform matrices */ +#define InitMatrix(M) ((M)->a = 1, (M)->b = 0, \ + (M)->c = 0, (M)->d = 1, \ + (M)->tx = 0, (M)->ty = 0 ) + +#define CopyMatrix(A,B) ((B)->a = (A)->a, (B)->b = (A)->b, \ + (B)->c = (A)->c, (B)->d = (A)->d, \ + (B)->tx = (A)->tx, (B)->ty = (A)->ty) + +/* matrix scaling, translation, rotation, mirroring, etc.*/ +#define TranslateMatrix(M,X,Y) ((M)->tx += (M)->a * (X) + (M)->c * (Y), \ + (M)->ty += (M)->b * (X) + (M)->d * (Y) ) + +#define ScaleMatrix(M,X,Y) ((M)->a *= (X), (M)->b *= (X), \ + (M)->c *= (Y), (M)->d *= (Y)) + +#define MirrorMatrixInX(M) (ScaleMatrix((M),-1,1)) +#define MirrorMatrixInY(M) (ScaleMatrix((M),1,-1)) +#define MirrorMatrixInXY(M) (ScaleMatrix((M),-1,-1)) + +/* using a matrix to map points*/ +#define MapX(M,X,Y) ((M)->a * (X) + (M)->c * (Y) + (M)->tx) +#define MapY(M,X,Y) ((M)->b * (X) + (M)->d * (Y) + (M)->ty) +#define MapPoint(M,A,B) (Xof(B) = MapX (M, Xof(A), Yof(A)), \ + Yof(B) = MapY (M, Xof(A), Yof(A))) +#define MapDx(M,DX,DY) ((M)->a * (DX) + (M)->c * (DY)) +#define MapDy(M,DX,DY) ((M)->b * (DX) + (M)->d * (DY)) + +/**---------------------------------------------------------------------------- + Public Function Prototypes +----------------------------------------------------------------------------**/ +void RotateMatrix(MATRIX_2D_PTR Matrix, FLOAT32 Angle); +#endif diff --git a/config/ac_compile_check_sizeof.m4 b/config/ac_compile_check_sizeof.m4 new file mode 100644 index 0000000000..e0b62a8e49 --- /dev/null +++ b/config/ac_compile_check_sizeof.m4 @@ -0,0 +1,28 @@ +dnl Available from the GNU Autoconf Macro Archive at: +dnl http://www.gnu.org/software/ac-archive/htmldoc/ac_compile_check_sizeof.html +dnl +AC_DEFUN([AC_COMPILE_CHECK_SIZEOF], +[changequote(<<, >>)dnl +dnl The name to #define. +define(<>, translit(sizeof_$1, [a-z *], [A-Z_P]))dnl +dnl The cache variable name. +define(<>, translit(ac_cv_sizeof_$1, [ *], [_p]))dnl +changequote([, ])dnl +AC_MSG_CHECKING(size of $1) +AC_CACHE_VAL(AC_CV_NAME, +[for ac_size in 4 8 1 2 16 $2 ; do # List sizes in rough order of prevalence. + AC_TRY_COMPILE([#include "confdefs.h" +#include +$2 +], [switch (0) case 0: case (sizeof ($1) == $ac_size):;], AC_CV_NAME=$ac_size) + if test x$AC_CV_NAME != x ; then break; fi +done +]) +if test x$AC_CV_NAME = x ; then + AC_MSG_ERROR([cannot determine a size for $1]) +fi +AC_MSG_RESULT($AC_CV_NAME) +AC_DEFINE_UNQUOTED(AC_TYPE_NAME, $AC_CV_NAME, [The number of bytes in type $1]) +undefine([AC_TYPE_NAME])dnl +undefine([AC_CV_NAME])dnl +]) diff --git a/config/ac_create_stdint_h.m4 b/config/ac_create_stdint_h.m4 new file mode 100644 index 0000000000..468a3b8874 --- /dev/null +++ b/config/ac_create_stdint_h.m4 @@ -0,0 +1,552 @@ +##### http://autoconf-archive.cryp.to/ac_create_stdint_h.html +# +# OBSOLETE MACRO +# +# Replaced by AX_CREATE_STDINT_H. +# +# SYNOPSIS +# +# AC_CREATE_STDINT_H [( HEADER-TO-GENERATE [, HEDERS-TO-CHECK])] +# +# DESCRIPTION +# +# the "ISO C9X: 7.18 Integer types " section requires the +# existence of an include file that defines a set of +# typedefs, especially uint8_t,int32_t,uintptr_t. Many older +# installations will not provide this file, but some will have the +# very same definitions in . In other enviroments we can +# use the inet-types in which would define the typedefs +# int8_t and u_int8_t respectivly. +# +# This macros will create a local "_stdint.h" or the headerfile given +# as an argument. In many cases that file will just have a singular +# "#include " or "#include " statement, while +# in other environments it will provide the set of basic 'stdint's +# defined: +# int8_t,uint8_t,int16_t,uint16_t,int32_t,uint32_t,intptr_t,uintptr_t +# int_least32_t.. int_fast32_t.. intmax_t which may or may not rely +# on the definitions of other files, or using the +# AC_COMPILE_CHECK_SIZEOF macro to determine the actual sizeof each +# type. +# +# if your header files require the stdint-types you will want to +# create an installable file mylib-int.h that all your other +# installable header may include. So if you have a library package +# named "mylib", just use +# +# AC_CREATE_STDINT_H(mylib-int.h) +# +# in configure.in and go to install that very header file in +# Makefile.am along with the other headers (mylib.h) - and the +# mylib-specific headers can simply use "#include " to +# obtain the stdint-types. +# +# Remember, if the system already had a valid , the +# generated file will include it directly. No need for fuzzy +# HAVE_STDINT_H things... +# +# (note also the newer variant AX_CREATE_STDINT_H of this macro) +# +# LAST MODIFICATION +# +# 2006-10-13 +# +# COPYLEFT +# +# Copyright (c) 2006 Guido U. Draheim +# +# 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 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, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. +# +# As a special exception, the respective Autoconf Macro's copyright +# owner gives unlimited permission to copy, distribute and modify the +# configure scripts that are the output of Autoconf when processing +# the Macro. You need not follow the terms of the GNU General Public +# License when using or distributing such scripts, even though +# portions of the text of the Macro appear in them. The GNU General +# Public License (GPL) does govern all other use of the material that +# constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the +# Autoconf Macro released by the Autoconf Macro Archive. When you +# make and distribute a modified version of the Autoconf Macro, you +# may extend this special exception to the GPL to apply to your +# modified version as well. + +AC_DEFUN([AC_CREATE_STDINT_H], +[# ------ AC CREATE STDINT H ------------------------------------- +AC_MSG_CHECKING([for stdint-types....]) +ac_stdint_h=`echo ifelse($1, , _stdint.h, $1)` +if test "$ac_stdint_h" = "stdint.h" ; then + AC_MSG_RESULT("(are you sure you want them in ./stdint.h?)") +elif test "$ac_stdint_h" = "inttypes.h" ; then + AC_MSG_RESULT("(are you sure you want them in ./inttypes.h?)") +else + AC_MSG_RESULT("(putting them into $ac_stdint_h)") +fi + +inttype_headers=`echo inttypes.h sys/inttypes.h sys/inttypes.h $2 \ +| sed -e 's/,/ /g'` + + ac_cv_header_stdint_x="no-file" + ac_cv_header_stdint_o="no-file" + ac_cv_header_stdint_u="no-file" + for i in stdint.h $inttype_headers ; do + unset ac_cv_type_uintptr_t + unset ac_cv_type_uint64_t + _AC_CHECK_TYPE_NEW(uintptr_t,[ac_cv_header_stdint_x=$i],dnl + continue,[#include <$i>]) + AC_CHECK_TYPE(uint64_t,[and64="(uint64_t too)"],[and64=""],[#include<$i>]) + AC_MSG_RESULT(... seen our uintptr_t in $i $and64) + break; + done + if test "$ac_cv_header_stdint_x" = "no-file" ; then + for i in stdint.h $inttype_headers ; do + unset ac_cv_type_uint32_t + unset ac_cv_type_uint64_t + AC_CHECK_TYPE(uint32_t,[ac_cv_header_stdint_o=$i],dnl + continue,[#include <$i>]) + AC_CHECK_TYPE(uint64_t,[and64="(uint64_t too)"],[and64=""],[#include<$i>]) + AC_MSG_RESULT(... seen our uint32_t in $i $and64) + break; + done + if test "$ac_cv_header_stdint_o" = "no-file" ; then + for i in sys/types.h $inttype_headers ; do + unset ac_cv_type_u_int32_t + unset ac_cv_type_u_int64_t + AC_CHECK_TYPE(u_int32_t,[ac_cv_header_stdint_u=$i],dnl + continue,[#include <$i>]) + AC_CHECK_TYPE(uint64_t,[and64="(u_int64_t too)"],[and64=""],[#include<$i>]) + AC_MSG_RESULT(... seen our u_int32_t in $i $and64) + break; + done + fi + fi + +# ----------------- DONE inttypes.h checks MAYBE C basic types -------- + +if test "$ac_cv_header_stdint_x" = "no-file" ; then + AC_COMPILE_CHECK_SIZEOF(char) + AC_COMPILE_CHECK_SIZEOF(short) + AC_COMPILE_CHECK_SIZEOF(int) + AC_COMPILE_CHECK_SIZEOF(long) + AC_COMPILE_CHECK_SIZEOF(void*) + ac_cv_header_stdint_test="yes" +else + ac_cv_header_stdint_test="no" +fi + +# ----------------- DONE inttypes.h checks START header ------------- +_ac_stdint_h=AS_TR_CPP(_$ac_stdint_h) +AC_MSG_RESULT(creating $ac_stdint_h : $_ac_stdint_h) +echo "#ifndef" $_ac_stdint_h >$ac_stdint_h +echo "#define" $_ac_stdint_h "1" >>$ac_stdint_h +echo "#ifndef" _GENERATED_STDINT_H >>$ac_stdint_h +echo "#define" _GENERATED_STDINT_H '"'$PACKAGE $VERSION'"' >>$ac_stdint_h +if test "$GCC" = "yes" ; then + echo "/* generated using a gnu compiler version" `$CC --version` "*/" \ + >>$ac_stdint_h +else + echo "/* generated using $CC */" >>$ac_stdint_h +fi +echo "" >>$ac_stdint_h + +if test "$ac_cv_header_stdint_x" != "no-file" ; then + ac_cv_header_stdint="$ac_cv_header_stdint_x" +elif test "$ac_cv_header_stdint_o" != "no-file" ; then + ac_cv_header_stdint="$ac_cv_header_stdint_o" +elif test "$ac_cv_header_stdint_u" != "no-file" ; then + ac_cv_header_stdint="$ac_cv_header_stdint_u" +else + ac_cv_header_stdint="stddef.h" +fi + +# ----------------- See if int_least and int_fast types are present +unset ac_cv_type_int_least32_t +unset ac_cv_type_int_fast32_t +AC_CHECK_TYPE(int_least32_t,,,[#include <$ac_cv_header_stdint>]) +AC_CHECK_TYPE(int_fast32_t,,,[#include<$ac_cv_header_stdint>]) + +if test "$ac_cv_header_stdint" != "stddef.h" ; then +if test "$ac_cv_header_stdint" != "stdint.h" ; then +AC_MSG_RESULT(..adding include stddef.h) + echo "#include " >>$ac_stdint_h +fi ; fi +AC_MSG_RESULT(..adding include $ac_cv_header_stdint) + echo "#include <$ac_cv_header_stdint>" >>$ac_stdint_h +echo "" >>$ac_stdint_h + +# ----------------- DONE header START basic int types ------------- +if test "$ac_cv_header_stdint_x" = "no-file" ; then + AC_MSG_RESULT(... need to look at C basic types) +dnl ac_cv_header_stdint_test="yes" # moved up before creating the file +else + AC_MSG_RESULT(... seen good stdint.h inttypes) +dnl ac_cv_header_stdint_test="no" # moved up before creating the file +fi + +if test "$ac_cv_header_stdint_u" != "no-file" ; then + AC_MSG_RESULT(... seen bsd/sysv typedefs) + cat >>$ac_stdint_h <>$ac_stdint_h <>$ac_stdint_h <>$ac_stdint_h <>$ac_stdint_h <>$ac_stdint_h <>$ac_stdint_h <>$ac_stdint_h <>$ac_stdint_h <>$ac_stdint_h <>$ac_stdint_h <>$ac_stdint_h < 199901L + +#ifndef _HAVE_UINT64_T +#define _HAVE_UINT64_T +typedef long long int64_t; +typedef unsigned long long uint64_t; +#endif + +#elif !defined __STRICT_ANSI__ +#if defined _MSC_VER || defined __WATCOMC__ || defined __BORLANDC__ + +#ifndef _HAVE_UINT64_T +#define _HAVE_UINT64_T +typedef __int64 int64_t; +typedef unsigned __int64 uint64_t; +#endif + +#elif defined __GNUC__ || defined __MWERKS__ || defined __ELF__ +dnl /* note: all ELF-systems seem to have loff-support which needs 64-bit */ + +#if !defined _NO_LONGLONG +#ifndef _HAVE_UINT64_T +#define _HAVE_UINT64_T +typedef long long int64_t; +typedef unsigned long long uint64_t; +#endif +#endif + +#elif defined __alpha || (defined __mips && defined _ABIN32) + +#if !defined _NO_LONGLONG +#ifndef _HAVE_UINT64_T +#define _HAVE_UINT64_T +typedef long int64_t; +typedef unsigned long uint64_t; +#endif +#endif + /* compiler/cpu type ... or just ISO C99 */ +#endif +#endif +EOF + +# plus a default 64-bit for systems that are likely to be 64-bit ready + case "$ac_cv_sizeof_x:$ac_cv_sizeof_voidp:$ac_cv_sizeof_long" in + 1:2:8:8) AC_MSG_RESULT(..adding uint64_t default, normal 64-bit system) +cat >>$ac_stdint_h <>$ac_stdint_h <>$ac_stdint_h <>$ac_stdint_h <>$ac_stdint_h <>$ac_stdint_h <>$ac_stdint_h <>$ac_stdint_h <>$ac_stdint_h <>$ac_stdint_h <>$ac_stdint_h <>$ac_stdint_h <>$ac_stdint_h < conftest.cc + if test -z "`${CXX} ${CXXFLAGS} ${OPTS} $opt -c conftest.cc 2>&1`"; then + AC_MSG_RESULT(yes) + rm conftest.* + $2 + else + AC_MSG_RESULT(no) + rm conftest.* + $3 + fi +]) + + +dnl ------------------------------------------------------- +dnl @synopsis AC_CC_OPTIMIZE +dnl Setup option --enable-debug +dnl Collects optimization/debug option in variable CFLAGS, +dnl filtering options already in CFLAGS. Also define +dnl DEBUG_MODE if appropriate. +dnl Adapted from AC_CXX_OPTIMIZE in djvulibre +dnl ------------------------------------------------------- +AC_DEFUN([AC_CXX_OPTIMIZE],[ + AC_REQUIRE([AC_CANONICAL_HOST]) + AC_ARG_ENABLE(debug, + AC_HELP_STRING([--enable-debug], + [Compile with debugging options (default: no)]), + [ac_debug=$enableval],[ac_debug=no]) + OPTS= + AC_SUBST(OPTS) + dnl VCOPTS_COMMON="/nologo /G5 /Zp1 /W3 /Za /Op- /GX" + # Note: the /Za option might be nice to have, but it is + # incompatible with , which some packages + # (like the tiff library) require + VCOPTS_COMMON="/nologo /G5 /Zp1 /W3" + dnl AC_SUBST(OPTS) + saved_CXXFLAGS="$CXXFLAGS" + saved_CFLAGS="$CFLAGS" + CXXFLAGS= + CFLAGS= + for opt in $saved_CXXFLAGS ; do + case $opt in + -g*) test $ac_debug != no && OPTS="$OPTS $opt" ;; + -O*) ;; + *) CXXFLAGS="$CXXFLAGS $opt" ;; + esac + done + for opt in $saved_CFLAGS ; do + case $opt in + -O*|-g*) ;; + *) CFLAGS="$CFLAGS $opt" ;; + esac + done + if test x$ac_debug = xno ; then + OPTS=-DNDEBUG + if test x$CXX = xcl.exe ; then + OPTS="$OPTS $VCOPTS_COMMON /Ow /O2" + else + AC_CHECK_CXX_OPT([-O3],[OPTS="$OPTS -O3"], + [ AC_CHECK_CXX_OPT([-O2], [OPTS="$OPTS -O2"] ) ] ) + dnl This triggers compiler bugs with gcc-3.2.2 - comment out for now + dnl AC_CHECK_CXX_OPT([-funroll-loops], [OPTS="$OPTS -funroll-loops"]) + cpu=`uname -m 2>/dev/null` + test -z "$cpu" && cpu=${host_cpu} + case "${host_cpu}" in + i?86) + opt="-mcpu=${host_cpu}" + AC_CHECK_CXX_OPT([$opt], [OPTS="$OPTS $opt"]) + ;; + esac + fi + else + if test x$CXX = xcl.exe ; then + OPTS="$OPTS $VCOPTS_COMMON /Od /Z7" + fi + AC_DEFINE(DEBUG_MODE,1,[Define when compiling in debug mode]) + fi + if test x$CXX != xcl.exe ; then + AC_CHECK_CXX_OPT([-Wall],[OPTS="$OPTS -Wall"]) + fi + case x"$ac_debug" in +changequote(<<, >>)dnl + x[0-9]) OPTS="$OPTS -DDEBUGLVL=$ac_debug" ;; + xr*) OPTS="$OPTS -DRUNTIME_DEBUG_ONLY" ;; +changequote([, ])dnl + esac + CXXFLAGS="$CXXFLAGS $OPTS" + CFLAGS="$CFLAGS $OPTS" +]) + + +dnl ------------------------------------------------------- +dnl @synopsis AC_CXX_MEMBER_TEMPLATES +dnl If the compiler supports member templates, +dnl define HAVE_MEMBER_TEMPLATES. +dnl ------------------------------------------------------- +AC_DEFUN([AC_CXX_MEMBER_TEMPLATES], +[AC_CACHE_CHECK(whether the compiler supports member templates, +ac_cv_cxx_member_templates, +[AC_LANG_SAVE + AC_LANG_CPLUSPLUS + AC_TRY_COMPILE([ +template class A +{ public: + template A operator=(const A& z) { return A(); } +};],[A x; A y; x = y; return 0;], + ac_cv_cxx_member_templates=yes, ac_cv_cxx_member_templates=no) + AC_LANG_RESTORE +]) +if test "$ac_cv_cxx_member_templates" = yes; then + AC_DEFINE(HAVE_MEMBER_TEMPLATES,1, + [define if the compiler supports member templates]) +fi +]) + + +dnl ------------------------------------------------------- +dnl @synopsis AC_CXX_NAMESPACES +dnl Define HAVE_NAMESPACES if the compiler supports +dnl namespaces. +dnl ------------------------------------------------------- +AC_DEFUN([AC_CXX_NAMESPACES], +[AC_CACHE_CHECK(whether the compiler implements namespaces, +ac_cv_cxx_namespaces, +[ AC_LANG_SAVE + AC_LANG_CPLUSPLUS + AC_TRY_COMPILE([namespace Outer { namespace Inner { int i = 0; }}], + [using namespace Outer::Inner; return i;], + ac_cv_cxx_namespaces=yes, ac_cv_cxx_namespaces=no) + AC_LANG_RESTORE +]) +if test "$ac_cv_cxx_namespaces" = yes && test "$ac_debug" = no; then + AC_DEFINE(HAVE_NAMESPACES,1, + [define if the compiler implements namespaces]) +fi +]) + + + +dnl ------------------------------------------------------- +dnl @synopsis AC_CXX_TYPENAME +dnl Define HAVE_TYPENAME if the compiler recognizes +dnl keyword typename. +dnl ------------------------------------------------------- +AC_DEFUN([AC_CXX_TYPENAME], +[AC_CACHE_CHECK(whether the compiler recognizes typename, +ac_cv_cxx_typename, +[AC_LANG_SAVE + AC_LANG_CPLUSPLUS + AC_TRY_COMPILE([templateclass X {public:X(){}};], +[X z; return 0;], + ac_cv_cxx_typename=yes, ac_cv_cxx_typename=no) + AC_LANG_RESTORE +]) +if test "$ac_cv_cxx_typename" = yes; then + AC_DEFINE(HAVE_TYPENAME,1,[define if the compiler recognizes typename]) +fi +]) + + +dnl ------------------------------------------------------- +dnl @synopsis AC_CXX_STDINCLUDES +dnl Define HAVE_STDINCLUDES if the compiler has the +dnl new style include files (without the .h) +dnl ------------------------------------------------------- +AC_DEFUN([AC_CXX_STDINCLUDES], +[AC_CACHE_CHECK(whether the compiler comes with standard includes, +ac_cv_cxx_stdincludes, +[AC_LANG_SAVE + AC_LANG_CPLUSPLUS + AC_TRY_COMPILE([#include +struct X { int a; X(int a):a(a){}; }; +X* foo(void *x) { return new(x) X(2); } ],[], + ac_cv_cxx_stdincludes=yes, ac_cv_cxx_stdincludes=no) + AC_LANG_RESTORE +]) +if test "$ac_cv_cxx_stdincludes" = yes; then + AC_DEFINE(HAVE_STDINCLUDES,1, + [define if the compiler comes with standard includes]) +fi +]) + + +dnl ------------------------------------------------------- +dnl @synopsis AC_CXX_BOOL +dnl If the compiler recognizes bool as a separate built-in type, +dnl define HAVE_BOOL. Note that a typedef is not a separate +dnl type since you cannot overload a function such that it +dnl accepts either the basic type or the typedef. +dnl ------------------------------------------------------- +AC_DEFUN([AC_CXX_BOOL], +[AC_CACHE_CHECK(whether the compiler recognizes bool as a built-in type, +ac_cv_cxx_bool, +[AC_LANG_SAVE + AC_LANG_CPLUSPLUS + AC_TRY_COMPILE([ +int f(int x){return 1;} +int f(char x){return 1;} +int f(bool x){return 1;} +],[bool b = true; return f(b);], + ac_cv_cxx_bool=yes, ac_cv_cxx_bool=no) + AC_LANG_RESTORE +]) +if test "$ac_cv_cxx_bool" = yes; then + AC_DEFINE(HAVE_BOOL,1,[define if bool is a built-in type]) +fi +]) + +dnl ------------------------------------------------------- +dnl @synopsis AC_CXX_EXCEPTIONS +dnl If the C++ compiler supports exceptions handling (try, +dnl throw and catch), define HAVE_EXCEPTIONS. +dnl ------------------------------------------------------- +AC_DEFUN([AC_CXX_EXCEPTIONS], +[AC_CACHE_CHECK(whether the compiler supports exceptions, +ac_cv_cxx_exceptions, +[AC_LANG_SAVE + AC_LANG_CPLUSPLUS + AC_TRY_COMPILE(,[try { throw 1; } catch (int i) { return i; }], + ac_cv_cxx_exceptions=yes, ac_cv_cxx_exceptions=no) + AC_LANG_RESTORE +]) +if test "$ac_cv_cxx_exceptions" = yes; then + AC_DEFINE(HAVE_EXCEPTIONS,1,[define if the compiler supports exceptions]) +fi +]) + + +dnl ------------------------------------------------------- +dnl @synopsis AC_CXX_RPO +dnl Defines option --enable-rpo and searches program RPO. +dnl Set output variables CXXRPOFLAGS and RPO. +dnl ------------------------------------------------------- +AC_DEFUN([AC_CXX_RPO], +[ CXXRPOFLAGS= + RPO_YES='#' + RPO_NO='' + if test x$GXX = xyes ; then + AC_ARG_ENABLE([rpo], + AC_HELP_STRING([--enable-rpo], + [Enable compilation with option -frepo]), + [ac_rpo=$enableval], [ac_rpo=no] ) + if test x$ac_rpo != xno ; then + CXXRPOFLAGS='-frepo -fno-rtti' + RPO_YES='' + RPO_NO='#' + fi + fi + AC_SUBST(CXXRPOFLAGS) + AC_SUBST(RPO_YES) + AC_SUBST(RPO_NO) +]) + + +dnl ------------------------------------------------------------------ +dnl @synopsis AC_PATH_GNUWIN32([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) +dnl Process option --with-gnuwin32. +dnl Try to determine where GNUWIN32 is located +dnl Define GNUWIN32 accordingly (either blank or PATH) +dnl Also update the LDFLAGS and CFLAGS accordingly +dnl ------------------------------------------------------------------ +AC_DEFUN([AC_PATH_GNUWIN32], +[ + # Test whether we are running on Windows. This test is becoming + # useless because other environments exist under Windows, such + # as mingw, whihc gives host i686-pc-mingw32. Just remove this test + # for now by setting variable to yes all the time. +# AC_REQUIRE([AC_CANONICAL_HOST])[]dnl +# case $host_os in +# *cygwin* ) USING_CYGWIN_OR_MINGW=yes;; +# *mingw* ) USING_CYGWIN_OR_MINGW=yes;; +# * ) USING_CYGWIN_OR_MINGW=no;; +# esac + USING_CYGWIN_OR_MINGW=yes + # If running on Windows, do some tests + if test x$USING_CYGWIN_OR_MINGW = xyes ; then + AC_ARG_VAR(GNUWIN32_DIR,[Base directory of GnuWin32 packages]) + ac_gnuwin32=no + AC_ARG_WITH(gnuwin32, + AC_HELP_STRING([--with-gnuwin32=DIR], + [where the GnuWin32 packages are installed]), + [ac_gnuwin32=$withval], [ac_gnuwin32=yes] ) + # Process specification + AC_MSG_CHECKING([for GnuWin32 directory]) + if test x$ac_gnuwin32 = xyes ; then + # GNUWIN32_BASE could have been set on the command line + if test x$GNUWIN32_BASE != x ; then + if test -d "$GNUWIN32_BASE" ; then + AC_MSG_RESULT([verified at $GNUWIN32_BASE]) + else + # AC_MSG_RESULT([no GnuWin32 at $GNUWIN32_BASE. Looking elsewhere]) + GNUWIN32_BASE= + fi + # Otherwise, GNUWIN32 is an environment variable that the user can set + elif test x$GNUWIN32 != x ; then + if test -d "$GNUWIN32" ; then + GNUWIN32_BASE=$GNUWIN32 + AC_MSG_RESULT([verified at $GNUWIN32_BASE]) + else + # AC_MSG_RESULT([no GnuWin32 at $GNUWIN32. Looking elsewhere]) + GNUWIN32_BASE= + fi + fi + # Look in default locations if needed: + if test x$GNUWIN32_BASE = x ; then + if test -d "C:/Program Files/GnuWin32" ; then + GNUWIN32_BASE="C:/Program Files/GnuWin32" + AC_MSG_RESULT([$GNUWIN32_BASE]) + elif test -d "C:/pckg/GnuWin32" ; then + GNUWIN32_BASE="C:/pckg/GnuWin32" + AC_MSG_RESULT([$GNUWIN32_BASE]) + else + AC_MSG_RESULT([not found]) + fi + fi + # If directory location specified on command line + elif test x$ac_gnuwin32 != xno ; then + if test -d "$ac_gnuwin32" ; then + GNUWIN32_BASE="$ac_gnuwin32" + AC_MSG_RESULT([verified at $GNUWIN32_BASE]) + else + GNUWIN32_BASE= + AC_MSG_RESULT([not found]) + fi + fi + # Now we can update LDFLAGS, CFLAGS and CXXFLAGS + # Do it in such a way that GnuWin32 has precedence + # over system includes and libraries + if test x$GNUWIN32_BASE != x ; then + CFLAGS="-I$GNUWIN32_BASE/include $CFLAGS" + CXXFLAGS="-I$GNUWIN32_BASE/include $CXXFLAGS" + if test x$CXX != xcl.exe ; then + LDFLAGS="-L$GNUWIN32_BASE/lib $LDFLAGS" + fi + fi + # If not running on cygwin, GNUWIN32 is useless + else + GNUWIN32_BASE= + fi + # Finally, sets automake conditional + AM_CONDITIONAL(HAVE_GNUWIN32, test x$GNUWIN32_BASE != x) +]) + + + +dnl ------------------------------------------------------------------ +dnl @synopsis AC_PATH_JPEG([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) +dnl Process option --with-jpeg. +dnl Search JPEG along the extra +dnl Define HAVE_JPEG. +dnl Set output variable JPEG_CFLAGS and JPEG_LIBS +dnl Designed by Leon. Unused at the moment. +dnl ------------------------------------------------------------------ + +AC_DEFUN([AC_PATH_JPEG], +[ + AC_ARG_VAR(JPEG_LIBS) + AC_ARG_VAR(JPEG_CFLAGS) + ac_jpeg=no + AC_ARG_WITH(jpeg, + AC_HELP_STRING([--with-jpeg=DIR], + [where the IJG jpeg library is located]), + [ac_jpeg=$withval], [ac_jpeg=yes] ) + # Process specification + if test x$ac_jpeg = xyes ; then + test x${JPEG_LIBS+set} != xset && JPEG_LIBS="-ljpeg" + elif test x$ac_jpeg != xno ; then + test x${JPEG_LIBS+set} != xset && JPEG_LIBS="-L$ac_jpeg -ljpeg" + test x${JPEG_CFLAGS+set} != xset && JPEG_CFLAGS="-I$ac_jpeg" + fi + # Try linking + if test x$ac_jpeg != xno ; then + AC_MSG_CHECKING([for jpeg library]) + save_CFLAGS="$CFLAGS" + save_CXXFLAGS="$CXXFLAGS" + save_LIBS="$LIBS" + CFLAGS="$CFLAGS $JPEG_CFLAGS" + CXXFLAGS="$CXXFLAGS $JPEG_CFLAGS" + LIBS="$LIBS $JPEG_LIBS" + AC_TRY_LINK([ +#ifdef __cplusplus +extern "C" { +#endif +#include +#include +#ifdef __cplusplus +} +#endif ],[ +jpeg_CreateDecompress(0,0,0);], + [ac_jpeg=yes], [ac_jpeg=no] ) + CFLAGS="$save_CFLAGS" + CXXFLAGS="$save_CXXFLAGS" + LIBS="$save_LIBS" + AC_MSG_RESULT($ac_jpeg) + fi + # Finish + if test x$ac_jpeg = xno; then + JPEG_CFLAGS= ; JPEG_LIBS= + ifelse([$2],,:,[$2]) + else + AC_DEFINE(HAVE_JPEG,1,[Define if you have the IJG JPEG library.]) + AC_MSG_RESULT([setting JPEG_CFLAGS=$JPEG_CFLAGS]) + AC_MSG_RESULT([setting JPEG_LIBS=$JPEG_LIBS]) + ifelse([$1],,:,[$1]) + fi +]) + + +dnl ------------------------------------------------------------------ +dnl @synopsis AC_PATH_LIBTIFF([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) +dnl Process option --with-libtiff. +dnl Search LIBTIFF along the extra +dnl Define HAVE_LIBTIFF. +dnl Set output variable LIBTIFF_CFLAGS and LIBTIFF_LIBS +dnl -- inspired from previous AC_PATH_JPEG +dnl ------------------------------------------------------------------ +AC_DEFUN([AC_PATH_LIBTIFF], +[ + AC_REQUIRE([AC_CANONICAL_HOST]) + case $host in + *msdos* | *go32* | *mingw32* | *cygwin* | *windows*) + USING_WIN=yes + ;; + *) + USING_WIN=no + esac + AC_REQUIRE([AC_PATH_GNUWIN32]) + AC_ARG_VAR(LIBTIFF_LIBS,[Tiff library to link against]) + AC_ARG_VAR(LIBTIFF_CFLAGS,[Compile flags needed for TIFF support]) + ac_libtiff=no + AC_ARG_WITH(libtiff, + AC_HELP_STRING([--with-libtiff=DIR], + [where the www.libtiff.org libtiff library is located]), + [ac_libtiff=$withval], [ac_libtiff=yes] ) + AC_MSG_CHECKING([for Leffler libtiff library]) + # Need to define TOP_SRCDIR for the cases where the LIBTIFF library + # is checked in with the code, at the top-level + TOP_SRCDIR=`cd $srcdir; pwd` + LOCAL_LIBTIFFDIR=$TOP_SRCDIR/libtiff + # First of all, deal with the case when 'cl.exe' is used as compiler + # indeed, when this is the case, we can only rely on finding + # the right library and includes, but we can't try compiling + # and linking. In addition, library needs to be specifically + # supplied on the link line and special flags are needed... + if test "x$CXX" = "xcl.exe" ; then + if test "x$ac_libtiff" = "xyes" ; then + # If LIBTIFF_LIBS has been set on configure command line + # or as environment variable, just use it if it exists + if test "x$LIBTIFF_LIBS" != "x" ; then + AC_MSG_RESULT(user specified as $LIBTIFF_LIBS) + if test "x$LIBTIFF_CFLAGS" = "x" ; then + AC_MSG_WARN(LIBTIFF_CFLAGS is empty) + fi + fi + # Otherwise, test if libtiff is at top level directory, under libtiff + if test "x$LIBTIFF_LIBS" = "x" && test -r "$LOCAL_LIBTIFFDIR/lib/libtiff.a" && test -r "$LOCAL_LIBTIFFDIR/lib/libjpeg.a" && test -r "$LOCAL_LIBTIFFDIR/lib/libz.a" ; then + AC_MSG_RESULT($LOCAL_LIBTIFFDIR/lib/libtiff.a) + LIBTIFF_LIBS="$LOCAL_LIBTIFFDIR/lib/libtiff.a $LOCAL_LIBTIFFDIR/lib/libjpeg.a $LOCAL_LIBTIFFDIR/lib/libz.a user32.lib" + LIBTIFF_CFLAGS="-I$LOCAL_LIBTIFFDIR/include" + fi + # Otherwise, if GnuWin32 was previously located + if test "x$LIBTIFF_LIBS" = "x" && test -r "$GNUWIN32_BASE/lib/libtiff.a" && test -r "$GNUWIN32_BASE/lib/libjpeg.a" && test -r "$GNUWIN32_BASE/lib/libz.a" ; then + AC_MSG_RESULT($GNUWIN32_BASE/lib/libtiff.a) + AC_MSG_WARN($GNUWIN32_BASE/lib/libtiff.a version 3.5.7 works. Some versions like 3.6.1 do not!) + LIBTIFF_LIBS="$GNUWIN32_BASE/lib/libtiff.a $GNUWIN32_BASE/lib/libjpeg.a $GNUWIN32_BASE/lib/libz.a user32.lib" + fi + if test "x$LIBTIFF_LIBS" = "x" ; then + AC_MSG_RESULT(not found or incomplete) + ac_libtiff=no + fi + elif test "x$ac_libtiff" != "xno" ; then + test x${LIBTIFF_LIBS+set} != xset && LIBTIFF_LIBS="$ac_libtiff/libtiff" + test x${LIBTIFF_CFLAGS+set} != xset && LIBTIFF_CFLAGS="-I$ac_libtiff" + fi + # If we are not using CL, that is we are either using gcc/cygwin + # or we are not running under Windows: + else + # Process specification + if test "x$ac_libtiff" = "xyes" ; then + if test "x$LIBTIFF_LIBS" = "x" ; then + # If local libtiff exists at top level, and we are running windows, use it first: + if test "x$USING_WIN" = "xyes" && test -r "$LOCAL_LIBTIFFDIR/lib/libtiff.a" && test -r "$LOCAL_LIBTIFFDIR/lib/libjpeg.a" && test -r "$LOCAL_LIBTIFFDIR/lib/libz.a" ; then + AC_MSG_RESULT($LOCAL_LIBTIFFDIR/lib/libtiff.a) + LIBTIFF_LIBS="$LOCAL_LIBTIFFDIR/lib/libtiff.a $LOCAL_LIBTIFFDIR/lib/libjpeg.a $LOCAL_LIBTIFFDIR/lib/libz.a -lole32 -luuid -lwsock32" + LIBTIFF_CFLAGS="-I$LOCAL_LIBTIFFDIR/include" + fi + # If GNUWIN32_BASE is defined, it means we are running under + # Windows and we should use these builds if available because + # they are the best bet + if test "x$LIBTIFF_LIBS" = "x" && test "x$GNUWIN32_BASE" != "x" ; then + # With version 3.5.7 of gnuwin32 libtiff, we do not need to link + # against "$GNUWIN32_BASE/lib/libgw32c.a", so no need to test + if test -r "$GNUWIN32_BASE/lib/libtiff.a" && test -r "$GNUWIN32_BASE/lib/libjpeg.a" && test -r "$GNUWIN32_BASE/lib/libz.a" ; then + AC_MSG_RESULT($GNUWIN32_BASE/lib/libtiff.a) + # With 3.5.7 version of libtiff, no need to add + # $GNUWIN32_BASE/lib/libgw32c.a after $GNUWIN32_BASE/lib/libz.a + LIBTIFF_LIBS="$GNUWIN32_BASE/lib/libtiff.a $GNUWIN32_BASE/lib/libjpeg.a $GNUWIN32_BASE/lib/libz.a -lole32 -luuid -lwsock32" + else + AC_MSG_RESULT([GNUWIN32 not found or incomplete. Trying something else]) + fi + fi + # If LIBTIFF_LIBS is still not defined after potentially going + # the GNUWIN32 route, then try using home built versions + if test "x$LIBTIFF_LIBS" = "x" ; then + TIFF_PACKAGE="$HOME_UNIX/packages/libtiff/libtiff" + if test -d "$TIFF_PACKAGE" ; then + LIBTIFF_LIBS="$TIFF_PACKAGE/libtiff.a" + LIBTIFF_CFLAGS="-I$TIFF_PACKAGE" + else + LIBTIFF_LIBS="-ltiff" + fi + fi + fi + elif test "x$ac_libtiff" != "xno" ; then + test "x${LIBTIFF_LIBS+set}" != "xset" && LIBTIFF_LIBS="$ac_libtiff/libtiff.a" + test "x${LIBTIFF_CFLAGS+set}" != "xset" && LIBTIFF_CFLAGS="-I$ac_libtiff" + fi + # Try linking - recall that -Wall is typically on, and any warning + # will cause this test to fail... This, by the way, is one of the + # reasons we cannot run this test when using the Microsoft compiler (cl), + # which outputs tons of "garbage" on stdout: + if test "x$ac_libtiff" != "xno" ; then + AC_MSG_CHECKING([linking with $LIBTIFF_LIBS]) + save_CFLAGS="$CFLAGS" + save_CXXFLAGS="$CXXFLAGS" + save_LIBS="$LIBS" + CFLAGS="$CFLAGS $LIBTIFF_CFLAGS" + CXXFLAGS="$CXXFLAGS $LIBTIFF_CFLAGS" + LIBS="$LIBS $LIBTIFF_LIBS -lm" + AC_TRY_LINK([ +#ifdef __cplusplus +extern "C" { +#endif +#include +#include +#ifdef __cplusplus +} +#endif ],[ +TIFFClose((TIFF *) 0);], + [ac_libtiff=ok],[ac_libtiff=no]) + CFLAGS="$save_CFLAGS" + CXXFLAGS="$save_CXXFLAGS" + LIBS="$save_LIBS" + AC_MSG_RESULT($ac_libtiff) + fi + fi + # Finish + if test "x$ac_libtiff" = "xno"; then + LIBTIFF_CFLAGS= ; LIBTIFF_LIBS= + ifelse([$2],,:,[$2]) + else + AC_DEFINE(HAVE_LIBTIFF,1,[Define if you have the www.libtiff.org LIBTIFF library.]) + AC_MSG_RESULT([setting LIBTIFF_CFLAGS=$LIBTIFF_CFLAGS]) + AC_MSG_RESULT([setting LIBTIFF_LIBS=$LIBTIFF_LIBS]) + CFLAGS="$LIBTIFF_CFLAGS $CFLAGS" + CXXFLAGS="$LIBTIFF_CFLAGS $CXXFLAGS" + if test "x$CXX" = "xcl.exe" ; then + LIBS="$LIBTIFF_LIBS $LIBS" + else + LIBS="$LIBTIFF_LIBS $LIBS -lm" + fi + ifelse([$1],,:,[$1]) + fi + AM_CONDITIONAL(HAVE_LIBTIFF, test x$ac_libtiff != xno) +]) diff --git a/config/ax_create_stdint_h.m4 b/config/ax_create_stdint_h.m4 new file mode 100644 index 0000000000..97df2bb61b --- /dev/null +++ b/config/ax_create_stdint_h.m4 @@ -0,0 +1,734 @@ +##### http://autoconf-archive.cryp.to/ax_create_stdint_h.html +# +# SYNOPSIS +# +# AX_CREATE_STDINT_H [( HEADER-TO-GENERATE [, HEDERS-TO-CHECK])] +# +# DESCRIPTION +# +# the "ISO C9X: 7.18 Integer types " section requires the +# existence of an include file that defines a set of +# typedefs, especially uint8_t,int32_t,uintptr_t. Many older +# installations will not provide this file, but some will have the +# very same definitions in . In other enviroments we can +# use the inet-types in which would define the typedefs +# int8_t and u_int8_t respectivly. +# +# This macros will create a local "_stdint.h" or the headerfile given +# as an argument. In many cases that file will just "#include +# " or "#include ", while in other environments +# it will provide the set of basic 'stdint's definitions/typedefs: +# +# int8_t,uint8_t,int16_t,uint16_t,int32_t,uint32_t,intptr_t,uintptr_t +# int_least32_t.. int_fast32_t.. intmax_t +# +# which may or may not rely on the definitions of other files, or +# using the AC_CHECK_SIZEOF macro to determine the actual sizeof each +# type. +# +# if your header files require the stdint-types you will want to +# create an installable file mylib-int.h that all your other +# installable header may include. So if you have a library package +# named "mylib", just use +# +# AX_CREATE_STDINT_H(mylib-int.h) +# +# in configure.ac and go to install that very header file in +# Makefile.am along with the other headers (mylib.h) - and the +# mylib-specific headers can simply use "#include " to +# obtain the stdint-types. +# +# Remember, if the system already had a valid , the +# generated file will include it directly. No need for fuzzy +# HAVE_STDINT_H things... (oops, GCC 4.2.x has deliberatly disabled +# its stdint.h for non-c99 compilation and the c99-mode is not the +# default. Therefore this macro will not use the compiler's stdint.h +# - please complain to the GCC developers). +# +# LAST MODIFICATION +# +# 2006-10-13 +# +# COPYLEFT +# +# Copyright (c) 2006 Guido U. Draheim +# +# 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 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, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. +# +# As a special exception, the respective Autoconf Macro's copyright +# owner gives unlimited permission to copy, distribute and modify the +# configure scripts that are the output of Autoconf when processing +# the Macro. You need not follow the terms of the GNU General Public +# License when using or distributing such scripts, even though +# portions of the text of the Macro appear in them. The GNU General +# Public License (GPL) does govern all other use of the material that +# constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the +# Autoconf Macro released by the Autoconf Macro Archive. When you +# make and distribute a modified version of the Autoconf Macro, you +# may extend this special exception to the GPL to apply to your +# modified version as well. + +AC_DEFUN([AX_CHECK_DATA_MODEL],[ + AC_CHECK_SIZEOF(char) + AC_CHECK_SIZEOF(short) + AC_CHECK_SIZEOF(int) + AC_CHECK_SIZEOF(long) + AC_CHECK_SIZEOF(void*) + ac_cv_char_data_model="" + ac_cv_char_data_model="$ac_cv_char_data_model$ac_cv_sizeof_char" + ac_cv_char_data_model="$ac_cv_char_data_model$ac_cv_sizeof_short" + ac_cv_char_data_model="$ac_cv_char_data_model$ac_cv_sizeof_int" + ac_cv_long_data_model="" + ac_cv_long_data_model="$ac_cv_long_data_model$ac_cv_sizeof_int" + ac_cv_long_data_model="$ac_cv_long_data_model$ac_cv_sizeof_long" + ac_cv_long_data_model="$ac_cv_long_data_model$ac_cv_sizeof_voidp" + AC_MSG_CHECKING([data model]) + case "$ac_cv_char_data_model/$ac_cv_long_data_model" in + 122/242) ac_cv_data_model="IP16" ; n="standard 16bit machine" ;; + 122/244) ac_cv_data_model="LP32" ; n="standard 32bit machine" ;; + 122/*) ac_cv_data_model="i16" ; n="unusual int16 model" ;; + 124/444) ac_cv_data_model="ILP32" ; n="standard 32bit unixish" ;; + 124/488) ac_cv_data_model="LP64" ; n="standard 64bit unixish" ;; + 124/448) ac_cv_data_model="LLP64" ; n="unusual 64bit unixish" ;; + 124/*) ac_cv_data_model="i32" ; n="unusual int32 model" ;; + 128/888) ac_cv_data_model="ILP64" ; n="unusual 64bit numeric" ;; + 128/*) ac_cv_data_model="i64" ; n="unusual int64 model" ;; + 222/*2) ac_cv_data_model="DSP16" ; n="strict 16bit dsptype" ;; + 333/*3) ac_cv_data_model="DSP24" ; n="strict 24bit dsptype" ;; + 444/*4) ac_cv_data_model="DSP32" ; n="strict 32bit dsptype" ;; + 666/*6) ac_cv_data_model="DSP48" ; n="strict 48bit dsptype" ;; + 888/*8) ac_cv_data_model="DSP64" ; n="strict 64bit dsptype" ;; + 222/*|333/*|444/*|666/*|888/*) : + ac_cv_data_model="iDSP" ; n="unusual dsptype" ;; + *) ac_cv_data_model="none" ; n="very unusual model" ;; + esac + AC_MSG_RESULT([$ac_cv_data_model ($ac_cv_long_data_model, $n)]) +]) + +dnl AX_CHECK_HEADER_STDINT_X([HEADERLIST][,ACTION-IF]) +AC_DEFUN([AX_CHECK_HEADER_STDINT_X],[ +AC_CACHE_CHECK([for stdint uintptr_t], [ac_cv_header_stdint_x],[ + ac_cv_header_stdint_x="" # the 1997 typedefs (inttypes.h) + AC_MSG_RESULT([(..)]) + for i in m4_ifval([$1],[$1],[stdint.h inttypes.h sys/inttypes.h sys/types.h]) + do + unset ac_cv_type_uintptr_t + unset ac_cv_type_uint64_t + AC_CHECK_TYPE(uintptr_t,[ac_cv_header_stdint_x=$i],continue,[#include <$i>]) + AC_CHECK_TYPE(uint64_t,[and64="/uint64_t"],[and64=""],[#include<$i>]) + m4_ifvaln([$1],[$1]) break + done + AC_MSG_CHECKING([for stdint uintptr_t]) + ]) +]) + +AC_DEFUN([AX_CHECK_HEADER_STDINT_O],[ +AC_CACHE_CHECK([for stdint uint32_t], [ac_cv_header_stdint_o],[ + ac_cv_header_stdint_o="" # the 1995 typedefs (sys/inttypes.h) + AC_MSG_RESULT([(..)]) + for i in m4_ifval([$1],[$1],[inttypes.h sys/inttypes.h sys/types.h stdint.h]) + do + unset ac_cv_type_uint32_t + unset ac_cv_type_uint64_t + AC_CHECK_TYPE(uint32_t,[ac_cv_header_stdint_o=$i],continue,[#include <$i>]) + AC_CHECK_TYPE(uint64_t,[and64="/uint64_t"],[and64=""],[#include<$i>]) + m4_ifvaln([$1],[$1]) break + break; + done + AC_MSG_CHECKING([for stdint uint32_t]) + ]) +]) + +AC_DEFUN([AX_CHECK_HEADER_STDINT_U],[ +AC_CACHE_CHECK([for stdint u_int32_t], [ac_cv_header_stdint_u],[ + ac_cv_header_stdint_u="" # the BSD typedefs (sys/types.h) + AC_MSG_RESULT([(..)]) + for i in m4_ifval([$1],[$1],[sys/types.h inttypes.h sys/inttypes.h]) ; do + unset ac_cv_type_u_int32_t + unset ac_cv_type_u_int64_t + AC_CHECK_TYPE(u_int32_t,[ac_cv_header_stdint_u=$i],continue,[#include <$i>]) + AC_CHECK_TYPE(u_int64_t,[and64="/u_int64_t"],[and64=""],[#include<$i>]) + m4_ifvaln([$1],[$1]) break + break; + done + AC_MSG_CHECKING([for stdint u_int32_t]) + ]) +]) + +AC_DEFUN([AX_CREATE_STDINT_H], +[# ------ AX CREATE STDINT H ------------------------------------- +AC_MSG_CHECKING([for stdint types]) +ac_stdint_h=`echo ifelse($1, , _stdint.h, $1)` +# try to shortcircuit - if the default include path of the compiler +# can find a "stdint.h" header then we assume that all compilers can. +AC_CACHE_VAL([ac_cv_header_stdint_t],[ +old_CXXFLAGS="$CXXFLAGS" ; CXXFLAGS="" +old_CPPFLAGS="$CPPFLAGS" ; CPPFLAGS="" +old_CFLAGS="$CFLAGS" ; CFLAGS="" +AC_TRY_COMPILE([#include ],[int_least32_t v = 0;], +[ac_cv_stdint_result="(assuming C99 compatible system)" + ac_cv_header_stdint_t="stdint.h"; ], +[ac_cv_header_stdint_t=""]) +if test "$GCC" = "yes" && test ".$ac_cv_header_stdint_t" = "."; then +CFLAGS="-std=c99" +AC_TRY_COMPILE([#include ],[int_least32_t v = 0;], +[AC_MSG_WARN(your GCC compiler has a defunct stdint.h for its default-mode)]) +fi +CXXFLAGS="$old_CXXFLAGS" +CPPFLAGS="$old_CPPFLAGS" +CFLAGS="$old_CFLAGS" ]) + +v="... $ac_cv_header_stdint_h" +if test "$ac_stdint_h" = "stdint.h" ; then + AC_MSG_RESULT([(are you sure you want them in ./stdint.h?)]) +elif test "$ac_stdint_h" = "inttypes.h" ; then + AC_MSG_RESULT([(are you sure you want them in ./inttypes.h?)]) +elif test "_$ac_cv_header_stdint_t" = "_" ; then + AC_MSG_RESULT([(putting them into $ac_stdint_h)$v]) +else + ac_cv_header_stdint="$ac_cv_header_stdint_t" + AC_MSG_RESULT([$ac_cv_header_stdint (shortcircuit)]) +fi + +if test "_$ac_cv_header_stdint_t" = "_" ; then # can not shortcircuit.. + +dnl .....intro message done, now do a few system checks..... +dnl btw, all old CHECK_TYPE macros do automatically "DEFINE" a type, +dnl therefore we use the autoconf implementation detail CHECK_TYPE_NEW +dnl instead that is triggered with 3 or more arguments (see types.m4) + +inttype_headers=`echo $2 | sed -e 's/,/ /g'` + +ac_cv_stdint_result="(no helpful system typedefs seen)" +AX_CHECK_HEADER_STDINT_X(dnl + stdint.h inttypes.h sys/inttypes.h $inttype_headers, + ac_cv_stdint_result="(seen uintptr_t$and64 in $i)") + +if test "_$ac_cv_header_stdint_x" = "_" ; then +AX_CHECK_HEADER_STDINT_O(dnl, + inttypes.h sys/inttypes.h stdint.h $inttype_headers, + ac_cv_stdint_result="(seen uint32_t$and64 in $i)") +fi + +if test "_$ac_cv_header_stdint_x" = "_" ; then +if test "_$ac_cv_header_stdint_o" = "_" ; then +AX_CHECK_HEADER_STDINT_U(dnl, + sys/types.h inttypes.h sys/inttypes.h $inttype_headers, + ac_cv_stdint_result="(seen u_int32_t$and64 in $i)") +fi fi + +dnl if there was no good C99 header file, do some typedef checks... +if test "_$ac_cv_header_stdint_x" = "_" ; then + AC_MSG_CHECKING([for stdint datatype model]) + AC_MSG_RESULT([(..)]) + AX_CHECK_DATA_MODEL +fi + +if test "_$ac_cv_header_stdint_x" != "_" ; then + ac_cv_header_stdint="$ac_cv_header_stdint_x" +elif test "_$ac_cv_header_stdint_o" != "_" ; then + ac_cv_header_stdint="$ac_cv_header_stdint_o" +elif test "_$ac_cv_header_stdint_u" != "_" ; then + ac_cv_header_stdint="$ac_cv_header_stdint_u" +else + ac_cv_header_stdint="stddef.h" +fi + +AC_MSG_CHECKING([for extra inttypes in chosen header]) +AC_MSG_RESULT([($ac_cv_header_stdint)]) +dnl see if int_least and int_fast types are present in _this_ header. +unset ac_cv_type_int_least32_t +unset ac_cv_type_int_fast32_t +AC_CHECK_TYPE(int_least32_t,,,[#include <$ac_cv_header_stdint>]) +AC_CHECK_TYPE(int_fast32_t,,,[#include<$ac_cv_header_stdint>]) +AC_CHECK_TYPE(intmax_t,,,[#include <$ac_cv_header_stdint>]) + +fi # shortcircut to system "stdint.h" +# ------------------ PREPARE VARIABLES ------------------------------ +if test "$GCC" = "yes" ; then +ac_cv_stdint_message="using gnu compiler "`$CC --version | head -1` +else +ac_cv_stdint_message="using $CC" +fi + +AC_MSG_RESULT([make use of $ac_cv_header_stdint in $ac_stdint_h dnl +$ac_cv_stdint_result]) + +dnl ----------------------------------------------------------------- +# ----------------- DONE inttypes.h checks START header ------------- +AC_CONFIG_COMMANDS([$ac_stdint_h],[ +AC_MSG_NOTICE(creating $ac_stdint_h : $_ac_stdint_h) +ac_stdint=$tmp/_stdint.h + +echo "#ifndef" $_ac_stdint_h >$ac_stdint +echo "#define" $_ac_stdint_h "1" >>$ac_stdint +echo "#ifndef" _GENERATED_STDINT_H >>$ac_stdint +echo "#define" _GENERATED_STDINT_H '"'$PACKAGE $VERSION'"' >>$ac_stdint +echo "/* generated $ac_cv_stdint_message */" >>$ac_stdint +if test "_$ac_cv_header_stdint_t" != "_" ; then +echo "#define _STDINT_HAVE_STDINT_H" "1" >>$ac_stdint +echo "#include " >>$ac_stdint +echo "#endif" >>$ac_stdint +echo "#endif" >>$ac_stdint +else + +cat >>$ac_stdint < +#else +#include + +/* .................... configured part ............................ */ + +STDINT_EOF + +echo "/* whether we have a C99 compatible stdint header file */" >>$ac_stdint +if test "_$ac_cv_header_stdint_x" != "_" ; then + ac_header="$ac_cv_header_stdint_x" + echo "#define _STDINT_HEADER_INTPTR" '"'"$ac_header"'"' >>$ac_stdint +else + echo "/* #undef _STDINT_HEADER_INTPTR */" >>$ac_stdint +fi + +echo "/* whether we have a C96 compatible inttypes header file */" >>$ac_stdint +if test "_$ac_cv_header_stdint_o" != "_" ; then + ac_header="$ac_cv_header_stdint_o" + echo "#define _STDINT_HEADER_UINT32" '"'"$ac_header"'"' >>$ac_stdint +else + echo "/* #undef _STDINT_HEADER_UINT32 */" >>$ac_stdint +fi + +echo "/* whether we have a BSD compatible inet types header */" >>$ac_stdint +if test "_$ac_cv_header_stdint_u" != "_" ; then + ac_header="$ac_cv_header_stdint_u" + echo "#define _STDINT_HEADER_U_INT32" '"'"$ac_header"'"' >>$ac_stdint +else + echo "/* #undef _STDINT_HEADER_U_INT32 */" >>$ac_stdint +fi + +echo "" >>$ac_stdint + +if test "_$ac_header" != "_" ; then if test "$ac_header" != "stddef.h" ; then + echo "#include <$ac_header>" >>$ac_stdint + echo "" >>$ac_stdint +fi fi + +echo "/* which 64bit typedef has been found */" >>$ac_stdint +if test "$ac_cv_type_uint64_t" = "yes" ; then +echo "#define _STDINT_HAVE_UINT64_T" "1" >>$ac_stdint +else +echo "/* #undef _STDINT_HAVE_UINT64_T */" >>$ac_stdint +fi +if test "$ac_cv_type_u_int64_t" = "yes" ; then +echo "#define _STDINT_HAVE_U_INT64_T" "1" >>$ac_stdint +else +echo "/* #undef _STDINT_HAVE_U_INT64_T */" >>$ac_stdint +fi +echo "" >>$ac_stdint + +echo "/* which type model has been detected */" >>$ac_stdint +if test "_$ac_cv_char_data_model" != "_" ; then +echo "#define _STDINT_CHAR_MODEL" "$ac_cv_char_data_model" >>$ac_stdint +echo "#define _STDINT_LONG_MODEL" "$ac_cv_long_data_model" >>$ac_stdint +else +echo "/* #undef _STDINT_CHAR_MODEL // skipped */" >>$ac_stdint +echo "/* #undef _STDINT_LONG_MODEL // skipped */" >>$ac_stdint +fi +echo "" >>$ac_stdint + +echo "/* whether int_least types were detected */" >>$ac_stdint +if test "$ac_cv_type_int_least32_t" = "yes"; then +echo "#define _STDINT_HAVE_INT_LEAST32_T" "1" >>$ac_stdint +else +echo "/* #undef _STDINT_HAVE_INT_LEAST32_T */" >>$ac_stdint +fi +echo "/* whether int_fast types were detected */" >>$ac_stdint +if test "$ac_cv_type_int_fast32_t" = "yes"; then +echo "#define _STDINT_HAVE_INT_FAST32_T" "1" >>$ac_stdint +else +echo "/* #undef _STDINT_HAVE_INT_FAST32_T */" >>$ac_stdint +fi +echo "/* whether intmax_t type was detected */" >>$ac_stdint +if test "$ac_cv_type_intmax_t" = "yes"; then +echo "#define _STDINT_HAVE_INTMAX_T" "1" >>$ac_stdint +else +echo "/* #undef _STDINT_HAVE_INTMAX_T */" >>$ac_stdint +fi +echo "" >>$ac_stdint + + cat >>$ac_stdint <= 199901L +#define _HAVE_UINT64_T +#define _HAVE_LONGLONG_UINT64_T +typedef long long int64_t; +typedef unsigned long long uint64_t; + +#elif !defined __STRICT_ANSI__ +#if defined _MSC_VER || defined __WATCOMC__ || defined __BORLANDC__ +#define _HAVE_UINT64_T +typedef __int64 int64_t; +typedef unsigned __int64 uint64_t; + +#elif defined __GNUC__ || defined __MWERKS__ || defined __ELF__ +/* note: all ELF-systems seem to have loff-support which needs 64-bit */ +#if !defined _NO_LONGLONG +#define _HAVE_UINT64_T +#define _HAVE_LONGLONG_UINT64_T +typedef long long int64_t; +typedef unsigned long long uint64_t; +#endif + +#elif defined __alpha || (defined __mips && defined _ABIN32) +#if !defined _NO_LONGLONG +typedef long int64_t; +typedef unsigned long uint64_t; +#endif + /* compiler/cpu type to define int64_t */ +#endif +#endif +#endif + +#if defined _STDINT_HAVE_U_INT_TYPES +/* int8_t int16_t int32_t defined by inet code, redeclare the u_intXX types */ +typedef u_int8_t uint8_t; +typedef u_int16_t uint16_t; +typedef u_int32_t uint32_t; + +/* glibc compatibility */ +#ifndef __int8_t_defined +#define __int8_t_defined +#endif +#endif + +#ifdef _STDINT_NEED_INT_MODEL_T +/* we must guess all the basic types. Apart from byte-adressable system, */ +/* there a few 32-bit-only dsp-systems that we guard with BYTE_MODEL 8-} */ +/* (btw, those nibble-addressable systems are way off, or so we assume) */ + +dnl /* have a look at "64bit and data size neutrality" at */ +dnl /* http://unix.org/version2/whatsnew/login_64bit.html */ +dnl /* (the shorthand "ILP" types always have a "P" part) */ + +#if defined _STDINT_BYTE_MODEL +#if _STDINT_LONG_MODEL+0 == 242 +/* 2:4:2 = IP16 = a normal 16-bit system */ +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned long uint32_t; +#ifndef __int8_t_defined +#define __int8_t_defined +typedef char int8_t; +typedef short int16_t; +typedef long int32_t; +#endif +#elif _STDINT_LONG_MODEL+0 == 244 || _STDINT_LONG_MODEL == 444 +/* 2:4:4 = LP32 = a 32-bit system derived from a 16-bit */ +/* 4:4:4 = ILP32 = a normal 32-bit system */ +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +#ifndef __int8_t_defined +#define __int8_t_defined +typedef char int8_t; +typedef short int16_t; +typedef int int32_t; +#endif +#elif _STDINT_LONG_MODEL+0 == 484 || _STDINT_LONG_MODEL+0 == 488 +/* 4:8:4 = IP32 = a 32-bit system prepared for 64-bit */ +/* 4:8:8 = LP64 = a normal 64-bit system */ +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +#ifndef __int8_t_defined +#define __int8_t_defined +typedef char int8_t; +typedef short int16_t; +typedef int int32_t; +#endif +/* this system has a "long" of 64bit */ +#ifndef _HAVE_UINT64_T +#define _HAVE_UINT64_T +typedef unsigned long uint64_t; +typedef long int64_t; +#endif +#elif _STDINT_LONG_MODEL+0 == 448 +/* LLP64 a 64-bit system derived from a 32-bit system */ +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +#ifndef __int8_t_defined +#define __int8_t_defined +typedef char int8_t; +typedef short int16_t; +typedef int int32_t; +#endif +/* assuming the system has a "long long" */ +#ifndef _HAVE_UINT64_T +#define _HAVE_UINT64_T +#define _HAVE_LONGLONG_UINT64_T +typedef unsigned long long uint64_t; +typedef long long int64_t; +#endif +#else +#define _STDINT_NO_INT32_T +#endif +#else +#define _STDINT_NO_INT8_T +#define _STDINT_NO_INT32_T +#endif +#endif + +/* + * quote from SunOS-5.8 sys/inttypes.h: + * Use at your own risk. As of February 1996, the committee is squarely + * behind the fixed sized types; the "least" and "fast" types are still being + * discussed. The probability that the "fast" types may be removed before + * the standard is finalized is high enough that they are not currently + * implemented. + */ + +#if defined _STDINT_NEED_INT_LEAST_T +typedef int8_t int_least8_t; +typedef int16_t int_least16_t; +typedef int32_t int_least32_t; +#ifdef _HAVE_UINT64_T +typedef int64_t int_least64_t; +#endif + +typedef uint8_t uint_least8_t; +typedef uint16_t uint_least16_t; +typedef uint32_t uint_least32_t; +#ifdef _HAVE_UINT64_T +typedef uint64_t uint_least64_t; +#endif + /* least types */ +#endif + +#if defined _STDINT_NEED_INT_FAST_T +typedef int8_t int_fast8_t; +typedef int int_fast16_t; +typedef int32_t int_fast32_t; +#ifdef _HAVE_UINT64_T +typedef int64_t int_fast64_t; +#endif + +typedef uint8_t uint_fast8_t; +typedef unsigned uint_fast16_t; +typedef uint32_t uint_fast32_t; +#ifdef _HAVE_UINT64_T +typedef uint64_t uint_fast64_t; +#endif + /* fast types */ +#endif + +#ifdef _STDINT_NEED_INTMAX_T +#ifdef _HAVE_UINT64_T +typedef int64_t intmax_t; +typedef uint64_t uintmax_t; +#else +typedef long intmax_t; +typedef unsigned long uintmax_t; +#endif +#endif + +#ifdef _STDINT_NEED_INTPTR_T +#ifndef __intptr_t_defined +#define __intptr_t_defined +/* we encourage using "long" to store pointer values, never use "int" ! */ +#if _STDINT_LONG_MODEL+0 == 242 || _STDINT_LONG_MODEL+0 == 484 +typedef unsigned int uintptr_t; +typedef int intptr_t; +#elif _STDINT_LONG_MODEL+0 == 244 || _STDINT_LONG_MODEL+0 == 444 +typedef unsigned long uintptr_t; +typedef long intptr_t; +#elif _STDINT_LONG_MODEL+0 == 448 && defined _HAVE_UINT64_T +typedef uint64_t uintptr_t; +typedef int64_t intptr_t; +#else /* matches typical system types ILP32 and LP64 - but not IP16 or LLP64 */ +typedef unsigned long uintptr_t; +typedef long intptr_t; +#endif +#endif +#endif + +/* The ISO C99 standard specifies that in C++ implementations these + should only be defined if explicitly requested. */ +#if !defined __cplusplus || defined __STDC_CONSTANT_MACROS +#ifndef UINT32_C + +/* Signed. */ +# define INT8_C(c) c +# define INT16_C(c) c +# define INT32_C(c) c +# ifdef _HAVE_LONGLONG_UINT64_T +# define INT64_C(c) c ## L +# else +# define INT64_C(c) c ## LL +# endif + +/* Unsigned. */ +# define UINT8_C(c) c ## U +# define UINT16_C(c) c ## U +# define UINT32_C(c) c ## U +# ifdef _HAVE_LONGLONG_UINT64_T +# define UINT64_C(c) c ## UL +# else +# define UINT64_C(c) c ## ULL +# endif + +/* Maximal type. */ +# ifdef _HAVE_LONGLONG_UINT64_T +# define INTMAX_C(c) c ## L +# define UINTMAX_C(c) c ## UL +# else +# define INTMAX_C(c) c ## LL +# define UINTMAX_C(c) c ## ULL +# endif + + /* literalnumbers */ +#endif +#endif + +/* These limits are merily those of a two complement byte-oriented system */ + +/* Minimum of signed integral types. */ +# define INT8_MIN (-128) +# define INT16_MIN (-32767-1) +# define INT32_MIN (-2147483647-1) +# define INT64_MIN (-__INT64_C(9223372036854775807)-1) +/* Maximum of signed integral types. */ +# define INT8_MAX (127) +# define INT16_MAX (32767) +# define INT32_MAX (2147483647) +# define INT64_MAX (__INT64_C(9223372036854775807)) + +/* Maximum of unsigned integral types. */ +# define UINT8_MAX (255) +# define UINT16_MAX (65535) +# define UINT32_MAX (4294967295U) +# define UINT64_MAX (__UINT64_C(18446744073709551615)) + +/* Minimum of signed integral types having a minimum size. */ +# define INT_LEAST8_MIN INT8_MIN +# define INT_LEAST16_MIN INT16_MIN +# define INT_LEAST32_MIN INT32_MIN +# define INT_LEAST64_MIN INT64_MIN +/* Maximum of signed integral types having a minimum size. */ +# define INT_LEAST8_MAX INT8_MAX +# define INT_LEAST16_MAX INT16_MAX +# define INT_LEAST32_MAX INT32_MAX +# define INT_LEAST64_MAX INT64_MAX + +/* Maximum of unsigned integral types having a minimum size. */ +# define UINT_LEAST8_MAX UINT8_MAX +# define UINT_LEAST16_MAX UINT16_MAX +# define UINT_LEAST32_MAX UINT32_MAX +# define UINT_LEAST64_MAX UINT64_MAX + + /* shortcircuit*/ +#endif + /* once */ +#endif +#endif +STDINT_EOF +fi + if cmp -s $ac_stdint_h $ac_stdint 2>/dev/null; then + AC_MSG_NOTICE([$ac_stdint_h is unchanged]) + else + ac_dir=`AS_DIRNAME(["$ac_stdint_h"])` + AS_MKDIR_P(["$ac_dir"]) + rm -f $ac_stdint_h + mv $ac_stdint $ac_stdint_h + fi +],[# variables for create stdint.h replacement +PACKAGE="$PACKAGE" +VERSION="$VERSION" +ac_stdint_h="$ac_stdint_h" +_ac_stdint_h=AS_TR_CPP(_$PACKAGE-$ac_stdint_h) +ac_cv_stdint_message="$ac_cv_stdint_message" +ac_cv_header_stdint_t="$ac_cv_header_stdint_t" +ac_cv_header_stdint_x="$ac_cv_header_stdint_x" +ac_cv_header_stdint_o="$ac_cv_header_stdint_o" +ac_cv_header_stdint_u="$ac_cv_header_stdint_u" +ac_cv_type_uint64_t="$ac_cv_type_uint64_t" +ac_cv_type_u_int64_t="$ac_cv_type_u_int64_t" +ac_cv_char_data_model="$ac_cv_char_data_model" +ac_cv_long_data_model="$ac_cv_long_data_model" +ac_cv_type_int_least32_t="$ac_cv_type_int_least32_t" +ac_cv_type_int_fast32_t="$ac_cv_type_int_fast32_t" +ac_cv_type_intmax_t="$ac_cv_type_intmax_t" +]) +]) diff --git a/config/config.guess b/config/config.guess new file mode 100755 index 0000000000..dff9e481b7 --- /dev/null +++ b/config/config.guess @@ -0,0 +1,1317 @@ +#! /bin/sh +# Attempt to guess a canonical system name. +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 +# Free Software Foundation, Inc. + +timestamp='2001-09-04' + +# 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 2 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, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# 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. + +# Written by Per Bothner . +# Please send patches to . +# +# This script attempts to guess a canonical system name similar to +# config.sub. If it succeeds, it prints the system name on stdout, and +# exits with 0. Otherwise, it exits with 1. +# +# The plan is that this can be called by configure scripts if you +# don't specify an explicit build system type. + +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 (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 +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 0 ;; + --version | -v ) + echo "$version" ; exit 0 ;; + --help | --h* | -h ) + echo "$usage"; exit 0 ;; + -- ) # 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 + + +dummy=dummy-$$ +trap 'rm -f $dummy.c $dummy.o $dummy.rel $dummy; exit 1' 1 2 15 + +# CC_FOR_BUILD -- compiler used by this script. +# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still +# use `HOST_CC' if defined, but it is deprecated. + +set_cc_for_build='case $CC_FOR_BUILD,$HOST_CC,$CC in + ,,) echo "int dummy(){}" > $dummy.c ; + for c in cc gcc c89 ; do + ($c $dummy.c -c -o $dummy.o) >/dev/null 2>&1 ; + if test $? = 0 ; then + CC_FOR_BUILD="$c"; break ; + fi ; + done ; + rm -f $dummy.c $dummy.o $dummy.rel ; + 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' + +# 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 + +# 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 tupples: *-*-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. + # Determine the machine/vendor (is the vendor relevant). + case "${UNAME_MACHINE}" in + amiga) machine=m68k-unknown ;; + arm32) machine=arm-unknown ;; + atari*) machine=m68k-atari ;; + sun3*) machine=m68k-sun ;; + mac68k) machine=m68k-apple ;; + macppc) machine=powerpc-apple ;; + hp3[0-9][05]) machine=m68k-hp ;; + ibmrt|romp-ibm) machine=romp-ibm ;; + *) machine=${UNAME_MACHINE}-unknown ;; + esac + # The Operating System including object format, if it has switched + # to ELF recently, or will in the future. + case "${UNAME_MACHINE}" in + i386|sparc|amiga|arm*|hp300|mvme68k|vax|atari|luna68k|mac68k|news68k|next68k|pc532|sun3*|x68k) + eval $set_cc_for_build + if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep __ELF__ >/dev/null + 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 + release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` + # 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 0 ;; + alpha:OSF1:*:*) + if test $UNAME_RELEASE = "V4.0"; then + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` + fi + # 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. + cat <$dummy.s + .data +\$Lformat: + .byte 37,100,45,37,120,10,0 # "%d-%x\n" + + .text + .globl main + .align 4 + .ent main +main: + .frame \$30,16,\$26,0 + ldgp \$29,0(\$27) + .prologue 1 + .long 0x47e03d80 # implver \$0 + lda \$2,-1 + .long 0x47e20c21 # amask \$2,\$1 + lda \$16,\$Lformat + mov \$0,\$17 + not \$1,\$18 + jsr \$26,printf + ldgp \$29,0(\$26) + mov 0,\$16 + jsr \$26,exit + .end main +EOF + eval $set_cc_for_build + $CC_FOR_BUILD $dummy.s -o $dummy 2>/dev/null + if test "$?" = 0 ; then + case `./$dummy` in + 0-0) + UNAME_MACHINE="alpha" + ;; + 1-0) + UNAME_MACHINE="alphaev5" + ;; + 1-1) + UNAME_MACHINE="alphaev56" + ;; + 1-101) + UNAME_MACHINE="alphapca56" + ;; + 2-303) + UNAME_MACHINE="alphaev6" + ;; + 2-307) + UNAME_MACHINE="alphaev67" + ;; + 2-1307) + UNAME_MACHINE="alphaev68" + ;; + esac + fi + rm -f $dummy.s $dummy + echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[VTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` + exit 0 ;; + 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 0 ;; + 21064:Windows_NT:50:3) + echo alpha-dec-winnt3.5 + exit 0 ;; + Amiga*:UNIX_System_V:4.0:*) + echo m68k-unknown-sysv4 + exit 0;; + amiga:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + *:[Aa]miga[Oo][Ss]:*:*) + echo ${UNAME_MACHINE}-unknown-amigaos + exit 0 ;; + arc64:OpenBSD:*:*) + echo mips64el-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + arc:OpenBSD:*:*) + echo mipsel-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + hkmips:OpenBSD:*:*) + echo mips-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + pmax:OpenBSD:*:*) + echo mipsel-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + sgi:OpenBSD:*:*) + echo mips-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + wgrisc:OpenBSD:*:*) + echo mipsel-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + *:OS/390:*:*) + echo i370-ibm-openedition + exit 0 ;; + arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) + echo arm-acorn-riscix${UNAME_RELEASE} + exit 0;; + SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) + echo hppa1.1-hitachi-hiuxmpp + exit 0;; + 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 0 ;; + NILE*:*:*:dcosx) + echo pyramid-pyramid-svr4 + exit 0 ;; + sun4H:SunOS:5.*:*) + echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) + echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + i86pc:SunOS:5.*:*) + echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + 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 0 ;; + 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 0 ;; + sun3*:SunOS:*:*) + echo m68k-sun-sunos${UNAME_RELEASE} + exit 0 ;; + sun*:*:4.2BSD:*) + UNAME_RELEASE=`(head -1 /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 0 ;; + aushp:SunOS:*:*) + echo sparc-auspex-sunos${UNAME_RELEASE} + exit 0 ;; + sparc*:NetBSD:*) + echo `uname -p`-unknown-netbsd${UNAME_RELEASE} + exit 0 ;; + atari*:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + # 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 0 ;; + atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit 0 ;; + *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit 0 ;; + milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) + echo m68k-milan-mint${UNAME_RELEASE} + exit 0 ;; + hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) + echo m68k-hades-mint${UNAME_RELEASE} + exit 0 ;; + *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) + echo m68k-unknown-mint${UNAME_RELEASE} + exit 0 ;; + sun3*:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + mac68k:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + mvme68k:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + mvme88k:OpenBSD:*:*) + echo m88k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + powerpc:machten:*:*) + echo powerpc-apple-machten${UNAME_RELEASE} + exit 0 ;; + RISC*:Mach:*:*) + echo mips-dec-mach_bsd4.3 + exit 0 ;; + RISC*:ULTRIX:*:*) + echo mips-dec-ultrix${UNAME_RELEASE} + exit 0 ;; + VAX*:ULTRIX*:*:*) + echo vax-dec-ultrix${UNAME_RELEASE} + exit 0 ;; + 2020:CLIX:*:* | 2430:CLIX:*:*) + echo clipper-intergraph-clix${UNAME_RELEASE} + exit 0 ;; + 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 $dummy.c -o $dummy \ + && ./$dummy `echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` \ + && rm -f $dummy.c $dummy && exit 0 + rm -f $dummy.c $dummy + echo mips-mips-riscos${UNAME_RELEASE} + exit 0 ;; + Motorola:PowerMAX_OS:*:*) + echo powerpc-motorola-powermax + exit 0 ;; + Night_Hawk:Power_UNIX:*:*) + echo powerpc-harris-powerunix + exit 0 ;; + m88k:CX/UX:7*:*) + echo m88k-harris-cxux7 + exit 0 ;; + m88k:*:4*:R4*) + echo m88k-motorola-sysv4 + exit 0 ;; + m88k:*:3*:R3*) + echo m88k-motorola-sysv3 + exit 0 ;; + 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 0 ;; + M88*:DolphinOS:*:*) # DolphinOS (SVR3) + echo m88k-dolphin-sysv3 + exit 0 ;; + M88*:*:R3*:*) + # Delta 88k system running SVR3 + echo m88k-motorola-sysv3 + exit 0 ;; + XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) + echo m88k-tektronix-sysv3 + exit 0 ;; + Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) + echo m68k-tektronix-bsd + exit 0 ;; + *:IRIX*:*:*) + echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'` + exit 0 ;; + ????????: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 0 ;; # Note that: echo "'`uname -s`'" gives 'AIX ' + i*86:AIX:*:*) + echo i386-ibm-aix + exit 0 ;; + 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 0 ;; + *: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 + $CC_FOR_BUILD $dummy.c -o $dummy && ./$dummy && rm -f $dummy.c $dummy && exit 0 + rm -f $dummy.c $dummy + echo rs6000-ibm-aix3.2.5 + 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 0 ;; + *:AIX:*:[45]) + IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | head -1 | 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 0 ;; + *:AIX:*:*) + echo rs6000-ibm-aix + exit 0 ;; + ibmrt:4.4BSD:*|romp-ibm:BSD:*) + echo romp-ibm-bsd4.4 + exit 0 ;; + ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and + echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to + exit 0 ;; # report: romp-ibm BSD 4.3 + *:BOSX:*:*) + echo rs6000-bull-bosx + exit 0 ;; + DPX/2?00:B.O.S.:*:*) + echo m68k-bull-sysv3 + exit 0 ;; + 9000/[34]??:4.3bsd:1.*:*) + echo m68k-hp-bsd + exit 0 ;; + hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) + echo m68k-hp-bsd4.4 + exit 0 ;; + 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]) + case "${HPUX_REV}" in + 11.[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" ;; + esac ;; + esac + fi ;; + esac + 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 $dummy.c -o $dummy 2>/dev/null ) && HP_ARCH=`./$dummy` + if test -z "$HP_ARCH"; then HP_ARCH=hppa; fi + rm -f $dummy.c $dummy + fi ;; + esac + echo ${HP_ARCH}-hp-hpux${HPUX_REV} + exit 0 ;; + ia64:HP-UX:*:*) + HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` + echo ia64-hp-hpux${HPUX_REV} + exit 0 ;; + 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 $dummy.c -o $dummy && ./$dummy && rm -f $dummy.c $dummy && exit 0 + rm -f $dummy.c $dummy + echo unknown-hitachi-hiuxwe2 + exit 0 ;; + 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* ) + echo hppa1.1-hp-bsd + exit 0 ;; + 9000/8??:4.3bsd:*:*) + echo hppa1.0-hp-bsd + exit 0 ;; + *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) + echo hppa1.0-hp-mpeix + exit 0 ;; + hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* ) + echo hppa1.1-hp-osf + exit 0 ;; + hp8??:OSF1:*:*) + echo hppa1.0-hp-osf + exit 0 ;; + i*86:OSF1:*:*) + if [ -x /usr/sbin/sysversion ] ; then + echo ${UNAME_MACHINE}-unknown-osf1mk + else + echo ${UNAME_MACHINE}-unknown-osf1 + fi + exit 0 ;; + parisc*:Lites*:*:*) + echo hppa1.1-hp-lites + exit 0 ;; + hppa*:OpenBSD:*:*) + echo hppa-unknown-openbsd + exit 0 ;; + C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) + echo c1-convex-bsd + exit 0 ;; + C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit 0 ;; + C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) + echo c34-convex-bsd + exit 0 ;; + C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) + echo c38-convex-bsd + exit 0 ;; + C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) + echo c4-convex-bsd + exit 0 ;; + CRAY*X-MP:*:*:*) + echo xmp-cray-unicos + exit 0 ;; + CRAY*Y-MP:*:*:*) + echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit 0 ;; + 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 0 ;; + CRAY*TS:*:*:*) + echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit 0 ;; + CRAY*T3D:*:*:*) + echo alpha-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit 0 ;; + CRAY*T3E:*:*:*) + echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit 0 ;; + CRAY*SV1:*:*:*) + echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit 0 ;; + CRAY-2:*:*:*) + echo cray2-cray-unicos + exit 0 ;; + 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 0 ;; + hp300:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) + echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} + exit 0 ;; + sparc*:BSD/OS:*:*) + echo sparc-unknown-bsdi${UNAME_RELEASE} + exit 0 ;; + *:BSD/OS:*:*) + echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE} + exit 0 ;; + *:FreeBSD:*:*) + echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` + exit 0 ;; + *:OpenBSD:*:*) + echo ${UNAME_MACHINE}-unknown-openbsd`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` + exit 0 ;; + i*:CYGWIN*:*) + echo ${UNAME_MACHINE}-pc-cygwin + exit 0 ;; + i*:MINGW*:*) + echo ${UNAME_MACHINE}-pc-mingw32 + exit 0 ;; + i*:PW*:*) + echo ${UNAME_MACHINE}-pc-pw32 + exit 0 ;; + 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 i386-pc-interix + exit 0 ;; + i*:UWIN*:*) + echo ${UNAME_MACHINE}-pc-uwin + exit 0 ;; + p*:CYGWIN*:*) + echo powerpcle-unknown-cygwin + exit 0 ;; + prep*:SunOS:5.*:*) + echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + *:GNU:*:*) + echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` + exit 0 ;; + i*86:Minix:*:*) + echo ${UNAME_MACHINE}-pc-minix + exit 0 ;; + arm*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit 0 ;; + ia64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux + exit 0 ;; + m68*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit 0 ;; + mips:Linux:*:*) + case `sed -n '/^byte/s/^.*: \(.*\) endian/\1/p' < /proc/cpuinfo` in + big) echo mips-unknown-linux-gnu && exit 0 ;; + little) echo mipsel-unknown-linux-gnu && exit 0 ;; + esac + ;; + ppc:Linux:*:*) + echo powerpc-unknown-linux-gnu + exit 0 ;; + ppc64:Linux:*:*) + echo powerpc64-unknown-linux-gnu + exit 0 ;; + 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 ld.so.1 >/dev/null + if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi + echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC} + exit 0 ;; + 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-unknown-linux-gnu ;; + PA8*) echo hppa2.0-unknown-linux-gnu ;; + *) echo hppa-unknown-linux-gnu ;; + esac + exit 0 ;; + parisc64:Linux:*:* | hppa64:Linux:*:*) + echo hppa64-unknown-linux-gnu + exit 0 ;; + s390:Linux:*:* | s390x:Linux:*:*) + echo ${UNAME_MACHINE}-ibm-linux + exit 0 ;; + sh*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit 0 ;; + sparc:Linux:*:* | sparc64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit 0 ;; + x86_64:Linux:*:*) + echo x86_64-unknown-linux-gnu + exit 0 ;; + i*86:Linux:*:*) + # The BFD linker knows what the default object file format is, so + # first see if it will tell us. cd to the root directory to prevent + # problems with other programs or directories called `ld' in the path. + ld_supported_targets=`cd /; ld --help 2>&1 \ + | sed -ne '/supported targets:/!d + s/[ ][ ]*/ /g + s/.*supported targets: *// + s/ .*// + p'` + case "$ld_supported_targets" in + elf32-i386) + TENTATIVE="${UNAME_MACHINE}-pc-linux-gnu" + ;; + a.out-i386-linux) + echo "${UNAME_MACHINE}-pc-linux-gnuaout" + exit 0 ;; + coff-i386) + echo "${UNAME_MACHINE}-pc-linux-gnucoff" + exit 0 ;; + "") + # Either a pre-BFD a.out linker (linux-gnuoldld) or + # one that does not give us useful --help. + echo "${UNAME_MACHINE}-pc-linux-gnuoldld" + exit 0 ;; + esac + # Determine whether the default compiler is a.out or elf + eval $set_cc_for_build + cat >$dummy.c < +#ifdef __cplusplus +#include /* for printf() prototype */ + int main (int argc, char *argv[]) { +#else + int main (argc, argv) int argc; char *argv[]; { +#endif +#ifdef __ELF__ +# ifdef __GLIBC__ +# if __GLIBC__ >= 2 + printf ("%s-pc-linux-gnu\n", argv[1]); +# else + printf ("%s-pc-linux-gnulibc1\n", argv[1]); +# endif +# else + printf ("%s-pc-linux-gnulibc1\n", argv[1]); +# endif +#else + printf ("%s-pc-linux-gnuaout\n", argv[1]); +#endif + return 0; +} +EOF + $CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null && ./$dummy "${UNAME_MACHINE}" && rm -f $dummy.c $dummy && exit 0 + rm -f $dummy.c $dummy + test x"${TENTATIVE}" != x && echo "${TENTATIVE}" && exit 0 + ;; + 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 0 ;; + 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 0 ;; + 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 0 ;; + i*86:*:5:[78]*) + case `/bin/uname -X | grep "^Machine"` in + *486*) UNAME_MACHINE=i486 ;; + *Pentium) UNAME_MACHINE=i586 ;; + *Pent*|*Celeron) UNAME_MACHINE=i686 ;; + esac + echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} + exit 0 ;; + 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|egrep Release|sed -e 's/.*= //')` + (/bin/uname -X|egrep i80486 >/dev/null) && UNAME_MACHINE=i486 + (/bin/uname -X|egrep '^Machine.*Pentium' >/dev/null) \ + && UNAME_MACHINE=i586 + (/bin/uname -X|egrep '^Machine.*Pent ?II' >/dev/null) \ + && UNAME_MACHINE=i686 + (/bin/uname -X|egrep '^Machine.*Pentium Pro' >/dev/null) \ + && UNAME_MACHINE=i686 + echo ${UNAME_MACHINE}-pc-sco$UNAME_REL + else + echo ${UNAME_MACHINE}-pc-sysv32 + fi + exit 0 ;; + i*86:*DOS:*:*) + echo ${UNAME_MACHINE}-pc-msdosdjgpp + exit 0 ;; + 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 i386. + echo i386-pc-msdosdjgpp + exit 0 ;; + Intel:Mach:3*:*) + echo i386-pc-mach3 + exit 0 ;; + paragon:*:*:*) + echo i860-intel-osf1 + exit 0 ;; + 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-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4 + fi + exit 0 ;; + mini*:CTIX:SYS*5:*) + # "miniframe" + echo m68010-convergent-sysv + exit 0 ;; + M68*:*:R3V[567]*:*) + test -r /sysV68 && echo 'm68k-motorola-sysv' && exit 0 ;; + 3[34]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 4850:*: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 0 + /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ + && echo i586-ncr-sysv4.3${OS_REL} && exit 0 ;; + 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && echo i486-ncr-sysv4 && exit 0 ;; + m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) + echo m68k-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + mc68030:UNIX_System_V:4.*:*) + echo m68k-atari-sysv4 + exit 0 ;; + i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.0*:*) + echo i386-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + TSUNAMI:LynxOS:2.*:*) + echo sparc-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + rs6000:LynxOS:2.*:*) + echo rs6000-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.0*:*) + echo powerpc-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + SM[BE]S:UNIX_SV:*:*) + echo mips-dde-sysv${UNAME_RELEASE} + exit 0 ;; + RM*:ReliantUNIX-*:*:*) + echo mips-sni-sysv4 + exit 0 ;; + RM*:SINIX-*:*:*) + echo mips-sni-sysv4 + exit 0 ;; + *: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 0 ;; + PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort + # says + echo i586-unisys-sysv4 + exit 0 ;; + *:UNIX_System_V:4*:FTX*) + # From Gerald Hewes . + # How about differentiating between stratus architectures? -djm + echo hppa1.1-stratus-sysv4 + exit 0 ;; + *:*:*:FTX*) + # From seanf@swdc.stratus.com. + echo i860-stratus-sysv4 + exit 0 ;; + *:VOS:*:*) + # From Paul.Green@stratus.com. + echo hppa1.1-stratus-vos + exit 0 ;; + mc68*:A/UX:*:*) + echo m68k-apple-aux${UNAME_RELEASE} + exit 0 ;; + news*:NEWS-OS:6*:*) + echo mips-sony-newsos6 + exit 0 ;; + 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-unknown-sysv${UNAME_RELEASE} + fi + exit 0 ;; + BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. + echo powerpc-be-beos + exit 0 ;; + BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. + echo powerpc-apple-beos + exit 0 ;; + BePC:BeOS:*:*) # BeOS running on Intel PC compatible. + echo i586-pc-beos + exit 0 ;; + SX-4:SUPER-UX:*:*) + echo sx4-nec-superux${UNAME_RELEASE} + exit 0 ;; + SX-5:SUPER-UX:*:*) + echo sx5-nec-superux${UNAME_RELEASE} + exit 0 ;; + Power*:Rhapsody:*:*) + echo powerpc-apple-rhapsody${UNAME_RELEASE} + exit 0 ;; + *:Rhapsody:*:*) + echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE} + exit 0 ;; + *:Darwin:*:*) + echo `uname -p`-apple-darwin${UNAME_RELEASE} + exit 0 ;; + *:procnto*:*:* | *:QNX:[0123456789]*:*) + if test "${UNAME_MACHINE}" = "x86pc"; then + UNAME_MACHINE=pc + fi + echo `uname -p`-${UNAME_MACHINE}-nto-qnx + exit 0 ;; + *:QNX:*:4*) + echo i386-pc-qnx + exit 0 ;; + NSR-[KW]:NONSTOP_KERNEL:*:*) + echo nsr-tandem-nsk${UNAME_RELEASE} + exit 0 ;; + *:NonStop-UX:*:*) + echo mips-compaq-nonstopux + exit 0 ;; + BS2000:POSIX*:*:*) + echo bs2000-siemens-sysv + exit 0 ;; + DS/*:UNIX_System_V:*:*) + echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE} + exit 0 ;; + *: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}-unknown-plan9 + exit 0 ;; + 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 0 ;; + *:TOPS-10:*:*) + echo pdp10-unknown-tops10 + exit 0 ;; + *:TENEX:*:*) + echo pdp10-unknown-tenex + exit 0 ;; + KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) + echo pdp10-dec-tops20 + exit 0 ;; + XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) + echo pdp10-xkl-tops20 + exit 0 ;; + *:TOPS-20:*:*) + echo pdp10-unknown-tops20 + exit 0 ;; + *:ITS:*:*) + echo pdp10-unknown-its + exit 0 ;; + i*86:XTS-300:*:STOP) + echo ${UNAME_MACHINE}-unknown-stop + exit 0 ;; + i*86:atheos:*:*) + echo ${UNAME_MACHINE}-unknown-atheos + exit 0 ;; +esac + +#echo '(No uname command or uname output not recognized.)' 1>&2 +#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2 + +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"); 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 $dummy.c -o $dummy 2>/dev/null && ./$dummy && rm -f $dummy.c $dummy && exit 0 +rm -f $dummy.c $dummy + +# Apollos put the system type in the environment. + +test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit 0; } + +# 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 0 ;; + c2*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit 0 ;; + c34*) + echo c34-convex-bsd + exit 0 ;; + c38*) + echo c38-convex-bsd + exit 0 ;; + c4*) + echo c4-convex-bsd + exit 0 ;; + 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: diff --git a/config/config.h.in b/config/config.h.in new file mode 100644 index 0000000000..a7fec1d2e8 --- /dev/null +++ b/config/config.h.in @@ -0,0 +1,203 @@ +/* config/config.h.in. Generated from configure.ac by autoheader. */ + + +#ifndef CONFIG_AUTO_H +#define CONFIG_AUTO_H +/* config_auto.h: begin */ + + +/* Define when compiling in debug mode */ +#undef DEBUG_MODE + +/* directory "bindir" */ +#undef DIR_BINDIR + +/* directory "datadir" */ +#undef DIR_DATADIR + +/* directory "exec_prefix" */ +#undef DIR_EXEC_PREFIX + +/* directory "libdir" */ +#undef DIR_LIBDIR + +/* directory "mandir" */ +#undef DIR_MANDIR + +/* directory "prefix" */ +#undef DIR_PREFIX + +/* Define to 1 if you have the `acos' function. */ +#undef HAVE_ACOS + +/* Define to 1 if you have the `asin' function. */ +#undef HAVE_ASIN + +/* define if bool is a built-in type */ +#undef HAVE_BOOL + +/* Define to 1 if you have the `fork' function. */ +#undef HAVE_FORK + +/* Define to 1 if you have the `gethostname' function. */ +#undef HAVE_GETHOSTNAME + +/* Define to 1 if you have the `getpagesize' function. */ +#undef HAVE_GETPAGESIZE + +/* Define to 1 if you have the header file. */ +#undef HAVE_INTTYPES_H + +/* Define if you have the www.libtiff.org LIBTIFF library. */ +#undef HAVE_LIBTIFF + +/* 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 `memcpy' function. */ +#undef HAVE_MEMCPY + +/* Define to 1 if you have the header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if you have a working `mmap' system call. */ +#undef HAVE_MMAP + +/* Define to 1 if stdbool.h conforms to C99. */ +#undef HAVE_STDBOOL_H + +/* define if the compiler comes with standard includes */ +#undef HAVE_STDINCLUDES + +/* 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 `strchr' function. */ +#undef HAVE_STRCHR + +/* Define to 1 if you have the `strerror' function. */ +#undef HAVE_STRERROR + +/* 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 if the compiler recognizes typename */ +#undef HAVE_TYPENAME + +/* Define to 1 if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define to 1 if you have the `vfork' function. */ +#undef HAVE_VFORK + +/* Define to 1 if you have the header file. */ +#undef HAVE_VFORK_H + +/* Define to 1 if you have the `vsnprintf' function. */ +#undef HAVE_VSNPRINTF + +/* Define to 1 if the system has the type `wchar_t'. */ +#undef HAVE_WCHAR_T + +/* Define to 1 if `fork' works. */ +#undef HAVE_WORKING_FORK + +/* Define to 1 if `vfork' works. */ +#undef HAVE_WORKING_VFORK + +/* Define to 1 if the system has the type `_Bool'. */ +#undef HAVE__BOOL + +/* Define to 1 if your C compiler doesn't accept -c and -o together. */ +#undef NO_MINUS_C_MINUS_O + +/* 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 + +/* 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 to 1 if your processor stores words with the most significant byte + first (like Motorola and SPARC, unlike Intel and VAX). */ +#undef WORDS_BIGENDIAN + +/* 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 + +/* Define to `int' if does not define. */ +#undef pid_t + +/* Define to `unsigned' if does not define. */ +#undef size_t + +/* Define as `fork' if `vfork' does not work. */ +#undef vfork + + + +/* Miscellaneous defines */ +#define AUTOCONF 1 + +/* config_auto.h: end */ +#endif + diff --git a/config/config.sub b/config/config.sub new file mode 100755 index 0000000000..393f13d373 --- /dev/null +++ b/config/config.sub @@ -0,0 +1,1411 @@ +#! /bin/sh +# Configuration validation subroutine script. +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 +# Free Software Foundation, Inc. + +timestamp='2001-09-07' + +# This file is (in principle) common to ALL GNU software. +# The presence of a machine in this file suggests that SOME GNU software +# can handle that machine. It does not imply ALL GNU software can. +# +# 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 2 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, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. + +# 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. + +# Please send patches to . +# +# 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. + +# 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 (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 +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 0 ;; + --version | -v ) + echo "$version" ; exit 0 ;; + --help | --h* | -h ) + echo "$usage"; exit 0 ;; + -- ) # 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 0;; + + * ) + 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* | storm-chaos* | os2-emx* | windows32-*) + os=-$maybe_os + basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` + ;; + *) + 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) + os= + basic_machine=$1 + ;; + -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 + ;; + -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/'` + ;; + -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*) + 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 \ + | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ + | arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr \ + | c4x | clipper \ + | d10v | d30v | dsp16xx \ + | fr30 \ + | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ + | i370 | i860 | i960 | ia64 \ + | m32r | m68000 | m68k | m88k | mcore \ + | mips16 | mips64 | mips64el | mips64orion | mips64orionel \ + | mips64vr4100 | mips64vr4100el | mips64vr4300 \ + | mips64vr4300el | mips64vr5000 | mips64vr5000el \ + | mipsbe | mipseb | mipsel | mipsle | mipstx39 | mipstx39el \ + | mipsisa32 \ + | mn10200 | mn10300 \ + | ns16k | ns32k \ + | openrisc \ + | pdp10 | pdp11 | pj | pjl \ + | powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \ + | pyramid \ + | s390 | s390x \ + | sh | sh[34] | sh[34]eb | shbe | shle \ + | sparc | sparc64 | sparclet | sparclite | sparcv9 | sparcv9b \ + | stormy16 | strongarm \ + | tahoe | thumb | tic80 | tron \ + | v850 \ + | we32k \ + | x86 | xscale \ + | z8k) + basic_machine=$basic_machine-unknown + ;; + m6811 | m68hc11 | m6812 | m68hc12) + # Motorola 68HC11/12. + basic_machine=$basic_machine-unknown + os=-none + ;; + m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k) + ;; + + # 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-* \ + | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ + | alphapca5[67]-* | arc-* \ + | arm-* | armbe-* | armle-* | armv*-* \ + | bs2000-* \ + | c[123]* | c30-* | [cjt]90-* | c54x-* \ + | clipper-* | cray2-* | cydra-* \ + | d10v-* | d30v-* \ + | elxsi-* \ + | f30[01]-* | f700-* | fr30-* | fx80-* \ + | h8300-* | h8500-* \ + | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ + | i*86-* | i860-* | i960-* | ia64-* \ + | m32r-* \ + | m68000-* | m680[01234]0-* | m68360-* | m683?2-* | m68k-* \ + | m88110-* | m88k-* | mcore-* \ + | mips-* | mips16-* | mips64-* | mips64el-* | mips64orion-* \ + | mips64orionel-* | mips64vr4100-* | mips64vr4100el-* \ + | mips64vr4300-* | mips64vr4300el-* | mipsbe-* | mipseb-* \ + | mipsle-* | mipsel-* | mipstx39-* | mipstx39el-* \ + | none-* | np1-* | ns16k-* | ns32k-* \ + | orion-* \ + | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ + | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \ + | pyramid-* \ + | romp-* | rs6000-* \ + | s390-* | s390x-* \ + | sh-* | sh[34]-* | sh[34]eb-* | shbe-* | shle-* \ + | sparc-* | sparc64-* | sparc86x-* | sparclite-* \ + | sparcv9-* | sparcv9b-* | stormy16-* | strongarm-* | sv1-* \ + | t3e-* | tahoe-* | thumb-* | tic30-* | tic54x-* | tic80-* | tron-* \ + | v850-* | vax-* \ + | we32k-* \ + | x86-* | x86_64-* | xmp-* | xps100-* | xscale-* \ + | ymp-* \ + | z8k-*) + ;; + # 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 + ;; + 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 + ;; + 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 + ;; + aux) + basic_machine=m68k-apple + os=-aux + ;; + balance) + basic_machine=ns32k-sequent + os=-dynix + ;; + 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 | ymp) + basic_machine=ymp-cray + os=-unicos + ;; + cray2) + basic_machine=cray2-cray + os=-unicos + ;; + [cjt]90) + basic_machine=${basic_machine}-cray + os=-unicos + ;; + crds | unos) + basic_machine=m68k-crds + ;; + cris | cris-* | etrax*) + basic_machine=cris-axis + ;; + da30 | da30-*) + basic_machine=m68k-da30 + ;; + decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn) + basic_machine=mips-dec + ;; + delta | 3300 | motorola-3300 | motorola-delta \ + | 3300-motorola | delta-motorola) + basic_machine=m68k-motorola + ;; + delta88) + basic_machine=m88k-motorola + os=-sysv3 + ;; + 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'm not sure what "Sysv32" means. Should this be sysv3.2? + 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 + ;; + m88k-omron*) + basic_machine=m88k-omron + ;; + magnum | m3230) + basic_machine=mips-mips + os=-sysv + ;; + merlin) + basic_machine=ns32k-utek + os=-sysv + ;; + mingw32) + basic_machine=i386-pc + os=-mingw32 + ;; + miniframe) + basic_machine=m68000-convergent + ;; + *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*) + basic_machine=m68k-atari + os=-mint + ;; + mipsel*-linux*) + basic_machine=mipsel-unknown + os=-linux-gnu + ;; + mips*-linux*) + basic_machine=mips-unknown + os=-linux-gnu + ;; + mips3*-*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'` + ;; + mips3*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown + ;; + mmix*) + basic_machine=mmix-knuth + os=-mmixware + ;; + monitor) + basic_machine=m68k-rom68k + os=-coff + ;; + msdos) + basic_machine=i386-pc + os=-msdos + ;; + mvs) + basic_machine=i370-ibm + os=-mvs + ;; + 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 + ;; + nsr-tandem) + basic_machine=nsr-tandem + ;; + op50n-* | op60c-*) + basic_machine=hppa1.1-oki + os=-proelf + ;; + 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 + ;; + pbd) + basic_machine=sparc-tti + ;; + pbb) + basic_machine=m68k-tti + ;; + pc532 | pc532-*) + basic_machine=ns32k-pc532 + ;; + pentium | p5 | k5 | k6 | nexgen) + basic_machine=i586-pc + ;; + pentiumpro | p6 | 6x86 | athlon) + basic_machine=i686-pc + ;; + pentiumii | pentium2) + basic_machine=i686-pc + ;; + pentium-* | p5-* | k5-* | k6-* | nexgen-*) + basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentiumpro-* | p6-* | 6x86-* | athlon-*) + basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentiumii-* | pentium2-*) + basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pn) + basic_machine=pn-gould + ;; + power) basic_machine=power-ibm + ;; + ppc) basic_machine=powerpc-unknown + ;; + ppc-*) 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 + ;; + rom68k) + basic_machine=m68k-rom68k + os=-coff + ;; + rm[46]00) + basic_machine=mips-siemens + ;; + rtpc | rtpc-*) + basic_machine=romp-ibm + ;; + sa29200) + basic_machine=a29k-amd + os=-udi + ;; + sequent) + basic_machine=i386-sequent + ;; + sh) + basic_machine=sh-hitachi + os=-hms + ;; + sparclite-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 + ;; + 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=t3e-cray + os=-unicos + ;; + tic54x | c54x*) + basic_machine=tic54x-unknown + os=-coff + ;; + tx39) + basic_machine=mipstx39-unknown + ;; + tx39el) + basic_machine=mipstx39el-unknown + ;; + tower | tower-32) + basic_machine=m68k-ncr + ;; + 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 + ;; + windows32) + basic_machine=i386-pc + os=-windows32-msvcrt + ;; + xmp) + basic_machine=xmp-cray + os=-unicos + ;; + xps | xps100) + basic_machine=xps100-honeywell + ;; + z8k-*-coff) + basic_machine=z8k-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 + ;; + mips) + if [ x$os = x-linux-gnu ]; then + basic_machine=mips-unknown + else + basic_machine=mips-mips + fi + ;; + romp) + basic_machine=romp-ibm + ;; + 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 + ;; + sh3 | sh4 | sh3eb | sh4eb) + basic_machine=sh-unknown + ;; + sparc | sparcv9 | sparcv9b) + 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 + ;; + c4x*) + basic_machine=c4x-none + os=-coff + ;; + *-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. + -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* | -sunos | -sunos[34]*\ + | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \ + | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ + | -aos* \ + | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ + | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ + | -hiux* | -386bsd* | -netbsd* | -openbsd* | -freebsd* | -riscix* \ + | -lynxos* | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ + | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ + | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ + | -chorusos* | -chorusrdb* \ + | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ + | -mingw32* | -linux-gnu* | -uxpv* | -beos* | -mpeix* | -udk* \ + | -interix* | -uwin* | -rhapsody* | -darwin* | -opened* \ + | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ + | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \ + | -os2* | -vos*) + # Remember, each alternative MUST END IN *, to match a version number. + ;; + -qnx*) + case $basic_machine in + x86-* | i*86-*) + ;; + *) + os=-nto$os + ;; + esac + ;; + -nto*) + os=-nto-qnx + ;; + -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \ + | -windows* | -osx | -abug | -netware* | -os9* | -beos* \ + | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*) + ;; + -mac*) + os=`echo $os | sed -e 's|mac|macos|'` + ;; + -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 + ;; + -wince*) + os=-wince + ;; + -osfrose*) + os=-osfrose + ;; + -osf*) + os=-osf + ;; + -utek*) + os=-bsd + ;; + -dynix*) + os=-bsd + ;; + -acis*) + os=-aos + ;; + -386bsd) + os=-bsd + ;; + -ctix* | -uts*) + os=-sysv + ;; + -ns2 ) + os=-nextstep2 + ;; + -nsk*) + os=-nsk + ;; + # Preserve the version number of sinix5. + -sinix5.*) + os=`echo $os | sed -e 's|sinix|sysv|'` + ;; + -sinix*) + os=-sysv4 + ;; + -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 + ;; + -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 + *-acorn) + os=-riscix1.2 + ;; + arm*-rebel) + os=-linux + ;; + arm*-semi) + os=-aout + ;; + 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 + # This also exists in the configure program, but was not the + # default. + # os=-sunos4 + ;; + m68*-cisco) + os=-aout + ;; + mips*-cisco) + os=-elf + ;; + mips*-*) + os=-elf + ;; + *-tti) # must be before sparc entry or we get the wrong os. + os=-sysv3 + ;; + sparc-* | *-sun) + os=-sunos4.1.1 + ;; + *-be) + os=-beos + ;; + *-ibm) + os=-aix + ;; + *-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 + ;; + -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 + ;; + -ptx*) + vendor=sequent + ;; + -vxsim* | -vxworks*) + 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 0 + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/config/depcomp b/config/depcomp new file mode 100755 index 0000000000..807b991f4a --- /dev/null +++ b/config/depcomp @@ -0,0 +1,423 @@ +#! /bin/sh + +# depcomp - compile a program generating dependencies as side-effects +# Copyright 1999, 2000 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, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +# 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 . + +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 +# `libtool' can also be set to `yes' or `no'. + +if test -z "$depfile"; then + base=`echo "$object" | sed -e 's,^.*/,,' -e 's,\.\([^.]*\)$,.P\1,'` + dir=`echo "$object" | sed 's,/.*$,/,'` + if test "$dir" = "$object"; then + dir= + fi + # FIXME: should be _deps on DOS. + depfile="$dir.deps/$base" +fi + +tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`} + +rm -f "$tmpdepfile" + +# 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 + +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. + "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" + stat=$? + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile" + exit $stat + fi + mv "$tmpdepfile" "$depfile" + ;; + +gcc) +## 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). +## - 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 -eq 0; then : + else + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + echo "$object : \\" > "$depfile" + alpha=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz +## 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. + tr ' ' ' +' < "$tmpdepfile" | +## 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. +## Some versions of the HPUX 10.20 sed can't process this invocation +## correctly. Breaking it into two sed invocations is a workaround. + sed -e 's/^\\$//' -e '/^$/d' -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 -eq 0; then : + else + 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 ' ' ' +' < "$tmpdepfile" \ + | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' | \ + tr ' +' ' ' >> $depfile + echo >> $depfile + + # The second pass generates a dummy entry for each header file. + tr ' ' ' +' < "$tmpdepfile" \ + | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \ + >> $depfile + else + # The sourcefile does not contain any dependencies, so just + # store a dummy comment line, to avoid errors with the Makefile + # "include basename.Plo" scheme. + echo "#dummy" > "$depfile" + fi + rm -f "$tmpdepfile" + ;; + +aix) + # The C for AIX Compiler uses -M and outputs the dependencies + # in a .u file. 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. + stripped=`echo "$object" | sed -e 's,^.*/,,' -e 's/\(.*\)\..*$/\1/'` + tmpdepfile="$stripped.u" + outname="$stripped.o" + if test "$libtool" = yes; then + "$@" -Wc,-M + else + "$@" -M + fi + + stat=$? + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile" + exit $stat + fi + + if test -f "$tmpdepfile"; then + # Each line is of the form `foo.o: dependent.h'. + # Do two passes, one to just change these to + # `$object: dependent.h' and one to simply `dependent.h:'. + sed -e "s,^$outname:,$object :," < "$tmpdepfile" > "$depfile" + sed -e "s,^$outname: \(.*\)$,\1:," < "$tmpdepfile" >> "$depfile" + else + # The sourcefile does not contain any dependencies, so just + # store a dummy comment line, to avoid errors with the Makefile + # "include basename.Plo" scheme. + echo "#dummy" > "$depfile" + fi + rm -f "$tmpdepfile" + ;; + +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. + dir=`echo "$object" | sed -e 's|/[^/]*$|/|'` + test "x$dir" = "x$object" && dir= + base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'` + + if test "$libtool" = yes; then + tmpdepfile1="$dir.libs/$base.lo.d" + tmpdepfile2="$dir.libs/$base.d" + "$@" -Wc,-MD + else + tmpdepfile1="$dir$base.o.d" + tmpdepfile2="$dir$base.d" + "$@" -MD + fi + + stat=$? + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile1" "$tmpdepfile2" + exit $stat + fi + + if test -f "$tmpdepfile1"; then + tmpdepfile="$tmpdepfile1" + else + tmpdepfile="$tmpdepfile2" + fi + if test -f "$tmpdepfile"; then + sed -e "s,^.*\.[a-z]*:,$object:," < "$tmpdepfile" > "$depfile" + # That's a space and a tab in the []. + sed -e 's,^.*\.[a-z]*:[ ]*,,' -e 's,$,:,' < "$tmpdepfile" >> "$depfile" + else + echo "#dummy" > "$depfile" + fi + rm -f "$tmpdepfile" + ;; + +#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 proprocessed file to stdout, regardless of -o. + "$@" || exit $? + + # Remove the call to Libtool. + if test "$libtool" = yes; then + while test $1 != '--mode=compile'; do + shift + done + shift + fi + + # Remove `-o $object'. We will use -o /dev/null later, + # however we can't do the remplacement now because + # `-o $object' might simply not be used + 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 + "$@" -o /dev/null $dashmflag | sed 's:^[^:]*\:[ ]*:'"$object"'\: :' > "$tmpdepfile" + rm -f "$depfile" + cat < "$tmpdepfile" > "$depfile" + tr ' ' ' +' < "$tmpdepfile" | \ +## Some versions of the HPUX 10.20 sed can't process this invocation +## correctly. Breaking it into two sed invocations is a workaround. + 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 $? + # X makedepend + shift + cleared=no + for arg in "$@"; do + case $cleared in + no) + set ""; shift + cleared=yes ;; + esac + case "$arg" in + -D*|-I*) + set fnord "$@" "$arg"; shift ;; + -*) + ;; + *) + set fnord "$@" "$arg"; shift ;; + esac + done + obj_suffix="`echo $object | sed 's/^.*\././'`" + touch "$tmpdepfile" + ${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@" + rm -f "$depfile" + cat < "$tmpdepfile" > "$depfile" + sed '1,2d' "$tmpdepfile" | tr ' ' ' +' | \ +## Some versions of the HPUX 10.20 sed can't process this invocation +## correctly. Breaking it into two sed invocations is a workaround. + 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 proprocessed file to stdout. + "$@" || exit $? + + # Remove the call to Libtool. + if test "$libtool" = yes; then + while test $1 != '--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 '/^# [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 proprocessed file to stdout, regardless of -o, + # because we must use -o when running libtool. + "$@" || exit $? + IFS=" " + for arg + do + case "$arg" in + "-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI") + set fnord "$@" + shift + shift + ;; + *) + set fnord "$@" "$arg" + shift + shift + ;; + esac + done + "$@" -E | + sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::echo "`cygpath -u \\"\1\\"`":p' | sort | uniq > "$tmpdepfile" + rm -f "$depfile" + echo "$object : \\" > "$depfile" + . "$tmpdepfile" | sed 's% %\\ %g' | sed -n '/^\(.*\)$/ s:: \1 \\:p' >> "$depfile" + echo " " >> "$depfile" + . "$tmpdepfile" | sed 's% %\\ %g' | sed -n '/^\(.*\)$/ s::\1\::p' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +none) + exec "$@" + ;; + +*) + echo "Unknown depmode $depmode" 1>&2 + exit 1 + ;; +esac + +exit 0 diff --git a/config/install-sh b/config/install-sh new file mode 100755 index 0000000000..11870f1b01 --- /dev/null +++ b/config/install-sh @@ -0,0 +1,251 @@ +#!/bin/sh +# +# install - install a program, script, or datafile +# This comes from X11R5 (mit/util/scripts/install.sh). +# +# Copyright 1991 by the Massachusetts Institute of Technology +# +# Permission to use, copy, modify, distribute, and sell this software and its +# documentation for any purpose is hereby granted without fee, provided that +# the above copyright notice appear in all copies and that both that +# copyright notice and this permission notice appear in supporting +# documentation, and that the name of M.I.T. not be used in advertising or +# publicity pertaining to distribution of the software without specific, +# written prior permission. M.I.T. makes no representations about the +# suitability of this software for any purpose. It is provided "as is" +# without express or implied warranty. +# +# 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. It can only install one file at a time, a restriction +# shared with many OS's install programs. + + +# set DOITPROG to echo to test this script + +# Don't use :- since 4.3BSD and earlier shells don't like it. +doit="${DOITPROG-}" + + +# put in absolute paths if you don't have them in your path; or use env. vars. + +mvprog="${MVPROG-mv}" +cpprog="${CPPROG-cp}" +chmodprog="${CHMODPROG-chmod}" +chownprog="${CHOWNPROG-chown}" +chgrpprog="${CHGRPPROG-chgrp}" +stripprog="${STRIPPROG-strip}" +rmprog="${RMPROG-rm}" +mkdirprog="${MKDIRPROG-mkdir}" + +transformbasename="" +transform_arg="" +instcmd="$mvprog" +chmodcmd="$chmodprog 0755" +chowncmd="" +chgrpcmd="" +stripcmd="" +rmcmd="$rmprog -f" +mvcmd="$mvprog" +src="" +dst="" +dir_arg="" + +while [ x"$1" != x ]; do + case $1 in + -c) instcmd="$cpprog" + shift + continue;; + + -d) dir_arg=true + shift + continue;; + + -m) chmodcmd="$chmodprog $2" + shift + shift + continue;; + + -o) chowncmd="$chownprog $2" + shift + shift + continue;; + + -g) chgrpcmd="$chgrpprog $2" + shift + shift + continue;; + + -s) stripcmd="$stripprog" + shift + continue;; + + -t=*) transformarg=`echo $1 | sed 's/-t=//'` + shift + continue;; + + -b=*) transformbasename=`echo $1 | sed 's/-b=//'` + shift + continue;; + + *) if [ x"$src" = x ] + then + src=$1 + else + # this colon is to work around a 386BSD /bin/sh bug + : + dst=$1 + fi + shift + continue;; + esac +done + +if [ x"$src" = x ] +then + echo "install: no input file specified" + exit 1 +else + : +fi + +if [ x"$dir_arg" != x ]; then + dst=$src + src="" + + if [ -d $dst ]; then + instcmd=: + chmodcmd="" + else + instcmd=$mkdirprog + fi +else + +# Waiting for this to be detected by the "$instcmd $src $dsttmp" command +# might cause directories to be created, which would be especially bad +# if $src (and thus $dsttmp) contains '*'. + + if [ -f "$src" ] || [ -d "$src" ] + then + : + else + echo "install: $src does not exist" + exit 1 + fi + + if [ x"$dst" = x ] + then + echo "install: no destination specified" + exit 1 + else + : + fi + +# If destination is a directory, append the input filename; if your system +# does not like double slashes in filenames, you may need to add some logic + + if [ -d $dst ] + then + dst="$dst"/`basename $src` + else + : + fi +fi + +## this sed command emulates the dirname command +dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'` + +# Make sure that the destination directory exists. +# this part is taken from Noah Friedman's mkinstalldirs script + +# Skip lots of stat calls in the usual case. +if [ ! -d "$dstdir" ]; then +defaultIFS=' + ' +IFS="${IFS-${defaultIFS}}" + +oIFS="${IFS}" +# Some sh's can't handle IFS=/ for some reason. +IFS='%' +set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'` +IFS="${oIFS}" + +pathcomp='' + +while [ $# -ne 0 ] ; do + pathcomp="${pathcomp}${1}" + shift + + if [ ! -d "${pathcomp}" ] ; + then + $mkdirprog "${pathcomp}" + else + : + fi + + pathcomp="${pathcomp}/" +done +fi + +if [ x"$dir_arg" != x ] +then + $doit $instcmd $dst && + + if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else : ; fi && + if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else : ; fi && + if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else : ; fi && + if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else : ; fi +else + +# If we're going to rename the final executable, determine the name now. + + if [ x"$transformarg" = x ] + then + dstfile=`basename $dst` + else + dstfile=`basename $dst $transformbasename | + sed $transformarg`$transformbasename + fi + +# don't allow the sed command to completely eliminate the filename + + if [ x"$dstfile" = x ] + then + dstfile=`basename $dst` + else + : + fi + +# Make a temp file name in the proper directory. + + dsttmp=$dstdir/#inst.$$# + +# Move or copy the file name to the temp name + + $doit $instcmd $src $dsttmp && + + trap "rm -f ${dsttmp}" 0 && + +# 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 $instcmd $src $dsttmp" command. + + if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else :;fi && + if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else :;fi && + if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else :;fi && + if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else :;fi && + +# Now rename the file to the real destination. + + $doit $rmcmd -f $dstdir/$dstfile && + $doit $mvcmd $dsttmp $dstdir/$dstfile + +fi && + + +exit 0 diff --git a/config/missing b/config/missing new file mode 100755 index 0000000000..6a37006e8f --- /dev/null +++ b/config/missing @@ -0,0 +1,336 @@ +#! /bin/sh +# Common stub for a few missing GNU programs while installing. +# Copyright (C) 1996, 1997, 1999, 2000, 2002 Free Software Foundation, Inc. +# Originally 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, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +# 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 + +run=: + +# In the cases where this matters, `missing' is being run in the +# srcdir already. +if test -f configure.ac; then + configure_ac=configure.ac +else + configure_ac=configure.in +fi + +case "$1" in +--run) + # Try to run requested program, and just exit if it succeeds. + run= + shift + "$@" && exit 0 + ;; +esac + +# If it does not exist, or fails to run (possibly an outdated version), +# try to emulate it. +case "$1" in + + -h|--h|--he|--hel|--help) + echo "\ +$0 [OPTION]... PROGRAM [ARGUMENT]... + +Handle \`PROGRAM [ARGUMENT]...' for when PROGRAM is missing, or return an +error status if there is no known handling for PROGRAM. + +Options: + -h, --help display this help and exit + -v, --version output version information and exit + --run try to run the given command, and emulate it if it fails + +Supported PROGRAM values: + aclocal touch file \`aclocal.m4' + autoconf touch file \`configure' + autoheader touch file \`config.h.in' + automake touch all \`Makefile.in' files + bison create \`y.tab.[ch]', if possible, from existing .[ch] + flex create \`lex.yy.c', if possible, from existing .c + help2man touch the output file + lex create \`lex.yy.c', if possible, from existing .c + makeinfo touch the output file + tar try tar, gnutar, gtar, then tar without non-portable flags + yacc create \`y.tab.[ch]', if possible, from existing .[ch]" + ;; + + -v|--v|--ve|--ver|--vers|--versi|--versio|--version) + echo "missing 0.4 - GNU automake" + ;; + + -*) + echo 1>&2 "$0: Unknown \`$1' option" + echo 1>&2 "Try \`$0 --help' for more information" + exit 1 + ;; + + aclocal*) + if test -z "$run" && ($1 --version) > /dev/null 2>&1; then + # We have it, but it failed. + exit 1 + fi + + echo 1>&2 "\ +WARNING: \`$1' is missing on your system. You should only need it if + you modified \`acinclude.m4' or \`${configure_ac}'. You might want + to install the \`Automake' and \`Perl' packages. Grab them from + any GNU archive site." + touch aclocal.m4 + ;; + + autoconf) + if test -z "$run" && ($1 --version) > /dev/null 2>&1; then + # We have it, but it failed. + exit 1 + fi + + echo 1>&2 "\ +WARNING: \`$1' is missing on your system. You should only need it if + you modified \`${configure_ac}'. You might want to install the + \`Autoconf' and \`GNU m4' packages. Grab them from any GNU + archive site." + touch configure + ;; + + autoheader) + if test -z "$run" && ($1 --version) > /dev/null 2>&1; then + # We have it, but it failed. + exit 1 + fi + + echo 1>&2 "\ +WARNING: \`$1' is missing on your system. You should only need it if + you modified \`acconfig.h' or \`${configure_ac}'. You might want + to install the \`Autoconf' and \`GNU m4' packages. Grab them + from any GNU archive site." + files=`sed -n 's/^[ ]*A[CM]_CONFIG_HEADER(\([^)]*\)).*/\1/p' ${configure_ac}` + test -z "$files" && files="config.h" + touch_files= + for f in $files; do + case "$f" in + *:*) touch_files="$touch_files "`echo "$f" | + sed -e 's/^[^:]*://' -e 's/:.*//'`;; + *) touch_files="$touch_files $f.in";; + esac + done + touch $touch_files + ;; + + automake*) + if test -z "$run" && ($1 --version) > /dev/null 2>&1; then + # We have it, but it failed. + exit 1 + fi + + echo 1>&2 "\ +WARNING: \`$1' is missing on your system. You should only need it if + you modified \`Makefile.am', \`acinclude.m4' or \`${configure_ac}'. + You might want to install the \`Automake' and \`Perl' packages. + Grab them from any GNU archive site." + find . -type f -name Makefile.am -print | + sed 's/\.am$/.in/' | + while read f; do touch "$f"; done + ;; + + autom4te) + if test -z "$run" && ($1 --version) > /dev/null 2>&1; then + # We have it, but it failed. + exit 1 + fi + + echo 1>&2 "\ +WARNING: \`$1' is needed, and you do not seem to have it handy on your + system. You might have modified some files without having the + proper tools for further handling them. + You can get \`$1Help2man' as part of \`Autoconf' from any GNU + archive site." + + file=`echo "$*" | sed -n 's/.*--output[ =]*\([^ ]*\).*/\1/p'` + test -z "$file" && file=`echo "$*" | sed -n 's/.*-o[ ]*\([^ ]*\).*/\1/p'` + if test -f "$file"; then + touch $file + else + test -z "$file" || exec >$file + echo "#! /bin/sh" + echo "# Created by GNU Automake missing as a replacement of" + echo "# $ $@" + echo "exit 0" + chmod +x $file + exit 1 + fi + ;; + + bison|yacc) + echo 1>&2 "\ +WARNING: \`$1' is missing on your system. You should only need it if + you modified a \`.y' file. You may need the \`Bison' package + in order for those modifications to take effect. You can get + \`Bison' from any GNU archive site." + rm -f y.tab.c y.tab.h + if [ $# -ne 1 ]; then + eval LASTARG="\${$#}" + case "$LASTARG" in + *.y) + SRCFILE=`echo "$LASTARG" | sed 's/y$/c/'` + if [ -f "$SRCFILE" ]; then + cp "$SRCFILE" y.tab.c + fi + SRCFILE=`echo "$LASTARG" | sed 's/y$/h/'` + if [ -f "$SRCFILE" ]; then + cp "$SRCFILE" y.tab.h + fi + ;; + esac + fi + if [ ! -f y.tab.h ]; then + echo >y.tab.h + fi + if [ ! -f y.tab.c ]; then + echo 'main() { return 0; }' >y.tab.c + fi + ;; + + lex|flex) + echo 1>&2 "\ +WARNING: \`$1' is missing on your system. You should only need it if + you modified a \`.l' file. You may need the \`Flex' package + in order for those modifications to take effect. You can get + \`Flex' from any GNU archive site." + rm -f lex.yy.c + if [ $# -ne 1 ]; then + eval LASTARG="\${$#}" + case "$LASTARG" in + *.l) + SRCFILE=`echo "$LASTARG" | sed 's/l$/c/'` + if [ -f "$SRCFILE" ]; then + cp "$SRCFILE" lex.yy.c + fi + ;; + esac + fi + if [ ! -f lex.yy.c ]; then + echo 'main() { return 0; }' >lex.yy.c + fi + ;; + + help2man) + if test -z "$run" && ($1 --version) > /dev/null 2>&1; then + # We have it, but it failed. + exit 1 + fi + + echo 1>&2 "\ +WARNING: \`$1' is missing on your system. You should only need it if + you modified a dependency of a manual page. You may need the + \`Help2man' package in order for those modifications to take + effect. You can get \`Help2man' from any GNU archive site." + + file=`echo "$*" | sed -n 's/.*-o \([^ ]*\).*/\1/p'` + if test -z "$file"; then + file=`echo "$*" | sed -n 's/.*--output=\([^ ]*\).*/\1/p'` + fi + if [ -f "$file" ]; then + touch $file + else + test -z "$file" || exec >$file + echo ".ab help2man is required to generate this page" + exit 1 + fi + ;; + + makeinfo) + if test -z "$run" && (makeinfo --version) > /dev/null 2>&1; then + # We have makeinfo, but it failed. + exit 1 + fi + + echo 1>&2 "\ +WARNING: \`$1' is missing on your system. You should only need it if + you modified a \`.texi' or \`.texinfo' file, or any other file + indirectly affecting the aspect of the manual. The spurious + call might also be the consequence of using a buggy \`make' (AIX, + DU, IRIX). You might want to install the \`Texinfo' package or + the \`GNU make' package. Grab either from any GNU archive site." + file=`echo "$*" | sed -n 's/.*-o \([^ ]*\).*/\1/p'` + if test -z "$file"; then + file=`echo "$*" | sed 's/.* \([^ ]*\) *$/\1/'` + file=`sed -n '/^@setfilename/ { s/.* \([^ ]*\) *$/\1/; p; q; }' $file` + fi + touch $file + ;; + + tar) + shift + if test -n "$run"; then + echo 1>&2 "ERROR: \`tar' requires --run" + exit 1 + fi + + # We have already tried tar in the generic part. + # Look for gnutar/gtar before invocation to avoid ugly error + # messages. + if (gnutar --version > /dev/null 2>&1); then + gnutar "$@" && exit 0 + fi + if (gtar --version > /dev/null 2>&1); then + gtar "$@" && exit 0 + fi + firstarg="$1" + if shift; then + case "$firstarg" in + *o*) + firstarg=`echo "$firstarg" | sed s/o//` + tar "$firstarg" "$@" && exit 0 + ;; + esac + case "$firstarg" in + *h*) + firstarg=`echo "$firstarg" | sed s/h//` + tar "$firstarg" "$@" && exit 0 + ;; + esac + fi + + echo 1>&2 "\ +WARNING: I can't seem to be able to run \`tar' with the given arguments. + You may want to install GNU tar or Free paxutils, or check the + command line arguments." + exit 1 + ;; + + *) + echo 1>&2 "\ +WARNING: \`$1' is needed, and you do not seem to have it handy on your + system. You might have modified some files without having the + proper tools for further handling them. Check the \`README' file, + it often tells you about the needed prerequirements for installing + this package. You may also peek at any GNU archive site, in case + some other package would contain this missing \`$1' program." + exit 1 + ;; +esac + +exit 0 diff --git a/config/mkinstalldirs b/config/mkinstalldirs new file mode 100755 index 0000000000..8ab885ec92 --- /dev/null +++ b/config/mkinstalldirs @@ -0,0 +1,99 @@ +#! /bin/sh +# mkinstalldirs --- make directory hierarchy +# Author: Noah Friedman +# Created: 1993-05-16 +# Public domain + +errstatus=0 +dirmode="" + +usage="\ +Usage: mkinstalldirs [-h] [--help] [-m mode] dir ..." + +# process command line arguments +while test $# -gt 0 ; do + case "${1}" in + -h | --help | --h* ) # -h for help + echo "${usage}" 1>&2; exit 0 ;; + -m ) # -m PERM arg + shift + test $# -eq 0 && { echo "${usage}" 1>&2; exit 1; } + dirmode="${1}" + shift ;; + -- ) shift; break ;; # stop option processing + -* ) echo "${usage}" 1>&2; exit 1 ;; # unknown option + * ) break ;; # first non-opt arg + esac +done + +for file +do + if test -d "$file"; then + shift + else + break + fi +done + +case $# in +0) exit 0 ;; +esac + +case $dirmode in +'') + if mkdir -p -- . 2>/dev/null; then + echo "mkdir -p -- $*" + exec mkdir -p -- "$@" + fi ;; +*) + if mkdir -m "$dirmode" -p -- . 2>/dev/null; then + echo "mkdir -m $dirmode -p -- $*" + exec mkdir -m "$dirmode" -p -- "$@" + fi ;; +esac + +for file +do + set fnord `echo ":$file" | sed -ne 's/^:\//#/;s/^://;s/\// /g;s/^#/\//;p'` + shift + + pathcomp= + for d + do + pathcomp="$pathcomp$d" + case "$pathcomp" in + -* ) pathcomp=./$pathcomp ;; + esac + + if test ! -d "$pathcomp"; then + echo "mkdir $pathcomp" + + mkdir "$pathcomp" || lasterr=$? + + if test ! -d "$pathcomp"; then + errstatus=$lasterr + else + if test ! -z "$dirmode"; then + echo "chmod $dirmode $pathcomp" + + lasterr="" + chmod "$dirmode" "$pathcomp" || lasterr=$? + + if test ! -z "$lasterr"; then + errstatus=$lasterr + fi + fi + fi + fi + + pathcomp="$pathcomp/" + done +done + +exit $errstatus + +# Local Variables: +# mode: shell-script +# sh-indentation: 3 +# End: +# mkinstalldirs ends here diff --git a/configure b/configure new file mode 100755 index 0000000000..ab8f797916 --- /dev/null +++ b/configure @@ -0,0 +1,9541 @@ +#! /bin/sh +# From configure.ac Id: configure.ac. +# Guess values for system-dependent variables and create Makefiles. +# Generated by GNU Autoconf 2.59 for Tesseract 1.03. +# +# Report bugs to . +# +# Copyright (C) 2003 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 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+"$@"}'='"$@"' +elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then + set -o posix +fi +DUALCASE=1; export DUALCASE # for MKS sh + +# Support unset when possible. +if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then + as_unset=unset +else + as_unset=false +fi + + +# Work around bugs in pre-3.0 UWIN ksh. +$as_unset ENV MAIL MAILPATH +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +for as_var in \ + LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \ + LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \ + LC_TELEPHONE LC_TIME +do + if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then + eval $as_var=C; export $as_var + else + $as_unset $as_var + fi +done + +# Required to use basename. +if expr a : '\(a\)' >/dev/null 2>&1; 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 + + +# Name of the executable. +as_me=`$as_basename "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)$' \| \ + . : '\(.\)' 2>/dev/null || +echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; } + /^X\/\(\/\/\)$/{ s//\1/; q; } + /^X\/\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + + +# PATH needs CR, and LINENO needs CR and PATH. +# 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 + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + echo "#! /bin/sh" >conf$$.sh + echo "exit 0" >>conf$$.sh + chmod +x conf$$.sh + if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then + PATH_SEPARATOR=';' + else + PATH_SEPARATOR=: + fi + rm -f conf$$.sh +fi + + + as_lineno_1=$LINENO + as_lineno_2=$LINENO + as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x$as_lineno_3" = "x$as_lineno_2" || { + # Find who we are. Look in the path if we contain no path at all + # relative or not. + 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 + + ;; + 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 + { echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2 + { (exit 1); exit 1; }; } + fi + case $CONFIG_SHELL in + '') + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for as_base in sh bash ksh sh5; do + case $as_dir in + /*) + if ("$as_dir/$as_base" -c ' + as_lineno_1=$LINENO + as_lineno_2=$LINENO + as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x$as_lineno_3" = "x$as_lineno_2" ') 2>/dev/null; then + $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; } + $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; } + CONFIG_SHELL=$as_dir/$as_base + export CONFIG_SHELL + exec "$CONFIG_SHELL" "$0" ${1+"$@"} + fi;; + esac + done +done +;; + esac + + # Create $as_me.lineno as a copy of $as_myself, but with $LINENO + # uniformly replaced by the line number. The first 'sed' inserts a + # line-number line before each line; the second 'sed' does the real + # work. The second script uses 'N' to pair each line-number line + # with the numbered line, and appends trailing '-' during + # substitution so that $LINENO is not a special case at line end. + # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the + # second 'sed' script. Blame Lee E. McMahon for sed's syntax. :-) + sed '=' <$as_myself | + sed ' + N + s,$,-, + : loop + s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3, + t loop + s,-$,, + s,^['$as_cr_digits']*\n,, + ' >$as_me.lineno && + chmod +x $as_me.lineno || + { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2 + { (exit 1); exit 1; }; } + + # 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 sensible to this). + . ./$as_me.lineno + # Exit status is that of the last command. + exit +} + + +case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in + *c*,-n*) ECHO_N= ECHO_C=' +' ECHO_T=' ' ;; + *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;; + *) ECHO_N= ECHO_C='\c' ECHO_T= ;; +esac + +if expr a : '\(a\)' >/dev/null 2>&1; then + as_expr=expr +else + as_expr=false +fi + +rm -f conf$$ conf$$.exe conf$$.file +echo >conf$$.file +if ln -s conf$$.file conf$$ 2>/dev/null; then + # We could just check for DJGPP; but this test a) works b) is more generic + # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04). + if test -f conf$$.exe; then + # Don't use ln at all; we don't have any links + as_ln_s='cp -p' + else + as_ln_s='ln -s' + fi +elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.file + +if mkdir -p . 2>/dev/null; then + as_mkdir_p=: +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +as_executable_p="test -f" + +# 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'" + + +# IFS +# We need space, tab and new line, in precisely that order. +as_nl=' +' +IFS=" $as_nl" + +# CDPATH. +$as_unset CDPATH + + +# Name of the host. +# hostname on some systems (SVR3.2, Linux) returns a bogus exit status, +# so uname gets run too. +ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` + +exec 6>&1 + +# +# Initializations. +# +ac_default_prefix=/usr/local +ac_config_libobj_dir=. +cross_compiling=no +subdirs= +MFLAGS= +MAKEFLAGS= +SHELL=${CONFIG_SHELL-/bin/sh} + +# Maximum number of lines to put in a shell here document. +# This variable seems obsolete. It should probably be removed, and +# only ac_max_sed_lines should be used. +: ${ac_max_here_lines=38} + +# Identity of this package. +PACKAGE_NAME='Tesseract' +PACKAGE_TARNAME='tesseract' +PACKAGE_VERSION='1.03' +PACKAGE_STRING='Tesseract 1.03' +PACKAGE_BUGREPORT='theraysmith@users.sourceforge.net' + +ac_unique_file="ccmain/tesseractmain.cpp" +ac_default_prefix=/usr/local +# Factoring default headers for most tests. +ac_includes_default="\ +#include +#if HAVE_SYS_TYPES_H +# include +#endif +#if HAVE_SYS_STAT_H +# include +#endif +#if STDC_HEADERS +# include +# include +#else +# if HAVE_STDLIB_H +# include +# endif +#endif +#if HAVE_STRING_H +# if !STDC_HEADERS && HAVE_MEMORY_H +# include +# endif +# include +#endif +#if HAVE_STRINGS_H +# include +#endif +#if HAVE_INTTYPES_H +# include +#else +# if HAVE_STDINT_H +# include +# endif +#endif +#if HAVE_UNISTD_H +# include +#endif" + +ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS build build_cpu build_vendor build_os host host_cpu host_vendor host_os PACKAGE_YEAR PACKAGE_DATE CXX CXXFLAGS LDFLAGS CPPFLAGS ac_ct_CXX EXEEXT OBJEXT INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA CYGPATH_W PACKAGE VERSION ACLOCAL AUTOCONF AUTOMAKE AUTOHEADER MAKEINFO install_sh STRIP ac_ct_STRIP INSTALL_STRIP_PROGRAM mkdir_p AWK SET_MAKE am__leading_dot AMTAR am__tar am__untar DEPDIR am__include am__quote AMDEP_TRUE AMDEP_FALSE AMDEPBACKSLASH CXXDEPMODE am__fastdepCXX_TRUE am__fastdepCXX_FALSE CC CFLAGS ac_ct_CC CCDEPMODE am__fastdepCC_TRUE am__fastdepCC_FALSE MAINTAINER_MODE_TRUE MAINTAINER_MODE_FALSE MAINT USING_CL_TRUE USING_CL_FALSE RANLIB ac_ct_RANLIB GNUWIN32_DIR HAVE_GNUWIN32_TRUE HAVE_GNUWIN32_FALSE OPTS CXXRPOFLAGS RPO_YES RPO_NO CXXCPP EGREP LIBTIFF_LIBS LIBTIFF_CFLAGS HAVE_LIBTIFF_TRUE HAVE_LIBTIFF_FALSE LIBOBJS LTLIBOBJS' +ac_subst_files='' + +# Initialize some variables set by options. +ac_init_help= +ac_init_version=false +# 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. +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datadir='${prefix}/share' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +libdir='${exec_prefix}/lib' +includedir='${prefix}/include' +oldincludedir='/usr/include' +infodir='${prefix}/info' +mandir='${prefix}/man' + +ac_prev= +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 + + ac_optarg=`expr "x$ac_option" : 'x[^=]*=\(.*\)'` + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case $ac_option in + + -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 | --data | --dat | --da) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \ + | --da=*) + datadir=$ac_optarg ;; + + -disable-* | --disable-*) + ac_feature=`expr "x$ac_option" : 'x-*disable-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid feature name: $ac_feature" >&2 + { (exit 1); exit 1; }; } + ac_feature=`echo $ac_feature | sed 's/-/_/g'` + eval "enable_$ac_feature=no" ;; + + -enable-* | --enable-*) + ac_feature=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid feature name: $ac_feature" >&2 + { (exit 1); exit 1; }; } + ac_feature=`echo $ac_feature | sed 's/-/_/g'` + case $ac_option in + *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;; + *) ac_optarg=yes ;; + esac + eval "enable_$ac_feature='$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 ;; + + -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 ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst \ + | --locals | --local | --loca | --loc | --lo) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* \ + | --locals=* | --local=* | --loca=* | --loc=* | --lo=*) + 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 ;; + + -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_package=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid package name: $ac_package" >&2 + { (exit 1); exit 1; }; } + ac_package=`echo $ac_package| sed 's/-/_/g'` + case $ac_option in + *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;; + *) ac_optarg=yes ;; + esac + eval "with_$ac_package='$ac_optarg'" ;; + + -without-* | --without-*) + ac_package=`expr "x$ac_option" : 'x-*without-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid package name: $ac_package" >&2 + { (exit 1); exit 1; }; } + ac_package=`echo $ac_package | sed 's/-/_/g'` + eval "with_$ac_package=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 ;; + + -*) { echo "$as_me: error: unrecognized option: $ac_option +Try \`$0 --help' for more information." >&2 + { (exit 1); exit 1; }; } + ;; + + *=*) + ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` + # Reject names that are not valid shell variable names. + expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid variable name: $ac_envvar" >&2 + { (exit 1); exit 1; }; } + ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` + eval "$ac_envvar='$ac_optarg'" + export $ac_envvar ;; + + *) + # FIXME: should be removed in autoconf 3.0. + echo "$as_me: WARNING: you should use --build, --host, --target" >&2 + expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && + 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'` + { echo "$as_me: error: missing argument to $ac_option" >&2 + { (exit 1); exit 1; }; } +fi + +# Be sure to have absolute paths. +for ac_var in exec_prefix prefix +do + eval ac_val=$`echo $ac_var` + case $ac_val in + [\\/$]* | ?:[\\/]* | NONE | '' ) ;; + *) { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2 + { (exit 1); exit 1; }; };; + esac +done + +# Be sure to have absolute paths. +for ac_var in bindir sbindir libexecdir datadir sysconfdir sharedstatedir \ + localstatedir libdir includedir oldincludedir infodir mandir +do + eval ac_val=$`echo $ac_var` + case $ac_val in + [\\/$]* | ?:[\\/]* ) ;; + *) { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2 + { (exit 1); exit 1; }; };; + esac +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 + echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host. + If a cross compiler is detected then cross compile mode will be used." >&2 + 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 + + +# 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 its parent. + ac_confdir=`(dirname "$0") 2>/dev/null || +$as_expr X"$0" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$0" : 'X\(//\)[^/]' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$0" | + 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 + if test "$ac_srcdir_defaulted" = yes; then + { echo "$as_me: error: cannot find sources ($ac_unique_file) in $ac_confdir or .." >&2 + { (exit 1); exit 1; }; } + else + { echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2 + { (exit 1); exit 1; }; } + fi +fi +(cd $srcdir && test -r ./$ac_unique_file) 2>/dev/null || + { echo "$as_me: error: sources are in $srcdir, but \`cd $srcdir' does not work" >&2 + { (exit 1); exit 1; }; } +srcdir=`echo "$srcdir" | sed 's%\([^\\/]\)[\\/]*$%\1%'` +ac_env_build_alias_set=${build_alias+set} +ac_env_build_alias_value=$build_alias +ac_cv_env_build_alias_set=${build_alias+set} +ac_cv_env_build_alias_value=$build_alias +ac_env_host_alias_set=${host_alias+set} +ac_env_host_alias_value=$host_alias +ac_cv_env_host_alias_set=${host_alias+set} +ac_cv_env_host_alias_value=$host_alias +ac_env_target_alias_set=${target_alias+set} +ac_env_target_alias_value=$target_alias +ac_cv_env_target_alias_set=${target_alias+set} +ac_cv_env_target_alias_value=$target_alias +ac_env_CXX_set=${CXX+set} +ac_env_CXX_value=$CXX +ac_cv_env_CXX_set=${CXX+set} +ac_cv_env_CXX_value=$CXX +ac_env_CXXFLAGS_set=${CXXFLAGS+set} +ac_env_CXXFLAGS_value=$CXXFLAGS +ac_cv_env_CXXFLAGS_set=${CXXFLAGS+set} +ac_cv_env_CXXFLAGS_value=$CXXFLAGS +ac_env_LDFLAGS_set=${LDFLAGS+set} +ac_env_LDFLAGS_value=$LDFLAGS +ac_cv_env_LDFLAGS_set=${LDFLAGS+set} +ac_cv_env_LDFLAGS_value=$LDFLAGS +ac_env_CPPFLAGS_set=${CPPFLAGS+set} +ac_env_CPPFLAGS_value=$CPPFLAGS +ac_cv_env_CPPFLAGS_set=${CPPFLAGS+set} +ac_cv_env_CPPFLAGS_value=$CPPFLAGS +ac_env_CC_set=${CC+set} +ac_env_CC_value=$CC +ac_cv_env_CC_set=${CC+set} +ac_cv_env_CC_value=$CC +ac_env_CFLAGS_set=${CFLAGS+set} +ac_env_CFLAGS_value=$CFLAGS +ac_cv_env_CFLAGS_set=${CFLAGS+set} +ac_cv_env_CFLAGS_value=$CFLAGS +ac_env_GNUWIN32_DIR_set=${GNUWIN32_DIR+set} +ac_env_GNUWIN32_DIR_value=$GNUWIN32_DIR +ac_cv_env_GNUWIN32_DIR_set=${GNUWIN32_DIR+set} +ac_cv_env_GNUWIN32_DIR_value=$GNUWIN32_DIR +ac_env_CXXCPP_set=${CXXCPP+set} +ac_env_CXXCPP_value=$CXXCPP +ac_cv_env_CXXCPP_set=${CXXCPP+set} +ac_cv_env_CXXCPP_value=$CXXCPP +ac_env_LIBTIFF_LIBS_set=${LIBTIFF_LIBS+set} +ac_env_LIBTIFF_LIBS_value=$LIBTIFF_LIBS +ac_cv_env_LIBTIFF_LIBS_set=${LIBTIFF_LIBS+set} +ac_cv_env_LIBTIFF_LIBS_value=$LIBTIFF_LIBS +ac_env_LIBTIFF_CFLAGS_set=${LIBTIFF_CFLAGS+set} +ac_env_LIBTIFF_CFLAGS_value=$LIBTIFF_CFLAGS +ac_cv_env_LIBTIFF_CFLAGS_set=${LIBTIFF_CFLAGS+set} +ac_cv_env_LIBTIFF_CFLAGS_value=$LIBTIFF_CFLAGS + +# +# 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 1.03 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 \`..'] + +_ACEOF + + cat <<_ACEOF +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] + --datadir=DIR read-only architecture-independent data [PREFIX/share] + --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] + --infodir=DIR info documentation [PREFIX/info] + --mandir=DIR man documentation [PREFIX/man] +_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 1.03:";; + esac + cat <<\_ACEOF + +Optional Features: + --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) + --enable-FEATURE[=ARG] include FEATURE [ARG=yes] + --disable-dependency-tracking speeds up one-time build + --enable-dependency-tracking do not reject slow dependency extractors + --enable-maintainer-mode enable make rules and dependencies not useful + (and sometimes confusing) to the casual installer + --enable-debug Compile with debugging options (default: no) + --enable-rpo Enable compilation with option -frepo + --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-gnuwin32=DIR where the GnuWin32 packages are installed + --with-libtiff=DIR where the www.libtiff.org libtiff library is located + +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 + CPPFLAGS C/C++ preprocessor flags, e.g. -I if you have + headers in a nonstandard directory + CC C compiler command + CFLAGS C compiler flags + GNUWIN32_DIR + Base directory of GnuWin32 packages + CXXCPP C++ preprocessor + LIBTIFF_LIBS + Tiff library to link against + LIBTIFF_CFLAGS + Compile flags needed for TIFF support + +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 +fi + +if test "$ac_init_help" = "recursive"; then + # If there are subdirs, report their specific --help. + ac_popdir=`pwd` + for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue + test -d $ac_dir || continue + ac_builddir=. + +if test "$ac_dir" != .; then + ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` + # A "../" for each directory in $ac_dir_suffix. + ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'` +else + ac_dir_suffix= ac_top_builddir= +fi + +case $srcdir in + .) # No --srcdir option. We are building in place. + ac_srcdir=. + if test -z "$ac_top_builddir"; then + ac_top_srcdir=. + else + ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'` + fi ;; + [\\/]* | ?:[\\/]* ) # Absolute path. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir ;; + *) # Relative path. + ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_builddir$srcdir ;; +esac + +# Do not use `cd foo && pwd` to compute absolute paths, because +# the directories may not exist. +case `pwd` in +.) ac_abs_builddir="$ac_dir";; +*) + case "$ac_dir" in + .) ac_abs_builddir=`pwd`;; + [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";; + *) ac_abs_builddir=`pwd`/"$ac_dir";; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_top_builddir=${ac_top_builddir}.;; +*) + case ${ac_top_builddir}. in + .) ac_abs_top_builddir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;; + *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_srcdir=$ac_srcdir;; +*) + case $ac_srcdir in + .) ac_abs_srcdir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;; + *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_top_srcdir=$ac_top_srcdir;; +*) + case $ac_top_srcdir in + .) ac_abs_top_srcdir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;; + *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;; + esac;; +esac + + cd $ac_dir + # Check for guested configure; otherwise get Cygnus style 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 + elif test -f $ac_srcdir/configure.ac || + test -f $ac_srcdir/configure.in; then + echo + $ac_configure --help + else + echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 + fi + cd $ac_popdir + done +fi + +test -n "$ac_init_help" && exit 0 +if $ac_init_version; then + cat <<\_ACEOF +Tesseract configure 1.03 +generated by GNU Autoconf 2.59 + +Copyright (C) 2003 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 0 +fi +exec 5>config.log +cat >&5 <<_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 1.03, which was +generated by GNU Autoconf 2.59. Invocation command line was + + $ $0 $@ + +_ACEOF +{ +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` +hostinfo = `(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=. + echo "PATH: $as_dir" +done + +} >&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_sep= +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=`echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + case $ac_pass in + 1) ac_configure_args0="$ac_configure_args0 '$ac_arg'" ;; + 2) + ac_configure_args1="$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 + ac_configure_args="$ac_configure_args$ac_sep'$ac_arg'" + # Get rid of the leading space. + ac_sep=" " + ;; + esac + done +done +$as_unset ac_configure_args0 || test "${ac_configure_args0+set}" != set || { ac_configure_args0=; export ac_configure_args0; } +$as_unset ac_configure_args1 || test "${ac_configure_args1+set}" != set || { ac_configure_args1=; export 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: Be sure not to use single quotes in there, as some shells, +# such as our DU 5.0 friend, will then `close' the trap. +trap 'exit_status=$? + # Save into config.log some information that might help in debugging. + { + echo + + cat <<\_ASBOX +## ---------------- ## +## Cache variables. ## +## ---------------- ## +_ASBOX + echo + # The following way of writing the cache mishandles newlines in values, +{ + (set) 2>&1 | + case `(ac_space='"'"' '"'"'; set | grep ac_space) 2>&1` in + *ac_space=\ *) + sed -n \ + "s/'"'"'/'"'"'\\\\'"'"''"'"'/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='"'"'\\2'"'"'/p" + ;; + *) + sed -n \ + "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p" + ;; + esac; +} + echo + + cat <<\_ASBOX +## ----------------- ## +## Output variables. ## +## ----------------- ## +_ASBOX + echo + for ac_var in $ac_subst_vars + do + eval ac_val=$`echo $ac_var` + echo "$ac_var='"'"'$ac_val'"'"'" + done | sort + echo + + if test -n "$ac_subst_files"; then + cat <<\_ASBOX +## ------------- ## +## Output files. ## +## ------------- ## +_ASBOX + echo + for ac_var in $ac_subst_files + do + eval ac_val=$`echo $ac_var` + echo "$ac_var='"'"'$ac_val'"'"'" + done | sort + echo + fi + + if test -s confdefs.h; then + cat <<\_ASBOX +## ----------- ## +## confdefs.h. ## +## ----------- ## +_ASBOX + echo + sed "/^$/d" confdefs.h | sort + echo + fi + test "$ac_signal" != 0 && + echo "$as_me: caught signal $ac_signal" + echo "$as_me: exit $exit_status" + } >&5 + rm -f core *.core && + rm -rf conftest* confdefs* conf$$* $ac_clean_files && + exit $exit_status + ' 0 +for ac_signal in 1 2 13 15; do + trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal +done +ac_signal=0 + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -rf conftest* confdefs.h +# AIX cpp loses on an empty file, so make sure it contains at least a newline. +echo >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 + + +# Let the site file select an alternate cache file if it wants to. +# Prefer explicitly selected file to automatically selected ones. +if test -z "$CONFIG_SITE"; then + if test "x$prefix" != xNONE; then + CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site" + else + CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" + fi +fi +for ac_site_file in $CONFIG_SITE; do + if test -r "$ac_site_file"; then + { echo "$as_me:$LINENO: loading site script $ac_site_file" >&5 +echo "$as_me: loading site script $ac_site_file" >&6;} + sed 's/^/| /' "$ac_site_file" >&5 + . "$ac_site_file" + 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. + if test -f "$cache_file"; then + { echo "$as_me:$LINENO: loading cache $cache_file" >&5 +echo "$as_me: loading cache $cache_file" >&6;} + case $cache_file in + [\\/]* | ?:[\\/]* ) . $cache_file;; + *) . ./$cache_file;; + esac + fi +else + { echo "$as_me:$LINENO: creating cache $cache_file" >&5 +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 `(set) 2>&1 | + sed -n 's/^ac_env_\([a-zA-Z_0-9]*\)_set=.*/\1/p'`; 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,) + { echo "$as_me:$LINENO: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 +echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,set) + { echo "$as_me:$LINENO: error: \`$ac_var' was not set in the previous run" >&5 +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 + { echo "$as_me:$LINENO: error: \`$ac_var' has changed since the previous run:" >&5 +echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} + { echo "$as_me:$LINENO: former value: $ac_old_val" >&5 +echo "$as_me: former value: $ac_old_val" >&2;} + { echo "$as_me:$LINENO: current value: $ac_new_val" >&5 +echo "$as_me: current value: $ac_new_val" >&2;} + ac_cache_corrupted=: + fi;; + esac + # Pass precious variables to config.status. + if test "$ac_new_set" = set; then + case $ac_new_val in + *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*) + ac_arg=$ac_var=`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. + *) ac_configure_args="$ac_configure_args '$ac_arg'" ;; + esac + fi +done +if $ac_cache_corrupted; then + { echo "$as_me:$LINENO: error: changes in the environment can compromise the build" >&5 +echo "$as_me: error: changes in the environment can compromise the build" >&2;} + { { echo "$as_me:$LINENO: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5 +echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;} + { (exit 1); exit 1; }; } +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 + + + + + + + + + + + + + + + + + + + + + + + + + + + +tesseract + +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 + { { echo "$as_me:$LINENO: error: cannot find install-sh or install.sh in config $srcdir/config" >&5 +echo "$as_me: error: cannot find install-sh or install.sh in config $srcdir/config" >&2;} + { (exit 1); exit 1; }; } +fi +ac_config_guess="$SHELL $ac_aux_dir/config.guess" +ac_config_sub="$SHELL $ac_aux_dir/config.sub" +ac_configure="$SHELL $ac_aux_dir/configure" # This should be Cygnus configure. + + + +# Make sure we can run config.sub. +$ac_config_sub sun4 >/dev/null 2>&1 || + { { echo "$as_me:$LINENO: error: cannot run $ac_config_sub" >&5 +echo "$as_me: error: cannot run $ac_config_sub" >&2;} + { (exit 1); exit 1; }; } + +echo "$as_me:$LINENO: checking build system type" >&5 +echo $ECHO_N "checking build system type... $ECHO_C" >&6 +if test "${ac_cv_build+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_build_alias=$build_alias +test -z "$ac_cv_build_alias" && + ac_cv_build_alias=`$ac_config_guess` +test -z "$ac_cv_build_alias" && + { { echo "$as_me:$LINENO: error: cannot guess build type; you must specify one" >&5 +echo "$as_me: error: cannot guess build type; you must specify one" >&2;} + { (exit 1); exit 1; }; } +ac_cv_build=`$ac_config_sub $ac_cv_build_alias` || + { { echo "$as_me:$LINENO: error: $ac_config_sub $ac_cv_build_alias failed" >&5 +echo "$as_me: error: $ac_config_sub $ac_cv_build_alias failed" >&2;} + { (exit 1); exit 1; }; } + +fi +echo "$as_me:$LINENO: result: $ac_cv_build" >&5 +echo "${ECHO_T}$ac_cv_build" >&6 +build=$ac_cv_build +build_cpu=`echo $ac_cv_build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'` +build_vendor=`echo $ac_cv_build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'` +build_os=`echo $ac_cv_build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'` + + +echo "$as_me:$LINENO: checking host system type" >&5 +echo $ECHO_N "checking host system type... $ECHO_C" >&6 +if test "${ac_cv_host+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_host_alias=$host_alias +test -z "$ac_cv_host_alias" && + ac_cv_host_alias=$ac_cv_build_alias +ac_cv_host=`$ac_config_sub $ac_cv_host_alias` || + { { echo "$as_me:$LINENO: error: $ac_config_sub $ac_cv_host_alias failed" >&5 +echo "$as_me: error: $ac_config_sub $ac_cv_host_alias failed" >&2;} + { (exit 1); exit 1; }; } + +fi +echo "$as_me:$LINENO: result: $ac_cv_host" >&5 +echo "${ECHO_T}$ac_cv_host" >&6 +host=$ac_cv_host +host_cpu=`echo $ac_cv_host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'` +host_vendor=`echo $ac_cv_host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'` +host_os=`echo $ac_cv_host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'` + + + +# Define date of package, etc. Could be useful in auto-generated +# documentation. +# TODO(luc) Generate good documentation using doxygen or equivalent +PACKAGE_YEAR=2006 +PACKAGE_DATE="06/2006" + + +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 + + + + + + + + +# Check whether --with-extra-includes or --without-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 + { { echo "$as_me:$LINENO: error: Cannot stat directory $withval" >&5 +echo "$as_me: error: Cannot stat directory $withval" >&2;} + { (exit 1); exit 1; }; } + fi +fi; + + +# Check whether --with-extra-libraries or --without-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 + { { echo "$as_me:$LINENO: error: Cannot stat directory $withval" >&5 +echo "$as_me: error: Cannot stat directory $withval" >&2;} + { (exit 1); exit 1; }; } + fi +fi; + +# 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 + +# Special cases +case "$host" in + *-darwin* | *-macos10*) + if test -d /opt/local ; then + CPPFLAGS="$CPPFLAGS -I/opt/local/include" + LDFLAGS="$LDFLAGS -L/opt/local/lib" + elif test -d /sw ; then + CPPFLAGS="$CPPFLAGS -I/sw/include" + LDFLAGS="$LDFLAGS -L/sw/lib" + fi + ;; +esac + +# ---------------------------------------- +# Check Compiler Characteristics and +# configure automake. The two appear to +# be intimately linked... +# ---------------------------------------- + +# Define order of compilers +ac_ext=cc +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 -n "$ac_tool_prefix"; then + for ac_prog in $CCC cl.exe g++ + 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 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CXX+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&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_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CXX="$ac_tool_prefix$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +CXX=$ac_cv_prog_CXX +if test -n "$CXX"; then + echo "$as_me:$LINENO: result: $CXX" >&5 +echo "${ECHO_T}$CXX" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$CXX" && break + done +fi +if test -z "$CXX"; then + ac_ct_CXX=$CXX + for ac_prog in $CCC cl.exe g++ +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_CXX+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&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_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CXX="$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +ac_ct_CXX=$ac_cv_prog_ac_ct_CXX +if test -n "$ac_ct_CXX"; then + echo "$as_me:$LINENO: result: $ac_ct_CXX" >&5 +echo "${ECHO_T}$ac_ct_CXX" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$ac_ct_CXX" && break +done +test -n "$ac_ct_CXX" || ac_ct_CXX="g++" + + CXX=$ac_ct_CXX +fi + + +# Provide some information about the compiler. +echo "$as_me:$LINENO:" \ + "checking for C++ compiler version" >&5 +ac_compiler=`set X $ac_compile; echo $2` +{ (eval echo "$as_me:$LINENO: \"$ac_compiler --version &5\"") >&5 + (eval $ac_compiler --version &5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (eval echo "$as_me:$LINENO: \"$ac_compiler -v &5\"") >&5 + (eval $ac_compiler -v &5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (eval echo "$as_me:$LINENO: \"$ac_compiler -V &5\"") >&5 + (eval $ac_compiler -V &5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } + +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files a.out 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. +echo "$as_me:$LINENO: checking for C++ compiler default output file name" >&5 +echo $ECHO_N "checking for C++ compiler default output file name... $ECHO_C" >&6 +ac_link_default=`echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` +if { (eval echo "$as_me:$LINENO: \"$ac_link_default\"") >&5 + (eval $ac_link_default) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + # Find the output, starting from the most likely. This scheme is +# not robust to junk in `.', hence go to wildcards (a.*) only as a last +# resort. + +# Be careful to initialize this variable, since it used to be cached. +# Otherwise an old cache value of `no' led to `EXEEXT = no' in a Makefile. +ac_cv_exeext= +# b.out is created by i960 compilers. +for ac_file in a_out.exe a.exe conftest.exe a.out conftest a.* conftest.* b.out +do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj ) + ;; + conftest.$ac_ext ) + # This is the source file. + ;; + [ab].out ) + # We found the default executable, but exeext='' is most + # certainly right. + break;; + *.* ) + ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + # FIXME: I believe we export ac_cv_exeext for Libtool, + # but it would be cool to find out if it's true. Does anybody + # maintain Libtool? --akim. + export ac_cv_exeext + break;; + * ) + break;; + esac +done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { echo "$as_me:$LINENO: error: C++ compiler cannot create executables +See \`config.log' for more details." >&5 +echo "$as_me: error: C++ compiler cannot create executables +See \`config.log' for more details." >&2;} + { (exit 77); exit 77; }; } +fi + +ac_exeext=$ac_cv_exeext +echo "$as_me:$LINENO: result: $ac_file" >&5 +echo "${ECHO_T}$ac_file" >&6 + +# Check the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +echo "$as_me:$LINENO: checking whether the C++ compiler works" >&5 +echo $ECHO_N "checking whether the C++ compiler works... $ECHO_C" >&6 +# FIXME: These cross compiler hacks should be removed for Autoconf 3.0 +# If not cross compiling, check that we can run a simple program. +if test "$cross_compiling" != yes; then + if { ac_try='./$ac_file' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + cross_compiling=no + else + if test "$cross_compiling" = maybe; then + cross_compiling=yes + else + { { echo "$as_me:$LINENO: error: cannot run C++ compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot run C++ compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } + fi + fi +fi +echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + +rm -f a.out a.exe conftest$ac_cv_exeext b.out +ac_clean_files=$ac_clean_files_save +# Check the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +echo "$as_me:$LINENO: checking whether we are cross compiling" >&5 +echo $ECHO_N "checking whether we are cross compiling... $ECHO_C" >&6 +echo "$as_me:$LINENO: result: $cross_compiling" >&5 +echo "${ECHO_T}$cross_compiling" >&6 + +echo "$as_me:$LINENO: checking for suffix of executables" >&5 +echo $ECHO_N "checking for suffix of executables... $ECHO_C" >&6 +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; 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 | *.o | *.obj ) ;; + *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + export ac_cv_exeext + break;; + * ) break;; + esac +done +else + { { echo "$as_me:$LINENO: error: cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi + +rm -f conftest$ac_cv_exeext +echo "$as_me:$LINENO: result: $ac_cv_exeext" >&5 +echo "${ECHO_T}$ac_cv_exeext" >&6 + +rm -f conftest.$ac_ext +EXEEXT=$ac_cv_exeext +ac_exeext=$EXEEXT +echo "$as_me:$LINENO: checking for suffix of object files" >&5 +echo $ECHO_N "checking for suffix of object files... $ECHO_C" >&6 +if test "${ac_cv_objext+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.o conftest.obj +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + for ac_file in `(ls conftest.o conftest.obj; ls conftest.*) 2>/dev/null`; do + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg ) ;; + *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` + break;; + esac +done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { echo "$as_me:$LINENO: error: cannot compute suffix of object files: cannot compile +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute suffix of object files: cannot compile +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi + +rm -f conftest.$ac_cv_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_objext" >&5 +echo "${ECHO_T}$ac_cv_objext" >&6 +OBJEXT=$ac_cv_objext +ac_objext=$OBJEXT +echo "$as_me:$LINENO: checking whether we are using the GNU C++ compiler" >&5 +echo $ECHO_N "checking whether we are using the GNU C++ compiler... $ECHO_C" >&6 +if test "${ac_cv_cxx_compiler_gnu+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_compiler_gnu=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_compiler_gnu=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_cxx_compiler_gnu=$ac_compiler_gnu + +fi +echo "$as_me:$LINENO: result: $ac_cv_cxx_compiler_gnu" >&5 +echo "${ECHO_T}$ac_cv_cxx_compiler_gnu" >&6 +GXX=`test $ac_compiler_gnu = yes && echo yes` +ac_test_CXXFLAGS=${CXXFLAGS+set} +ac_save_CXXFLAGS=$CXXFLAGS +CXXFLAGS="-g" +echo "$as_me:$LINENO: checking whether $CXX accepts -g" >&5 +echo $ECHO_N "checking whether $CXX accepts -g... $ECHO_C" >&6 +if test "${ac_cv_prog_cxx_g+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_prog_cxx_g=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_prog_cxx_g=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_prog_cxx_g" >&5 +echo "${ECHO_T}$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 +for ac_declaration in \ + '' \ + 'extern "C" void std::exit (int) throw (); using std::exit;' \ + 'extern "C" void std::exit (int); using std::exit;' \ + 'extern "C" void exit (int) throw ();' \ + 'extern "C" void exit (int);' \ + 'void exit (int);' +do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_declaration +#include +int +main () +{ +exit (42); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +continue +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_declaration +int +main () +{ +exit (42); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +done +rm -f conftest* +if test -n "$ac_declaration"; then + echo '#ifdef __cplusplus' >>confdefs.h + echo $ac_declaration >>confdefs.h + echo '#endif' >>confdefs.h +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 + +# Not needed +# AC_PROG_CC + +# Automake configuration +# ---------------------------------------- + +# Note: may need to configure automake to use ZIP as a distribution +# format because of an apparent bug with GZIP, which results in bogus +# archives. +# TODO(luc) Resolve this issue. +#AM_INIT_AUTOMAKE(dist-zip) +am__api_version="1.9" +# 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. +echo "$as_me:$LINENO: checking for a BSD-compatible install" >&5 +echo $ECHO_N "checking for a BSD-compatible install... $ECHO_C" >&6 +if test -z "$INSTALL"; then +if test "${ac_cv_path_install+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&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_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 + ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" + break 3 + fi + fi + done + done + ;; +esac +done + + +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. We don't cache a + # path for INSTALL within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the path is relative. + INSTALL=$ac_install_sh + fi +fi +echo "$as_me:$LINENO: result: $INSTALL" >&5 +echo "${ECHO_T}$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' + +echo "$as_me:$LINENO: checking whether build environment is sane" >&5 +echo $ECHO_N "checking whether build environment is sane... $ECHO_C" >&6 +# Just in case +sleep 1 +echo timestamp > conftest.file +# 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 ( + 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 + rm -f conftest.file + 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". + { { echo "$as_me:$LINENO: error: ls -t appears to fail. Make sure there is not a broken +alias in your environment" >&5 +echo "$as_me: error: ls -t appears to fail. Make sure there is not a broken +alias in your environment" >&2;} + { (exit 1); exit 1; }; } + fi + + test "$2" = conftest.file + ) +then + # Ok. + : +else + { { echo "$as_me:$LINENO: error: newly created file is older than distributed files! +Check your system clock" >&5 +echo "$as_me: error: newly created file is older than distributed files! +Check your system clock" >&2;} + { (exit 1); exit 1; }; } +fi +echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 +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 $. echo might interpret backslashes. +# By default was `s,x,x', remove it if useless. +cat <<\_ACEOF >conftest.sed +s/[\\$]/&&/g;s/;s,x,x,$// +_ACEOF +program_transform_name=`echo $program_transform_name | sed -f conftest.sed` +rm conftest.sed + +# expand $ac_aux_dir to an absolute path +am_aux_dir=`cd $ac_aux_dir && pwd` + +test x"${MISSING+set}" = xset || MISSING="\${SHELL} $am_aux_dir/missing" +# Use eval to expand $SHELL +if eval "$MISSING --run true"; then + am_missing_run="$MISSING --run " +else + am_missing_run= + { echo "$as_me:$LINENO: WARNING: \`missing' script is too old or missing" >&5 +echo "$as_me: WARNING: \`missing' script is too old or missing" >&2;} +fi + +if mkdir -p --version . >/dev/null 2>&1 && test ! -d ./--version; then + # We used to keeping the `.' as first argument, in order to + # allow $(mkdir_p) to be used without argument. As in + # $(mkdir_p) $(somedir) + # where $(somedir) is conditionally defined. However this is wrong + # for two reasons: + # 1. if the package is installed by a user who cannot write `.' + # make install will fail, + # 2. the above comment should most certainly read + # $(mkdir_p) $(DESTDIR)$(somedir) + # so it does not work when $(somedir) is undefined and + # $(DESTDIR) is not. + # To support the latter case, we have to write + # test -z "$(somedir)" || $(mkdir_p) $(DESTDIR)$(somedir), + # so the `.' trick is pointless. + mkdir_p='mkdir -p --' +else + # On NextStep and OpenStep, the `mkdir' command does not + # recognize any option. It will interpret all options as + # directories to create, and then abort because `.' already + # exists. + for d in ./-p ./--version; + do + test -d $d && rmdir $d + done + # $(mkinstalldirs) is defined by Automake if mkinstalldirs exists. + if test -f "$ac_aux_dir/mkinstalldirs"; then + mkdir_p='$(mkinstalldirs)' + else + mkdir_p='$(install_sh) -d' + fi +fi + +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 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_AWK+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&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_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_AWK="$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +AWK=$ac_cv_prog_AWK +if test -n "$AWK"; then + echo "$as_me:$LINENO: result: $AWK" >&5 +echo "${ECHO_T}$AWK" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$AWK" && break +done + +echo "$as_me:$LINENO: checking whether ${MAKE-make} sets \$(MAKE)" >&5 +echo $ECHO_N "checking whether ${MAKE-make} sets \$(MAKE)... $ECHO_C" >&6 +set dummy ${MAKE-make}; ac_make=`echo "$2" | sed 'y,:./+-,___p_,'` +if eval "test \"\${ac_cv_prog_make_${ac_make}_set+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.make <<\_ACEOF +all: + @echo 'ac_maketemp="$(MAKE)"' +_ACEOF +# GNU make sometimes prints "make[1]: Entering...", which would confuse us. +eval `${MAKE-make} -f conftest.make 2>/dev/null | grep temp=` +if test -n "$ac_maketemp"; then + eval ac_cv_prog_make_${ac_make}_set=yes +else + eval ac_cv_prog_make_${ac_make}_set=no +fi +rm -f conftest.make +fi +if eval "test \"`echo '$ac_cv_prog_make_'${ac_make}_set`\" = yes"; then + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + SET_MAKE= +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}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 done +.PHONY: am__doit +END +# If we don't find an include directive, just comment out the code. +echo "$as_me:$LINENO: checking for style of include used by $am_make" >&5 +echo $ECHO_N "checking for style of include used by $am_make... $ECHO_C" >&6 +am__include="#" +am__quote= +_am_result=none +# First try GNU make style include. +echo "include confinc" > confmf +# We grep out `Entering directory' and `Leaving directory' +# messages which can occur if `w' ends up in MAKEFLAGS. +# In particular we don't look at `^make:' because GNU make might +# be invoked under some other name (usually "gmake"), in which +# case it prints its new name instead of `make'. +if test "`$am_make -s -f confmf 2> /dev/null | grep -v 'ing directory'`" = "done"; then + am__include=include + am__quote= + _am_result=GNU +fi +# Now try BSD make style include. +if test "$am__include" = "#"; then + echo '.include "confinc"' > confmf + if test "`$am_make -s -f confmf 2> /dev/null`" = "done"; then + am__include=.include + am__quote="\"" + _am_result=BSD + fi +fi + + +echo "$as_me:$LINENO: result: $_am_result" >&5 +echo "${ECHO_T}$_am_result" >&6 +rm -f confinc confmf + +# Check whether --enable-dependency-tracking or --disable-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='\' +fi + + +if test "x$enable_dependency_tracking" != xno; then + AMDEP_TRUE= + AMDEP_FALSE='#' +else + AMDEP_TRUE='#' + AMDEP_FALSE= +fi + + + +# test to see if srcdir already configured +if test "`cd $srcdir && pwd`" != "`pwd`" && + test -f $srcdir/config.status; then + { { echo "$as_me:$LINENO: error: source directory already configured; run \"make distclean\" there first" >&5 +echo "$as_me: error: source directory already configured; run \"make distclean\" there first" >&2;} + { (exit 1); exit 1; }; } +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='1.03' + + +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"} + +install_sh=${install_sh-"$am_aux_dir/install-sh"} + +# 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 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_STRIP+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&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_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_STRIP="${ac_tool_prefix}strip" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +STRIP=$ac_cv_prog_STRIP +if test -n "$STRIP"; then + echo "$as_me:$LINENO: result: $STRIP" >&5 +echo "${ECHO_T}$STRIP" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}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 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_STRIP+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&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_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_STRIP="strip" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + + test -z "$ac_cv_prog_ac_ct_STRIP" && ac_cv_prog_ac_ct_STRIP=":" +fi +fi +ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP +if test -n "$ac_ct_STRIP"; then + echo "$as_me:$LINENO: result: $ac_ct_STRIP" >&5 +echo "${ECHO_T}$ac_ct_STRIP" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + STRIP=$ac_ct_STRIP +else + STRIP="$ac_cv_prog_STRIP" +fi + +fi +INSTALL_STRIP_PROGRAM="\${SHELL} \$(install_sh) -c -s" + +# We need awk for the "check" target. The system "awk" is bad on +# some platforms. +# Always define AMTAR for backward compatibility. + +AMTAR=${AMTAR-"${am_missing_run}tar"} + +am__tar='${AMTAR} chof - "$$tardir"'; am__untar='${AMTAR} xf -' + + + + +depcc="$CXX" am_compiler_list= + +echo "$as_me:$LINENO: checking dependency style of $depcc" >&5 +echo $ECHO_N "checking dependency style of $depcc... $ECHO_C" >&6 +if test "${am_cv_CXX_dependencies_compiler_type+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&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'. + 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 + 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 8's {/usr,}/bin/sh. + touch sub/conftst$i.h + done + echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf + + case $depmode in + 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 + ;; + none) break ;; + esac + # 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. + if depmode=$depmode \ + source=sub/conftest.c object=sub/conftest.${OBJEXT-o} \ + depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ + $SHELL ./depcomp $depcc -c -o sub/conftest.${OBJEXT-o} sub/conftest.c \ + >/dev/null 2>conftest.err && + grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && + grep sub/conftest.${OBJEXT-o} 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 +echo "$as_me:$LINENO: result: $am_cv_CXX_dependencies_compiler_type" >&5 +echo "${ECHO_T}$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" + +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 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&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_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}gcc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}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 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&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_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="gcc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + CC=$ac_ct_CC +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 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&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_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}cc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&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_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="cc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + CC=$ac_ct_CC +else + CC="$ac_cv_prog_CC" +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 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&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_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" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +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 + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + for ac_prog in cl + 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 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&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_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="$ac_tool_prefix$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in cl +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&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_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$ac_ct_CC" && break +done + + CC=$ac_ct_CC +fi + +fi + + +test -z "$CC" && { { echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH +See \`config.log' for more details." >&5 +echo "$as_me: error: no acceptable C compiler found in \$PATH +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } + +# Provide some information about the compiler. +echo "$as_me:$LINENO:" \ + "checking for C compiler version" >&5 +ac_compiler=`set X $ac_compile; echo $2` +{ (eval echo "$as_me:$LINENO: \"$ac_compiler --version &5\"") >&5 + (eval $ac_compiler --version &5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (eval echo "$as_me:$LINENO: \"$ac_compiler -v &5\"") >&5 + (eval $ac_compiler -v &5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (eval echo "$as_me:$LINENO: \"$ac_compiler -V &5\"") >&5 + (eval $ac_compiler -V &5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } + +echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5 +echo $ECHO_N "checking whether we are using the GNU C compiler... $ECHO_C" >&6 +if test "${ac_cv_c_compiler_gnu+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_compiler_gnu=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_compiler_gnu=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu + +fi +echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5 +echo "${ECHO_T}$ac_cv_c_compiler_gnu" >&6 +GCC=`test $ac_compiler_gnu = yes && echo yes` +ac_test_CFLAGS=${CFLAGS+set} +ac_save_CFLAGS=$CFLAGS +CFLAGS="-g" +echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5 +echo $ECHO_N "checking whether $CC accepts -g... $ECHO_C" >&6 +if test "${ac_cv_prog_cc_g+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_prog_cc_g=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_prog_cc_g=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5 +echo "${ECHO_T}$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 +echo "$as_me:$LINENO: checking for $CC option to accept ANSI C" >&5 +echo $ECHO_N "checking for $CC option to accept ANSI C... $ECHO_C" >&6 +if test "${ac_cv_prog_cc_stdc+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_prog_cc_stdc=no +ac_save_CC=$CC +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#include +#include +/* 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 -std1 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 -std1. */ +int osf4_cc_array ['\x00' == 0 ? 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 +# Don't try gcc -ansi; that turns off useful extensions and +# breaks some systems' header files. +# AIX -qlanglvl=ansi +# Ultrix and OSF/1 -std1 +# HP-UX 10.20 and later -Ae +# HP-UX older versions -Aa -D_HPUX_SOURCE +# SVR4 -Xc -D__EXTENSIONS__ +for ac_arg in "" -qlanglvl=ansi -std1 -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_prog_cc_stdc=$ac_arg +break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext +done +rm -f conftest.$ac_ext conftest.$ac_objext +CC=$ac_save_CC + +fi + +case "x$ac_cv_prog_cc_stdc" in + x|xno) + echo "$as_me:$LINENO: result: none needed" >&5 +echo "${ECHO_T}none needed" >&6 ;; + *) + echo "$as_me:$LINENO: result: $ac_cv_prog_cc_stdc" >&5 +echo "${ECHO_T}$ac_cv_prog_cc_stdc" >&6 + CC="$CC $ac_cv_prog_cc_stdc" ;; +esac + +# Some people use a C++ compiler to compile C. Since we use `exit', +# in C++ we need to declare it. In case someone uses the same compiler +# for both compiling C and C++ we need to have the C++ compiler decide +# the declaration of exit, since it's the most demanding environment. +cat >conftest.$ac_ext <<_ACEOF +#ifndef __cplusplus + choke me +#endif +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + for ac_declaration in \ + '' \ + 'extern "C" void std::exit (int) throw (); using std::exit;' \ + 'extern "C" void std::exit (int); using std::exit;' \ + 'extern "C" void exit (int) throw ();' \ + 'extern "C" void exit (int);' \ + 'void exit (int);' +do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_declaration +#include +int +main () +{ +exit (42); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +continue +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_declaration +int +main () +{ +exit (42); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +done +rm -f conftest* +if test -n "$ac_declaration"; then + echo '#ifdef __cplusplus' >>confdefs.h + echo $ac_declaration >>confdefs.h + echo '#endif' >>confdefs.h +fi + +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext 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 + +depcc="$CC" am_compiler_list= + +echo "$as_me:$LINENO: checking dependency style of $depcc" >&5 +echo $ECHO_N "checking dependency style of $depcc... $ECHO_C" >&6 +if test "${am_cv_CC_dependencies_compiler_type+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&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'. + 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 + 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 8's {/usr,}/bin/sh. + touch sub/conftst$i.h + done + echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf + + case $depmode in + 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 + ;; + none) break ;; + esac + # 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. + if depmode=$depmode \ + source=sub/conftest.c object=sub/conftest.${OBJEXT-o} \ + depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ + $SHELL ./depcomp $depcc -c -o sub/conftest.${OBJEXT-o} sub/conftest.c \ + >/dev/null 2>conftest.err && + grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && + grep sub/conftest.${OBJEXT-o} 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 +echo "$as_me:$LINENO: result: $am_cv_CC_dependencies_compiler_type" >&5 +echo "${ECHO_T}$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 + + +if test "x$CC" != xcc; then + echo "$as_me:$LINENO: checking whether $CC and cc understand -c and -o together" >&5 +echo $ECHO_N "checking whether $CC and cc understand -c and -o together... $ECHO_C" >&6 +else + echo "$as_me:$LINENO: checking whether cc understands -c and -o together" >&5 +echo $ECHO_N "checking whether cc understands -c and -o together... $ECHO_C" >&6 +fi +set dummy $CC; ac_cc=`echo $2 | + sed 's/[^a-zA-Z0-9_]/_/g;s/^[0-9]/_/'` +if eval "test \"\${ac_cv_prog_cc_${ac_cc}_c_o+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +# Make sure it works both with $CC and with simple cc. +# We do the test twice because some compilers refuse to overwrite an +# existing .o file with -o, though they will create one. +ac_try='$CC -c conftest.$ac_ext -o conftest.$ac_objext >&5' +if { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + test -f conftest.$ac_objext && { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; +then + eval ac_cv_prog_cc_${ac_cc}_c_o=yes + if test "x$CC" != xcc; then + # Test first that cc exists at all. + if { ac_try='cc -c conftest.$ac_ext >&5' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_try='cc -c conftest.$ac_ext -o conftest.$ac_objext >&5' + if { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + test -f conftest.$ac_objext && { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; + then + # cc works too. + : + else + # cc exists but doesn't like -o. + eval ac_cv_prog_cc_${ac_cc}_c_o=no + fi + fi + fi +else + eval ac_cv_prog_cc_${ac_cc}_c_o=no +fi +rm -f conftest* + +fi +if eval "test \"`echo '$ac_cv_prog_cc_'${ac_cc}_c_o`\" = yes"; then + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + +cat >>confdefs.h <<\_ACEOF +#define NO_MINUS_C_MINUS_O 1 +_ACEOF + +fi + +# FIXME: we rely on the cache variable name because +# there is no other way. +set dummy $CC +ac_cc=`echo $2 | sed 's/[^a-zA-Z0-9_]/_/g;s/^[0-9]/_/'` +if eval "test \"`echo '$ac_cv_prog_cc_'${ac_cc}_c_o`\" != yes"; then + # Losing compiler, so override with the script. + # FIXME: It is wrong to rewrite CC. + # But if we don't then we get into trouble of one sort or another. + # A longer-term fix would be to have automake use am__CC in this case, + # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" + CC="$am_aux_dir/compile $CC" +fi + +echo "$as_me:$LINENO: checking whether to enable maintainer-specific portions of Makefiles" >&5 +echo $ECHO_N "checking whether to enable maintainer-specific portions of Makefiles... $ECHO_C" >&6 + # Check whether --enable-maintainer-mode or --disable-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; + echo "$as_me:$LINENO: result: $USE_MAINTAINER_MODE" >&5 +echo "${ECHO_T}$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 + + +# Need to tell automake if Visual C++ is being used: + + +if test x$CC = xcl.exe; then + USING_CL_TRUE= + USING_CL_FALSE='#' +else + USING_CL_TRUE='#' + USING_CL_FALSE= +fi + + +# 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. + +echo "$as_me:$LINENO: checking whether byte ordering is bigendian" >&5 +echo $ECHO_N "checking whether byte ordering is bigendian... $ECHO_C" >&6 +if test "${ac_cv_c_bigendian+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + # See if sys/param.h defines the BYTE_ORDER macro. +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include + +int +main () +{ +#if !BYTE_ORDER || !BIG_ENDIAN || !LITTLE_ENDIAN + bogus endian macros +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + # It does; now see whether it defined to BIG_ENDIAN or not. +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include + +int +main () +{ +#if BYTE_ORDER != BIG_ENDIAN + not big endian +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_c_bigendian=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_c_bigendian=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +# It does not; compile a test program. +if test "$cross_compiling" = yes; then + # try to guess the endianness by grepping values into an object file + ac_cv_c_bigendian=unknown + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +short ascii_mm[] = { 0x4249, 0x4765, 0x6E44, 0x6961, 0x6E53, 0x7953, 0 }; +short ascii_ii[] = { 0x694C, 0x5454, 0x656C, 0x6E45, 0x6944, 0x6E61, 0 }; +void _ascii () { char *s = (char *) ascii_mm; s = (char *) ascii_ii; } +short ebcdic_ii[] = { 0x89D3, 0xE3E3, 0x8593, 0x95C5, 0x89C4, 0x9581, 0 }; +short ebcdic_mm[] = { 0xC2C9, 0xC785, 0x95C4, 0x8981, 0x95E2, 0xA8E2, 0 }; +void _ebcdic () { char *s = (char *) ebcdic_mm; s = (char *) ebcdic_ii; } +int +main () +{ + _ascii (); _ebcdic (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; 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 +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +int +main () +{ + /* Are we little or big endian? From Harbison&Steele. */ + union + { + long l; + char c[sizeof (long)]; + } u; + u.l = 1; + exit (u.c[sizeof (long) - 1] == 1); +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_c_bigendian=no +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_c_bigendian=yes +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_c_bigendian" >&5 +echo "${ECHO_T}$ac_cv_c_bigendian" >&6 +case $ac_cv_c_bigendian in + yes) + +cat >>confdefs.h <<\_ACEOF +#define WORDS_BIGENDIAN 1 +_ACEOF + ;; + no) + ;; + *) + { { echo "$as_me:$LINENO: error: unknown endianness +presetting ac_cv_c_bigendian=no (or yes) will help" >&5 +echo "$as_me: error: unknown endianness +presetting ac_cv_c_bigendian=no (or yes) will help" >&2;} + { (exit 1); exit 1; }; } ;; +esac + + + +# ---------------------------------------- +# Check for programs we need +# ---------------------------------------- + +# Check where all the following programs are and set +# variables accordingly: +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 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_RANLIB+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&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_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +RANLIB=$ac_cv_prog_RANLIB +if test -n "$RANLIB"; then + echo "$as_me:$LINENO: result: $RANLIB" >&5 +echo "${ECHO_T}$RANLIB" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}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 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_RANLIB+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&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_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_RANLIB="ranlib" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + + test -z "$ac_cv_prog_ac_ct_RANLIB" && ac_cv_prog_ac_ct_RANLIB=":" +fi +fi +ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB +if test -n "$ac_ct_RANLIB"; then + echo "$as_me:$LINENO: result: $ac_ct_RANLIB" >&5 +echo "${ECHO_T}$ac_ct_RANLIB" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + RANLIB=$ac_ct_RANLIB +else + RANLIB="$ac_cv_prog_RANLIB" +fi + +# AC_PROG_LN_S +# AC_PATH_PROG(MV, mv) +# AC_PATH_PROG(CP, cp) +# AC_PATH_PROG(RM, rm) +# AC_PATH_PROG(AR, ar) +# AC_PATH_PROG(TOUCH, touch) +# AC_PATH_PROG(SED, sed) +# AC_PATH_PROG(BASH, bash, ,[$PATH:/usr/bin:/util/tools/bin]) +# # To use substitution in makefiles, use something like: +# AC_SUBST(BASH) + +# TODO(luc) Handle documentation. None of the following +# is really needed until then +# +# AC_PROG_DOXYGEN_VERSION(1.3.2,[DOXYGEN_OK=1]) +# AC_PATH_PROG(DOT, dot) +# AC_PATH_PROG(LATEX, latex) +# AC_PATH_PROG(DVIPS, dvips) +# AC_PATH_PROG(MAKEINDEX, makeindex) +# AC_PATH_PROG(PDFLATEX, pdflatex) +# AC_PATH_PROG(GZIP, gzip) +# +# if test -z "$DOXYGEN_OK" -o -z "$DOT"; then +# AC_MSG_WARN([------------------------------------ +# *** Disabling automatic documentation generation for this +# *** package. Please check that you have 'doxygen' (version +# *** $ac_doxygen_version or later) and 'graphviz' (aka, 'dot') +# *** installed on your system. In addition, to generate +# *** PostScript and PDF documentation, you will need to have +# *** LaTeX and PdfLaTeX respectively. Re-run this configuration +# *** script after you have updated your environment. +# --------------------------------------------------------]) +# +# # We have appropriate version of doxygen and dot, so we +# # can generate documentation. It remains to be seen whether +# # we can generate PDF and PostScript documentation.. +# else +# GENERATE_DOCUMENTATION="true" +# +# # Determine if PostScript documentation is generated: +# if test -z "$LATEX" -o -z "$DVIPS" -o -z "$MAKEINDEX"; then +# AC_MSG_WARN([Disabling generation of PostScript documentation]) +# else +# GENERATE_PS_DOCUMENTATION="true" +# fi +# +# # Determine if PDF documentation is generated: +# if test -z "$PDFLATEX" -o -z "$MAKEINDEX"; then +# AC_MSG_WARN([Disabling generation of PDF documentation]) +# else +# GENERATE_PDF_DOCUMENTATION="true" +# fi +# fi +# +# # These substitutions could be inside the 'else' +# # conditionals above, but it is not necessary and would +# # only cause some confusion... +# AC_SUBST(DOXYGEN) +# AC_SUBST(DOT) +# AC_SUBST(LATEX) +# AC_SUBST(DVIPS) +# AC_SUBST(MAKEINDEX) +# AC_SUBST(PDFLATEX) +# AC_SUBST(GZIP) +# +# # Adjust makefiles based on the kind of documentation that +# # is being generated, +# AM_CONDITIONAL(GENERATE_DOCUMENTATION, test -n "$GENERATE_DOCUMENTATION") +# AM_CONDITIONAL(GENERATE_PS_DOCUMENTATION, test -n "$GENERATE_PS_DOCUMENTATION") +# AM_CONDITIONAL(GENERATE_PDF_DOCUMENTATION, test -n "$GENERATE_PDF_DOCUMENTATION") + + +# Test for GNUWIN32 tools (only useful under windows) + + # Test whether we are running on Windows. This test is becoming + # useless because other environments exist under Windows, such + # as mingw, whihc gives host i686-pc-mingw32. Just remove this test + # for now by setting variable to yes all the time. +# AC_REQUIRE([AC_CANONICAL_HOST])[]dnl +# case $host_os in +# *cygwin* ) USING_CYGWIN_OR_MINGW=yes;; +# *mingw* ) USING_CYGWIN_OR_MINGW=yes;; +# * ) USING_CYGWIN_OR_MINGW=no;; +# esac + USING_CYGWIN_OR_MINGW=yes + # If running on Windows, do some tests + if test x$USING_CYGWIN_OR_MINGW = xyes ; then + + ac_gnuwin32=no + +# Check whether --with-gnuwin32 or --without-gnuwin32 was given. +if test "${with_gnuwin32+set}" = set; then + withval="$with_gnuwin32" + ac_gnuwin32=$withval +else + ac_gnuwin32=yes +fi; + # Process specification + echo "$as_me:$LINENO: checking for GnuWin32 directory" >&5 +echo $ECHO_N "checking for GnuWin32 directory... $ECHO_C" >&6 + if test x$ac_gnuwin32 = xyes ; then + # GNUWIN32_BASE could have been set on the command line + if test x$GNUWIN32_BASE != x ; then + if test -d "$GNUWIN32_BASE" ; then + echo "$as_me:$LINENO: result: verified at $GNUWIN32_BASE" >&5 +echo "${ECHO_T}verified at $GNUWIN32_BASE" >&6 + else + # AC_MSG_RESULT([no GnuWin32 at $GNUWIN32_BASE. Looking elsewhere]) + GNUWIN32_BASE= + fi + # Otherwise, GNUWIN32 is an environment variable that the user can set + elif test x$GNUWIN32 != x ; then + if test -d "$GNUWIN32" ; then + GNUWIN32_BASE=$GNUWIN32 + echo "$as_me:$LINENO: result: verified at $GNUWIN32_BASE" >&5 +echo "${ECHO_T}verified at $GNUWIN32_BASE" >&6 + else + # AC_MSG_RESULT([no GnuWin32 at $GNUWIN32. Looking elsewhere]) + GNUWIN32_BASE= + fi + fi + # Look in default locations if needed: + if test x$GNUWIN32_BASE = x ; then + if test -d "C:/Program Files/GnuWin32" ; then + GNUWIN32_BASE="C:/Program Files/GnuWin32" + echo "$as_me:$LINENO: result: $GNUWIN32_BASE" >&5 +echo "${ECHO_T}$GNUWIN32_BASE" >&6 + elif test -d "C:/pckg/GnuWin32" ; then + GNUWIN32_BASE="C:/pckg/GnuWin32" + echo "$as_me:$LINENO: result: $GNUWIN32_BASE" >&5 +echo "${ECHO_T}$GNUWIN32_BASE" >&6 + else + echo "$as_me:$LINENO: result: not found" >&5 +echo "${ECHO_T}not found" >&6 + fi + fi + # If directory location specified on command line + elif test x$ac_gnuwin32 != xno ; then + if test -d "$ac_gnuwin32" ; then + GNUWIN32_BASE="$ac_gnuwin32" + echo "$as_me:$LINENO: result: verified at $GNUWIN32_BASE" >&5 +echo "${ECHO_T}verified at $GNUWIN32_BASE" >&6 + else + GNUWIN32_BASE= + echo "$as_me:$LINENO: result: not found" >&5 +echo "${ECHO_T}not found" >&6 + fi + fi + # Now we can update LDFLAGS, CFLAGS and CXXFLAGS + # Do it in such a way that GnuWin32 has precedence + # over system includes and libraries + if test x$GNUWIN32_BASE != x ; then + CFLAGS="-I$GNUWIN32_BASE/include $CFLAGS" + CXXFLAGS="-I$GNUWIN32_BASE/include $CXXFLAGS" + if test x$CXX != xcl.exe ; then + LDFLAGS="-L$GNUWIN32_BASE/lib $LDFLAGS" + fi + fi + # If not running on cygwin, GNUWIN32 is useless + else + GNUWIN32_BASE= + fi + # Finally, sets automake conditional + + +if test x$GNUWIN32_BASE != x; then + HAVE_GNUWIN32_TRUE= + HAVE_GNUWIN32_FALSE='#' +else + HAVE_GNUWIN32_TRUE='#' + HAVE_GNUWIN32_FALSE= +fi + + + +# ---------------------------------------- +# C++ related options +# ---------------------------------------- + +ac_ext=cc +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 + + +# Enable --enable-debug or --disable-debug and set +# compile options accordingly. We are supposed to be either +# in debug mode or in optimize mode. Note that in debug mode, +# DEBUG_MODE will be set by this macro + + + # Check whether --enable-debug or --disable-debug was given. +if test "${enable_debug+set}" = set; then + enableval="$enable_debug" + ac_debug=$enableval +else + ac_debug=no +fi; + OPTS= + + # Note: the /Za option might be nice to have, but it is + # incompatible with , which some packages + # (like the tiff library) require + VCOPTS_COMMON="/nologo /G5 /Zp1 /W3" + saved_CXXFLAGS="$CXXFLAGS" + saved_CFLAGS="$CFLAGS" + CXXFLAGS= + CFLAGS= + for opt in $saved_CXXFLAGS ; do + case $opt in + -g*) test $ac_debug != no && OPTS="$OPTS $opt" ;; + -O*) ;; + *) CXXFLAGS="$CXXFLAGS $opt" ;; + esac + done + for opt in $saved_CFLAGS ; do + case $opt in + -O*|-g*) ;; + *) CFLAGS="$CFLAGS $opt" ;; + esac + done + if test x$ac_debug = xno ; then + OPTS=-DNDEBUG + if test x$CXX = xcl.exe ; then + OPTS="$OPTS $VCOPTS_COMMON /Ow /O2" + else + + opt="-O3" + echo "$as_me:$LINENO: checking if $CXX accepts $opt" >&5 +echo $ECHO_N "checking if $CXX accepts $opt... $ECHO_C" >&6 + echo 'void f(){}' > conftest.cc + if test -z "`${CXX} ${CXXFLAGS} ${OPTS} $opt -c conftest.cc 2>&1`"; then + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + rm conftest.* + OPTS="$OPTS -O3" + else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + rm conftest.* + + opt="-O2" + echo "$as_me:$LINENO: checking if $CXX accepts $opt" >&5 +echo $ECHO_N "checking if $CXX accepts $opt... $ECHO_C" >&6 + echo 'void f(){}' > conftest.cc + if test -z "`${CXX} ${CXXFLAGS} ${OPTS} $opt -c conftest.cc 2>&1`"; then + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + rm conftest.* + OPTS="$OPTS -O2" + else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + rm conftest.* + + fi + + fi + + cpu=`uname -m 2>/dev/null` + test -z "$cpu" && cpu=${host_cpu} + case "${host_cpu}" in + i?86) + opt="-mcpu=${host_cpu}" + + opt="$opt" + echo "$as_me:$LINENO: checking if $CXX accepts $opt" >&5 +echo $ECHO_N "checking if $CXX accepts $opt... $ECHO_C" >&6 + echo 'void f(){}' > conftest.cc + if test -z "`${CXX} ${CXXFLAGS} ${OPTS} $opt -c conftest.cc 2>&1`"; then + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + rm conftest.* + OPTS="$OPTS $opt" + else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + rm conftest.* + + fi + + ;; + esac + fi + else + if test x$CXX = xcl.exe ; then + OPTS="$OPTS $VCOPTS_COMMON /Od /Z7" + fi + +cat >>confdefs.h <<\_ACEOF +#define DEBUG_MODE 1 +_ACEOF + + fi + if test x$CXX != xcl.exe ; then + + opt="-Wall" + echo "$as_me:$LINENO: checking if $CXX accepts $opt" >&5 +echo $ECHO_N "checking if $CXX accepts $opt... $ECHO_C" >&6 + echo 'void f(){}' > conftest.cc + if test -z "`${CXX} ${CXXFLAGS} ${OPTS} $opt -c conftest.cc 2>&1`"; then + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + rm conftest.* + OPTS="$OPTS -Wall" + else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + rm conftest.* + + fi + + fi + case x"$ac_debug" in + x[0-9]) OPTS="$OPTS -DDEBUGLVL=$ac_debug" ;; + xr*) OPTS="$OPTS -DRUNTIME_DEBUG_ONLY" ;; + esac + CXXFLAGS="$CXXFLAGS $OPTS" + CFLAGS="$CFLAGS $OPTS" + + + +echo "$as_me:$LINENO: checking whether the compiler recognizes bool as a built-in type" >&5 +echo $ECHO_N "checking whether the compiler recognizes bool as a built-in type... $ECHO_C" >&6 +if test "${ac_cv_cxx_bool+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + + + ac_ext=cc +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 + + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int f(int x){return 1;} +int f(char x){return 1;} +int f(bool x){return 1;} + +int +main () +{ +bool b = true; return f(b); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_cxx_bool=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_cxx_bool=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + ac_ext=cc +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 + + +fi +echo "$as_me:$LINENO: result: $ac_cv_cxx_bool" >&5 +echo "${ECHO_T}$ac_cv_cxx_bool" >&6 +if test "$ac_cv_cxx_bool" = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_BOOL 1 +_ACEOF + +fi + +echo "$as_me:$LINENO: checking whether the compiler recognizes typename" >&5 +echo $ECHO_N "checking whether the compiler recognizes typename... $ECHO_C" >&6 +if test "${ac_cv_cxx_typename+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + + + ac_ext=cc +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 + + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +templateclass X {public:X(){}}; +int +main () +{ +X z; return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_cxx_typename=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_cxx_typename=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + ac_ext=cc +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 + + +fi +echo "$as_me:$LINENO: result: $ac_cv_cxx_typename" >&5 +echo "${ECHO_T}$ac_cv_cxx_typename" >&6 +if test "$ac_cv_cxx_typename" = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_TYPENAME 1 +_ACEOF + +fi + +echo "$as_me:$LINENO: checking whether the compiler comes with standard includes" >&5 +echo $ECHO_N "checking whether the compiler comes with standard includes... $ECHO_C" >&6 +if test "${ac_cv_cxx_stdincludes+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + + + ac_ext=cc +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 + + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +struct X { int a; X(int a):a(a){}; }; +X* foo(void *x) { return new(x) X(2); } +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_cxx_stdincludes=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_cxx_stdincludes=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + ac_ext=cc +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 + + +fi +echo "$as_me:$LINENO: result: $ac_cv_cxx_stdincludes" >&5 +echo "${ECHO_T}$ac_cv_cxx_stdincludes" >&6 +if test "$ac_cv_cxx_stdincludes" = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_STDINCLUDES 1 +_ACEOF + +fi + + CXXRPOFLAGS= + RPO_YES='#' + RPO_NO='' + if test x$GXX = xyes ; then + # Check whether --enable-rpo or --disable-rpo was given. +if test "${enable_rpo+set}" = set; then + enableval="$enable_rpo" + ac_rpo=$enableval +else + ac_rpo=no +fi; + if test x$ac_rpo != xno ; then + CXXRPOFLAGS='-frepo -fno-rtti' + RPO_YES='' + RPO_NO='#' + fi + fi + + + + + +# ---------------------------------------- +# Check for libraries +# ---------------------------------------- + +# This option seems to always add -lm to the link line, +# which causes unnecessary warnings with Visual C++. +# Comment it out for now. +#AC_CHECK_LIB(m,sqrt) + + +# ---------------------------------------- +# Checks for header files. +# ---------------------------------------- + +ac_ext=cc +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 +echo "$as_me:$LINENO: checking how to run the C++ preprocessor" >&5 +echo $ECHO_N "checking how to run the C++ preprocessor... $ECHO_C" >&6 +if test -z "$CXXCPP"; then + if test "${ac_cv_prog_CXXCPP+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&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 >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_cxx_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_cxx_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.$ac_ext + + # OK, works on sane cases. Now check whether non-existent headers + # can be detected and how. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_cxx_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_cxx_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + # Broken: success on invalid input. +continue +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f 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 +echo "$as_me:$LINENO: result: $CXXCPP" >&5 +echo "${ECHO_T}$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 >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_cxx_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_cxx_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.$ac_ext + + # OK, works on sane cases. Now check whether non-existent headers + # can be detected and how. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_cxx_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_cxx_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + # Broken: success on invalid input. +continue +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.err conftest.$ac_ext +if $ac_preproc_ok; then + : +else + { { echo "$as_me:$LINENO: error: C++ preprocessor \"$CXXCPP\" fails sanity check +See \`config.log' for more details." >&5 +echo "$as_me: error: C++ preprocessor \"$CXXCPP\" fails sanity check +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi + +ac_ext=cc +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 + + +echo "$as_me:$LINENO: checking for egrep" >&5 +echo $ECHO_N "checking for egrep... $ECHO_C" >&6 +if test "${ac_cv_prog_egrep+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if echo a | (grep -E '(a|b)') >/dev/null 2>&1 + then ac_cv_prog_egrep='grep -E' + else ac_cv_prog_egrep='egrep' + fi +fi +echo "$as_me:$LINENO: result: $ac_cv_prog_egrep" >&5 +echo "${ECHO_T}$ac_cv_prog_egrep" >&6 + EGREP=$ac_cv_prog_egrep + + +echo "$as_me:$LINENO: checking for ANSI C header files" >&5 +echo $ECHO_N "checking for ANSI C header files... $ECHO_C" >&6 +if test "${ac_cv_header_stdc+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#include +#include + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_header_stdc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_header_stdc=no +fi +rm -f 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 >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* 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 >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* 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 >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#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)) + exit(2); + exit (0); +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + : +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_header_stdc=no +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5 +echo "${ECHO_T}$ac_cv_header_stdc" >&6 +if test $ac_cv_header_stdc = yes; then + +cat >>confdefs.h <<\_ACEOF +#define STDC_HEADERS 1 +_ACEOF + +fi + +echo "$as_me:$LINENO: checking whether time.h and sys/time.h may both be included" >&5 +echo $ECHO_N "checking whether time.h and sys/time.h may both be included... $ECHO_C" >&6 +if test "${ac_cv_header_time+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#include + +int +main () +{ +if ((struct tm *) 0) +return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_header_time=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_header_time=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_header_time" >&5 +echo "${ECHO_T}$ac_cv_header_time" >&6 +if test $ac_cv_header_time = yes; then + +cat >>confdefs.h <<\_ACEOF +#define TIME_WITH_SYS_TIME 1 +_ACEOF + +fi + +echo "$as_me:$LINENO: checking for sys/wait.h that is POSIX.1 compatible" >&5 +echo $ECHO_N "checking for sys/wait.h that is POSIX.1 compatible... $ECHO_C" >&6 +if test "${ac_cv_header_sys_wait_h+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#ifndef WEXITSTATUS +# define WEXITSTATUS(stat_val) ((unsigned)(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 +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_header_sys_wait_h=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_header_sys_wait_h=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_header_sys_wait_h" >&5 +echo "${ECHO_T}$ac_cv_header_sys_wait_h" >&6 +if test $ac_cv_header_sys_wait_h = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_SYS_WAIT_H 1 +_ACEOF + +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=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default + +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_Header=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_Header=no" +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + + + +for ac_header in sys/ipc.h sys/shm.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_cxx_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_cxx_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_cxx_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + ( + cat <<\_ASBOX +## ------------------------------------------------ ## +## Report this to theraysmith@users.sourceforge.net ## +## ------------------------------------------------ ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + + +for ac_header in limits.h malloc.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_cxx_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_cxx_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_cxx_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + ( + cat <<\_ASBOX +## ------------------------------------------------ ## +## Report this to theraysmith@users.sourceforge.net ## +## ------------------------------------------------ ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + +# Enable use of system-defined bool type if available: +echo "$as_me:$LINENO: checking for stdbool.h that conforms to C99" >&5 +echo $ECHO_N "checking for stdbool.h that conforms to C99... $ECHO_C" >&6 +if test "${ac_cv_header_stdbool_h+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* 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]; + bool e = &s; + char f[(_Bool) -0.0 == false ? 1 : -1]; + char g[true]; + char h[sizeof (_Bool)]; + char i[sizeof s.t]; + +int +main () +{ + return !a + !b + !c + !d + !e + !f + !g + !h + !i; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_header_stdbool_h=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_header_stdbool_h=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_header_stdbool_h" >&5 +echo "${ECHO_T}$ac_cv_header_stdbool_h" >&6 +echo "$as_me:$LINENO: checking for _Bool" >&5 +echo $ECHO_N "checking for _Bool... $ECHO_C" >&6 +if test "${ac_cv_type__Bool+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +if ((_Bool *) 0) + return 0; +if (sizeof (_Bool)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type__Bool=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_type__Bool=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_type__Bool" >&5 +echo "${ECHO_T}$ac_cv_type__Bool" >&6 +if test $ac_cv_type__Bool = yes; then + +cat >>confdefs.h <<_ACEOF +#define HAVE__BOOL 1 +_ACEOF + + +fi + +if test $ac_cv_header_stdbool_h = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_STDBOOL_H 1 +_ACEOF + +fi + + +# Misc +echo "$as_me:$LINENO: checking whether #! works in shell scripts" >&5 +echo $ECHO_N "checking whether #! works in shell scripts... $ECHO_C" >&6 +if test "${ac_cv_sys_interpreter+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + echo '#! /bin/cat +exit 69 +' >conftest +chmod u+x conftest +(SHELL=/bin/sh; export SHELL; ./conftest >/dev/null) +if test $? -ne 69; then + ac_cv_sys_interpreter=yes +else + ac_cv_sys_interpreter=no +fi +rm -f conftest +fi +echo "$as_me:$LINENO: result: $ac_cv_sys_interpreter" >&5 +echo "${ECHO_T}$ac_cv_sys_interpreter" >&6 +interpval=$ac_cv_sys_interpreter + +# Check whether --enable-largefile or --disable-largefile was given. +if test "${enable_largefile+set}" = set; then + enableval="$enable_largefile" + +fi; +if test "$enable_largefile" != no; then + + echo "$as_me:$LINENO: checking for special C compiler options needed for large files" >&5 +echo $ECHO_N "checking for special C compiler options needed for large files... $ECHO_C" >&6 +if test "${ac_cv_sys_largefile_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&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 >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* 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 + rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext + CC="$CC -n32" + rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_sys_largefile_CC=' -n32'; break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext + break + done + CC=$ac_save_CC + rm -f conftest.$ac_ext + fi +fi +echo "$as_me:$LINENO: result: $ac_cv_sys_largefile_CC" >&5 +echo "${ECHO_T}$ac_cv_sys_largefile_CC" >&6 + if test "$ac_cv_sys_largefile_CC" != no; then + CC=$CC$ac_cv_sys_largefile_CC + fi + + echo "$as_me:$LINENO: checking for _FILE_OFFSET_BITS value needed for large files" >&5 +echo $ECHO_N "checking for _FILE_OFFSET_BITS value needed for large files... $ECHO_C" >&6 +if test "${ac_cv_sys_file_offset_bits+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + while :; do + ac_cv_sys_file_offset_bits=no + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* 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 +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* 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 +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_sys_file_offset_bits=64; break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + break +done +fi +echo "$as_me:$LINENO: result: $ac_cv_sys_file_offset_bits" >&5 +echo "${ECHO_T}$ac_cv_sys_file_offset_bits" >&6 +if test "$ac_cv_sys_file_offset_bits" != no; then + +cat >>confdefs.h <<_ACEOF +#define _FILE_OFFSET_BITS $ac_cv_sys_file_offset_bits +_ACEOF + +fi +rm -f conftest* + echo "$as_me:$LINENO: checking for _LARGE_FILES value needed for large files" >&5 +echo $ECHO_N "checking for _LARGE_FILES value needed for large files... $ECHO_C" >&6 +if test "${ac_cv_sys_large_files+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + while :; do + ac_cv_sys_large_files=no + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* 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 +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* 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 +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_sys_large_files=1; break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + break +done +fi +echo "$as_me:$LINENO: result: $ac_cv_sys_large_files" >&5 +echo "${ECHO_T}$ac_cv_sys_large_files" >&6 +if test "$ac_cv_sys_large_files" != no; then + +cat >>confdefs.h <<_ACEOF +#define _LARGE_FILES $ac_cv_sys_large_files +_ACEOF + +fi +rm -f conftest* +fi + + + +# ---------------------------------------- +# Checks for typedefs, structures, and compiler characteristics. +# ---------------------------------------- + +echo "$as_me:$LINENO: checking for wchar_t" >&5 +echo $ECHO_N "checking for wchar_t... $ECHO_C" >&6 +if test "${ac_cv_type_wchar_t+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +if ((wchar_t *) 0) + return 0; +if (sizeof (wchar_t)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_wchar_t=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_type_wchar_t=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_type_wchar_t" >&5 +echo "${ECHO_T}$ac_cv_type_wchar_t" >&6 +if test $ac_cv_type_wchar_t = yes; then + +cat >>confdefs.h <<_ACEOF +#define HAVE_WCHAR_T 1 +_ACEOF + + +fi + +echo "$as_me:$LINENO: checking for long long int" >&5 +echo $ECHO_N "checking for long long int... $ECHO_C" >&6 +if test "${ac_cv_type_long_long_int+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +if ((long long int *) 0) + return 0; +if (sizeof (long long int)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_long_long_int=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_type_long_long_int=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_type_long_long_int" >&5 +echo "${ECHO_T}$ac_cv_type_long_long_int" >&6 +if test $ac_cv_type_long_long_int = yes; then + +cat >>confdefs.h <<_ACEOF +#define HAVE_LONG_LONG_INT 1 +_ACEOF + + +fi + +echo "$as_me:$LINENO: checking for mbstate_t" >&5 +echo $ECHO_N "checking for mbstate_t... $ECHO_C" >&6 +if test "${ac_cv_type_mbstate_t+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include "wchar.h" + +int +main () +{ +if ((mbstate_t *) 0) + return 0; +if (sizeof (mbstate_t)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_mbstate_t=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_type_mbstate_t=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_type_mbstate_t" >&5 +echo "${ECHO_T}$ac_cv_type_mbstate_t" >&6 +if test $ac_cv_type_mbstate_t = yes; then + +cat >>confdefs.h <<_ACEOF +#define HAVE_MBSTATE_T 1 +_ACEOF + + +fi + + +#AC_TYPE_MODE_T +#AC_TYPE_OFF_T +echo "$as_me:$LINENO: checking for size_t" >&5 +echo $ECHO_N "checking for size_t... $ECHO_C" >&6 +if test "${ac_cv_type_size_t+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +if ((size_t *) 0) + return 0; +if (sizeof (size_t)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_size_t=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_type_size_t=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_type_size_t" >&5 +echo "${ECHO_T}$ac_cv_type_size_t" >&6 +if test $ac_cv_type_size_t = yes; then + : +else + +cat >>confdefs.h <<_ACEOF +#define size_t unsigned +_ACEOF + +fi + +#AC_TYPE_PID_T + + +# ---------------------------------------- +# Checks for library functions. +# ---------------------------------------- + + + +for ac_header in stdlib.h unistd.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_cxx_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_cxx_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_cxx_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + ( + cat <<\_ASBOX +## ------------------------------------------------ ## +## Report this to theraysmith@users.sourceforge.net ## +## ------------------------------------------------ ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + +for ac_func in getpagesize +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func (); +/* 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_$ac_func) || defined (__stub___$ac_func) +choke me +#else +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + +echo "$as_me:$LINENO: checking for working mmap" >&5 +echo $ECHO_N "checking for working mmap... $ECHO_C" >&6 +if test "${ac_cv_func_mmap_fixed_mapped+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$cross_compiling" = yes; then + ac_cv_func_mmap_fixed_mapped=no +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +/* malloc might have been renamed as rpl_malloc. */ +#undef malloc + +/* Thanks to Mike Haertel and Jim Avera for this test. + Here is a matrix of mmap possibilities: + mmap private not fixed + mmap private fixed at somewhere currently unmapped + mmap private fixed at somewhere already mapped + mmap shared not fixed + mmap shared fixed at somewhere currently unmapped + mmap shared fixed at somewhere already mapped + For private mappings, we should verify that changes cannot be read() + back from the file, nor mmap's back from the file at a different + address. (There have been systems where private was not correctly + implemented like the infamous i386 svr4.0, and systems where the + VM page cache was not coherent with the file system buffer cache + like early versions of FreeBSD and possibly contemporary NetBSD.) + For shared mappings, we should conversely verify that changes get + propagated back to all the places they're supposed to be. + + Grep wants private fixed already mapped. + The main things grep needs to know about mmap are: + * does it exist and is it safe to write into the mmap'd area + * how to use it (BSD variants) */ + +#include +#include + +#if !STDC_HEADERS && !HAVE_STDLIB_H +char *malloc (); +#endif + +/* This mess was copied from the GNU getpagesize.h. */ +#if !HAVE_GETPAGESIZE +/* Assume that all systems that can run configure have sys/param.h. */ +# if !HAVE_SYS_PARAM_H +# define HAVE_SYS_PARAM_H 1 +# endif + +# ifdef _SC_PAGESIZE +# define getpagesize() sysconf(_SC_PAGESIZE) +# else /* no _SC_PAGESIZE */ +# if HAVE_SYS_PARAM_H +# include +# ifdef EXEC_PAGESIZE +# define getpagesize() EXEC_PAGESIZE +# else /* no EXEC_PAGESIZE */ +# ifdef NBPG +# define getpagesize() NBPG * CLSIZE +# ifndef CLSIZE +# define CLSIZE 1 +# endif /* no CLSIZE */ +# else /* no NBPG */ +# ifdef NBPC +# define getpagesize() NBPC +# else /* no NBPC */ +# ifdef PAGESIZE +# define getpagesize() PAGESIZE +# endif /* PAGESIZE */ +# endif /* no NBPC */ +# endif /* no NBPG */ +# endif /* no EXEC_PAGESIZE */ +# else /* no HAVE_SYS_PARAM_H */ +# define getpagesize() 8192 /* punt totally */ +# endif /* no HAVE_SYS_PARAM_H */ +# endif /* no _SC_PAGESIZE */ + +#endif /* no HAVE_GETPAGESIZE */ + +int +main () +{ + char *data, *data2, *data3; + int i, pagesize; + int fd; + + pagesize = getpagesize (); + + /* First, make a file with some known garbage in it. */ + data = (char *) malloc (pagesize); + if (!data) + exit (1); + for (i = 0; i < pagesize; ++i) + *(data + i) = rand (); + umask (0); + fd = creat ("conftest.mmap", 0600); + if (fd < 0) + exit (1); + if (write (fd, data, pagesize) != pagesize) + exit (1); + close (fd); + + /* Next, try to mmap the file at a fixed address which already has + something else allocated at it. If we can, also make sure that + we see the same garbage. */ + fd = open ("conftest.mmap", O_RDWR); + if (fd < 0) + exit (1); + data2 = (char *) malloc (2 * pagesize); + if (!data2) + exit (1); + data2 += (pagesize - ((long) data2 & (pagesize - 1))) & (pagesize - 1); + if (data2 != mmap (data2, pagesize, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_FIXED, fd, 0L)) + exit (1); + for (i = 0; i < pagesize; ++i) + if (*(data + i) != *(data2 + i)) + exit (1); + + /* Finally, make sure that changes to the mapped area do not + percolate back to the file as seen by read(). (This is a bug on + some variants of i386 svr4.0.) */ + for (i = 0; i < pagesize; ++i) + *(data2 + i) = *(data2 + i) + 1; + data3 = (char *) malloc (pagesize); + if (!data3) + exit (1); + if (read (fd, data3, pagesize) != pagesize) + exit (1); + for (i = 0; i < pagesize; ++i) + if (*(data + i) != *(data3 + i)) + exit (1); + close (fd); + exit (0); +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_mmap_fixed_mapped=yes +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_func_mmap_fixed_mapped=no +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_func_mmap_fixed_mapped" >&5 +echo "${ECHO_T}$ac_cv_func_mmap_fixed_mapped" >&6 +if test $ac_cv_func_mmap_fixed_mapped = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_MMAP 1 +_ACEOF + +fi +rm -f conftest.mmap + +echo "$as_me:$LINENO: checking for pid_t" >&5 +echo $ECHO_N "checking for pid_t... $ECHO_C" >&6 +if test "${ac_cv_type_pid_t+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +if ((pid_t *) 0) + return 0; +if (sizeof (pid_t)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_pid_t=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_type_pid_t=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_type_pid_t" >&5 +echo "${ECHO_T}$ac_cv_type_pid_t" >&6 +if test $ac_cv_type_pid_t = yes; then + : +else + +cat >>confdefs.h <<_ACEOF +#define pid_t int +_ACEOF + +fi + + + +for ac_header in unistd.h vfork.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_cxx_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_cxx_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_cxx_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + ( + cat <<\_ASBOX +## ------------------------------------------------ ## +## Report this to theraysmith@users.sourceforge.net ## +## ------------------------------------------------ ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + + +for ac_func in fork vfork +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func (); +/* 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_$ac_func) || defined (__stub___$ac_func) +choke me +#else +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + +if test "x$ac_cv_func_fork" = xyes; then + echo "$as_me:$LINENO: checking for working fork" >&5 +echo $ECHO_N "checking for working fork... $ECHO_C" >&6 +if test "${ac_cv_func_fork_works+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$cross_compiling" = yes; then + ac_cv_func_fork_works=cross +else + cat >conftest.$ac_ext <<_ACEOF +/* By Ruediger Kuhlmann. */ + #include + #if HAVE_UNISTD_H + # include + #endif + /* Some systems only have a dummy stub for fork() */ + int main () + { + if (fork() < 0) + exit (1); + exit (0); + } +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_fork_works=yes +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_func_fork_works=no +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_func_fork_works" >&5 +echo "${ECHO_T}$ac_cv_func_fork_works" >&6 + +else + ac_cv_func_fork_works=$ac_cv_func_fork +fi +if test "x$ac_cv_func_fork_works" = xcross; then + case $host in + *-*-amigaos* | *-*-msdosdjgpp*) + # Override, as these systems have only a dummy fork() stub + ac_cv_func_fork_works=no + ;; + *) + ac_cv_func_fork_works=yes + ;; + esac + { echo "$as_me:$LINENO: WARNING: result $ac_cv_func_fork_works guessed because of cross compilation" >&5 +echo "$as_me: WARNING: result $ac_cv_func_fork_works guessed because of cross compilation" >&2;} +fi +ac_cv_func_vfork_works=$ac_cv_func_vfork +if test "x$ac_cv_func_vfork" = xyes; then + echo "$as_me:$LINENO: checking for working vfork" >&5 +echo $ECHO_N "checking for working vfork... $ECHO_C" >&6 +if test "${ac_cv_func_vfork_works+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$cross_compiling" = yes; then + ac_cv_func_vfork_works=cross +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Thanks to Paul Eggert for this test. */ +#include +#include +#include +#include +#include +#if HAVE_UNISTD_H +# include +#endif +#if HAVE_VFORK_H +# include +#endif +/* On some sparc systems, changes by the child to local and incoming + argument registers are propagated back to the parent. The compiler + is told about this with #include , but some compilers + (e.g. gcc -O) don't grok . Test for this by using a + static variable whose address is put into a register that is + clobbered by the vfork. */ +static void +#ifdef __cplusplus +sparc_address_test (int arg) +# else +sparc_address_test (arg) int arg; +#endif +{ + static pid_t child; + if (!child) { + child = vfork (); + if (child < 0) { + perror ("vfork"); + _exit(2); + } + if (!child) { + arg = getpid(); + write(-1, "", 0); + _exit (arg); + } + } +} + +int +main () +{ + pid_t parent = getpid (); + pid_t child; + + sparc_address_test (0); + + child = vfork (); + + if (child == 0) { + /* Here is another test for sparc vfork register problems. This + test uses lots of local variables, at least as many local + variables as main has allocated so far including compiler + temporaries. 4 locals are enough for gcc 1.40.3 on a Solaris + 4.1.3 sparc, but we use 8 to be safe. A buggy compiler should + reuse the register of parent for one of the local variables, + since it will think that parent can't possibly be used any more + in this routine. Assigning to the local variable will thus + munge parent in the parent process. */ + pid_t + p = getpid(), p1 = getpid(), p2 = getpid(), p3 = getpid(), + p4 = getpid(), p5 = getpid(), p6 = getpid(), p7 = getpid(); + /* Convince the compiler that p..p7 are live; otherwise, it might + use the same hardware register for all 8 local variables. */ + if (p != p1 || p != p2 || p != p3 || p != p4 + || p != p5 || p != p6 || p != p7) + _exit(1); + + /* On some systems (e.g. IRIX 3.3), vfork doesn't separate parent + from child file descriptors. If the child closes a descriptor + before it execs or exits, this munges the parent's descriptor + as well. Test for this by closing stdout in the child. */ + _exit(close(fileno(stdout)) != 0); + } else { + int status; + struct stat st; + + while (wait(&status) != child) + ; + exit( + /* Was there some problem with vforking? */ + child < 0 + + /* Did the child fail? (This shouldn't happen.) */ + || status + + /* Did the vfork/compiler bug occur? */ + || parent != getpid() + + /* Did the file descriptor bug occur? */ + || fstat(fileno(stdout), &st) != 0 + ); + } +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_vfork_works=yes +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_func_vfork_works=no +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_func_vfork_works" >&5 +echo "${ECHO_T}$ac_cv_func_vfork_works" >&6 + +fi; +if test "x$ac_cv_func_fork_works" = xcross; then + ac_cv_func_vfork_works=$ac_cv_func_vfork + { echo "$as_me:$LINENO: WARNING: result $ac_cv_func_vfork_works guessed because of cross compilation" >&5 +echo "$as_me: WARNING: result $ac_cv_func_vfork_works guessed because of cross compilation" >&2;} +fi + +if test "x$ac_cv_func_vfork_works" = xyes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_WORKING_VFORK 1 +_ACEOF + +else + +cat >>confdefs.h <<\_ACEOF +#define vfork fork +_ACEOF + +fi +if test "x$ac_cv_func_fork_works" = xyes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_WORKING_FORK 1 +_ACEOF + +fi + + + +for ac_func in strerror vsnprintf +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func (); +/* 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_$ac_func) || defined (__stub___$ac_func) +choke me +#else +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + + +for ac_func in gethostname +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func (); +/* 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_$ac_func) || defined (__stub___$ac_func) +choke me +#else +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + + + +for ac_func in strchr memcpy +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func (); +/* 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_$ac_func) || defined (__stub___$ac_func) +choke me +#else +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + + + +for ac_func in acos asin +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func (); +/* 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_$ac_func) || defined (__stub___$ac_func) +choke me +#else +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + + +# ---------------------------------------- +# Test auxilliary packages +# ---------------------------------------- + +# Search JPEG library - not needed at the moment +# AC_PATH_JPEG(, +# [ no_jpeg=yes +# AC_MSG_WARN([JPEG support is disabled]) ]) + +# Search LIBTIFF library + + + case $host in + *msdos* | *go32* | *mingw32* | *cygwin* | *windows*) + USING_WIN=yes + ;; + *) + USING_WIN=no + esac + + + + ac_libtiff=no + +# Check whether --with-libtiff or --without-libtiff was given. +if test "${with_libtiff+set}" = set; then + withval="$with_libtiff" + ac_libtiff=$withval +else + ac_libtiff=yes +fi; + echo "$as_me:$LINENO: checking for Leffler libtiff library" >&5 +echo $ECHO_N "checking for Leffler libtiff library... $ECHO_C" >&6 + # Need to define TOP_SRCDIR for the cases where the LIBTIFF library + # is checked in with the code, at the top-level + TOP_SRCDIR=`cd $srcdir; pwd` + LOCAL_LIBTIFFDIR=$TOP_SRCDIR/libtiff + # First of all, deal with the case when 'cl.exe' is used as compiler + # indeed, when this is the case, we can only rely on finding + # the right library and includes, but we can't try compiling + # and linking. In addition, library needs to be specifically + # supplied on the link line and special flags are needed... + if test "x$CXX" = "xcl.exe" ; then + if test "x$ac_libtiff" = "xyes" ; then + # If LIBTIFF_LIBS has been set on configure command line + # or as environment variable, just use it if it exists + if test "x$LIBTIFF_LIBS" != "x" ; then + echo "$as_me:$LINENO: result: user specified as $LIBTIFF_LIBS" >&5 +echo "${ECHO_T}user specified as $LIBTIFF_LIBS" >&6 + if test "x$LIBTIFF_CFLAGS" = "x" ; then + { echo "$as_me:$LINENO: WARNING: LIBTIFF_CFLAGS is empty" >&5 +echo "$as_me: WARNING: LIBTIFF_CFLAGS is empty" >&2;} + fi + fi + # Otherwise, test if libtiff is at top level directory, under libtiff + if test "x$LIBTIFF_LIBS" = "x" && test -r "$LOCAL_LIBTIFFDIR/lib/libtiff.a" && test -r "$LOCAL_LIBTIFFDIR/lib/libjpeg.a" && test -r "$LOCAL_LIBTIFFDIR/lib/libz.a" ; then + echo "$as_me:$LINENO: result: $LOCAL_LIBTIFFDIR/lib/libtiff.a" >&5 +echo "${ECHO_T}$LOCAL_LIBTIFFDIR/lib/libtiff.a" >&6 + LIBTIFF_LIBS="$LOCAL_LIBTIFFDIR/lib/libtiff.a $LOCAL_LIBTIFFDIR/lib/libjpeg.a $LOCAL_LIBTIFFDIR/lib/libz.a user32.lib" + LIBTIFF_CFLAGS="-I$LOCAL_LIBTIFFDIR/include" + fi + # Otherwise, if GnuWin32 was previously located + if test "x$LIBTIFF_LIBS" = "x" && test -r "$GNUWIN32_BASE/lib/libtiff.a" && test -r "$GNUWIN32_BASE/lib/libjpeg.a" && test -r "$GNUWIN32_BASE/lib/libz.a" ; then + echo "$as_me:$LINENO: result: $GNUWIN32_BASE/lib/libtiff.a" >&5 +echo "${ECHO_T}$GNUWIN32_BASE/lib/libtiff.a" >&6 + { echo "$as_me:$LINENO: WARNING: $GNUWIN32_BASE/lib/libtiff.a version 3.5.7 works. Some versions like 3.6.1 do not!" >&5 +echo "$as_me: WARNING: $GNUWIN32_BASE/lib/libtiff.a version 3.5.7 works. Some versions like 3.6.1 do not!" >&2;} + LIBTIFF_LIBS="$GNUWIN32_BASE/lib/libtiff.a $GNUWIN32_BASE/lib/libjpeg.a $GNUWIN32_BASE/lib/libz.a user32.lib" + fi + if test "x$LIBTIFF_LIBS" = "x" ; then + echo "$as_me:$LINENO: result: not found or incomplete" >&5 +echo "${ECHO_T}not found or incomplete" >&6 + ac_libtiff=no + fi + elif test "x$ac_libtiff" != "xno" ; then + test x${LIBTIFF_LIBS+set} != xset && LIBTIFF_LIBS="$ac_libtiff/libtiff" + test x${LIBTIFF_CFLAGS+set} != xset && LIBTIFF_CFLAGS="-I$ac_libtiff" + fi + # If we are not using CL, that is we are either using gcc/cygwin + # or we are not running under Windows: + else + # Process specification + if test "x$ac_libtiff" = "xyes" ; then + if test "x$LIBTIFF_LIBS" = "x" ; then + # If local libtiff exists at top level, and we are running windows, use it first: + if test "x$USING_WIN" = "xyes" && test -r "$LOCAL_LIBTIFFDIR/lib/libtiff.a" && test -r "$LOCAL_LIBTIFFDIR/lib/libjpeg.a" && test -r "$LOCAL_LIBTIFFDIR/lib/libz.a" ; then + echo "$as_me:$LINENO: result: $LOCAL_LIBTIFFDIR/lib/libtiff.a" >&5 +echo "${ECHO_T}$LOCAL_LIBTIFFDIR/lib/libtiff.a" >&6 + LIBTIFF_LIBS="$LOCAL_LIBTIFFDIR/lib/libtiff.a $LOCAL_LIBTIFFDIR/lib/libjpeg.a $LOCAL_LIBTIFFDIR/lib/libz.a -lole32 -luuid -lwsock32" + LIBTIFF_CFLAGS="-I$LOCAL_LIBTIFFDIR/include" + fi + # If GNUWIN32_BASE is defined, it means we are running under + # Windows and we should use these builds if available because + # they are the best bet + if test "x$LIBTIFF_LIBS" = "x" && test "x$GNUWIN32_BASE" != "x" ; then + # With version 3.5.7 of gnuwin32 libtiff, we do not need to link + # against "$GNUWIN32_BASE/lib/libgw32c.a", so no need to test + if test -r "$GNUWIN32_BASE/lib/libtiff.a" && test -r "$GNUWIN32_BASE/lib/libjpeg.a" && test -r "$GNUWIN32_BASE/lib/libz.a" ; then + echo "$as_me:$LINENO: result: $GNUWIN32_BASE/lib/libtiff.a" >&5 +echo "${ECHO_T}$GNUWIN32_BASE/lib/libtiff.a" >&6 + # With 3.5.7 version of libtiff, no need to add + # $GNUWIN32_BASE/lib/libgw32c.a after $GNUWIN32_BASE/lib/libz.a + LIBTIFF_LIBS="$GNUWIN32_BASE/lib/libtiff.a $GNUWIN32_BASE/lib/libjpeg.a $GNUWIN32_BASE/lib/libz.a -lole32 -luuid -lwsock32" + else + echo "$as_me:$LINENO: result: GNUWIN32 not found or incomplete. Trying something else" >&5 +echo "${ECHO_T}GNUWIN32 not found or incomplete. Trying something else" >&6 + fi + fi + # If LIBTIFF_LIBS is still not defined after potentially going + # the GNUWIN32 route, then try using home built versions + if test "x$LIBTIFF_LIBS" = "x" ; then + TIFF_PACKAGE="$HOME_UNIX/packages/libtiff/libtiff" + if test -d "$TIFF_PACKAGE" ; then + LIBTIFF_LIBS="$TIFF_PACKAGE/libtiff.a" + LIBTIFF_CFLAGS="-I$TIFF_PACKAGE" + else + LIBTIFF_LIBS="-ltiff" + fi + fi + fi + elif test "x$ac_libtiff" != "xno" ; then + test "x${LIBTIFF_LIBS+set}" != "xset" && LIBTIFF_LIBS="$ac_libtiff/libtiff.a" + test "x${LIBTIFF_CFLAGS+set}" != "xset" && LIBTIFF_CFLAGS="-I$ac_libtiff" + fi + # Try linking - recall that -Wall is typically on, and any warning + # will cause this test to fail... This, by the way, is one of the + # reasons we cannot run this test when using the Microsoft compiler (cl), + # which outputs tons of "garbage" on stdout: + if test "x$ac_libtiff" != "xno" ; then + echo "$as_me:$LINENO: checking linking with $LIBTIFF_LIBS" >&5 +echo $ECHO_N "checking linking with $LIBTIFF_LIBS... $ECHO_C" >&6 + save_CFLAGS="$CFLAGS" + save_CXXFLAGS="$CXXFLAGS" + save_LIBS="$LIBS" + CFLAGS="$CFLAGS $LIBTIFF_CFLAGS" + CXXFLAGS="$CXXFLAGS $LIBTIFF_CFLAGS" + LIBS="$LIBS $LIBTIFF_LIBS -lm" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +#ifdef __cplusplus +extern "C" { +#endif +#include +#include +#ifdef __cplusplus +} +#endif +int +main () +{ + +TIFFClose((TIFF *) 0); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_libtiff=ok +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_libtiff=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + CFLAGS="$save_CFLAGS" + CXXFLAGS="$save_CXXFLAGS" + LIBS="$save_LIBS" + echo "$as_me:$LINENO: result: $ac_libtiff" >&5 +echo "${ECHO_T}$ac_libtiff" >&6 + fi + fi + # Finish + if test "x$ac_libtiff" = "xno"; then + LIBTIFF_CFLAGS= ; LIBTIFF_LIBS= + no_libtiff=yes + { echo "$as_me:$LINENO: WARNING: TIFF support is disabled" >&5 +echo "$as_me: WARNING: TIFF support is disabled" >&2;} + else + +cat >>confdefs.h <<\_ACEOF +#define HAVE_LIBTIFF 1 +_ACEOF + + echo "$as_me:$LINENO: result: setting LIBTIFF_CFLAGS=$LIBTIFF_CFLAGS" >&5 +echo "${ECHO_T}setting LIBTIFF_CFLAGS=$LIBTIFF_CFLAGS" >&6 + echo "$as_me:$LINENO: result: setting LIBTIFF_LIBS=$LIBTIFF_LIBS" >&5 +echo "${ECHO_T}setting LIBTIFF_LIBS=$LIBTIFF_LIBS" >&6 + CFLAGS="$LIBTIFF_CFLAGS $CFLAGS" + CXXFLAGS="$LIBTIFF_CFLAGS $CXXFLAGS" + if test "x$CXX" = "xcl.exe" ; then + LIBS="$LIBTIFF_LIBS $LIBS" + else + LIBS="$LIBTIFF_LIBS $LIBS -lm" + fi + : + fi + + +if test x$ac_libtiff != xno; then + HAVE_LIBTIFF_TRUE= + HAVE_LIBTIFF_FALSE='#' +else + HAVE_LIBTIFF_TRUE='#' + HAVE_LIBTIFF_FALSE= +fi + + + + +# ---------------------------------------- +# Final Tasks and Output +# ---------------------------------------- + +# Define installation paths + + save_prefix="${prefix}" + save_exec_prefix="${exec_prefix}" + test "x$prefix" = xNONE && prefix="$ac_default_prefix" + test "x$exec_prefix" = xNONE && exec_prefix="$prefix" + DIR_PREFIX="`eval echo \"$prefix\"`" + +cat >>confdefs.h <<_ACEOF +#define DIR_PREFIX "${DIR_PREFIX}" +_ACEOF + + DIR_EXEC_PREFIX="`eval echo \"$exec_prefix\"`" + +cat >>confdefs.h <<_ACEOF +#define DIR_EXEC_PREFIX "${DIR_EXEC_PREFIX}" +_ACEOF + + DIR_BINDIR="`eval echo \"$bindir\"`" + +cat >>confdefs.h <<_ACEOF +#define DIR_BINDIR "${DIR_BINDIR}" +_ACEOF + + DIR_LIBDIR="`eval echo \"$libdir\"`" + +cat >>confdefs.h <<_ACEOF +#define DIR_LIBDIR "${DIR_LIBDIR}" +_ACEOF + + DIR_DATADIR="`eval echo \"$datadir\"`" + +cat >>confdefs.h <<_ACEOF +#define DIR_DATADIR "${DIR_DATADIR}" +_ACEOF + + DIR_MANDIR="`eval echo \"$mandir\"`" + +cat >>confdefs.h <<_ACEOF +#define DIR_MANDIR "${DIR_MANDIR}" +_ACEOF + + prefix="${save_prefix}" + exec_prefix="${save_exec_prefix}" + +# Redundant with PACKAGE_VERSION - comment out +# AC_DEFINE_UNQUOTED(TESSERACT_VERSION,["${PACKAGE_VERSION}"],[version string]) + +# Output files + ac_config_files="$ac_config_files Makefile" + + ac_config_files="$ac_config_files ccmain/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 cutil/Makefile" + + ac_config_files="$ac_config_files dict/Makefile" + + ac_config_files="$ac_config_files display/Makefile" + + ac_config_files="$ac_config_files image/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 training/Makefile" + +# AC_CONFIG_FILES(doc/Doxyfile) +# AC_CONFIG_FILES(doc/header.html) +# AC_CONFIG_FILES(doc/footer.html) +# AC_CONFIG_FILES(doc/header.tex) +# AC_CONFIG_FILES(doc/RTF_ExtensionFile) +# AC_CONFIG_FILES(doc/Makefile) +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, don't put newlines in cache variables' values. +# 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. +{ + (set) 2>&1 | + case `(ac_space=' '; set | grep ac_space) 2>&1` in + *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 \ + "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p" + ;; + esac; +} | + sed ' + t clear + : clear + s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ + t end + /^ac_cv_env/!s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ + : end' >>confcache +if diff $cache_file confcache >/dev/null 2>&1; then :; else + if test -w $cache_file; then + test "x$cache_file" != "x/dev/null" && echo "updating cache $cache_file" + cat confcache >$cache_file + else + echo "not updating unwritable cache $cache_file" + 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}' + +# VPATH may cause trouble with some makes, so we remove $(srcdir), +# ${srcdir} and @srcdir@ 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[ ]*=/{ +s/:*\$(srcdir):*/:/; +s/:*\${srcdir}:*/:/; +s/:*@srcdir@:*/:/; +s/^\([^=]*=[ ]*\):*/\1/; +s/:*$//; +s/^[^=]*=[ ]*$//; +}' +fi + +DEFS=-DHAVE_CONFIG_H + +ac_libobjs= +ac_ltlibobjs= +for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue + # 1. Remove the extension, and $U if already installed. + ac_i=`echo "$ac_i" | + sed 's/\$U\././;s/\.o$//;s/\.obj$//'` + # 2. Add them. + ac_libobjs="$ac_libobjs $ac_i\$U.$ac_objext" + ac_ltlibobjs="$ac_ltlibobjs $ac_i"'$U.lo' +done +LIBOBJS=$ac_libobjs + +LTLIBOBJS=$ac_ltlibobjs + + +if test -z "${AMDEP_TRUE}" && test -z "${AMDEP_FALSE}"; then + { { echo "$as_me:$LINENO: error: conditional \"AMDEP\" was never defined. +Usually this means the macro was only invoked conditionally." >&5 +echo "$as_me: error: conditional \"AMDEP\" was never defined. +Usually this means the macro was only invoked conditionally." >&2;} + { (exit 1); exit 1; }; } +fi +if test -z "${am__fastdepCXX_TRUE}" && test -z "${am__fastdepCXX_FALSE}"; then + { { echo "$as_me:$LINENO: error: conditional \"am__fastdepCXX\" was never defined. +Usually this means the macro was only invoked conditionally." >&5 +echo "$as_me: error: conditional \"am__fastdepCXX\" was never defined. +Usually this means the macro was only invoked conditionally." >&2;} + { (exit 1); exit 1; }; } +fi +if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then + { { echo "$as_me:$LINENO: error: conditional \"am__fastdepCC\" was never defined. +Usually this means the macro was only invoked conditionally." >&5 +echo "$as_me: error: conditional \"am__fastdepCC\" was never defined. +Usually this means the macro was only invoked conditionally." >&2;} + { (exit 1); exit 1; }; } +fi +if test -z "${MAINTAINER_MODE_TRUE}" && test -z "${MAINTAINER_MODE_FALSE}"; then + { { echo "$as_me:$LINENO: error: conditional \"MAINTAINER_MODE\" was never defined. +Usually this means the macro was only invoked conditionally." >&5 +echo "$as_me: error: conditional \"MAINTAINER_MODE\" was never defined. +Usually this means the macro was only invoked conditionally." >&2;} + { (exit 1); exit 1; }; } +fi +if test -z "${USING_CL_TRUE}" && test -z "${USING_CL_FALSE}"; then + { { echo "$as_me:$LINENO: error: conditional \"USING_CL\" was never defined. +Usually this means the macro was only invoked conditionally." >&5 +echo "$as_me: error: conditional \"USING_CL\" was never defined. +Usually this means the macro was only invoked conditionally." >&2;} + { (exit 1); exit 1; }; } +fi +if test -z "${HAVE_GNUWIN32_TRUE}" && test -z "${HAVE_GNUWIN32_FALSE}"; then + { { echo "$as_me:$LINENO: error: conditional \"HAVE_GNUWIN32\" was never defined. +Usually this means the macro was only invoked conditionally." >&5 +echo "$as_me: error: conditional \"HAVE_GNUWIN32\" was never defined. +Usually this means the macro was only invoked conditionally." >&2;} + { (exit 1); exit 1; }; } +fi +if test -z "${HAVE_LIBTIFF_TRUE}" && test -z "${HAVE_LIBTIFF_FALSE}"; then + { { echo "$as_me:$LINENO: error: conditional \"HAVE_LIBTIFF\" was never defined. +Usually this means the macro was only invoked conditionally." >&5 +echo "$as_me: error: conditional \"HAVE_LIBTIFF\" was never defined. +Usually this means the macro was only invoked conditionally." >&2;} + { (exit 1); exit 1; }; } +fi + +: ${CONFIG_STATUS=./config.status} +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files $CONFIG_STATUS" +{ echo "$as_me:$LINENO: creating $CONFIG_STATUS" >&5 +echo "$as_me: creating $CONFIG_STATUS" >&6;} +cat >$CONFIG_STATUS <<_ACEOF +#! $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} +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF +## --------------------- ## +## M4sh Initialization. ## +## --------------------- ## + +# 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+"$@"}'='"$@"' +elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then + set -o posix +fi +DUALCASE=1; export DUALCASE # for MKS sh + +# Support unset when possible. +if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then + as_unset=unset +else + as_unset=false +fi + + +# Work around bugs in pre-3.0 UWIN ksh. +$as_unset ENV MAIL MAILPATH +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +for as_var in \ + LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \ + LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \ + LC_TELEPHONE LC_TIME +do + if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then + eval $as_var=C; export $as_var + else + $as_unset $as_var + fi +done + +# Required to use basename. +if expr a : '\(a\)' >/dev/null 2>&1; 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 + + +# Name of the executable. +as_me=`$as_basename "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)$' \| \ + . : '\(.\)' 2>/dev/null || +echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; } + /^X\/\(\/\/\)$/{ s//\1/; q; } + /^X\/\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + + +# PATH needs CR, and LINENO needs CR and PATH. +# 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 + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + echo "#! /bin/sh" >conf$$.sh + echo "exit 0" >>conf$$.sh + chmod +x conf$$.sh + if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then + PATH_SEPARATOR=';' + else + PATH_SEPARATOR=: + fi + rm -f conf$$.sh +fi + + + as_lineno_1=$LINENO + as_lineno_2=$LINENO + as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x$as_lineno_3" = "x$as_lineno_2" || { + # Find who we are. Look in the path if we contain no path at all + # relative or not. + 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 + + ;; + 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 + { { echo "$as_me:$LINENO: error: cannot find myself; rerun with an absolute path" >&5 +echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2;} + { (exit 1); exit 1; }; } + fi + case $CONFIG_SHELL in + '') + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for as_base in sh bash ksh sh5; do + case $as_dir in + /*) + if ("$as_dir/$as_base" -c ' + as_lineno_1=$LINENO + as_lineno_2=$LINENO + as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x$as_lineno_3" = "x$as_lineno_2" ') 2>/dev/null; then + $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; } + $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; } + CONFIG_SHELL=$as_dir/$as_base + export CONFIG_SHELL + exec "$CONFIG_SHELL" "$0" ${1+"$@"} + fi;; + esac + done +done +;; + esac + + # Create $as_me.lineno as a copy of $as_myself, but with $LINENO + # uniformly replaced by the line number. The first 'sed' inserts a + # line-number line before each line; the second 'sed' does the real + # work. The second script uses 'N' to pair each line-number line + # with the numbered line, and appends trailing '-' during + # substitution so that $LINENO is not a special case at line end. + # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the + # second 'sed' script. Blame Lee E. McMahon for sed's syntax. :-) + sed '=' <$as_myself | + sed ' + N + s,$,-, + : loop + s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3, + t loop + s,-$,, + s,^['$as_cr_digits']*\n,, + ' >$as_me.lineno && + chmod +x $as_me.lineno || + { { echo "$as_me:$LINENO: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&5 +echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2;} + { (exit 1); exit 1; }; } + + # 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 sensible to this). + . ./$as_me.lineno + # Exit status is that of the last command. + exit +} + + +case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in + *c*,-n*) ECHO_N= ECHO_C=' +' ECHO_T=' ' ;; + *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;; + *) ECHO_N= ECHO_C='\c' ECHO_T= ;; +esac + +if expr a : '\(a\)' >/dev/null 2>&1; then + as_expr=expr +else + as_expr=false +fi + +rm -f conf$$ conf$$.exe conf$$.file +echo >conf$$.file +if ln -s conf$$.file conf$$ 2>/dev/null; then + # We could just check for DJGPP; but this test a) works b) is more generic + # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04). + if test -f conf$$.exe; then + # Don't use ln at all; we don't have any links + as_ln_s='cp -p' + else + as_ln_s='ln -s' + fi +elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.file + +if mkdir -p . 2>/dev/null; then + as_mkdir_p=: +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +as_executable_p="test -f" + +# 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'" + + +# IFS +# We need space, tab and new line, in precisely that order. +as_nl=' +' +IFS=" $as_nl" + +# CDPATH. +$as_unset CDPATH + +exec 6>&1 + +# Open the log real soon, to keep \$[0] and so on meaningful, and to +# report actual input values of CONFIG_FILES etc. instead of their +# values after options handling. Logging --version etc. is OK. +exec 5>>config.log +{ + echo + sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX +## Running $as_me. ## +_ASBOX +} >&5 +cat >&5 <<_CSEOF + +This file was extended by Tesseract $as_me 1.03, which was +generated by GNU Autoconf 2.59. Invocation command line was + + CONFIG_FILES = $CONFIG_FILES + CONFIG_HEADERS = $CONFIG_HEADERS + CONFIG_LINKS = $CONFIG_LINKS + CONFIG_COMMANDS = $CONFIG_COMMANDS + $ $0 $@ + +_CSEOF +echo "on `(hostname || uname -n) 2>/dev/null | sed 1q`" >&5 +echo >&5 +_ACEOF + +# Files that config.status was made for. +if test -n "$ac_config_files"; then + echo "config_files=\"$ac_config_files\"" >>$CONFIG_STATUS +fi + +if test -n "$ac_config_headers"; then + echo "config_headers=\"$ac_config_headers\"" >>$CONFIG_STATUS +fi + +if test -n "$ac_config_links"; then + echo "config_links=\"$ac_config_links\"" >>$CONFIG_STATUS +fi + +if test -n "$ac_config_commands"; then + echo "config_commands=\"$ac_config_commands\"" >>$CONFIG_STATUS +fi + +cat >>$CONFIG_STATUS <<\_ACEOF + +ac_cs_usage="\ +\`$as_me' instantiates files from templates according to the +current configuration. + +Usage: $0 [OPTIONS] [FILE]... + + -h, --help print this help, then exit + -V, --version print version number, then exit + -q, --quiet 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_cs_version="\\ +Tesseract config.status 1.03 +configured by $0, generated by GNU Autoconf 2.59, + with options \\"`echo "$ac_configure_args" | sed 's/[\\""\`\$]/\\\\&/g'`\\" + +Copyright (C) 2003 Free Software Foundation, Inc. +This config.status script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it." +srcdir=$srcdir +INSTALL="$INSTALL" +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF +# If no file are specified by the user, then we need to provide default +# value. By we need to know if files were specified by the user. +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=$1 + ac_optarg=$2 + ac_shift=shift + ;; + *) # This is not an option, so the user has probably given explicit + # arguments. + ac_option=$1 + ac_need_defaults=false;; + esac + + case $ac_option in + # Handling of the options. +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + ac_cs_recheck=: ;; + --version | --vers* | -V ) + echo "$ac_cs_version"; exit 0 ;; + --he | --h) + # Conflict between --help and --header + { { echo "$as_me:$LINENO: error: ambiguous option: $1 +Try \`$0 --help' for more information." >&5 +echo "$as_me: error: ambiguous option: $1 +Try \`$0 --help' for more information." >&2;} + { (exit 1); exit 1; }; };; + --help | --hel | -h ) + echo "$ac_cs_usage"; exit 0 ;; + --debug | --d* | -d ) + debug=: ;; + --file | --fil | --fi | --f ) + $ac_shift + CONFIG_FILES="$CONFIG_FILES $ac_optarg" + ac_need_defaults=false;; + --header | --heade | --head | --hea ) + $ac_shift + CONFIG_HEADERS="$CONFIG_HEADERS $ac_optarg" + ac_need_defaults=false;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil | --si | --s) + ac_cs_silent=: ;; + + # This is an error. + -*) { { echo "$as_me:$LINENO: error: unrecognized option: $1 +Try \`$0 --help' for more information." >&5 +echo "$as_me: error: unrecognized option: $1 +Try \`$0 --help' for more information." >&2;} + { (exit 1); exit 1; }; } ;; + + *) ac_config_targets="$ac_config_targets $1" ;; + + 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 +if \$ac_cs_recheck; then + echo "running $SHELL $0 " $ac_configure_args \$ac_configure_extra_args " --no-create --no-recursion" >&6 + exec $SHELL $0 $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion +fi + +_ACEOF + +cat >>$CONFIG_STATUS <<_ACEOF +# +# INIT-COMMANDS section. +# + +AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir" + +_ACEOF + + + +cat >>$CONFIG_STATUS <<\_ACEOF +for ac_config_target in $ac_config_targets +do + case "$ac_config_target" in + # Handling of arguments. + "Makefile" ) CONFIG_FILES="$CONFIG_FILES Makefile" ;; + "ccmain/Makefile" ) CONFIG_FILES="$CONFIG_FILES ccmain/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" ;; + "cutil/Makefile" ) CONFIG_FILES="$CONFIG_FILES cutil/Makefile" ;; + "dict/Makefile" ) CONFIG_FILES="$CONFIG_FILES dict/Makefile" ;; + "display/Makefile" ) CONFIG_FILES="$CONFIG_FILES display/Makefile" ;; + "image/Makefile" ) CONFIG_FILES="$CONFIG_FILES image/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" ;; + "training/Makefile" ) CONFIG_FILES="$CONFIG_FILES training/Makefile" ;; + "depfiles" ) CONFIG_COMMANDS="$CONFIG_COMMANDS depfiles" ;; + "config_auto.h" ) CONFIG_HEADERS="$CONFIG_HEADERS config_auto.h:config/config.h.in" ;; + *) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5 +echo "$as_me: error: invalid argument: $ac_config_target" >&2;} + { (exit 1); exit 1; }; };; + 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 to put it here, and in addition, +# creating and moving files from /tmp can sometimes cause problems. +# Create a temporary directory, and hook for its removal unless debugging. +$debug || +{ + trap 'exit_status=$?; rm -rf $tmp && exit $exit_status' 0 + trap '{ (exit 1); exit 1; }' 1 2 13 15 +} + +# Create a (secure) tmp directory for tmp files. + +{ + tmp=`(umask 077 && mktemp -d -q "./confstatXXXXXX") 2>/dev/null` && + test -n "$tmp" && test -d "$tmp" +} || +{ + tmp=./confstat$$-$RANDOM + (umask 077 && mkdir $tmp) +} || +{ + echo "$me: cannot create a temporary directory in ." >&2 + { (exit 1); exit 1; } +} + +_ACEOF + +cat >>$CONFIG_STATUS <<_ACEOF + +# +# CONFIG_FILES section. +# + +# No need to generate the scripts if there are no CONFIG_FILES. +# This happens for instance when ./config.status config.h +if test -n "\$CONFIG_FILES"; then + # Protect against being on the right side of a sed subst in config.status. + sed 's/,@/@@/; s/@,/@@/; s/,;t t\$/@;t t/; /@;t t\$/s/[\\\\&,]/\\\\&/g; + s/@@/,@/; s/@@/@,/; s/@;t t\$/,;t t/' >\$tmp/subs.sed <<\\CEOF +s,@SHELL@,$SHELL,;t t +s,@PATH_SEPARATOR@,$PATH_SEPARATOR,;t t +s,@PACKAGE_NAME@,$PACKAGE_NAME,;t t +s,@PACKAGE_TARNAME@,$PACKAGE_TARNAME,;t t +s,@PACKAGE_VERSION@,$PACKAGE_VERSION,;t t +s,@PACKAGE_STRING@,$PACKAGE_STRING,;t t +s,@PACKAGE_BUGREPORT@,$PACKAGE_BUGREPORT,;t t +s,@exec_prefix@,$exec_prefix,;t t +s,@prefix@,$prefix,;t t +s,@program_transform_name@,$program_transform_name,;t t +s,@bindir@,$bindir,;t t +s,@sbindir@,$sbindir,;t t +s,@libexecdir@,$libexecdir,;t t +s,@datadir@,$datadir,;t t +s,@sysconfdir@,$sysconfdir,;t t +s,@sharedstatedir@,$sharedstatedir,;t t +s,@localstatedir@,$localstatedir,;t t +s,@libdir@,$libdir,;t t +s,@includedir@,$includedir,;t t +s,@oldincludedir@,$oldincludedir,;t t +s,@infodir@,$infodir,;t t +s,@mandir@,$mandir,;t t +s,@build_alias@,$build_alias,;t t +s,@host_alias@,$host_alias,;t t +s,@target_alias@,$target_alias,;t t +s,@DEFS@,$DEFS,;t t +s,@ECHO_C@,$ECHO_C,;t t +s,@ECHO_N@,$ECHO_N,;t t +s,@ECHO_T@,$ECHO_T,;t t +s,@LIBS@,$LIBS,;t t +s,@build@,$build,;t t +s,@build_cpu@,$build_cpu,;t t +s,@build_vendor@,$build_vendor,;t t +s,@build_os@,$build_os,;t t +s,@host@,$host,;t t +s,@host_cpu@,$host_cpu,;t t +s,@host_vendor@,$host_vendor,;t t +s,@host_os@,$host_os,;t t +s,@PACKAGE_YEAR@,$PACKAGE_YEAR,;t t +s,@PACKAGE_DATE@,$PACKAGE_DATE,;t t +s,@CXX@,$CXX,;t t +s,@CXXFLAGS@,$CXXFLAGS,;t t +s,@LDFLAGS@,$LDFLAGS,;t t +s,@CPPFLAGS@,$CPPFLAGS,;t t +s,@ac_ct_CXX@,$ac_ct_CXX,;t t +s,@EXEEXT@,$EXEEXT,;t t +s,@OBJEXT@,$OBJEXT,;t t +s,@INSTALL_PROGRAM@,$INSTALL_PROGRAM,;t t +s,@INSTALL_SCRIPT@,$INSTALL_SCRIPT,;t t +s,@INSTALL_DATA@,$INSTALL_DATA,;t t +s,@CYGPATH_W@,$CYGPATH_W,;t t +s,@PACKAGE@,$PACKAGE,;t t +s,@VERSION@,$VERSION,;t t +s,@ACLOCAL@,$ACLOCAL,;t t +s,@AUTOCONF@,$AUTOCONF,;t t +s,@AUTOMAKE@,$AUTOMAKE,;t t +s,@AUTOHEADER@,$AUTOHEADER,;t t +s,@MAKEINFO@,$MAKEINFO,;t t +s,@install_sh@,$install_sh,;t t +s,@STRIP@,$STRIP,;t t +s,@ac_ct_STRIP@,$ac_ct_STRIP,;t t +s,@INSTALL_STRIP_PROGRAM@,$INSTALL_STRIP_PROGRAM,;t t +s,@mkdir_p@,$mkdir_p,;t t +s,@AWK@,$AWK,;t t +s,@SET_MAKE@,$SET_MAKE,;t t +s,@am__leading_dot@,$am__leading_dot,;t t +s,@AMTAR@,$AMTAR,;t t +s,@am__tar@,$am__tar,;t t +s,@am__untar@,$am__untar,;t t +s,@DEPDIR@,$DEPDIR,;t t +s,@am__include@,$am__include,;t t +s,@am__quote@,$am__quote,;t t +s,@AMDEP_TRUE@,$AMDEP_TRUE,;t t +s,@AMDEP_FALSE@,$AMDEP_FALSE,;t t +s,@AMDEPBACKSLASH@,$AMDEPBACKSLASH,;t t +s,@CXXDEPMODE@,$CXXDEPMODE,;t t +s,@am__fastdepCXX_TRUE@,$am__fastdepCXX_TRUE,;t t +s,@am__fastdepCXX_FALSE@,$am__fastdepCXX_FALSE,;t t +s,@CC@,$CC,;t t +s,@CFLAGS@,$CFLAGS,;t t +s,@ac_ct_CC@,$ac_ct_CC,;t t +s,@CCDEPMODE@,$CCDEPMODE,;t t +s,@am__fastdepCC_TRUE@,$am__fastdepCC_TRUE,;t t +s,@am__fastdepCC_FALSE@,$am__fastdepCC_FALSE,;t t +s,@MAINTAINER_MODE_TRUE@,$MAINTAINER_MODE_TRUE,;t t +s,@MAINTAINER_MODE_FALSE@,$MAINTAINER_MODE_FALSE,;t t +s,@MAINT@,$MAINT,;t t +s,@USING_CL_TRUE@,$USING_CL_TRUE,;t t +s,@USING_CL_FALSE@,$USING_CL_FALSE,;t t +s,@RANLIB@,$RANLIB,;t t +s,@ac_ct_RANLIB@,$ac_ct_RANLIB,;t t +s,@GNUWIN32_DIR@,$GNUWIN32_DIR,;t t +s,@HAVE_GNUWIN32_TRUE@,$HAVE_GNUWIN32_TRUE,;t t +s,@HAVE_GNUWIN32_FALSE@,$HAVE_GNUWIN32_FALSE,;t t +s,@OPTS@,$OPTS,;t t +s,@CXXRPOFLAGS@,$CXXRPOFLAGS,;t t +s,@RPO_YES@,$RPO_YES,;t t +s,@RPO_NO@,$RPO_NO,;t t +s,@CXXCPP@,$CXXCPP,;t t +s,@EGREP@,$EGREP,;t t +s,@LIBTIFF_LIBS@,$LIBTIFF_LIBS,;t t +s,@LIBTIFF_CFLAGS@,$LIBTIFF_CFLAGS,;t t +s,@HAVE_LIBTIFF_TRUE@,$HAVE_LIBTIFF_TRUE,;t t +s,@HAVE_LIBTIFF_FALSE@,$HAVE_LIBTIFF_FALSE,;t t +s,@LIBOBJS@,$LIBOBJS,;t t +s,@LTLIBOBJS@,$LTLIBOBJS,;t t +CEOF + +_ACEOF + + cat >>$CONFIG_STATUS <<\_ACEOF + # Split the substitutions into bite-sized pieces for seds with + # small command number limits, like on Digital OSF/1 and HP-UX. + ac_max_sed_lines=48 + ac_sed_frag=1 # Number of current file. + ac_beg=1 # First line for current file. + ac_end=$ac_max_sed_lines # Line after last line for current file. + ac_more_lines=: + ac_sed_cmds= + while $ac_more_lines; do + if test $ac_beg -gt 1; then + sed "1,${ac_beg}d; ${ac_end}q" $tmp/subs.sed >$tmp/subs.frag + else + sed "${ac_end}q" $tmp/subs.sed >$tmp/subs.frag + fi + if test ! -s $tmp/subs.frag; then + ac_more_lines=false + else + # The purpose of the label and of the branching condition is to + # speed up the sed processing (if there are no `@' at all, there + # is no need to browse any of the substitutions). + # These are the two extra sed commands mentioned above. + (echo ':t + /@[a-zA-Z_][a-zA-Z_0-9]*@/!b' && cat $tmp/subs.frag) >$tmp/subs-$ac_sed_frag.sed + if test -z "$ac_sed_cmds"; then + ac_sed_cmds="sed -f $tmp/subs-$ac_sed_frag.sed" + else + ac_sed_cmds="$ac_sed_cmds | sed -f $tmp/subs-$ac_sed_frag.sed" + fi + ac_sed_frag=`expr $ac_sed_frag + 1` + ac_beg=$ac_end + ac_end=`expr $ac_end + $ac_max_sed_lines` + fi + done + if test -z "$ac_sed_cmds"; then + ac_sed_cmds=cat + fi +fi # test -n "$CONFIG_FILES" + +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF +for ac_file in : $CONFIG_FILES; do test "x$ac_file" = x: && continue + # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". + case $ac_file in + - | *:- | *:-:* ) # input from stdin + cat >$tmp/stdin + ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; + *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; + * ) ac_file_in=$ac_file.in ;; + esac + + # Compute @srcdir@, @top_srcdir@, and @INSTALL@ for subdirectories. + ac_dir=`(dirname "$ac_file") 2>/dev/null || +$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + { if $as_mkdir_p; then + mkdir -p "$ac_dir" + else + as_dir="$ac_dir" + as_dirs= + while test ! -d "$as_dir"; do + as_dirs="$as_dir $as_dirs" + as_dir=`(dirname "$as_dir") 2>/dev/null || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + done + test ! -n "$as_dirs" || mkdir $as_dirs + fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5 +echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;} + { (exit 1); exit 1; }; }; } + + ac_builddir=. + +if test "$ac_dir" != .; then + ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` + # A "../" for each directory in $ac_dir_suffix. + ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'` +else + ac_dir_suffix= ac_top_builddir= +fi + +case $srcdir in + .) # No --srcdir option. We are building in place. + ac_srcdir=. + if test -z "$ac_top_builddir"; then + ac_top_srcdir=. + else + ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'` + fi ;; + [\\/]* | ?:[\\/]* ) # Absolute path. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir ;; + *) # Relative path. + ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_builddir$srcdir ;; +esac + +# Do not use `cd foo && pwd` to compute absolute paths, because +# the directories may not exist. +case `pwd` in +.) ac_abs_builddir="$ac_dir";; +*) + case "$ac_dir" in + .) ac_abs_builddir=`pwd`;; + [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";; + *) ac_abs_builddir=`pwd`/"$ac_dir";; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_top_builddir=${ac_top_builddir}.;; +*) + case ${ac_top_builddir}. in + .) ac_abs_top_builddir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;; + *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_srcdir=$ac_srcdir;; +*) + case $ac_srcdir in + .) ac_abs_srcdir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;; + *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_top_srcdir=$ac_top_srcdir;; +*) + case $ac_top_srcdir in + .) ac_abs_top_srcdir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;; + *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;; + esac;; +esac + + + case $INSTALL in + [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; + *) ac_INSTALL=$ac_top_builddir$INSTALL ;; + esac + + if test x"$ac_file" != x-; then + { echo "$as_me:$LINENO: creating $ac_file" >&5 +echo "$as_me: creating $ac_file" >&6;} + rm -f "$ac_file" + fi + # 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. */ + if test x"$ac_file" = x-; then + configure_input= + else + configure_input="$ac_file. " + fi + configure_input=$configure_input"Generated from `echo $ac_file_in | + sed 's,.*/,,'` by configure." + + # First look for the input files in the build tree, otherwise in the + # src tree. + ac_file_inputs=`IFS=: + for f in $ac_file_in; do + case $f in + -) echo $tmp/stdin ;; + [\\/$]*) + # Absolute (can't be DOS-style, as IFS=:) + test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 +echo "$as_me: error: cannot find input file: $f" >&2;} + { (exit 1); exit 1; }; } + echo "$f";; + *) # Relative + if test -f "$f"; then + # Build tree + echo "$f" + elif test -f "$srcdir/$f"; then + # Source tree + echo "$srcdir/$f" + else + # /dev/null tree + { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 +echo "$as_me: error: cannot find input file: $f" >&2;} + { (exit 1); exit 1; }; } + fi;; + esac + done` || { (exit 1); exit 1; } +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF + sed "$ac_vpsub +$extrasub +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF +:t +/@[a-zA-Z_][a-zA-Z_0-9]*@/!b +s,@configure_input@,$configure_input,;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,@top_builddir@,$ac_top_builddir,;t t +s,@abs_top_builddir@,$ac_abs_top_builddir,;t t +s,@INSTALL@,$ac_INSTALL,;t t +" $ac_file_inputs | (eval "$ac_sed_cmds") >$tmp/out + rm -f $tmp/stdin + if test x"$ac_file" != x-; then + mv $tmp/out $ac_file + else + cat $tmp/out + rm -f $tmp/out + fi + +done +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF + +# +# CONFIG_HEADER section. +# + +# These sed commands are passed to sed as "A NAME B NAME C VALUE D", where +# NAME is the cpp macro being defined and VALUE is the value it is being given. +# +# ac_d sets the value in "#define NAME VALUE" lines. +ac_dA='s,^\([ ]*\)#\([ ]*define[ ][ ]*\)' +ac_dB='[ ].*$,\1#\2' +ac_dC=' ' +ac_dD=',;t' +# ac_u turns "#undef NAME" without trailing blanks into "#define NAME VALUE". +ac_uA='s,^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)' +ac_uB='$,\1#\2define\3' +ac_uC=' ' +ac_uD=',;t' + +for ac_file in : $CONFIG_HEADERS; do test "x$ac_file" = x: && continue + # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". + case $ac_file in + - | *:- | *:-:* ) # input from stdin + cat >$tmp/stdin + ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; + *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; + * ) ac_file_in=$ac_file.in ;; + esac + + test x"$ac_file" != x- && { echo "$as_me:$LINENO: creating $ac_file" >&5 +echo "$as_me: creating $ac_file" >&6;} + + # First look for the input files in the build tree, otherwise in the + # src tree. + ac_file_inputs=`IFS=: + for f in $ac_file_in; do + case $f in + -) echo $tmp/stdin ;; + [\\/$]*) + # Absolute (can't be DOS-style, as IFS=:) + test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 +echo "$as_me: error: cannot find input file: $f" >&2;} + { (exit 1); exit 1; }; } + # Do quote $f, to prevent DOS paths from being IFS'd. + echo "$f";; + *) # Relative + if test -f "$f"; then + # Build tree + echo "$f" + elif test -f "$srcdir/$f"; then + # Source tree + echo "$srcdir/$f" + else + # /dev/null tree + { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 +echo "$as_me: error: cannot find input file: $f" >&2;} + { (exit 1); exit 1; }; } + fi;; + esac + done` || { (exit 1); exit 1; } + # Remove the trailing spaces. + sed 's/[ ]*$//' $ac_file_inputs >$tmp/in + +_ACEOF + +# Transform confdefs.h into two sed scripts, `conftest.defines' and +# `conftest.undefs', that substitutes the proper values into +# config.h.in to produce config.h. The first handles `#define' +# templates, and the second `#undef' templates. +# And first: Protect against being on the right side of a sed subst in +# config.status. Protect against being in an unquoted here document +# in config.status. +rm -f conftest.defines conftest.undefs +# Using a here document instead of a string reduces the quoting nightmare. +# Putting comments in sed scripts is not portable. +# +# `end' is used to avoid that the second main sed command (meant for +# 0-ary CPP macros) applies to n-ary macro definitions. +# See the Autoconf documentation for `clear'. +cat >confdef2sed.sed <<\_ACEOF +s/[\\&,]/\\&/g +s,[\\$`],\\&,g +t clear +: clear +s,^[ ]*#[ ]*define[ ][ ]*\([^ (][^ (]*\)\(([^)]*)\)[ ]*\(.*\)$,${ac_dA}\1${ac_dB}\1\2${ac_dC}\3${ac_dD},gp +t end +s,^[ ]*#[ ]*define[ ][ ]*\([^ ][^ ]*\)[ ]*\(.*\)$,${ac_dA}\1${ac_dB}\1${ac_dC}\2${ac_dD},gp +: end +_ACEOF +# If some macros were called several times there might be several times +# the same #defines, which is useless. Nevertheless, we may not want to +# sort them, since we want the *last* AC-DEFINE to be honored. +uniq confdefs.h | sed -n -f confdef2sed.sed >conftest.defines +sed 's/ac_d/ac_u/g' conftest.defines >conftest.undefs +rm -f confdef2sed.sed + +# This sed command replaces #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. +cat >>conftest.undefs <<\_ACEOF +s,^[ ]*#[ ]*undef[ ][ ]*[a-zA-Z_][a-zA-Z_0-9]*,/* & */, +_ACEOF + +# Break up conftest.defines because some shells have a limit on the size +# of here documents, and old seds have small limits too (100 cmds). +echo ' # Handle all the #define templates only if necessary.' >>$CONFIG_STATUS +echo ' if grep "^[ ]*#[ ]*define" $tmp/in >/dev/null; then' >>$CONFIG_STATUS +echo ' # If there are no defines, we may have an empty if/fi' >>$CONFIG_STATUS +echo ' :' >>$CONFIG_STATUS +rm -f conftest.tail +while grep . conftest.defines >/dev/null +do + # Write a limited-size here document to $tmp/defines.sed. + echo ' cat >$tmp/defines.sed <>$CONFIG_STATUS + # Speed up: don't consider the non `#define' lines. + echo '/^[ ]*#[ ]*define/!b' >>$CONFIG_STATUS + # Work around the forget-to-reset-the-flag bug. + echo 't clr' >>$CONFIG_STATUS + echo ': clr' >>$CONFIG_STATUS + sed ${ac_max_here_lines}q conftest.defines >>$CONFIG_STATUS + echo 'CEOF + sed -f $tmp/defines.sed $tmp/in >$tmp/out + rm -f $tmp/in + mv $tmp/out $tmp/in +' >>$CONFIG_STATUS + sed 1,${ac_max_here_lines}d conftest.defines >conftest.tail + rm -f conftest.defines + mv conftest.tail conftest.defines +done +rm -f conftest.defines +echo ' fi # grep' >>$CONFIG_STATUS +echo >>$CONFIG_STATUS + +# Break up conftest.undefs because some shells have a limit on the size +# of here documents, and old seds have small limits too (100 cmds). +echo ' # Handle all the #undef templates' >>$CONFIG_STATUS +rm -f conftest.tail +while grep . conftest.undefs >/dev/null +do + # Write a limited-size here document to $tmp/undefs.sed. + echo ' cat >$tmp/undefs.sed <>$CONFIG_STATUS + # Speed up: don't consider the non `#undef' + echo '/^[ ]*#[ ]*undef/!b' >>$CONFIG_STATUS + # Work around the forget-to-reset-the-flag bug. + echo 't clr' >>$CONFIG_STATUS + echo ': clr' >>$CONFIG_STATUS + sed ${ac_max_here_lines}q conftest.undefs >>$CONFIG_STATUS + echo 'CEOF + sed -f $tmp/undefs.sed $tmp/in >$tmp/out + rm -f $tmp/in + mv $tmp/out $tmp/in +' >>$CONFIG_STATUS + sed 1,${ac_max_here_lines}d conftest.undefs >conftest.tail + rm -f conftest.undefs + mv conftest.tail conftest.undefs +done +rm -f conftest.undefs + +cat >>$CONFIG_STATUS <<\_ACEOF + # 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. */ + if test x"$ac_file" = x-; then + echo "/* Generated by configure. */" >$tmp/config.h + else + echo "/* $ac_file. Generated by configure. */" >$tmp/config.h + fi + cat $tmp/in >>$tmp/config.h + rm -f $tmp/in + if test x"$ac_file" != x-; then + if diff $ac_file $tmp/config.h >/dev/null 2>&1; then + { echo "$as_me:$LINENO: $ac_file is unchanged" >&5 +echo "$as_me: $ac_file is unchanged" >&6;} + else + ac_dir=`(dirname "$ac_file") 2>/dev/null || +$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + { if $as_mkdir_p; then + mkdir -p "$ac_dir" + else + as_dir="$ac_dir" + as_dirs= + while test ! -d "$as_dir"; do + as_dirs="$as_dir $as_dirs" + as_dir=`(dirname "$as_dir") 2>/dev/null || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + done + test ! -n "$as_dirs" || mkdir $as_dirs + fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5 +echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;} + { (exit 1); exit 1; }; }; } + + rm -f $ac_file + mv $tmp/config.h $ac_file + fi + else + cat $tmp/config.h + rm -f $tmp/config.h + fi +# Compute $ac_file's index in $config_headers. +_am_stamp_count=1 +for _am_header in $config_headers :; do + case $_am_header in + $ac_file | $ac_file:* ) + break ;; + * ) + _am_stamp_count=`expr $_am_stamp_count + 1` ;; + esac +done +echo "timestamp for $ac_file" >`(dirname $ac_file) 2>/dev/null || +$as_expr X$ac_file : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X$ac_file : 'X\(//\)[^/]' \| \ + X$ac_file : 'X\(//\)$' \| \ + X$ac_file : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X$ac_file | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'`/stamp-h$_am_stamp_count +done +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF + +# +# CONFIG_COMMANDS section. +# +for ac_file in : $CONFIG_COMMANDS; do test "x$ac_file" = x: && continue + ac_dest=`echo "$ac_file" | sed 's,:.*,,'` + ac_source=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_dir=`(dirname "$ac_dest") 2>/dev/null || +$as_expr X"$ac_dest" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_dest" : 'X\(//\)[^/]' \| \ + X"$ac_dest" : 'X\(//\)$' \| \ + X"$ac_dest" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$ac_dest" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + { if $as_mkdir_p; then + mkdir -p "$ac_dir" + else + as_dir="$ac_dir" + as_dirs= + while test ! -d "$as_dir"; do + as_dirs="$as_dir $as_dirs" + as_dir=`(dirname "$as_dir") 2>/dev/null || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + done + test ! -n "$as_dirs" || mkdir $as_dirs + fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5 +echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;} + { (exit 1); exit 1; }; }; } + + ac_builddir=. + +if test "$ac_dir" != .; then + ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` + # A "../" for each directory in $ac_dir_suffix. + ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'` +else + ac_dir_suffix= ac_top_builddir= +fi + +case $srcdir in + .) # No --srcdir option. We are building in place. + ac_srcdir=. + if test -z "$ac_top_builddir"; then + ac_top_srcdir=. + else + ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'` + fi ;; + [\\/]* | ?:[\\/]* ) # Absolute path. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir ;; + *) # Relative path. + ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_builddir$srcdir ;; +esac + +# Do not use `cd foo && pwd` to compute absolute paths, because +# the directories may not exist. +case `pwd` in +.) ac_abs_builddir="$ac_dir";; +*) + case "$ac_dir" in + .) ac_abs_builddir=`pwd`;; + [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";; + *) ac_abs_builddir=`pwd`/"$ac_dir";; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_top_builddir=${ac_top_builddir}.;; +*) + case ${ac_top_builddir}. in + .) ac_abs_top_builddir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;; + *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_srcdir=$ac_srcdir;; +*) + case $ac_srcdir in + .) ac_abs_srcdir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;; + *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_top_srcdir=$ac_top_srcdir;; +*) + case $ac_top_srcdir in + .) ac_abs_top_srcdir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;; + *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;; + esac;; +esac + + + { echo "$as_me:$LINENO: executing $ac_dest commands" >&5 +echo "$as_me: executing $ac_dest commands" >&6;} + case $ac_dest in + depfiles ) test x"$AMDEP_TRUE" != x"" || for mf in $CONFIG_FILES; 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. + # So let's grep whole file. + if grep '^#.*generated by automake' $mf > /dev/null 2>&1; then + dirpart=`(dirname "$mf") 2>/dev/null || +$as_expr X"$mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$mf" : 'X\(//\)[^/]' \| \ + X"$mf" : 'X\(//\)$' \| \ + X"$mf" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +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"` + # When using ansi2knr, U may be empty or an underscore; expand it + U=`sed -n 's/^U = //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' -e 's/\$U/'"$U"'/g'`; do + # Make sure the directory exists. + test -f "$dirpart/$file" && continue + fdir=`(dirname "$file") 2>/dev/null || +$as_expr X"$file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$file" : 'X\(//\)[^/]' \| \ + X"$file" : 'X\(//\)$' \| \ + X"$file" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + { if $as_mkdir_p; then + mkdir -p $dirpart/$fdir + else + as_dir=$dirpart/$fdir + as_dirs= + while test ! -d "$as_dir"; do + as_dirs="$as_dir $as_dirs" + as_dir=`(dirname "$as_dir") 2>/dev/null || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + done + test ! -n "$as_dirs" || mkdir $as_dirs + fi || { { echo "$as_me:$LINENO: error: cannot create directory $dirpart/$fdir" >&5 +echo "$as_me: error: cannot create directory $dirpart/$fdir" >&2;} + { (exit 1); exit 1; }; }; } + + # echo "creating $dirpart/$file" + echo '# dummy' > "$dirpart/$file" + done +done + ;; + esac +done +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF + +{ (exit 0); exit 0; } +_ACEOF +chmod +x $CONFIG_STATUS +ac_clean_files=$ac_clean_files_save + + +# 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 || { (exit 1); exit 1; } +fi + + +# Final message +echo "" +echo "Configuration is done." +echo "You can now build $PACKAGE_NAME by running:" +# test x$GXX = xyes && \ +# echo "% make depend [optional]" +echo "" +echo "% make" +echo "" +echo "Note: 'make install' has not been implemented yet. Avoid using." + + +# ---------------------------------------- +# CONFIG Template +# ---------------------------------------- + +# Fence added in configuration file + + + +# Stuff added at bottom of file + + + diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000000..45d20522d3 --- /dev/null +++ b/configure.ac @@ -0,0 +1,338 @@ +# -*-Shell-script-*- +# +# Copyright (c) Luc Vincent + +# ---------------------------------------- +# Initialization +# ---------------------------------------- + +AC_PREREQ(2.50) +AC_INIT(Tesseract, 1.03, theraysmith@users.sourceforge.net) +AC_PACKAGE_TARNAME(tesseract-ocr) +AC_REVISION($Id: configure.ac,v 1.4 2007/02/02 22:38:17 theraysmith Exp $) +AC_CONFIG_AUX_DIR(config) +AC_CONFIG_SRCDIR(ccmain/tesseractmain.cpp) +AC_PREFIX_DEFAULT(/usr/local) +AC_CANONICAL_HOST + +# Define date of package, etc. Could be useful in auto-generated +# documentation. +# TODO(luc) Generate good documentation using doxygen or equivalent +PACKAGE_YEAR=2006 +PACKAGE_DATE="06/2006" + +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) + +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 ] ) + +# 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 + +# Special cases +case "$host" in + *-darwin* | *-macos10*) + if test -d /opt/local ; then + CPPFLAGS="$CPPFLAGS -I/opt/local/include" + LDFLAGS="$LDFLAGS -L/opt/local/lib" + elif test -d /sw ; then + CPPFLAGS="$CPPFLAGS -I/sw/include" + LDFLAGS="$LDFLAGS -L/sw/lib" + fi + ;; +esac + +# ---------------------------------------- +# Check Compiler Characteristics and +# configure automake. The two appear to +# be intimately linked... +# ---------------------------------------- + +# Define order of compilers +AC_PROG_CXX(cl.exe g++) +# Not needed +# AC_PROG_CC + +# Automake configuration +# ---------------------------------------- + +# Note: may need to configure automake to use ZIP as a distribution +# format because of an apparent bug with GZIP, which results in bogus +# archives. +# TODO(luc) Resolve this issue. +#AM_INIT_AUTOMAKE(dist-zip) +AM_INIT_AUTOMAKE +AM_CONFIG_HEADER(config_auto.h:config/config.h.in) +AM_PROG_CC_C_O +AM_MAINTAINER_MODE +# Need to tell automake if Visual C++ is being used: +AM_CONDITIONAL(USING_CL, test x$CC = xcl.exe) + +# 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: +AC_PROG_RANLIB +# AC_PROG_LN_S +# AC_PATH_PROG(MV, mv) +# AC_PATH_PROG(CP, cp) +# AC_PATH_PROG(RM, rm) +# AC_PATH_PROG(AR, ar) +# AC_PATH_PROG(TOUCH, touch) +# AC_PATH_PROG(SED, sed) +# AC_PATH_PROG(BASH, bash, ,[$PATH:/usr/bin:/util/tools/bin]) +# # To use substitution in makefiles, use something like: +# AC_SUBST(BASH) + +# TODO(luc) Handle documentation. None of the following +# is really needed until then +# +# AC_PROG_DOXYGEN_VERSION(1.3.2,[DOXYGEN_OK=1]) +# AC_PATH_PROG(DOT, dot) +# AC_PATH_PROG(LATEX, latex) +# AC_PATH_PROG(DVIPS, dvips) +# AC_PATH_PROG(MAKEINDEX, makeindex) +# AC_PATH_PROG(PDFLATEX, pdflatex) +# AC_PATH_PROG(GZIP, gzip) +# +# if test -z "$DOXYGEN_OK" -o -z "$DOT"; then +# AC_MSG_WARN([------------------------------------ +# *** Disabling automatic documentation generation for this +# *** package. Please check that you have 'doxygen' (version +# *** $ac_doxygen_version or later) and 'graphviz' (aka, 'dot') +# *** installed on your system. In addition, to generate +# *** PostScript and PDF documentation, you will need to have +# *** LaTeX and PdfLaTeX respectively. Re-run this configuration +# *** script after you have updated your environment. +# --------------------------------------------------------]) +# +# # We have appropriate version of doxygen and dot, so we +# # can generate documentation. It remains to be seen whether +# # we can generate PDF and PostScript documentation.. +# else +# GENERATE_DOCUMENTATION="true" +# +# # Determine if PostScript documentation is generated: +# if test -z "$LATEX" -o -z "$DVIPS" -o -z "$MAKEINDEX"; then +# AC_MSG_WARN([Disabling generation of PostScript documentation]) +# else +# GENERATE_PS_DOCUMENTATION="true" +# fi +# +# # Determine if PDF documentation is generated: +# if test -z "$PDFLATEX" -o -z "$MAKEINDEX"; then +# AC_MSG_WARN([Disabling generation of PDF documentation]) +# else +# GENERATE_PDF_DOCUMENTATION="true" +# fi +# fi +# +# # These substitutions could be inside the 'else' +# # conditionals above, but it is not necessary and would +# # only cause some confusion... +# AC_SUBST(DOXYGEN) +# AC_SUBST(DOT) +# AC_SUBST(LATEX) +# AC_SUBST(DVIPS) +# AC_SUBST(MAKEINDEX) +# AC_SUBST(PDFLATEX) +# AC_SUBST(GZIP) +# +# # Adjust makefiles based on the kind of documentation that +# # is being generated, +# AM_CONDITIONAL(GENERATE_DOCUMENTATION, test -n "$GENERATE_DOCUMENTATION") +# AM_CONDITIONAL(GENERATE_PS_DOCUMENTATION, test -n "$GENERATE_PS_DOCUMENTATION") +# AM_CONDITIONAL(GENERATE_PDF_DOCUMENTATION, test -n "$GENERATE_PDF_DOCUMENTATION") + + +# Test for GNUWIN32 tools (only useful under windows) +AC_PATH_GNUWIN32 + +# ---------------------------------------- +# C++ related options +# ---------------------------------------- + +AC_LANG_CPLUSPLUS + +# Enable --enable-debug or --disable-debug and set +# compile options accordingly. We are supposed to be either +# in debug mode or in optimize mode. Note that in debug mode, +# DEBUG_MODE will be set by this macro +AC_CXX_OPTIMIZE + +AC_CXX_BOOL +AC_CXX_TYPENAME +AC_CXX_STDINCLUDES +AC_CXX_RPO + +# ---------------------------------------- +# Check for libraries +# ---------------------------------------- + +# This option seems to always add -lm to the link line, +# which causes unnecessary warnings with Visual C++. +# Comment it out for now. +#AC_CHECK_LIB(m,sqrt) + + +# ---------------------------------------- +# 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 + + +# ---------------------------------------- +# Checks for typedefs, structures, and compiler characteristics. +# ---------------------------------------- + +AC_CHECK_TYPES(wchar_t) +AC_CHECK_TYPES(long long int) +AC_CHECK_TYPES(mbstate_t,,,[#include "wchar.h"]) + +#AC_TYPE_MODE_T +#AC_TYPE_OFF_T +AC_TYPE_SIZE_T +#AC_TYPE_PID_T + + +# ---------------------------------------- +# Checks for library functions. +# ---------------------------------------- + +AC_FUNC_MMAP +AC_FUNC_FORK +AC_CHECK_FUNCS(strerror vsnprintf) +AC_CHECK_FUNCS(gethostname) +AC_CHECK_FUNCS(strchr memcpy) +AC_CHECK_FUNCS(acos asin) + +# ---------------------------------------- +# Test auxilliary packages +# ---------------------------------------- + +# Search JPEG library - not needed at the moment +# AC_PATH_JPEG(, +# [ no_jpeg=yes +# AC_MSG_WARN([JPEG support is disabled]) ]) + +# Search LIBTIFF library +AC_PATH_LIBTIFF(, +[ no_libtiff=yes + AC_MSG_WARN([TIFF support is disabled]) ]) + + +# ---------------------------------------- +# Final Tasks and Output +# ---------------------------------------- + +# Define installation paths +AC_DEFINE_INSTALL_PATHS +# Redundant with PACKAGE_VERSION - comment out +# AC_DEFINE_UNQUOTED(TESSERACT_VERSION,["${PACKAGE_VERSION}"],[version string]) + +# Output files +AC_CONFIG_FILES(Makefile) +AC_CONFIG_FILES(ccmain/Makefile) +AC_CONFIG_FILES(ccstruct/Makefile) +AC_CONFIG_FILES(ccutil/Makefile) +AC_CONFIG_FILES(classify/Makefile) +AC_CONFIG_FILES(cutil/Makefile) +AC_CONFIG_FILES(dict/Makefile) +AC_CONFIG_FILES(display/Makefile) +AC_CONFIG_FILES(image/Makefile) +AC_CONFIG_FILES(textord/Makefile) +AC_CONFIG_FILES(viewer/Makefile) +AC_CONFIG_FILES(wordrec/Makefile) +AC_CONFIG_FILES(training/Makefile) +# AC_CONFIG_FILES(doc/Doxyfile) +# AC_CONFIG_FILES(doc/header.html) +# AC_CONFIG_FILES(doc/footer.html) +# AC_CONFIG_FILES(doc/header.tex) +# AC_CONFIG_FILES(doc/RTF_ExtensionFile) +# AC_CONFIG_FILES(doc/Makefile) +AC_OUTPUT + +# Final message +echo "" +echo "Configuration is done." +echo "You can now build $PACKAGE_NAME by running:" +# test x$GXX = xyes && \ +# echo "% make depend [optional]" +echo "" +echo "% make" +echo "" +echo "Note: 'make install' has not been implemented yet. Avoid using." + + +# ---------------------------------------- +# 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 + +/* config_auto.h: end */ +#endif +]) + diff --git a/cutil/Makefile.am b/cutil/Makefile.am new file mode 100644 index 0000000000..f71b9858c4 --- /dev/null +++ b/cutil/Makefile.am @@ -0,0 +1,14 @@ +SUBDIRS = +AM_CPPFLAGS = -I$(top_srcdir)/ccutil + +EXTRA_DIST = \ + bitvec.h callcpp.h const.h cutil.h danerror.h debug.h efio.h \ + emalloc.h freelist.h funcdefs.h general.h globals.h listio.h \ + minmax.h oldheap.h oldlist.h structures.h tessarray.h \ + tordvars.h variables.h + +noinst_LIBRARIES = libtesseract_cutil.a +libtesseract_cutil_a_SOURCES = \ + tessarray.cpp bitvec.cpp cutil.cpp danerror.cpp debug.cpp efio.cpp \ + emalloc.cpp freelist.cpp globals.cpp listio.cpp oldheap.cpp \ + oldlist.cpp structures.cpp tordvars.cpp variables.cpp diff --git a/cutil/Makefile.in b/cutil/Makefile.in new file mode 100644 index 0000000000..3d54d88d3d --- /dev/null +++ b/cutil/Makefile.in @@ -0,0 +1,549 @@ +# Makefile.in generated by automake 1.9.6 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005 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@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = .. +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +INSTALL = @INSTALL@ +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@ +subdir = cutil +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \ + $(top_srcdir)/config/ac_define_versionlevel.m4 \ + $(top_srcdir)/config/acinclude_custom.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(SHELL) $(top_srcdir)/config/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config_auto.h +CONFIG_CLEAN_FILES = +LIBRARIES = $(noinst_LIBRARIES) +AR = ar +ARFLAGS = cru +libtesseract_cutil_a_AR = $(AR) $(ARFLAGS) +libtesseract_cutil_a_LIBADD = +am_libtesseract_cutil_a_OBJECTS = tessarray.$(OBJEXT) bitvec.$(OBJEXT) \ + cutil.$(OBJEXT) danerror.$(OBJEXT) debug.$(OBJEXT) \ + efio.$(OBJEXT) emalloc.$(OBJEXT) freelist.$(OBJEXT) \ + globals.$(OBJEXT) listio.$(OBJEXT) oldheap.$(OBJEXT) \ + oldlist.$(OBJEXT) structures.$(OBJEXT) tordvars.$(OBJEXT) \ + variables.$(OBJEXT) +libtesseract_cutil_a_OBJECTS = $(am_libtesseract_cutil_a_OBJECTS) +DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/config/depcomp +am__depfiles_maybe = depfiles +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +CXXLD = $(CXX) +CXXLINK = $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) \ + -o $@ +SOURCES = $(libtesseract_cutil_a_SOURCES) +DIST_SOURCES = $(libtesseract_cutil_a_SOURCES) +RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \ + html-recursive info-recursive install-data-recursive \ + install-exec-recursive install-info-recursive \ + install-recursive installcheck-recursive installdirs-recursive \ + pdf-recursive ps-recursive uninstall-info-recursive \ + uninstall-recursive +ETAGS = etags +CTAGS = ctags +DIST_SUBDIRS = $(SUBDIRS) +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMDEP_FALSE = @AMDEP_FALSE@ +AMDEP_TRUE = @AMDEP_TRUE@ +AMTAR = @AMTAR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CXXRPOFLAGS = @CXXRPOFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +GNUWIN32_DIR = @GNUWIN32_DIR@ +HAVE_GNUWIN32_FALSE = @HAVE_GNUWIN32_FALSE@ +HAVE_GNUWIN32_TRUE = @HAVE_GNUWIN32_TRUE@ +HAVE_LIBTIFF_FALSE = @HAVE_LIBTIFF_FALSE@ +HAVE_LIBTIFF_TRUE = @HAVE_LIBTIFF_TRUE@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTIFF_CFLAGS = @LIBTIFF_CFLAGS@ +LIBTIFF_LIBS = @LIBTIFF_LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAINTAINER_MODE_FALSE = @MAINTAINER_MODE_FALSE@ +MAINTAINER_MODE_TRUE = @MAINTAINER_MODE_TRUE@ +MAKEINFO = @MAKEINFO@ +OBJEXT = @OBJEXT@ +OPTS = @OPTS@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_DATE = @PACKAGE_DATE@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PACKAGE_YEAR = @PACKAGE_YEAR@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +RPO_NO = @RPO_NO@ +RPO_YES = @RPO_YES@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +USING_CL_FALSE = @USING_CL_FALSE@ +USING_CL_TRUE = @USING_CL_TRUE@ +VERSION = @VERSION@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_RANLIB = @ac_ct_RANLIB@ +ac_ct_STRIP = @ac_ct_STRIP@ +am__fastdepCC_FALSE = @am__fastdepCC_FALSE@ +am__fastdepCC_TRUE = @am__fastdepCC_TRUE@ +am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@ +am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@ +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@ +datadir = @datadir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +SUBDIRS = +AM_CPPFLAGS = -I$(top_srcdir)/ccutil +EXTRA_DIST = \ + bitvec.h callcpp.h const.h cutil.h danerror.h debug.h efio.h \ + emalloc.h freelist.h funcdefs.h general.h globals.h listio.h \ + minmax.h oldheap.h oldlist.h structures.h tessarray.h \ + tordvars.h variables.h + +noinst_LIBRARIES = libtesseract_cutil.a +libtesseract_cutil_a_SOURCES = \ + tessarray.cpp bitvec.cpp cutil.cpp danerror.cpp debug.cpp efio.cpp \ + emalloc.cpp freelist.cpp globals.cpp listio.cpp oldheap.cpp \ + oldlist.cpp structures.cpp tordvars.cpp variables.cpp + +all: all-recursive + +.SUFFIXES: +.SUFFIXES: .cpp .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 \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu cutil/Makefile'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu 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 + +clean-noinstLIBRARIES: + -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) +libtesseract_cutil.a: $(libtesseract_cutil_a_OBJECTS) $(libtesseract_cutil_a_DEPENDENCIES) + -rm -f libtesseract_cutil.a + $(libtesseract_cutil_a_AR) libtesseract_cutil.a $(libtesseract_cutil_a_OBJECTS) $(libtesseract_cutil_a_LIBADD) + $(RANLIB) libtesseract_cutil.a + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bitvec.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cutil.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/danerror.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/debug.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/efio.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/emalloc.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/freelist.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/globals.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/listio.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/oldheap.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/oldlist.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/structures.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tessarray.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tordvars.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/variables.Po@am__quote@ + +.cpp.o: +@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ $< + +.cpp.obj: +@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` +uninstall-info-am: + +# 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. +$(RECURSIVE_TARGETS): + @failcom='exit 1'; \ + for f in x $$MAKEFLAGS; do \ + case $$f in \ + *=* | --[!k]*);; \ + *k*) failcom='fail=yes';; \ + esac; \ + done; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + list='$(SUBDIRS)'; 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; \ + (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" + +mostlyclean-recursive clean-recursive distclean-recursive \ +maintainer-clean-recursive: + @failcom='exit 1'; \ + for f in x $$MAKEFLAGS; do \ + case $$f in \ + *=* | --[!k]*);; \ + *k*) failcom='fail=yes';; \ + esac; \ + done; \ + dot_seen=no; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + rev=''; for subdir in $$list; do \ + if test "$$subdir" = "."; then :; else \ + rev="$$subdir $$rev"; \ + fi; \ + done; \ + rev="$$rev ."; \ + target=`echo $@ | sed s/-recursive//`; \ + for subdir in $$rev; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done && test -z "$$fail" +tags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \ + done +ctags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \ + done + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + 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 || \ + tags="$$tags $$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique; \ + fi +ctags: CTAGS +CTAGS: ctags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(CTAGS_ARGS)$$tags$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +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)'; for file in $$list; do \ + case $$file in \ + $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \ + $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \ + esac; \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + dir="/$$dir"; \ + $(mkdir_p) "$(distdir)$$dir"; \ + else \ + dir=''; \ + fi; \ + if test -d $$d/$$file; then \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$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 \ + test -d "$(distdir)/$$subdir" \ + || $(mkdir_p) "$(distdir)/$$subdir" \ + || exit 1; \ + distdir=`$(am__cd) $(distdir) && pwd`; \ + top_distdir=`$(am__cd) $(top_distdir) && pwd`; \ + (cd $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$top_distdir" \ + distdir="$$distdir/$$subdir" \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-recursive +all-am: Makefile $(LIBRARIES) +installdirs: installdirs-recursive +installdirs-am: +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: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_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-recursive + +clean-am: clean-generic clean-noinstLIBRARIES mostlyclean-am + +distclean: distclean-recursive + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +info: info-recursive + +info-am: + +install-data-am: + +install-exec-am: + +install-info: install-info-recursive + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-compile mostlyclean-generic + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: uninstall-info-am + +uninstall-info: uninstall-info-recursive + +.PHONY: $(RECURSIVE_TARGETS) CTAGS GTAGS all all-am check check-am \ + clean clean-generic clean-noinstLIBRARIES clean-recursive \ + ctags ctags-recursive distclean distclean-compile \ + distclean-generic distclean-recursive distclean-tags distdir \ + dvi dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-exec install-exec-am \ + install-info install-info-am install-man install-strip \ + installcheck installcheck-am installdirs installdirs-am \ + maintainer-clean maintainer-clean-generic \ + maintainer-clean-recursive mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-recursive pdf pdf-am ps ps-am \ + tags tags-recursive uninstall uninstall-am uninstall-info-am + +# 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: diff --git a/cutil/bitvec.cpp b/cutil/bitvec.cpp new file mode 100644 index 0000000000..5952383bd6 --- /dev/null +++ b/cutil/bitvec.cpp @@ -0,0 +1,122 @@ +/****************************************************************************** + ** 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 "emalloc.h" +#include "freelist.h" +#include "callcpp.h" + +#include + +/**---------------------------------------------------------------------------- + Global Data Definitions and Declarations +----------------------------------------------------------------------------**/ +static int BitVectorCount = 0; + +/**---------------------------------------------------------------------------- + Public Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +BIT_VECTOR ExpandBitVector(BIT_VECTOR Vector, int NewNumBits) { +/* + ** Parameters: + ** Vector bit vector to be expanded + ** NewNumBits new size of bit vector + ** Globals: none + ** Operation: This routine uses realloc to increase the size of + ** the specified bit vector. + ** Return: New expanded bit vector. + ** Exceptions: none + ** History: Fri Nov 16 10:11:16 1990, DSJ, Created. + */ + return ((BIT_VECTOR) Erealloc (Vector, + sizeof (unsigned long) * + WordsInVectorOfSize (NewNumBits))); + +} /* ExpandBitVector */ + + +/*---------------------------------------------------------------------------*/ +void FreeBitVector(BIT_VECTOR BitVector) { +/* + ** Parameters: + ** BitVector bit vector to be freed + ** Globals: + ** BitVectorCount count of number of bit vectors allocated + ** Operation: 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. + ** Return: none + ** Exceptions: none + ** History: Tue Oct 23 16:46:09 1990, DSJ, Created. + */ + if (BitVector) { + memfree(BitVector); + BitVectorCount--; + } + else { + cprintf ("%6d BITVECTOR elements in use\n", BitVectorCount); + } + +} /* FreeBitVector */ + + + /*hamming_distance(array1,array2,length) computes the hamming distance + between two bit strings */ +/*--------------------------------------------------------------------------*/ +int hamming_distance( /*arrays to match */ + register unsigned long *array1, + register unsigned long *array2, + register int length) { /*length of arrays */ + register unsigned long diff; /*bit difference */ + register int dist; /*total distance */ + + dist = 0; + for (; length > 0; length--) { + diff = *array1++ ^ *array2++;/*different bits */ + while (diff) { + diff &= diff - 1; /*lose a bit */ + dist++; + } + } + return dist; /*total distance */ +} + + +/*---------------------------------------------------------------------------*/ +BIT_VECTOR NewBitVector(int NumBits) { +/* + ** Parameters: + ** NumBits number of bits in new bit vector + ** Globals: + ** BitVectorCount number of bit vectors allocated + ** Operation: Allocate and return a new bit vector large enough to + ** hold the specified number of bits. + ** Return: New bit vector. + ** Exceptions: none + ** History: Tue Oct 23 16:51:27 1990, DSJ, Created. + */ + BitVectorCount++; + return ((BIT_VECTOR) Emalloc (sizeof (unsigned long) * + WordsInVectorOfSize (NumBits))); + +} /* NewBitVector */ diff --git a/cutil/bitvec.h b/cutil/bitvec.h new file mode 100644 index 0000000000..bfcc3a554a --- /dev/null +++ b/cutil/bitvec.h @@ -0,0 +1,100 @@ +/****************************************************************************** + ** 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 Files and Type Defines +----------------------------------------------------------------------------**/ +#define BITSINLONG 32 /*no of bits in a long */ +typedef unsigned int *BIT_VECTOR; + +/**---------------------------------------------------------------------------- + Public Function Prototypes +----------------------------------------------------------------------------**/ +#define zero_all_bits(array,length) \ +{\ + register int index; /*temporary index*/\ +\ +for (index=0;index 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 "callcpp.h" + +#include + +#define RESET_COUNT 2000 + +void_proc deallocate = (void_proc) c_free_string; +char_proc allocate = (char_proc) c_alloc_string; + +/********************************************************************** + * 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) { + printf ("Could not open file, %s\n", filename); + exit (1); + } + return (thisfile); +} diff --git a/cutil/cutil.h b/cutil/cutil.h new file mode 100644 index 0000000000..7b183cbb04 --- /dev/null +++ b/cutil/cutil.h @@ -0,0 +1,159 @@ +/* -*-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. + * + ******************************************************************************** +$Log: cutil.h,v $ +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 "general.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 char *(*char_proc) _ARGS ((...)); +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 *); + +extern void_proc deallocate; +extern char_proc allocate; + +/*---------------------------------------------------------------------- + M a c r o s +----------------------------------------------------------------------*/ +/********************************************************************** + * min + * + * Minimum of two values + **********************************************************************/ + +#ifndef min +#define min(x,y) \ + ((x) < (y) ? (x) : (y)) +#endif + +/********************************************************************** + * max + * + * Maximum of two values + **********************************************************************/ + +#ifndef max +#define max(x,y) \ + ((y) < (x) ? (x) : (y)) +#endif + +/********************************************************************** + * new_line + * + * Print a new line character on stdout. + **********************************************************************/ + +#define new_line() \ + printf ("\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) ((*deallocate) (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) ? \ + ((char*) strcpy ((*allocate) (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); + +/* util.c +long long_rand + _ARGS ((long limit)); + +FILE *open_file + _ARGS((char *filename, + char *mode)); + +#undef _ARGS +*/ +#endif diff --git a/cutil/danerror.cpp b/cutil/danerror.cpp new file mode 100644 index 0000000000..874ea169d7 --- /dev/null +++ b/cutil/danerror.cpp @@ -0,0 +1,145 @@ +/****************************************************************************** + ** 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 "general.h" +#include "danerror.h" +#include "callcpp.h" +#include "globaloc.h" +#ifdef __UNIX__ +#include "assert.h" +#endif + +#include +#include + +#define MAXTRAPDEPTH 100 + +#define ERRORTRAPDEPTH 1000 + +/**---------------------------------------------------------------------------- + Global Data Definitions and Declarations +----------------------------------------------------------------------------**/ +static jmp_buf ErrorTrapStack[MAXTRAPDEPTH]; +static VOID_PROC ProcTrapStack[MAXTRAPDEPTH]; +static INT32 CurrentTrapDepth = 0; + +/**---------------------------------------------------------------------------- + Public Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +void ReleaseErrorTrap() { +/* + ** Parameters: + ** None + ** Globals: + ** CurrentTrapDepth number of traps on the stack + ** Operation: + ** This routine removes the current error trap from the + ** error trap stack, thus returning control to the previous + ** error trap. If the error trap stack is empty, nothing is + ** done. + ** Return: + ** None + ** Exceptions: + ** None + ** History: + ** 4/3/89, DSJ, Created. + */ + if (CurrentTrapDepth > 0) { + CurrentTrapDepth--; + } +} /* ReleaseErrorTrap */ + + +/*---------------------------------------------------------------------------*/ +void DoError(int Error, const char *Message) { +/* + ** Parameters: + ** Error error number which is to be trapped + ** Message pointer to a string to be printed as an error message + ** Globals: + ** ErrorTrapStack stack of error traps + ** CurrentTrapDepth number of traps on the stack + ** Operation: + ** 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. + ** Return: + ** None - this routine does not return. + ** Exceptions: + ** Empty error trap stack terminates the calling program. + ** History: + ** 4/3/89, DSJ, Created. + */ + if (Message != NULL) { + cprintf ("\nError: %s!\n", Message); + } + + if (CurrentTrapDepth <= 0) { + cprintf ("\nFatal error: No error trap defined!\n"); + + /* SPC 20/4/94 + There used to be a call to abort() here. I've changed it to call into the + C++ error code to generate a meaningful status code + */ + signal_termination_handler(Error); + } + + if (ProcTrapStack[CurrentTrapDepth - 1] != DO_NOTHING) + (*ProcTrapStack[CurrentTrapDepth - 1]) (); + + longjmp (ErrorTrapStack[CurrentTrapDepth - 1], 1); + assert(FALSE); +} /* DoError */ + + +/**---------------------------------------------------------------------------- + Private Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +jmp_buf &PushErrorTrap(VOID_PROC Procedure) { +/* + ** Parameters: + ** Procedure trap procedure to execute + ** Globals: + ** ErrorTrapStack stack of error traps + ** CurrentTrapDepth number of traps on the stack + ** Operation: + ** This routine pushes a new error trap onto the top of + ** the error trap stack. This new error trap can then be + ** used in a call to setjmp. This trap is then in effect + ** until ReleaseErrorTrap is called. WARNING: a procedure + ** that calls PushErrorTrap should never exit before calling + ** ReleaseErrorTrap. + ** Return: + ** Pointer to a new error trap buffer + ** Exceptions: + ** Traps an error if the error trap stack is already full + ** History: + ** 3/17/89, DSJ, Created. + ** 9/12/90, DSJ, Added trap procedure parameter. + */ + if (CurrentTrapDepth >= MAXTRAPDEPTH) + DoError (ERRORTRAPDEPTH, "Error trap depth exceeded"); + ProcTrapStack[CurrentTrapDepth] = Procedure; + return ErrorTrapStack[CurrentTrapDepth++]; + +} /* PushErrorTrap */ diff --git a/cutil/danerror.h b/cutil/danerror.h new file mode 100644 index 0000000000..23cd014d65 --- /dev/null +++ b/cutil/danerror.h @@ -0,0 +1,41 @@ +/****************************************************************************** + ** 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 +----------------------------------------------------------------------------**/ +#include + +#define SetErrorTrap(Proc) setjmp(PushErrorTrap(Proc)) +#define NOERROR 0 +#define DO_NOTHING 0 + +typedef int TRAPERROR; +typedef void (*VOID_PROC) (); + +/**---------------------------------------------------------------------------- + Public Function Prototypes +----------------------------------------------------------------------------**/ +void ReleaseErrorTrap(); + +void DoError(int Error, const char *Message); + +jmp_buf &PushErrorTrap(VOID_PROC Procedure); +#endif diff --git a/cutil/debug.cpp b/cutil/debug.cpp new file mode 100644 index 0000000000..70fde48f1d --- /dev/null +++ b/cutil/debug.cpp @@ -0,0 +1,97 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: debug.c (Formerly debug.c) + * Description: Combinatorial Splitter + * Author: Mark Seaman, OCR Technology + * Created: Thu Jul 27 08:59:01 1989 + * Modified: Tue Feb 19 10:34:36 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 +#include "debug.h" +#include "callcpp.h" + +/*---------------------------------------------------------------------- + V a r i a b l e s +----------------------------------------------------------------------*/ +MENU_ITEM menu_table[NUM_MENUS][NUM_MENU_ITEMS]; + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +/********************************************************************** + * set_float_value + * + * Set the value of a floating point variable from the break handler. + **********************************************************************/ +int set_float_value(const char *message, float *variable) { + char this_string[CHARS_PER_LINE]; + + cprintf ("%s (%7.5f) ? ", message, *variable); + fflush(stdout); + + if (fgets (this_string, CHARS_PER_LINE, stdin) == NULL) + return (1); + + if (fgets (this_string, CHARS_PER_LINE, stdin) != NULL) { + sscanf (this_string, "%f", variable); + cprintf ("%s = %7.5f\n", message, *variable); + } + return (1); +} + + +/********************************************************************** + * set_int_value + * + * Set the value of a floating point variable from the break handler. + **********************************************************************/ +int set_int_value(const char *message, int *variable) { + char this_string[CHARS_PER_LINE]; + + cprintf ("%s (%d) ? ", message, *variable); + fflush(stdout); + + if (fgets (this_string, CHARS_PER_LINE, stdin) == NULL) + return (1); + + if (fgets (this_string, CHARS_PER_LINE, stdin) != NULL) { + sscanf (this_string, "%d", variable); + cprintf ("%s = %d\n", message, *variable); + } + return (1); +} + + +/********************************************************************** + * make_menu_item + * + * Create an entry in the menu handler table that will create a menu + * entry. When this entry is selected it will invoke the requested + * function. + **********************************************************************/ +void make_menu_item(int menu, + int menu_item, + const char *menu_string, + int_void menu_funct) { + menu_table[menu][menu_item].menu_string = menu_string; + menu_table[menu][menu_item].menu_function = (void_proc) menu_funct; +} diff --git a/cutil/debug.h b/cutil/debug.h new file mode 100644 index 0000000000..7905c9fa84 --- /dev/null +++ b/cutil/debug.h @@ -0,0 +1,348 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: debug.h (Formerly debug.h) + * Description: Combinatorial Splitter + * Author: Mark Seaman, OCR Technology + * Created: Thu Jul 27 08:59:01 1989 + * Modified: Wed Feb 27 14:38:16 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. + * + *********************************************************************************/ +/* To hide variable names for relase to UNLV DONT put the variable names in + the executable: Toggle be either defining or not defining SECURE_NAMES + This stage prevents the menu construction*/ +/* #define SECURE_NAMES done in secnames.h when necessary */ + +#ifndef DEBUG_H +#define DEBUG_H + +#include "variables.h" +#include "callcpp.h" +#include + +/*---------------------------------------------------------------------- + T y p e s +----------------------------------------------------------------------*/ +typedef struct +{ + const char *menu_string; + void_proc menu_function; +} MENU_ITEM; + +/*---------------------------------------------------------------------- + V a r i a b l e s +----------------------------------------------------------------------*/ +#define NUM_MENUS 30 +#define NUM_MENU_ITEMS 30 +extern MENU_ITEM menu_table[NUM_MENUS][NUM_MENU_ITEMS]; + +/*---------------------------------------------------------------------- + M a c r o s +----------------------------------------------------------------------*/ +/********************************************************************** + * float_value + * + * Template procedures to set a floating point value of a variable. + **********************************************************************/ + +#define float_value(proc,string,variable,default) \ + \ +float variable = default; \ + \ +int proc () \ +{ \ + return (set_float_value (string, &variable)); \ +} \ + + +/********************************************************************** + * handle_menu_x + * + * Create a procedure to handle menu items. + **********************************************************************/ + +#define handle_menu(menu,handle_menu_x) \ + \ +int handle_menu_x () \ +{ \ + int x; \ + cprintf ("\t 0. Continue\n"); \ + for (x = 0; x < NUM_MENU_ITEMS; x++) { \ + if (menu_table[menu][x].menu_string) \ + cprintf ("\t%2d. %s\n", x, menu_table[menu][x].menu_string); \ + } \ + \ + scanf ("%d", &x); \ + \ + if (x == 0) return (0); \ + if ((0 < x && x < NUM_MENU_ITEMS) && \ + (menu_table[menu][x].menu_function)) { \ + (*menu_table[menu][x].menu_function) (); \ + return (1); \ + } \ + else { \ + cprintf ("Bad menu selection"); \ + return (0); \ + } \ +} \ + + +/********************************************************************** + * int_value + * + * Template procedures to set a floating point value of a variable. + **********************************************************************/ + +#define int_value(proc,string,variable,default) \ + \ +int variable = default; \ + \ +int proc () \ +{ \ + return (set_int_value (string, &variable)); \ +} \ + + +/********************************************************************** + * toggle_value + * + * Template procedures to toggle the value of a variable. + **********************************************************************/ + +#ifdef SECURE_NAMES +#define toggle_value(proc,string,variable,default) \ + \ +int variable = default; \ + \ +int proc () \ +{ \ + if (variable) { \ + variable = 0; \ + } \ + else { \ + variable = 1; \ + } \ + return (1); \ +} \ + +#else + +#define toggle_value(proc,string,variable,default) \ + \ +int variable = default; \ + \ +int proc () \ +{ \ + if (variable) { \ + cprintf( "%s is OFF\n", string); \ + variable = 0; \ + } \ + else { \ + cprintf( "%s is ON\n", string); \ + variable = 1; \ + } \ + return (1); \ +} \ + +#endif + +/********************************************************************** + * make_float_const + * + * Create a constant with a config file reader + **********************************************************************/ + +#define make_float_const(name,default,installer) \ + \ +float name = default; \ + \ +void installer () \ +{ \ +float_variable (name, #name, default); \ +} \ + + +/********************************************************************** + * make_int_const + * + * Create a constant with a config file reader + **********************************************************************/ + +#define make_int_const(name,default,installer) \ + \ +int name = default; \ + \ +void installer () \ +{ \ +int_variable (name, #name, default); \ +} \ + + +/********************************************************************** + * make_toggle_const + * + * Create a constant with a config file reader + **********************************************************************/ + +#define make_toggle_const(name,default,installer) \ + \ +int name = default; \ + \ +void installer () \ +{ \ +int_variable (name, #name, default); \ +} \ + + +#ifdef SECURE_NAMES +/********************************************************************** + * make_float_var + * + * Create a variable with a config file reader and a menu handler. + **********************************************************************/ + +#define make_float_var(name,default,installer,menu,menuitem,menufunct,menustring) \ + \ +float_value (menufunct, "", name, default); \ + \ +void installer () \ +{ \ +float_variable (name, #name, default); \ +} \ + + +/********************************************************************** + * make_int_var + * + * Create a variable with a config file reader and a menu handler. + **********************************************************************/ + +#define make_int_var(name,default,installer,menu,menuitem,menufunct,menustring) \ + \ +int_value (menufunct, "", name, default); \ + \ +void installer () \ +{ \ +int_variable (name, #name, default); \ +} \ + + +/********************************************************************** + * make_toggle_var + * + * Create a variable with a config file reader and a menu handler. + **********************************************************************/ + +#define make_toggle_var(name,default,installer,menu,menuitem,menufunct,menustring) \ + \ +toggle_value (menufunct, "", name, default); \ + \ +void installer () \ +{ \ +int_variable (name, #name, default); \ +} \ + +#else + +/********************************************************************** + * make_float_var + * + * Create a variable with a config file reader and a menu handler. + **********************************************************************/ + +#define make_float_var(name,default,installer,menu,menuitem,menufunct,menustring) \ + \ +float_value (menufunct, menustring, name, default); \ + \ +void installer () \ +{ \ +float_variable (name, #name, default); \ +make_menu_item (menu, menuitem, menustring, menufunct); \ +} \ + + +/********************************************************************** + * make_int_var + * + * Create a variable with a config file reader and a menu handler. + **********************************************************************/ + +#define make_int_var(name,default,installer,menu,menuitem,menufunct,menustring) \ + \ +int_value (menufunct, menustring, name, default); \ + \ +void installer () \ +{ \ +int_variable (name, #name, default); \ +make_menu_item (menu, menuitem, menustring, menufunct); \ +} \ + + +/********************************************************************** + * make_toggle_var + * + * Create a variable with a config file reader and a menu handler. + **********************************************************************/ + +#define make_toggle_var(name,default,installer,menu,menuitem,menufunct,menustring) \ + \ +toggle_value (menufunct, menustring, name, default); \ + \ +void installer () \ +{ \ +int_variable (name, #name, default); \ +make_menu_item (menu, menuitem, menustring, menufunct); \ +} \ + +#endif + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +int set_float_value(const char *message, float *variable); + +int set_int_value(const char *message, int *variable); + +void make_menu_item(int menu, + int menu_item, + const char *menu_string, + int_void menu_funct); + +/* +#if defined(__STDC__) || defined(__cplusplus) +# define _ARGS(s) s +#else +# define _ARGS(s) () +#endif + +int set_float_value +_ARGS((char *message, + float *variable)); + +int set_int_value +_ARGS((char *message, + int *variable)); + +void make_menu_item +_ARGS((int menu, + int menu_item, + char *menu_string, + int_proc menu_funct)); + +#undef _ARGS +*/ +#endif diff --git a/cutil/efio.cpp b/cutil/efio.cpp new file mode 100644 index 0000000000..20daae927d --- /dev/null +++ b/cutil/efio.cpp @@ -0,0 +1,62 @@ +/****************************************************************************** + ** 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 +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +FILE *Efopen(const char *Name, const char *Mode) { +/* + ** Parameters: + ** Name name of file to be opened + ** Mode mode to be used to open file + ** Globals: + ** None + ** Operation: + ** 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. + ** Return: + ** Pointer to open file. + ** Exceptions: + ** FOPENERROR unable to open specified file + ** History: + ** 5/21/89, DSJ, Created. + */ + 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 */ diff --git a/cutil/efio.h b/cutil/efio.h new file mode 100644 index 0000000000..ad706efa4d --- /dev/null +++ b/cutil/efio.h @@ -0,0 +1,32 @@ +/****************************************************************************** + ** 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 diff --git a/cutil/emalloc.cpp b/cutil/emalloc.cpp new file mode 100644 index 0000000000..a9679c95b0 --- /dev/null +++ b/cutil/emalloc.cpp @@ -0,0 +1,91 @@ +/****************************************************************************** + ** 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 +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +void *Emalloc(size_t Size) { +/* + ** Parameters: + ** Size + number of bytes of memory to be allocated +** Globals: none +** Operation: +** 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. +** Return: Pointer to allocated memory. +** Exceptions: NOTENOUGHMEMORY + unable to allocate Size bytes +** ILLEGALMALLOCREQUEST + negative or zero request size +** History: 4/3/89, DSJ, Created. +*/ + 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, size_t 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 */ diff --git a/cutil/emalloc.h b/cutil/emalloc.h new file mode 100644 index 0000000000..c6dbb37463 --- /dev/null +++ b/cutil/emalloc.h @@ -0,0 +1,72 @@ +/****************************************************************************** + ** 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(size_t Size); + +void *Erealloc(void *ptr, size_t size); + +void Efree(void *ptr); + +/* +#if defined(__STDC__) || defined(__cplusplus) +# define _ARGS(s) s +#else +# define _ARGS(s) () +#endif*/ + +/* emalloc.c +void *Emalloc + _ARGS((size_t Size)); + +void *Erealloc + _ARGS((void *ptr, + size_t size)); + +void Efree + _ARGS((void *ptr)); + +#undef _ARGS +*/ + +/**---------------------------------------------------------------------------- + Global Data Definitions and Declarations +----------------------------------------------------------------------------**/ + +//extern void* c_alloc_struct(); +//#define alloc_struct c_alloc_struct +/*extern void c_free_struct(void* + deadstruct, //structure to free +INT32 count, //no of bytes +const char* name //class name +);*/ +//#define free_struct c_free_struct +#endif diff --git a/cutil/freelist.cpp b/cutil/freelist.cpp new file mode 100644 index 0000000000..72b46de267 --- /dev/null +++ b/cutil/freelist.cpp @@ -0,0 +1,85 @@ +/************************************************************************** + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT 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 "danerror.h" +#include "callcpp.h" + +#include + +static int mem_alloc_counter = 0; + +/********************************************************************** + * memalloc_p + * + * Memory allocator with protection. + **********************************************************************/ +int *memalloc_p(int size) { + mem_alloc_counter++; + if (!size) + DoError (0, "Allocation of 0 bytes"); + return ((int *) c_alloc_mem_p (size)); +} + + +/********************************************************************** + * memalloc + * + * Memory allocator with protection. + **********************************************************************/ +int *memalloc(int size) { + mem_alloc_counter++; + return ((int *) c_alloc_mem (size)); +} + + +/********************************************************************** + * memrealloc + * + * Memory allocator with protection. + **********************************************************************/ +int *memrealloc(void *ptr, int size, int oldsize) { + int shiftsize; + int *newbuf; + + shiftsize = size > oldsize ? oldsize : size; + newbuf = (int *) c_alloc_mem (size); + memcpy(newbuf, ptr, shiftsize); + c_free_mem(ptr); + return newbuf; +} + + +/********************************************************************** + * memfree + * + * Memory allocator with protection. + **********************************************************************/ +void memfree(void *element) { + if (element) { + c_free_mem(element); + mem_alloc_counter--; + } + else { + cprintf ("%d MEM_ALLOC's used\n", mem_alloc_counter); + DoError (0, "Memfree of NULL pointer"); + } +} + + +/********************************************************************** + * mem_tidy + * + * Do nothing. + **********************************************************************/ +void mem_tidy(int level) { + c_check_mem ("Old tidy", level); +} diff --git a/cutil/freelist.h b/cutil/freelist.h new file mode 100644 index 0000000000..159c10d45e --- /dev/null +++ b/cutil/freelist.h @@ -0,0 +1,69 @@ +/* -*-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_p(int size); + +int *memalloc(int size); + +int *memrealloc(void *ptr, int size, int oldsize); + +void memfree(void *element); + +void mem_tidy(int level); + +/* +#if defined(__STDC__) || defined(__cplusplus) +# define _ARGS(s) s +#else +# define _ARGS(s) () +#endif*/ + +/* freelist.c +int *memalloc_p + _ARGS((int size)); + +int *memalloc + _ARGS((int size)); + +void memfree + _ARGS((void *element)); + +void mem_tidy + _ARGS((void)); + +#undef _ARGS +*/ +#endif diff --git a/cutil/funcdefs.h b/cutil/funcdefs.h new file mode 100644 index 0000000000..7426f5b115 --- /dev/null +++ b/cutil/funcdefs.h @@ -0,0 +1,55 @@ +/****************************************************************************** + ** Filename: funcdefs.h + ** Purpose: Definition of function types for passing as params. + ** Author: Dan Johnson + ** History: Fri Sep 14 10:04:47 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 FUNCDEFS_H +#define FUNCDEFS_H + +/**---------------------------------------------------------------------------- + Include Files and Type Defines +----------------------------------------------------------------------------**/ +#include "general.h" + +typedef INT8 (*INT8_FUNC) (); +typedef UINT8 (*UINT8_FUNC) (); +typedef INT16 (*INT16_FUNC) (); +typedef UINT16 (*UINT16_FUNC) (); +typedef INT32 (*INT32_FUNC) (); +typedef UINT32 (*UINT32_FUNC) (); +typedef FLOAT32 (*FLOAT32_FUNC) (); +typedef FLOAT64 (*FLOAT64_FUNC) (); +typedef PINT8 (*PINT8_FUNC) (); +typedef PUINT8 (*PUINT8_FUNC) (); +typedef PINT16 (*PINT16_FUNC) (); +typedef PUINT16 (*PUINT16_FUNC) (); +typedef PINT32 (*PINT32_FUNC) (); +typedef PUINT32 (*PUINT32_FUNC) (); +typedef PFLOAT32 (*PFLOAT32_FUNC) (); +typedef PFLOAT64 (*PFLOAT64_FUNC) (); + +typedef CHAR (*CHAR_FUNC) (); +typedef BOOL8 (*BOOL8_FUNC) (); +typedef int (*INT_FUNC) (); +typedef void (*VOID_FUNC) (); + +/**---------------------------------------------------------------------------- + Public Function Prototypes +----------------------------------------------------------------------------**/ + +/**---------------------------------------------------------------------------- + Global Data Definitions and Declarations +----------------------------------------------------------------------------**/ +#endif diff --git a/cutil/general.h b/cutil/general.h new file mode 100644 index 0000000000..2c51d36a45 --- /dev/null +++ b/cutil/general.h @@ -0,0 +1,33 @@ +/****************************************************************************** + ** Filename: General.h + ** Purpose: this is the system independent typedefs and defines + ** Author: Mike Niquette / Dan Johnson + ** History: Creation Date: 09/13/1988, MLN + ** Added UNIX: 11/10/88, DSJ + ** Changed name to General.h 11/24/88, DSJ + ** Added BOOL, CHAR, TRUE, FALSE, 11/24/88, DSJ + ** STATUS + ** + ** (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 GENERAL_H +#define GENERAL_H + +#include "host.h" + +typedef char CHAR; +typedef int STATUS; + +#ifndef NULL +#define NULL 0 +#endif +#endif diff --git a/cutil/globals.cpp b/cutil/globals.cpp new file mode 100644 index 0000000000..22f8d96a49 --- /dev/null +++ b/cutil/globals.cpp @@ -0,0 +1,65 @@ +/* +################################################################################ +# +# File: globals.c +# Description: Global flag definitions +# Author: Mark Seaman, OCR Technology +# Created: Thu Oct 19 16:51:26 1989 +# Modified: Fri Jan 26 13:16:37 1990 (Mark Seaman) marks@hpgrlt +# Language: Text +# 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. +# +################################################################################ +**************************************************************************/ +#include +#include "globals.h" + +/* This file contains the global declarations used by all demonstrator files*/ + +//IMAGE info; /*image info record*/ +TBLOB *pageblobs; /*first blob on page */ +TEXTBLOCK *pageblocks; /*first block on page */ +char classes[CLASSIZE][CLASSLENGTH]; + /*class definitions */ + /*indices to to_classes */ + +int resolution; /*scanner res in dpi */ +int acts[MAXPROC]; /*action flags */ +int debugs[MAXPROC]; /*debug flags */ +int plots[MAXPROC]; /*plot flags */ + +/*---------------------------------------------------------------------- + V a r i a b l e s +----------------------------------------------------------------------*/ +int corners[4]; /*corners of scan window */ + +char imagefile[FILENAMESIZE]; /*image file name */ +char directory[FILENAMESIZE]; /* main directory */ +char *debugfile; /* debug file name */ + +int plots_fx; +int plots_ocr; + +int debugs_fx; +int debugs_ocr; + +int acts_fx; +int acts_ocr; + +char *demodir; /*demo home directory */ + +int edgefd; /*edges window */ +int debugfd; /*debug window fd */ +FILE *debugfp; /*debug log file */ diff --git a/cutil/globals.h b/cutil/globals.h new file mode 100644 index 0000000000..7f86be89af --- /dev/null +++ b/cutil/globals.h @@ -0,0 +1,65 @@ +/* -*-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 "tessclas.h" +#include "const.h" + +#include + +/*---------------------------------------------------------------------- + V a r i a b l e s +----------------------------------------------------------------------*/ +//extern IMAGE info; /*image info record*/ +extern TBLOB *pageblobs; /*first blob on page */ +extern TEXTBLOCK *pageblocks; /*first block on page */ + /*class definitions */ +extern char classes[CLASSIZE][CLASSLENGTH]; +extern int resolution; /*scanner res in dpi */ +extern int acts[MAXPROC]; /*action flags */ +extern int debugs[MAXPROC]; /*debug flags */ +extern int plots[MAXPROC]; /*plot flags */ +extern int corners[4]; /*corners of scan window */ +extern int optind; /*option index */ +extern char *optarg; /*option argument */ + /*image file name */ +extern char imagefile[FILENAMESIZE]; + /* main directory */ +extern char directory[FILENAMESIZE]; +extern char *debugfile; /* debug file name */ + +extern int plots_fx; +extern int plots_ocr; + +extern int debugs_fx; +extern int debugs_ocr; + +extern int acts_fx; +extern int acts_ocr; + +extern char *demodir; +extern FILE *debugfp; /*debug log file */ +#endif diff --git a/cutil/listio.cpp b/cutil/listio.cpp new file mode 100644 index 0000000000..5c9894dffe --- /dev/null +++ b/cutil/listio.cpp @@ -0,0 +1,68 @@ +/* -*-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; + char *chopAt250(); + + if ((infile = open_file (filename, "r")) == NULL) + return (NIL); + + list = NIL; + 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)); +} diff --git a/cutil/listio.h b/cutil/listio.h new file mode 100644 index 0000000000..e758c9bcb2 --- /dev/null +++ b/cutil/listio.h @@ -0,0 +1,43 @@ +/* -*-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 Funtion Prototypes +--------------------------------------------------------------------------*/ +LIST read_list(const char *filename); +#endif diff --git a/cutil/minmax.h b/cutil/minmax.h new file mode 100644 index 0000000000..c1d319a756 --- /dev/null +++ b/cutil/minmax.h @@ -0,0 +1,40 @@ +/****************************************************************************** + ** Filename: minmax.h + ** Purpose: Utility macros for min and max functions. + ** Author: Dan Johnson + ** History: Wed Oct 17 09:54: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 MINMAX_H +#define MINMAX_H + +/**---------------------------------------------------------------------------- + Include Files and Type Defines +----------------------------------------------------------------------------**/ + +/**---------------------------------------------------------------------------- + Public Function Prototypes +----------------------------------------------------------------------------**/ + +#ifndef MAX +#define MAX(x,y) (((x)>=(y))?(x):(y)) +#endif + +#ifndef MIN +#define MIN(x,y) (((x)<=(y))?(x):(y)) +#endif + +/**---------------------------------------------------------------------------- + Global Data Definitions and Declarations +----------------------------------------------------------------------------**/ +#endif diff --git a/cutil/oldheap.cpp b/cutil/oldheap.cpp new file mode 100644 index 0000000000..abddf0da19 --- /dev/null +++ b/cutil/oldheap.cpp @@ -0,0 +1,337 @@ +/****************************************************************************** + ** Filename: heap.c + ** Purpose: Routines for managing heaps (smallest at root) + ** Author: Dan Johnson + ** History: 3/13/89, DSJ, Created. + ** + ** (c) Copyright Hewlett-Packard Company, 1988. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + ******************************************************************************/ +/**---------------------------------------------------------------------------- + Include Files and Type Defines +----------------------------------------------------------------------------**/ +#include "oldheap.h" +#include "freelist.h" +#include "danerror.h" +#include "emalloc.h" +#include + +#define FATHER(N) ((N)>>1) +#define LEFTSON(N) ((N)<<1) +#define RIGHTSON(N) ((N)<<1 + 1) + +/**---------------------------------------------------------------------------- + Public Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +HEAP *MakeHeap(int Size) { +/* + ** Parameters: + ** Size maximum number of entries in the heap + ** Globals: + ** None + ** Operation: + ** This routine creates and initializes a new heap data + ** structure containing Size elements. In actuality, Size + 1 + ** elements are allocated. The first element, element 0, is + ** unused, this makes the index arithmetic easier. + ** Return: + ** Pointer to the new heap. + ** Exceptions: + ** None + ** History: + ** 3/13/89, DSJ, Created. + */ + HEAP *NewHeap; + + NewHeap = (HEAP *) Emalloc (sizeof (HEAP) + Size * sizeof (HEAPENTRY)); + + NewHeap->Size = Size; + NewHeap->FirstFree = 1; + return (NewHeap); +} /* MakeHeap */ + + +/*---------------------------------------------------------------------------*/ +int HeapPop(HEAP *Heap, FLOAT32 *Key, void *out_ptr) { +/* + ** Parameters: + ** Heap ptr to heap whose top is to be removed and returned + ** Key place to put key of top heap item + ** Data place to put data of top heap item + ** Globals: + ** None + ** Operation: + ** This routine removes the top item on the heap and places + ** its contents into Key and Data. + ** Return: + ** OK if top entry returned, EMPTY if heap is empty + ** Exceptions: + ** None + ** History: + ** 5/10/91, DSJ, Created (Modified from GetTopOfHeap). + */ + INT32 Hole; + FLOAT32 HoleKey; + INT32 Son; + void **Data = (void **) out_ptr; + + if (Heap->FirstFree <= 1) + return (EMPTY); + + *Key = Heap->Entry[1].Key; + *Data = Heap->Entry[1].Data; + + Heap->FirstFree--; + + /* imagine the hole at the root is filled with the last entry in the heap */ + HoleKey = Heap->Entry[Heap->FirstFree].Key; + Hole = 1; + + /* while hole has 2 sons */ + while ((Son = LEFTSON (Hole)) < Heap->FirstFree) { + /* find the son with the smallest key */ + if (Heap->Entry[Son].Key > Heap->Entry[Son + 1].Key) + Son++; + + /* if key for hole is greater than key for son, sift hole down */ + if (HoleKey > Heap->Entry[Son].Key) { + Heap->Entry[Hole].Key = Heap->Entry[Son].Key; + Heap->Entry[Hole].Data = Heap->Entry[Son].Data; + Hole = Son; + } + else + break; + } + Heap->Entry[Hole].Key = HoleKey; + Heap->Entry[Hole].Data = Heap->Entry[Heap->FirstFree].Data; + return (OK); +} /* HeapPop */ + + +/********************************************************************** + * HeapPopWorst + * + * Remove the largest item from the heap. + **********************************************************************/ +int HeapPopWorst(HEAP *Heap, FLOAT32 *Key, void *out_ptr) { +/* + ** Parameters: + ** Heap ptr to heap whose top is to be removed and returned + ** Key place to put key of top heap item + ** Data place to put data of top heap item + */ + INT32 Index; /*current index */ + INT32 Hole; + FLOAT32 HoleKey; + INT32 Father; + void *HoleData; + void **Data = (void **) out_ptr; + + if (Heap->FirstFree <= 1) + return (EMPTY); + + HoleKey = Heap->Entry[1].Key; + Hole = 1; + Heap->FirstFree--; + for (Index = Heap->FirstFree, Father = FATHER (Index); Index > Father; + Index--) + if (Heap->Entry[Index].Key > HoleKey) { + /*find biggest */ + HoleKey = Heap->Entry[Index].Key; + Hole = Index; + } + *Key = HoleKey; + *Data = Heap->Entry[Hole].Data; + + HoleKey = Heap->Entry[Heap->FirstFree].Key; + Heap->Entry[Hole].Key = HoleKey; + HoleData = Heap->Entry[Heap->FirstFree].Data; + Heap->Entry[Hole].Data = HoleData; + + /* now sift last entry to its rightful place */ + Father = FATHER (Hole); /*father of hole */ + while (Hole > 1 && Heap->Entry[Father].Key > HoleKey) { + /*swap entries */ + Heap->Entry[Hole].Key = Heap->Entry[Father].Key; + Heap->Entry[Hole].Data = Heap->Entry[Father].Data; + Heap->Entry[Father].Data = HoleData; + Heap->Entry[Father].Key = HoleKey; + Hole = Father; + Father = FATHER (Hole); + } + return (OK); +} /* HeapPop */ + + +/*---------------------------------------------------------------------------*/ +void HeapPush(HEAP *Heap, FLOAT32 Key, void *Data) { +/* + ** Parameters: + ** Heap ptr to heap to store new item in + ** Key numeric key associated with new item + ** Data ptr to data contents of new item + ** Globals: + ** None + ** Operation: + ** This routine stores Data into Heap and associates it + ** with Key. The heap is + ** maintained in such a way that the item with the lowest key + ** is always at the top of the heap. + ** Return: + ** None + ** Exceptions: + ** HEAPFULL error if heap size is exceeded + ** History: + ** 5/10/91, DSJ, Created (Modified version of HeapStore). + */ + INT32 Item; + INT32 Father; + + if (Heap->FirstFree > Heap->Size) + DoError (HEAPFULL, "Heap size exceeded"); + + Item = Heap->FirstFree; + Heap->FirstFree++; + while (Item != 1) { + Father = FATHER (Item); + if (Heap->Entry[Father].Key > Key) { + Heap->Entry[Item].Key = Heap->Entry[Father].Key; + Heap->Entry[Item].Data = Heap->Entry[Father].Data; + Item = Father; + } + else + break; + } + Heap->Entry[Item].Key = Key; + Heap->Entry[Item].Data = Data; +} /* HeapPush */ + + +/*---------------------------------------------------------------------------*/ +void HeapStore(HEAP *Heap, HEAPENTRY *Entry) { +/* + ** Parameters: + ** Heap ptr to heap to store new item in + ** Entry ptr to item to be stored in Heap + ** Globals: + ** None + ** Operation: + ** This routine stores Entry into Heap. The heap is + ** maintained in such a way that the item with the lowest key + ** is always at the top of the heap. + ** Return: + ** None + ** Exceptions: + ** HEAPFULL error if heap size is exceeded + ** History: + ** 3/13/89, DSJ, Created. + */ + INT32 Item; + INT32 Father; + + if (Heap->FirstFree > Heap->Size) + DoError (HEAPFULL, "Heap size exceeded"); + + Item = Heap->FirstFree; + Heap->FirstFree++; + while (Item != 1) { + Father = FATHER (Item); + if (Heap->Entry[Father].Key > Entry->Key) { + Heap->Entry[Item].Key = Heap->Entry[Father].Key; + Heap->Entry[Item].Data = Heap->Entry[Father].Data; + Item = Father; + } + else + break; + } + Heap->Entry[Item].Key = Entry->Key; + Heap->Entry[Item].Data = Entry->Data; +} /* HeapStore */ + + +/*---------------------------------------------------------------------------*/ +int GetTopOfHeap(HEAP *Heap, HEAPENTRY *Entry) { +/* + ** Parameters: + ** Heap ptr to heap whose top is to be removed and returned + ** Entry ptr to heap entry to be filled with top entry on Heap + ** Globals: + ** None + ** Operation: + ** This routine removes the top item on the heap and copies its + ** contents into Entry. + ** Return: + ** OK if top entry returned, EMPTY if heap is empty + ** Exceptions: + ** None + ** History: + ** 3/13/89, DSJ, Created. + */ + INT32 Hole; + FLOAT32 HoleKey; + INT32 Son; + + if (Heap->FirstFree <= 1) + return (EMPTY); + + Entry->Key = Heap->Entry[1].Key; + Entry->Data = Heap->Entry[1].Data; + + Heap->FirstFree--; + + /* imagine the hole at the root is filled with the last entry in the heap */ + HoleKey = Heap->Entry[Heap->FirstFree].Key; + Hole = 1; + + /* while hole has 2 sons */ + while ((Son = LEFTSON (Hole)) < Heap->FirstFree) { + /* find the son with the smallest key */ + if (Heap->Entry[Son].Key > Heap->Entry[Son + 1].Key) + Son++; + + /* if key for hole is greater than key for son, sift hole down */ + if (HoleKey > Heap->Entry[Son].Key) { + Heap->Entry[Hole].Key = Heap->Entry[Son].Key; + Heap->Entry[Hole].Data = Heap->Entry[Son].Data; + Hole = Son; + } + else + break; + } + Heap->Entry[Hole].Key = HoleKey; + Heap->Entry[Hole].Data = Heap->Entry[Heap->FirstFree].Data; + return (OK); +} /* GetTopOfHeap */ + + +/*---------------------------------------------------------------------------*/ +void FreeHeapData(HEAP *Heap, void_dest destructor) { +/* + ** Parameters: + ** Heap heap whose data is to be freed + ** Deallocator function to be used to deallocate data + ** Globals: none + ** Operation: This routine is similar to FreeHeap in that it + ** deallocates the memory consumed by the heap. However, it + ** also calls Deallocator for each item in the heap so that + ** this data is also deallocated. + ** Return: none + ** Exceptions: none + ** History: Tue May 15 08:52:04 1990, DSJ, Created. + */ + HEAPENTRY Entry; + + while (GetTopOfHeap (Heap, &Entry) != EMPTY) + destructor (Entry.Data); + + FreeHeap(Heap); +} /* FreeHeapData */ diff --git a/cutil/oldheap.h b/cutil/oldheap.h new file mode 100644 index 0000000000..8a98d00a78 --- /dev/null +++ b/cutil/oldheap.h @@ -0,0 +1,126 @@ +/****************************************************************************** + ** Filename: heap.h + ** Purpose: Definition of heap access routines. + ** Author: Dan Johnson + ** History: 3/13/89, DSJ, Created. + ** + ** (c) Copyright Hewlett-Packard Company, 1988. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + ******************************************************************************/ +#ifndef HEAP_H +#define HEAP_H + +/**---------------------------------------------------------------------------- + Include Files and Type Defines +----------------------------------------------------------------------------**/ +#include "general.h" +#include "cutil.h" + +#define HEAPFULL 3000 + +#define OK 0 +#define EMPTY -1 + +typedef struct +{ + FLOAT32 Key; + void *Data; +} + + +HEAPENTRY; + +typedef struct +{ + INT32 Size; + INT32 FirstFree; + HEAPENTRY Entry[1]; +} + + +HEAP; + +/**---------------------------------------------------------------------------- + Macros +----------------------------------------------------------------------------**/ +#define FreeHeap(H) memfree(H) +#define MaxSizeOfHeap(H) (H->Size) +#define SizeOfHeap(H) (H->FirstFree - 1) +#define InitHeap(H) (H->FirstFree = 1) +#define HeapFull(H) ((H)->FirstFree > (H)->Size) +#define HeapEmpty(H) ((H)->FirstFree <= 1) + +/* macros for accessing elements in heap by index. The indicies vary from + 0 to SizeOfHeap-1. No bounds checking is done. Elements accessed in + this manner are in random order relative to the Key values. These + macros should never be used as the LHS of an assignment statement as this + will corrupt the heap.*/ +#define HeapKeyFor(H,E) ((H)->Entry[(E)+1].Key) +#define HeapDataFor(H,E) ((H)->Entry[(E)+1].Data) + +/**---------------------------------------------------------------------------- + Public Function Prototypes +----------------------------------------------------------------------------**/ +HEAP *MakeHeap(int Size); + +int HeapPop(HEAP *Heap, FLOAT32 *Key, void *out_ptr); + +int HeapPopWorst(HEAP *Heap, FLOAT32 *Key, void *out_ptr); + +void HeapPush(HEAP *Heap, FLOAT32 Key, void *Data); + +void HeapStore(HEAP *Heap, HEAPENTRY *Entry); + +int GetTopOfHeap(HEAP *Heap, HEAPENTRY *Entry); + +void FreeHeapData(HEAP *Heap, void_dest destructor); + +/* +#if defined(__STDC__) || defined(__cplusplus) +# define _ARGS(s) s +#else +# define _ARGS(s) () +#endif*/ + +/* heap.c +HEAP *MakeHeap + _ARGS((int Size)); + +int HeapPop + _ARGS((HEAP *Heap, + FLOAT32 *Key, + char **Data)); + +int HeapPopWorst + _ARGS((HEAP *Heap, + FLOAT32 *Key, + char **Data)); + +void HeapPush + _ARGS((HEAP *Heap, + FLOAT32 Key, + char *Data)); + +void HeapStore + _ARGS((HEAP *Heap, + HEAPENTRY *Entry)); + +int GetTopOfHeap + _ARGS((HEAP *Heap, + HEAPENTRY *Entry)); + +void FreeHeapData + _ARGS((HEAP *Heap, + void (*Deallocator )())); + +#undef _ARGS +*/ +#endif diff --git a/cutil/oldlist.cpp b/cutil/oldlist.cpp new file mode 100644 index 0000000000..3e5adc7d7b --- /dev/null +++ b/cutil/oldlist.cpp @@ -0,0 +1,393 @@ +/* -*-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'. + All of these routines check for the NIL 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 to access the head name = (char *) first (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 to access the head name = (char *) first (l); + + To implement LISP like functions use: + + first CAR x = (int) first (l); + rest CDR l = 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 (push (a, b)) + b = rest (push (a, b)) + a = push (pop (a), a)) For all a <> NIL + 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 (x)) +#define next_one(l) l = 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 last_one = NIL; + + if (is_equal == NULL) + is_equal = is_same; + + while (list != NIL) { + if (!(*is_equal) (first (list), key)) { + if (last_one == NIL) { + last_one = list; + list = rest (list); + result = last_one; + set_rest(last_one, NIL); + } + else { + set_rest(last_one, list); + last_one = list; + list = rest (list); + set_rest(last_one, NIL); + } + } + 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) { + next = rest (list); + free_cell(list); + list = next; + } + return (NIL); +} + + +/********************************************************************** + * 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) { + (*destructor) (first (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) { + element = push (NIL, node); + set_rest (element, rest (list)); + set_rest(list, element); + node = first (list); + list->node = first (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) + 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 (rest (var_list) != NIL) + var_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 = rest (list); + + if (list != NIL) { + 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) { + t = last (list); + t->next = push (NIL, item); + return (list); + } + else + return (push (NIL, 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; + + 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 (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 if not found. Return the list starting from + * the item if found. The compare routine "is_equal" is passed in as + * the third paramter 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 (list), key)) + return (list); + return (NIL); +} diff --git a/cutil/oldlist.h b/cutil/oldlist.h new file mode 100644 index 0000000000..68692b206f --- /dev/null +++ b/cutil/oldlist.h @@ -0,0 +1,350 @@ +/* -*-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 - Macro to return the first list node (not the cell). + * 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 - 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) + * 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" + +/*---------------------------------------------------------------------- + T y p e s +----------------------------------------------------------------------*/ +#define NIL (LIST) 0 +typedef struct list_rec +{ + struct list_rec *node; + struct list_rec *next; +} _LIST_; +typedef _LIST_ *LIST; + +/*---------------------------------------------------------------------- + M a c r o s +----------------------------------------------------------------------*/ +/* Predefinitions */ +#define rest(l) ((l) ? (l)->next : NIL) +#define first(l) ((l) ? (l)->node : NIL) + +/********************************************************************** + * 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(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. + **********************************************************************/ + +#define iterate(l) \ +for (; (l) != NIL; (l) = 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)=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(l) first (rest (l)) + **********************************************************************/ + +#define second(l) \ +first (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 (rest (rest (l))) + **********************************************************************/ + +#define third(l) \ +first (rest (rest (l))) + +/*---------------------------------------------------------------------- + Public Funtion Prototypes +----------------------------------------------------------------------*/ +int count(LIST var_list); + +LIST delete_d(LIST list, void *key, int_compare 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); + +/* +#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 diff --git a/cutil/structures.cpp b/cutil/structures.cpp new file mode 100644 index 0000000000..d2d155c7cf --- /dev/null +++ b/cutil/structures.cpp @@ -0,0 +1,66 @@ +/* -*-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 "callcpp.h" + +#include + +/*---------------------------------------------------------------------- + M a c r o s +----------------------------------------------------------------------*/ +#define BLOBBLOCK 64 /*no allocated together */ +#define OUTLINEBLOCK 300 /*of each type */ +#define NODEBLOCK 36 /*blocks all about 1K bytes */ +#define EDGEPTBLOCK 50 +#define WERDBLOCK 42 +#define LISTBLOCK 300 + +/*---------------------------------------------------------------------- + V a r i a b l e s +----------------------------------------------------------------------*/ +int structblockcount = 0; +void_void memory_print_functions[NUM_DATA_TYPES]; +int max_data_types = 0; + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +makestructure (newword, oldword, printword, TWERD, +freeword, WERDBLOCK, "TWERD", wordcount) +makestructure (newoutline, oldoutline, printol, TESSLINE, +freeoutline, OUTLINEBLOCK, "TESSLINE", outlinecount); + +makestructure (new_cell, free_cell, printcell, _LIST_, +freelist, LISTBLOCK, "LIST", listcount); + +newstructure (newblob, TBLOB, freeblob, BLOBBLOCK, "newblob", blobcount); +oldstructure (oldblob, TBLOB, freeblob, "BLOB", blobcount); + +newstructure (newedgept, EDGEPT, freeedgept, EDGEPTBLOCK, "newedgept", +edgeptcount); +oldstructure (oldedgept, EDGEPT, freeedgept, "EDGEPT", edgeptcount); diff --git a/cutil/structures.h b/cutil/structures.h new file mode 100644 index 0000000000..ef666fc46f --- /dev/null +++ b/cutil/structures.h @@ -0,0 +1,112 @@ +/* -*-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 "tessclas.h" +#include "oldlist.h" +#include "freelist.h" +#include "danerror.h" + +#define NUM_DATA_TYPES 20 + +extern int max_data_types; +extern void_void memory_print_functions[NUM_DATA_TYPES]; + +/*---------------------------------------------------------------------- + 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,print,type,nextfree,blocksize,typestring,usecount) \ +type *newfunc() \ +{ \ + return new type; \ +} \ + \ + \ + \ +void old(type* deadelement) \ +{ \ + delete deadelement; \ +} \ + + +/********************************************************************** + * newstructure + * + * Allocate a chunk of memory for a particular data type. + **********************************************************************/ + +#define newstructure(name,type,nextfree,blocksize,errorstring,usecount)\ +type *name() /*returns a new type*/\ +{\ + return new type;\ +} + +/********************************************************************** + * oldstructure + * + * Returns a structure to the freelist + **********************************************************************/ + +#define oldstructure(name,type,nextfree,stringtype,usecount)\ +\ +type *name(type* deadelement)\ +{\ + type *returnelement; /*return next ptr*/\ +\ + returnelement=deadelement->next; /*return link*/\ + delete deadelement; \ + return returnelement;\ +} + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +extern TBLOB *newblob(); +extern TBLOB *oldblob(TBLOB *); + +extern TESSLINE *newoutline(); +extern void oldoutline(TESSLINE *); + +extern EDGEPT *newedgept(); +extern EDGEPT *oldedgept(EDGEPT *); + +extern TWERD *newword(); +extern void oldword(TWERD *); + +extern LIST new_cell(); +extern void free_cell(LIST); +#endif diff --git a/cutil/tessarray.cpp b/cutil/tessarray.cpp new file mode 100644 index 0000000000..944c42596c --- /dev/null +++ b/cutil/tessarray.cpp @@ -0,0 +1,115 @@ +/* -*-C-*- +################################################################################ +# +# File: array.c +# Description: Dynamic Array of Strings +# Author: Mark Seaman, Software Productivity +# Created: Thu Jul 23 13:24:09 1987 +# Modified: Wed Mar 6 15:18:33 1991 (Mark Seaman) marks@hpgrlt +# Language: C +# Package: N/A +# Status: Reusable Software Component +# +# (c) Copyright 1987, Hewlett-Packard Company. +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** http://www.apache.org/licenses/LICENSE-2.0 +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +# +################################################################################ + +This file contains the implentations of a set of dynamic array of string +manipulation routines. For the interface definitions and documentation +of these routines see the file "das.h". + +***************************************************************************/ + +#include "tessarray.h" +#include "callcpp.h" +#include "freelist.h" + +#include +#include +#ifdef __MSW32__ +#include +#endif +#include +#if MAC_OR_DOS +#include +#endif + +/********************************************************************** + * array_insert + * + * Insert a data element into a particular spot in the array. Move all + * the elements in the array (past that spot) down one to make room for + * the new element. + **********************************************************************/ +ARRAY array_insert(ARRAY array, int index, void *value) { + int x; + + array = array_push (array, NULL); + for (x = array_count (array) - 1; x > index; x--) + array_value (array, x) = array_value (array, x - 1); + array_value (array, index) = value; + return (array); +} + + +/********************************************************************** + * array_new + * + * Create a new array with a certain number of elements. If the number + * of elements requested is 0 then the default number will be used. + **********************************************************************/ +ARRAY array_new(int num) { + ARRAY temp; + int x; + + if (num == 0) + num = DEFAULT_SIZE; + temp = (ARRAY) memalloc ((num - 2) * sizeof (char *) + + sizeof (struct array_record)); + if (!temp) { + cprintf ("error: Out of memory in array_new\n"); + exit (1); //?err_exit (); + } + array_count (temp) = 0; + array_limit (temp) = num; + for (x = 0; x < num; x++) + array_value (temp, x) = (char *) 0; + return (temp); +} + + +/********************************************************************** + * array_push + * + * Add a new element onto the top of the array. If there is not room + * more room is made by "realloc"ing the array. This means that the + * new array location may change. All previous references to its old + * location may no longer be valid. + **********************************************************************/ +ARRAY array_push(ARRAY array, void *value) { + if (array_count (array) == array_limit (array)) { + array = (ARRAY) memrealloc (array, (array_limit (array) * 2 - 2) * + sizeof (char *) + + sizeof (struct array_record), + (array_limit (array) - + 2) * sizeof (char *) + + sizeof (struct array_record)); + if (!array) { + cprintf ("error: Out of memory in array_push\n"); + exit (1); //?err_exit (); + } + array_limit (array) *= 2; + } + array_count (array)++; + array_top (array) = value; + return (array); +} diff --git a/cutil/tessarray.h b/cutil/tessarray.h new file mode 100644 index 0000000000..8b5a38cead --- /dev/null +++ b/cutil/tessarray.h @@ -0,0 +1,166 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: array.h (Formerly array.h) + * Description: Dynamic Array of String + * Author: Mark Seaman, SW Productivity + * Created: Fri Oct 16 14:37:00 1987 + * Modified: Mon Sep 24 14:15:59 1990 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Reusable Software Component + * + * (c) Copyright 1987, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + ***************************************************************************** + +This file contains a set of general purpose dynamic array of string routines. +These routines can be used in a wide variety of ways to provide several +different popular data structures. A new "das" can be created by declaring +a variable of type 'DAS' +******************************************************************************/ + +#ifndef ARRAY_H +#define ARRAY_H + +/* +---------------------------------------------------------------------- + I n c l u d e s +---------------------------------------------------------------------- +*/ + +#include + +/* +---------------------------------------------------------------------- + T y p e s +---------------------------------------------------------------------- +*/ + +typedef struct array_record +{ + size_t limit; + size_t top; + void *base[2]; +} *ARRAY; + +typedef void (*voidProc) (); + +typedef int (*intProc) (); + +/* +---------------------------------------------------------------------- + M a c r o s +---------------------------------------------------------------------- +*/ + +#define DEFAULT_SIZE 2 + +/********************************************************************** + * array_count + * + * Return the value of the number of elements currently in the array. + **********************************************************************/ + +#define array_count(a) \ +((a)->top) + +/********************************************************************** + * array_free + * + * Free the memory allocated to this array. + **********************************************************************/ + +#define array_free \ +memfree + +/********************************************************************** + * array_index + * + * Check to make sure that the index value is valid. Return the + * value of the nth element currently in the array. + **********************************************************************/ + +#define array_index(a,i) \ +((ibase[i] : 0) + +/********************************************************************** + * array_limit + * + * Return the maximum number of elements that could be currently held + * in this array without further expansion. + **********************************************************************/ + +#define array_limit(a) \ +((a)->limit) + +/********************************************************************** + * array_loop + * + * Iterate through each of the array elements. Each value can then be + * accessed by: + * array_index (a, x) + **********************************************************************/ + +#define array_loop(a,x) \ +for (x=0; x < array_count (a); x++) + +/********************************************************************** + * array_top + * + * Return the last element that was pushed on this array. + **********************************************************************/ + +#define array_top(a) \ +((a)->base[array_count (a) - 1]) + +/********************************************************************** + * array_value + * + * Return the nth element of the array. Don't do range checking. + **********************************************************************/ + +#define array_value(a,i) \ +((a)->base[i]) + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +ARRAY array_insert(ARRAY array, int index, void *value); + +ARRAY array_new(int num); + +ARRAY array_push(ARRAY array, void *value); + +/* +#if defined(__STDC__) || defined(__cplusplus) +# define _ARGS(s) s +#else +# define _ARGS(s) () +#endif*/ + +/* array.c +ARRAY array_insert + _ARGS((ARRAY array, + int index, + char *value)); + +ARRAY array_new + _ARGS((int num)); + +ARRAY array_push + _ARGS((ARRAY array, + char *value)); + +#undef _ARGS +*/ +#endif diff --git a/cutil/tordvars.cpp b/cutil/tordvars.cpp new file mode 100644 index 0000000000..7ef1bbc95e --- /dev/null +++ b/cutil/tordvars.cpp @@ -0,0 +1,95 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: tovars.c (Formerly to-vars.c) + * Description: Text Ordering Control Variables + * Author: Mark Seaman, OCR Technology + * Created: Wed Jan 17 12:47:29 1990 + * Modified: Tue Jul 30 16:22:40 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 "mfcpch.h" +#include "debug.h" + +/*---------------------------------------------------------------------- + V a r i a b l e s +----------------------------------------------------------------------*/ +FILE *rawfile; /* Text before dictionary */ +FILE *textfile; /* Text output file */ +FILE *matcher_fp; //matcher log +FILE *correct_fp; //correct text + +int write_output; /* Text file output */ +int write_raw_output; /* Text before context */ + +int similarity_enable = 0; +int similarity_debug = 0; + +make_float_var (certainty_threshold, -2.25, make_certainty_threshold, +4, 5, set_certainty_value, "Certainty Value"); + +make_int_var (num_word_choices, 30, make_num_word_choices, +4, 6, set_num_choices, "Number of choices"); + +make_toggle_var (blob_skip, 0, make_blob_skip, +4, 7, toggle_skip, "Skip to Next selection"); + +make_float_var (overlap_threshold, 0.33, make_overlap_threshold, +9, 7, set_overlap, "Overlap Threshold"); + +make_toggle_var (debug_3, 0, make_debug_3, 6, 3, toggle_debug_3, "Debug #3"); + +make_toggle_var (debug_5, 0, make_debug_5, 6, 5, toggle_debug_5, "Debug #5"); + +make_toggle_var (debug_8, 0, make_debug_8, 6, 8, toggle_debug_8, "Debug #8"); + +make_toggle_var (display_ratings, 0, make_display_ratings, +6, 9, toggle_ratings, "Ratings display"); + +make_toggle_var (display_text, 1, make_display_text, +6, 10, toggle_text, "Display Text"); + +make_toggle_var (show_bold, 1, make_show_bold, +6, 17, set_show_bold, "Show Bold Text"); + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +/********************************************************************** + * init_textord_vars + * + * Create variables to be used by the texord section of code. + **********************************************************************/ +void init_textord_vars() { + int_variable (write_output, "write_output", 0); + int_variable (write_raw_output, "write_raw_output", 0); + make_certainty_threshold(); + make_num_word_choices(); + make_blob_skip(); + make_overlap_threshold(); + make_show_bold(); + + make_debug_3(); + make_debug_5(); + make_debug_8(); + + make_display_ratings(); + make_display_text(); +} diff --git a/cutil/tordvars.h b/cutil/tordvars.h new file mode 100644 index 0000000000..130b83f799 --- /dev/null +++ b/cutil/tordvars.h @@ -0,0 +1,60 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: tovars.h (Formerly to-vars.h) + * Description: Do word classification + * Author: Mark Seaman, OCR Technology + * Created: Wed Oct 25 16:33:01 1989 + * Modified: Mon Jul 1 14:28:23 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 TOVARS_H +#define TOVARS_H + +#include + +/*---------------------------------------------------------------------- + V a r i a b l e s +----------------------------------------------------------------------*/ +extern int debug_3; +extern int debug_5; +extern int debug_8; + +extern FILE *rawfile; /* Text before dictionary */ +extern FILE *textfile; /* Text output file */ +extern FILE *correct_fp; //correct text +extern FILE *matcher_fp; + +extern int blob_skip; /* Skip to next selection */ +extern int num_word_choices; /* How many words to keep */ +extern int similarity_enable; /* Switch for Similarity */ +extern int similarity_debug; /* Level of debug output */ +extern int write_raw_output; /* Text before context */ +extern int write_output; /* Text file output */ +extern int display_ratings; /* Show the ratings */ +extern int show_bold; /* Use bold text */ +extern int display_text; /* Show word text */ +extern int display_blocks; /* Show word as boxes */ + +extern float overlap_threshold; /* Overlap Threshold */ +extern float certainty_threshold;/* When to quit looking */ + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +void init_textord_vars(); +#endif diff --git a/cutil/variables.cpp b/cutil/variables.cpp new file mode 100644 index 0000000000..2a40b811b8 --- /dev/null +++ b/cutil/variables.cpp @@ -0,0 +1,310 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: variables.c (Formerly variables.c) + * Description: Variable handler for control flags + * Author: Mark Seaman, OCR Technology + * Created: Tue Dec 12 09:03:49 1989 + * Modified: Thu Apr 4 11:01: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. + * + *********************************************************************************/ +/*---------------------------------------------------------------------- + I n c l u d e s +----------------------------------------------------------------------*/ +#include +#include + +#include "variables.h" +#include "callcpp.h" +#include "listio.h" +#include "globals.h" +#include "scanutils.h" + +/*---------------------------------------------------------------------- + V a r i a b l e s +----------------------------------------------------------------------*/ +static LIST variable_list = NIL; +//extern char *demodir; + +VALUE dummy; + +/*---------------------------------------------------------------------- + Macros +----------------------------------------------------------------------*/ +/********************************************************************** + * scan_int + * + * Scan in an integer value from a string. It might be in decimal or + * hex. Read it into "integer". + **********************************************************************/ + +#define scan_int(stripped,integer) \ +if (stripped [2] == 'x') \ + sscanf (&stripped[3], "%x", &integer); \ +else \ + sscanf (&stripped[1], "%d", &integer) + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ + +void free_variables() { + destroy_nodes(variable_list, free); + variable_list = NIL; +} +/********************************************************************** + * add_ptr_variable + * + * Add a new ptr variable to the global variable list. Initalize its + * value. + **********************************************************************/ +void add_ptr_variable(void *address, + const char *string, + VALUE default_value, + variables_io reader, + variables_io writer) { + VARIABLE *this_var; + this_var = (VARIABLE *) malloc (sizeof (VARIABLE)); + + this_var->address = address; + this_var->string = string; + this_var->default_value = default_value; + this_var->type_reader = reader; + this_var->type_writer = writer; + + *((void **) this_var->address) = default_value.ptr_part; + variable_list = push (variable_list, this_var); +} + + +/********************************************************************** + * add_32bit_variable + * + * Add a new 32bit variable to the global variable list. Initalize + * its value. + **********************************************************************/ +void add_32bit_variable(void *address, + const char *string, + VALUE default_value, + variables_io reader, + variables_io writer) { + VARIABLE *this_var; + this_var = (VARIABLE *) malloc (sizeof (VARIABLE)); + + this_var->address = address; + this_var->string = string; + this_var->default_value = default_value; + this_var->type_reader = reader; + this_var->type_writer = writer; + + *((int *) this_var->address) = default_value.int_part; + variable_list = push (variable_list, this_var); +} + + +/********************************************************************** + * float_read + * + * Read an integer value and save it in a variable structure. + **********************************************************************/ +void float_read(VARIABLE *variable, char *string) { + float f; + + #ifdef EMBEDDED + // We have no sscanf with float functionality here + *((float *) variable->address) = strtofloat(strip_line (string)); + #else + sscanf (strip_line (string), "%f", &f); + *((float *) variable->address) = f; + #endif +} + + +/********************************************************************** + * float_write + * + * Read an integer value and save it in a variable structure. + **********************************************************************/ +void float_write(VARIABLE *variable, char *string) { + float *f; + f = (float *) variable->address; + sprintf (string, "%s\t%4.2f", variable->string, *f); +} + + +/********************************************************************** + * int_read + * + * Read an integer value and save it in a variable structure. + **********************************************************************/ +void int_read(VARIABLE *variable, char *string) { + char *stripped; + int integer; + + stripped = strip_line (string); + /* Add the value */ + if (stripped[0] == '+') { + scan_int(stripped, integer); + *((int *) variable->address) += integer; + } + else if (stripped[0] == '|') { + scan_int(stripped, integer); + *((int *) variable->address) = integer | *((int *) variable->address); + } /* Subtract the value */ + else if (stripped[0] == '_') { + scan_int(stripped, integer); + *((int *) variable->address) = (~integer) & + *((int *) variable->address); + } + else { + /* Set the value */ + if (stripped[1] == 'x') { + sscanf (&stripped[2], "%x", &integer); + } + else { + sscanf (stripped, "%d", &integer); + } + *((int *) variable->address) = integer; + } +} + + +/********************************************************************** + * int_write + * + * Read an integer value and save it in a variable structure. + **********************************************************************/ +void int_write(VARIABLE *variable, char *string) { + sprintf (string, "%s\t%d", variable->string, *((int *) variable->address)); +} + + +/********************************************************************** + * read_variables + * + * Read a file that contains assignments for all the desired variables. + * This type of file can be written using the function write_variables. + **********************************************************************/ +void read_variables(const char *filename) { + int x = 0; + char *this_string; + LIST var_strings; + char name[1024]; + FILE *fp; + VARIABLE *this_var; + /* Read the strings */ + if (filename == NULL || filename[0] == '\0') + return; + + strcpy(name, demodir); + strcat (name, "tessdata/tessconfigs/"); + strcat(name, filename); + if ((fp = fopen (name, "r")) == NULL) + strcpy(name, filename); + else + fclose(fp); + var_strings = read_list (name); + iterate(var_strings) { + /* Get the name string */ + this_string = (char *) first (var_strings); + if (this_string[0] != '#') { + for (x = 0; + x < strlen (this_string) && this_string[x] != ' ' + && this_string[x] != '\t'; x++); + this_string[x] = '\0'; + /* Find variable record */ + this_var = (VARIABLE *) first (search (variable_list, this_string, + same_var_name)); + if (this_var == 0) { + cprintf ("error: Could not find variable '%s'\n", this_string); + exit (1); //?err_exit (); + } + /* Read the value */ + this_string[x] = '\t'; + (*(this_var->type_reader)) (this_var, this_string); + } + } +} + + +/********************************************************************** + * same_var_name + * + * Return TRUE if the VARIABLE has the name string in it. + **********************************************************************/ +int same_var_name(void *item1, //VARIABLE *variable, + void *item2) { //char *string) + VARIABLE *variable; + char *string; + + variable = (VARIABLE *) item1; + string = (char *) item2; + + if (strcmp (variable->string, string)) + return (FALSE); + else + return (TRUE); +} + + +/********************************************************************** + * string_read + * + * Read an integer value and save it in a variable structure. + **********************************************************************/ +void string_read(VARIABLE *variable, char *string) { + char *value; + + value = strsave (strip_line (string)); + *((char **) variable->address) = value; +} + + +/********************************************************************** + * string_write + * + * Read an integer value and save it in a variable structure. + **********************************************************************/ +void string_write(VARIABLE *variable, char *string) { + sprintf (string, "%s\t%s", variable->string, + *((char **) variable->address)); +} + + +/********************************************************************** + * strip_line + * + * Remove the name off the front of the line and strip the white space + * before and after the value. + **********************************************************************/ +char *strip_line(char *string) { + int x; + int y; + + /* Skip over name */ + for (x = 0; + x < strlen (string) && string[x] != '\t' && string[x] != ' '; x++); + /* Skip over whitespace */ + while (x < strlen (string) && (string[x] == '\t' || string[x] == ' ')) + x++; + /* Strip trailing whitespace */ + for (y = strlen (string); + y >= 0 && (string[y - 1] == '\t' || string[y - 1] == ' '); y--) + string[y] = '\0'; + /* Return result */ + return (&string[x]); +} diff --git a/cutil/variables.h b/cutil/variables.h new file mode 100644 index 0000000000..02de6ab583 --- /dev/null +++ b/cutil/variables.h @@ -0,0 +1,168 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: variables.h (Formerly variables.h) + * Description: Variable handler for control flags + * Author: Mark Seaman, OCR Technology + * Created: Tue Dec 12 09:03:49 1989 + * Modified: Fri Jan 12 16:57:36 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 VARIABLES_H +#define VARIABLES_H + +#include "cutil.h" +#include "oldlist.h" + +/*---------------------------------------------------------------------- + T y p e s +----------------------------------------------------------------------*/ +typedef union +{ + float float_part; + int int_part; + void *ptr_part; + const char *char_part; +} VALUE; + +class VARIABLE; + +typedef void (*variables_io) (VARIABLE * variable, char *string); + +class VARIABLE +{ + public: + void *address; + const char *string; + VALUE default_value; + variables_io type_reader; + variables_io type_writer; +}; + +extern VALUE dummy; /* Needed for macros */ + +/*---------------------------------------------------------------------- + M a c r o s +----------------------------------------------------------------------*/ +/* To hide variable names for relase to UNLV DONT put the variable names in + the executable: Toggle be either defining or not defining SECURE_NAMES*/ +/* #define SECURE_NAMES defined in secnames.h when necessary */ +#ifdef SECURE_NAMES +/********************************************************************** + * float_variable + * + * Create a floating point variable that can be read and written from + * a configuration file. + **********************************************************************/ + +#define float_variable(name,string,default) \ +dummy.float_part = default; \ +add_32bit_variable (&name, "", dummy, float_read, float_write) + +/********************************************************************** + * string_variable + * + * Create a string point variable that can be read and written from + * a configuration file. + **********************************************************************/ + +#define string_variable(name,string,default) \ +dummy.char_part = default; \ +add_ptr_variable (&name, "", dummy, string_read, string_write) + +/********************************************************************** + * int_variable + * + * Create a int point variable that can be read and written from + * a configuration file. + **********************************************************************/ + +#define int_variable(name,string,default) \ +dummy.int_part = default; \ +add_32bit_variable (&name, "", dummy, int_read, int_write) + +#else +/********************************************************************** + * float_variable + * + * Create a floating point variable that can be read and written from + * a configuration file. + **********************************************************************/ + +#define float_variable(name,string,default) \ +dummy.float_part = default; \ +add_32bit_variable (&name, string, dummy, float_read, float_write) + +/********************************************************************** + * string_variable + * + * Create a string point variable that can be read and written from + * a configuration file. + **********************************************************************/ + +#define string_variable(name,string,default) \ +dummy.char_part = default; \ +add_ptr_variable (&name, string, dummy, string_read, string_write) + +/********************************************************************** + * int_variable + * + * Create a int point variable that can be read and written from + * a configuration file. + **********************************************************************/ + +#define int_variable(name,string,default) \ +dummy.int_part = default; \ +add_32bit_variable (&name, string, dummy, int_read, int_write) +#endif + +/*-------------------------------------------------------------------------- + Public Function Prototoypes +----------------------------------------------------------------------------*/ +void free_variables(); + +void add_ptr_variable(void *address, + const char *string, + VALUE default_value, + variables_io reader, + variables_io writer); + +void add_32bit_variable(void *address, + const char *string, + VALUE default_value, + variables_io reader, + variables_io writer); + +void float_read(VARIABLE *variable, char *string); + +void float_write(VARIABLE *variable, char *string); + +void int_read(VARIABLE *variable, char *string); + +void int_write(VARIABLE *variable, char *string); + +void read_variables(const char *filename); + +int same_var_name(void *item1, //VARIABLE *variable, + void *item2); //char *string) + +void string_read(VARIABLE *variable, char *string); + +void string_write(VARIABLE *variable, char *string); + +char *strip_line(char *string); +#endif diff --git a/dict/Makefile.am b/dict/Makefile.am new file mode 100644 index 0000000000..582c5504e5 --- /dev/null +++ b/dict/Makefile.am @@ -0,0 +1,11 @@ +SUBDIRS = +AM_CPPFLAGS = -I$(top_srcdir)/cutil -I$(top_srcdir)/ccutil + +EXTRA_DIST = \ + choicearr.h choices.h context.h dawg.h hyphen.h matchdefs.h \ + permdawg.h permnum.h permute.h states.h stopper.h trie.h + +noinst_LIBRARIES = libtesseract_dict.a +libtesseract_dict_a_SOURCES = \ + choices.cpp context.cpp dawg.cpp hyphen.cpp permdawg.cpp \ + permnum.cpp permute.cpp states.cpp stopper.cpp trie.cpp diff --git a/dict/Makefile.in b/dict/Makefile.in new file mode 100644 index 0000000000..95e0d1282c --- /dev/null +++ b/dict/Makefile.in @@ -0,0 +1,539 @@ +# Makefile.in generated by automake 1.9.6 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005 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@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = .. +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +INSTALL = @INSTALL@ +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@ +subdir = dict +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \ + $(top_srcdir)/config/ac_define_versionlevel.m4 \ + $(top_srcdir)/config/acinclude_custom.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(SHELL) $(top_srcdir)/config/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config_auto.h +CONFIG_CLEAN_FILES = +LIBRARIES = $(noinst_LIBRARIES) +AR = ar +ARFLAGS = cru +libtesseract_dict_a_AR = $(AR) $(ARFLAGS) +libtesseract_dict_a_LIBADD = +am_libtesseract_dict_a_OBJECTS = choices.$(OBJEXT) context.$(OBJEXT) \ + dawg.$(OBJEXT) hyphen.$(OBJEXT) permdawg.$(OBJEXT) \ + permnum.$(OBJEXT) permute.$(OBJEXT) states.$(OBJEXT) \ + stopper.$(OBJEXT) trie.$(OBJEXT) +libtesseract_dict_a_OBJECTS = $(am_libtesseract_dict_a_OBJECTS) +DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/config/depcomp +am__depfiles_maybe = depfiles +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +CXXLD = $(CXX) +CXXLINK = $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) \ + -o $@ +SOURCES = $(libtesseract_dict_a_SOURCES) +DIST_SOURCES = $(libtesseract_dict_a_SOURCES) +RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \ + html-recursive info-recursive install-data-recursive \ + install-exec-recursive install-info-recursive \ + install-recursive installcheck-recursive installdirs-recursive \ + pdf-recursive ps-recursive uninstall-info-recursive \ + uninstall-recursive +ETAGS = etags +CTAGS = ctags +DIST_SUBDIRS = $(SUBDIRS) +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMDEP_FALSE = @AMDEP_FALSE@ +AMDEP_TRUE = @AMDEP_TRUE@ +AMTAR = @AMTAR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CXXRPOFLAGS = @CXXRPOFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +GNUWIN32_DIR = @GNUWIN32_DIR@ +HAVE_GNUWIN32_FALSE = @HAVE_GNUWIN32_FALSE@ +HAVE_GNUWIN32_TRUE = @HAVE_GNUWIN32_TRUE@ +HAVE_LIBTIFF_FALSE = @HAVE_LIBTIFF_FALSE@ +HAVE_LIBTIFF_TRUE = @HAVE_LIBTIFF_TRUE@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTIFF_CFLAGS = @LIBTIFF_CFLAGS@ +LIBTIFF_LIBS = @LIBTIFF_LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAINTAINER_MODE_FALSE = @MAINTAINER_MODE_FALSE@ +MAINTAINER_MODE_TRUE = @MAINTAINER_MODE_TRUE@ +MAKEINFO = @MAKEINFO@ +OBJEXT = @OBJEXT@ +OPTS = @OPTS@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_DATE = @PACKAGE_DATE@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PACKAGE_YEAR = @PACKAGE_YEAR@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +RPO_NO = @RPO_NO@ +RPO_YES = @RPO_YES@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +USING_CL_FALSE = @USING_CL_FALSE@ +USING_CL_TRUE = @USING_CL_TRUE@ +VERSION = @VERSION@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_RANLIB = @ac_ct_RANLIB@ +ac_ct_STRIP = @ac_ct_STRIP@ +am__fastdepCC_FALSE = @am__fastdepCC_FALSE@ +am__fastdepCC_TRUE = @am__fastdepCC_TRUE@ +am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@ +am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@ +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@ +datadir = @datadir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +SUBDIRS = +AM_CPPFLAGS = -I$(top_srcdir)/cutil -I$(top_srcdir)/ccutil +EXTRA_DIST = \ + choicearr.h choices.h context.h dawg.h hyphen.h matchdefs.h \ + permdawg.h permnum.h permute.h states.h stopper.h trie.h + +noinst_LIBRARIES = libtesseract_dict.a +libtesseract_dict_a_SOURCES = \ + choices.cpp context.cpp dawg.cpp hyphen.cpp permdawg.cpp \ + permnum.cpp permute.cpp states.cpp stopper.cpp trie.cpp + +all: all-recursive + +.SUFFIXES: +.SUFFIXES: .cpp .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 \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu dict/Makefile'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu 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 + +clean-noinstLIBRARIES: + -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) +libtesseract_dict.a: $(libtesseract_dict_a_OBJECTS) $(libtesseract_dict_a_DEPENDENCIES) + -rm -f libtesseract_dict.a + $(libtesseract_dict_a_AR) libtesseract_dict.a $(libtesseract_dict_a_OBJECTS) $(libtesseract_dict_a_LIBADD) + $(RANLIB) libtesseract_dict.a + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/choices.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/context.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dawg.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hyphen.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/permdawg.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/permnum.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/permute.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/states.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stopper.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/trie.Po@am__quote@ + +.cpp.o: +@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ $< + +.cpp.obj: +@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` +uninstall-info-am: + +# 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. +$(RECURSIVE_TARGETS): + @failcom='exit 1'; \ + for f in x $$MAKEFLAGS; do \ + case $$f in \ + *=* | --[!k]*);; \ + *k*) failcom='fail=yes';; \ + esac; \ + done; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + list='$(SUBDIRS)'; 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; \ + (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" + +mostlyclean-recursive clean-recursive distclean-recursive \ +maintainer-clean-recursive: + @failcom='exit 1'; \ + for f in x $$MAKEFLAGS; do \ + case $$f in \ + *=* | --[!k]*);; \ + *k*) failcom='fail=yes';; \ + esac; \ + done; \ + dot_seen=no; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + rev=''; for subdir in $$list; do \ + if test "$$subdir" = "."; then :; else \ + rev="$$subdir $$rev"; \ + fi; \ + done; \ + rev="$$rev ."; \ + target=`echo $@ | sed s/-recursive//`; \ + for subdir in $$rev; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done && test -z "$$fail" +tags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \ + done +ctags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \ + done + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + 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 || \ + tags="$$tags $$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique; \ + fi +ctags: CTAGS +CTAGS: ctags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(CTAGS_ARGS)$$tags$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +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)'; for file in $$list; do \ + case $$file in \ + $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \ + $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \ + esac; \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + dir="/$$dir"; \ + $(mkdir_p) "$(distdir)$$dir"; \ + else \ + dir=''; \ + fi; \ + if test -d $$d/$$file; then \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$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 \ + test -d "$(distdir)/$$subdir" \ + || $(mkdir_p) "$(distdir)/$$subdir" \ + || exit 1; \ + distdir=`$(am__cd) $(distdir) && pwd`; \ + top_distdir=`$(am__cd) $(top_distdir) && pwd`; \ + (cd $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$top_distdir" \ + distdir="$$distdir/$$subdir" \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-recursive +all-am: Makefile $(LIBRARIES) +installdirs: installdirs-recursive +installdirs-am: +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: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_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-recursive + +clean-am: clean-generic clean-noinstLIBRARIES mostlyclean-am + +distclean: distclean-recursive + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +info: info-recursive + +info-am: + +install-data-am: + +install-exec-am: + +install-info: install-info-recursive + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-compile mostlyclean-generic + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: uninstall-info-am + +uninstall-info: uninstall-info-recursive + +.PHONY: $(RECURSIVE_TARGETS) CTAGS GTAGS all all-am check check-am \ + clean clean-generic clean-noinstLIBRARIES clean-recursive \ + ctags ctags-recursive distclean distclean-compile \ + distclean-generic distclean-recursive distclean-tags distdir \ + dvi dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-exec install-exec-am \ + install-info install-info-am install-man install-strip \ + installcheck installcheck-am installdirs installdirs-am \ + maintainer-clean maintainer-clean-generic \ + maintainer-clean-recursive mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-recursive pdf pdf-am ps ps-am \ + tags tags-recursive uninstall uninstall-am uninstall-info-am + +# 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: diff --git a/dict/choicearr.h b/dict/choicearr.h new file mode 100644 index 0000000000..c573b6345e --- /dev/null +++ b/dict/choicearr.h @@ -0,0 +1,96 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: choicearr.h (Formerly choicearr.h) + * Description: + * Author: Mark Seaman, SW Productivity + * Created: Fri Oct 16 14:37:00 1987 + * Modified: Tue Mar 19 15: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. + * + ******************************************************************************** + */ + +#ifndef CHOICEARR_H +#define CHOICEARR_H + +/* +---------------------------------------------------------------------- + I n c l u d e s +---------------------------------------------------------------------- +*/ + +#include "tessarray.h" +#include "choices.h" + +/* +---------------------------------------------------------------------- + T y p e s +---------------------------------------------------------------------- +*/ + +typedef ARRAY CHOICES_LIST; + +#define CHOICES_PER_LIST 40 + +/* +---------------------------------------------------------------------- + M a c r o s +---------------------------------------------------------------------- +*/ + +/********************************************************************** + * free_choice_list + * + * Free a list of choices. Free the array structure but not each of the + * sublists of choices. + **********************************************************************/ + +#define free_choice_list(choice_list) \ +array_free (choice_list) + +/********************************************************************** + * for_each_choice + * + * Iterate through each of the possible choices. + **********************************************************************/ + +#define for_each_choice(array,index) \ +array_loop (array, index) + +/********************************************************************** + * free_all_choices + * + * Free an array of choices (deep free). + **********************************************************************/ + +#define free_all_choices(choices,index) \ +for_each_choice (choices, index) { \ + free_choices ((CHOICES) array_value (choices, index)); \ +} \ +array_free (choices) \ + + +/********************************************************************** + * new_choice_list + * + * Return a new array structure that is a list of choices. Each set of + * choices will be of type CHOICES. + **********************************************************************/ + +#define new_choice_list() \ +array_new (CHOICES_PER_LIST) +#endif diff --git a/dict/choices.cpp b/dict/choices.cpp new file mode 100644 index 0000000000..7440d5d41e --- /dev/null +++ b/dict/choices.cpp @@ -0,0 +1,161 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: choices.c (Formerly choices.c) + * Description: Handle the new ratings choices for Wise Owl + * Author: Mark Seaman, OCR Technology + * Created: Fri Sep 22 14:05:51 1989 + * Modified: Wed May 22 14:12:34 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. + * + *********************************************************************************/ +#include "choices.h" +#include "structures.h" +#include "tordvars.h" +#include "callcpp.h" +#include "danerror.h" +#include "host.h" + +/*---------------------------------------------------------------------- + Variables +------------------------------------------------------------------------*/ +#define CHOICEBLOCK 100 /* Cells per block */ + +makestructure (newchoice, oldchoice, printchoice, A_CHOICE, +freechoice, CHOICEBLOCK, "A_CHOICE", choicecount) +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +/********************************************************************** + * append_choice + * + * Create a new choice record. Store the string value in a safe place. + * Add the new choice record to the list. + * + * NB - This is only used by matchers, so permuter is always NO_PERM + * SPC 16/9/92 + **********************************************************************/ +CHOICES append_choice(CHOICES ratings, + const char *string, + float rating, + float certainty, + INT8 config) { + A_CHOICE *this_choice; + + this_choice = new_choice (string, rating, certainty, config, NO_PERM); + ratings = push_last (ratings, (LIST) this_choice); + return (ratings); +} + + +/********************************************************************** + * copy_choices + * + * Copy a list of choices. This means that there will be two copies + * in memory. + **********************************************************************/ +CHOICES copy_choices(CHOICES choices) { + CHOICES l; + CHOICES result = NIL; + + iterate_list(l, choices) { + result = push (result, + (LIST) new_choice (class_string (first (l)), + class_probability (first (l)), + class_certainty (first (l)), + class_config (first (l)), + class_permuter (first (l)))); + } + return (reverse_d (result)); +} + + +/********************************************************************** + * free_choice + * + * Free up the memory taken by one choice rating. + **********************************************************************/ +void free_choice(void *arg) { //LIST choice) + A_CHOICE *this_choice; + LIST choice = (LIST) arg; + + this_choice = (A_CHOICE *) choice; + if (this_choice) { + if (this_choice->string) + strfree (this_choice->string); + oldchoice(this_choice); + } +} + + +/********************************************************************** + * new_choice + * + * Create a new choice record. Store the string value in a safe place. + **********************************************************************/ +A_CHOICE *new_choice(const char *string, + float rating, + float certainty, + INT8 config, + char permuter) { + A_CHOICE *this_choice; + + this_choice = newchoice (); + this_choice->string = strsave (string); + this_choice->rating = rating; + this_choice->certainty = certainty; + this_choice->config = config; + this_choice->permuter = permuter; + return (this_choice); +} + + +/********************************************************************** + * print_choices + * + * Print the probability ratings for a particular blob or word. + **********************************************************************/ +void print_choices( /* List of (A_CHOICE*) */ + const char *label, + CHOICES rating) { + int first_one = TRUE; + char str[CHARS_PER_LINE]; + int len; + + cprintf ("%-20s\n", label); + if (rating == NIL) + cprintf (" No rating "); + + iterate(rating) { + + if (first_one && show_bold) { + cprintf ("|"); + len = sprintf (str, " %s ", best_string (rating)); + print_bold(str); + while (len++ < 8) + cprintf (" "); + } + else { + cprintf ("| %-7s", best_string (rating)); + } + + cprintf ("%5.2lf ", best_probability (rating)); + + cprintf ("%5.2lf", best_certainty (rating)); + first_one = FALSE; + } + cprintf ("\n"); +} diff --git a/dict/choices.h b/dict/choices.h new file mode 100644 index 0000000000..7e7b588888 --- /dev/null +++ b/dict/choices.h @@ -0,0 +1,191 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: choices.h (Formerly choices.h) + * Description: Handle the new ratings choices for Wise Owl + * Author: Mark Seaman, OCR Technology + * Created: Fri Sep 22 14:05:51 1989 + * Modified: Fri Jan 4 12:04:01 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. + * + ******************************************************************************** + * + * FUNCTIONS TO CALL + * ----------------- + * append_choice - Create a new choice and add it to the list. + * class_probability - Return the probability of a given character class. + * class_string - Return the string corresponding to a character choice. + * free_choice - Free up the memory taken by one choice rating. + * new_choice - Create one choice record one set up the fields. + * + *********************************************************************************/ + +#ifndef CHOICES_H +#define CHOICES_H + +#include +#include + +#include "oldlist.h" + +/*---------------------------------------------------------------------- + T y p e s +----------------------------------------------------------------------*/ +typedef LIST CHOICES; /* CHOICES */ +//typedef float PROBABILITY; /* PROBABILITY */ +//typedef char PERM_TYPE; /* PERMUTER CODE */ + +/* permuter codes used in A_CHOICEs for words */ + +#define NO_PERM 0 +#define TOP_CHOICE_PERM 1 +#define LOWER_CASE_PERM 2 +#define UPPER_CASE_PERM 3 +#define NUMBER_PERM 4 +#define SYSTEM_DAWG_PERM 5 +#define DOC_DAWG_PERM 6 +#define USER_DAWG_PERM 7 +#define FREQ_DAWG_PERM 8 +#define COMPOUND_PERM 9 + +typedef struct choicestruct +{ /* A_CHOICE */ + float rating; + float certainty; + char permuter; + INT8 config; + char *string; +} A_CHOICE; + +/*---------------------------------------------------------------------- + M a c r o s +----------------------------------------------------------------------*/ +/********************************************************************** + * best_string + * + * Return the string corresponding to the best choice. + **********************************************************************/ +#define best_string(choices) \ +(first (choices) ? ((A_CHOICE*) (first (choices)))->string : NULL) + +/********************************************************************** + * best_probability + * + * Return the probability of the best choice. + **********************************************************************/ +#define best_probability(choices) \ +(((A_CHOICE*) (first (choices)))->rating) + +/********************************************************************** + * best_certainty + * + * Return the certainty of the best choice. + **********************************************************************/ +#define best_certainty(choices) \ +(((A_CHOICE*) (first (choices)))->certainty) + +/********************************************************************** + * class_probability + * + * Return the probability of a given character class. + **********************************************************************/ +#define class_probability(choice) \ +(((A_CHOICE*) (choice))->rating) + +/********************************************************************** + * class_certainty + * + * Return the certainty of a given character class. + **********************************************************************/ +#define class_certainty(choice) \ +(((A_CHOICE*) (choice))->certainty) + +/********************************************************************** + * class_string + * + * Return the probability of a given character class. + **********************************************************************/ +#define class_string(choice) \ +(((A_CHOICE*) (choice))->string) + +/********************************************************************** + * class_permuter + * + * Return the permuter of a given character class. + **********************************************************************/ +#define class_permuter(choice) \ +(((A_CHOICE*) (choice))->permuter) + +/********************************************************************** + * class_config + * + * Return the config of a given character class. + **********************************************************************/ +#define class_config(choice) \ +(((A_CHOICE*) (choice))->config) + +/********************************************************************** + * clone_choice + * + * Copy the contents of this choice record onto another replacing any + * previous value it might of had. + **********************************************************************/ +#define clone_choice(choice_2,choice_1) \ +if (class_string (choice_2)) strfree (class_string (choice_2)); \ +class_probability (choice_2) = class_probability (choice_1); \ +class_certainty (choice_2) = class_certainty (choice_1); \ +class_permuter (choice_2) = class_permuter (choice_1); \ +class_string (choice_2) = strsave (class_string (choice_1)) \ + + +/********************************************************************** + * free_choices + * + * Free a list of choices. + **********************************************************************/ +#define free_choices(c) \ +destroy_nodes ((c), free_choice) + +/********************************************************************** + * print_bold + * + * Print a string in bold type by using escape sequences. This only + * works for certain output devices. + **********************************************************************/ +#define print_bold(string) \ + cprintf ("\033&dB%s\033&d@", string) + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +CHOICES append_choice(CHOICES ratings, + const char *string, + float rating, + float certainty, + INT8 config); + +CHOICES copy_choices(CHOICES choices); + +void free_choice(void *arg); //LIST choice); + +A_CHOICE *new_choice(const char *string, + float rating, + float certainty, + INT8 config, + char permuter); + +void print_choices(const char *label, CHOICES rating); +#endif diff --git a/dict/context.cpp b/dict/context.cpp new file mode 100644 index 0000000000..20920dc4fe --- /dev/null +++ b/dict/context.cpp @@ -0,0 +1,225 @@ +/* -*-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 "context.h" +#include "tordvars.h" +#include "callcpp.h" + +#include +#include +#include +#include + +/*---------------------------------------------------------------------- + V a r i a b l e s +----------------------------------------------------------------------*/ +static FILE *choice_file = NULL; /* File to save choices */ + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +/********************************************************************** + * close_choices + * + * Close the choices file. + **********************************************************************/ +void close_choices() { + if (choice_file) + fclose(choice_file); +} + + +/********************************************************************** + * fix_quotes + * + * Fix up two single quote to make them two double quotes. + **********************************************************************/ +void fix_quotes(char *str) { + int i; + for (i = 0; i < strlen (str); i++) { + + if (((str[i] == '\'') || (str[i] == '`')) && + ((str[i + 1] == '\'') || (str[i + 1] == '`'))) { + str[i] = '\"'; + strcpy (str + i + 1, str + i + 2); + } + } +} + + +/********************************************************************** + * punctuation_ok + * + * Check a string to see if it matches a set of punctuation rules. + **********************************************************************/ +int punctuation_ok(const char *word) { + int punctuation_types[5]; + int trailing = 0; + int num_puncts = 0; + register int x; + register char ch; + + for (x = 0; x < 5; x++) + punctuation_types[x] = 0; + + for (x = 0; x < strlen (word); x++) { + + if (isalpha (word[x])) { + if (trailing && + !(isalpha (word[x - 1]) || + (word[x - 1] == '\'' && + (word[x] == 's' || word[x] == 'd' || word[x] == 'l')) || + (word[x - 1] == '-'))) + return (-1); + trailing = 1; + } + else { + ch = word[x]; + + if (ch == '.' && trailing) { + if (punctuation_types[0]) + return (-1); + (punctuation_types[0])++; + } + + else if (((ch == '{') || (ch == '[') || (ch == '(')) && !trailing) { + if (punctuation_types[1]) + return (-1); + (punctuation_types[1])++; + } + + else if (((ch == '}') || (ch == ']') || (ch == ')')) && trailing) { + if (punctuation_types[2]) + return (-1); + (punctuation_types[2])++; + } + + else if (((ch == ':') || + (ch == ';') || + (ch == '!') || + (ch == '-') || (ch == ',') || (ch == '?')) && trailing) { + if (punctuation_types[3]) + return (-1); + (punctuation_types[3])++; + if (ch == '-') + punctuation_types[3] = 0; + } + + else if ((ch == '`') || (ch == '\"') || (ch == '\'')) { + if ((word[x + 1] == '`') || (word[x + 1] == '\'')) { + x++; + } + (punctuation_types[4])++; + if (punctuation_types[4] > 2) + return (-1); + } + + else if (!isdigit (ch)) + return (-1); + } + } + + for (x = 0; x < 5; x++) { + if (punctuation_types[x]) + num_puncts++; + } + + return (num_puncts); +} + + +/********************************************************************** + * case_ok + * + * Check a string to see if it matches a set of lexical rules. + **********************************************************************/ +int case_ok(const char *word) { + static int case_state_table[6][4] = { { + /* 0. Begining 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 + }, + }; + + register int last_state = 0; + register int state = 0; + register int x; + + for (x = 0; x < strlen (word); x++) { + + if (islower (word[x])) + state = case_state_table[state][2]; + else if (isupper (word[x])) + state = case_state_table[state][1]; + else if (isdigit (word[x])) + state = case_state_table[state][3]; + else + state = case_state_table[state][0]; + + if (debug_3) + cprintf ("Case state = %d, char = %c\n", state, word[x]); + + if (state == -1) { + /* Handle ACCRONYMs */ + if (word[x] == 's' && + !isalpha (word[x + 1]) && !isdigit (word[x + 1])) + state = last_state; + else + return (FALSE); + } + + last_state = state; + } + return state != 5; /*single lower is bad */ +} + + +/********************************************************************** + * write_choice_line + * + * Write a blank line to the choices file. This will indicate that + * there is a new word that is following. + **********************************************************************/ +void write_choice_line() { + if (choice_file) { + fprintf (choice_file, "\n"); + fflush(choice_file); + } +} diff --git a/dict/context.h b/dict/context.h new file mode 100644 index 0000000000..abf98810de --- /dev/null +++ b/dict/context.h @@ -0,0 +1,70 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: context.h (Formerly context.h) + * Description: Context checking functions + * Author: Mark Seaman, OCR Technology + * Created: Thu Feb 15 11:18:24 1990 + * Modified: Tue Jul 9 17:00:38 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 CONTEXT_H +#define CONTEXT_H + +#include "choices.h" + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +void close_choices(); + +void fix_quotes(char *str); + +int punctuation_ok(const char *word); + +int case_ok(const char *word); + +void write_choice_line(); + +/* +#if defined(__STDC__) || defined(__cplusplus) +# define _ARGS(s) s +#else +# define _ARGS(s) () +#endif*/ + +/* context.c +void close_choices + _ARGS((void)); + +void fix_quotes + _ARGS((char *str)); + +int punctuation_ok + _ARGS((char *word)); + +int case_ok + _ARGS((char *word)); + +void write_choice_line + _ARGS((void)); + +#undef _ARGS +*/ +#endif diff --git a/dict/dawg.cpp b/dict/dawg.cpp new file mode 100644 index 0000000000..ba0e147d40 --- /dev/null +++ b/dict/dawg.cpp @@ -0,0 +1,366 @@ +/* -*-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 +----------------------------------------------------------------------*/ +#include "dawg.h" +#include "cutil.h" +#include "callcpp.h" +#include "context.h" +#include "strngs.h" + +/*---------------------------------------------------------------------- + V a r i a b l e s +----------------------------------------------------------------------*/ +INT32 debug = 0; +INT32 case_sensative = 0; + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +/********************************************************************** + * edge_char_of + * + * Return the edge that corresponds to the letter out of this node. + **********************************************************************/ +EDGE_REF edge_char_of(EDGE_ARRAY dawg, + NODE_REF node, + int character, + int word_end) { + EDGE_REF edge = node; + + if (! case_sensative) character = tolower (character); + + if (edge_occupied (dawg, edge)) { + do { + if ((edge_letter (dawg, edge) == character) && + (! word_end || end_of_word(dawg,edge))) + return (edge); + + } edge_loop (dawg, edge); + } + + return (NO_EDGE); +} + + +/********************************************************************** + * edges_in_node + * + * Count the number of edges in this node in the DAWG. This includes + * both forward and back links. + **********************************************************************/ +INT32 edges_in_node(EDGE_ARRAY dawg, NODE_REF node) { + EDGE_REF edge = node; + + if (edge_occupied (dawg, edge)) { + edge_loop(dawg, edge); + if (edge_occupied (dawg, edge) && backward_edge (dawg, edge)) { + edge_loop(dawg, edge); + return (edge - node); + } + else { + return (edge - node); + } + } + else { + return (edge - node); + } +} + + +/********************************************************************** + * letter_is_okay + * + * Check this letter in light of the current state. If everything is + * still OK then return TRUE; + **********************************************************************/ +INT32 letter_is_okay(EDGE_ARRAY dawg, + NODE_REF *node, + INT32 char_index, + char prevchar, + const char *word, + INT32 word_end) { + EDGE_REF edge; + STRING dummy_word(word); // Auto-deleting string fixes memory leak. + + if (*node == NO_EDGE) { /* Trailing punctuation */ + if (trailing_punc (dummy_word [char_index]) + && (!trailing_punc (prevchar) + || punctuation_ok(dummy_word.string())>=0)) + return (TRUE); + else + return (FALSE); + } + else { + /* Leading punctuation */ + if (*node == 0 && + char_index != 0 && + isalpha (dummy_word [char_index]) && + ! leading_punc (dummy_word [char_index-1]) && + dummy_word [char_index-1] != '-') { + return (FALSE); + } + } + /* Handle compund words */ + if (dummy_word [char_index] == '-') { + if (char_index>0 && !word_end + && word [char_index-1] == '-' + && word [char_index+1] == '-') + return FALSE; /*not allowed*/ + dummy_word [char_index] = (char) 0; + if (word_in_dawg (dawg, dummy_word.string())) { + dummy_word [char_index] = '-'; + *node = 0; + return (TRUE); + } + else { + dummy_word [char_index] = '-'; + return (FALSE); + } + } + /* Check the DAWG */ + edge = edge_char_of (dawg, *node, dummy_word [char_index], word_end); + + if (edge != NO_EDGE) { /* Normal edge in DAWG */ + if (case_sensative || case_is_okay (dummy_word, char_index)) { + //next_node (dawg, edge); + *node = (dawg)[edge] & NO_EDGE; + return (TRUE); + } + else { + return (FALSE); + } + } + else { + /* Leading punctuation */ + if (leading_punc (word [char_index]) && + (char_index == 0 || leading_punc (dummy_word [char_index-1]))) { + *node = 0; + if (leading_punc (prevchar) || punctuation_ok (word)>=0) + return (TRUE); + else + return FALSE; + } + /* Trailing punctuation */ + if (verify_trailing_punct (dawg, &dummy_word[0], char_index)) { + *node = NO_EDGE; + return (TRUE); + } + + return (FALSE); + } +} + + +/********************************************************************** + * num_forward_edges + * + * Count and return the number of forward edges for this node. + **********************************************************************/ +INT32 num_forward_edges(EDGE_ARRAY dawg, NODE_REF node) { + EDGE_REF edge = node; + INT32 num = 0; + + if (forward_edge (dawg, edge)) { + do { + num++; + } edge_loop (dawg, edge); + } + + return (num); +} + + +/********************************************************************** + * print_dawg_node + * + * Print the contents of one of the nodes in the DAWG. + **********************************************************************/ +void print_dawg_node(EDGE_ARRAY dawg, NODE_REF node) { + 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; + + char ch; + + if (edge_occupied (dawg, edge)) { + do { + if (forward_edge (dawg, edge)) direction = forward_string; + else direction = backward_string; + + if (last_edge (dawg, edge)) is_last = last_string; + else is_last = not_last_string; + + if (end_of_word (dawg, edge)) eow = eow_string; + else eow = not_eow_string; + + ch = edge_letter (dawg, edge); + cprintf ("%7d : next = %7d, char = '%c', %s %s %s\n", + (int) edge, (int) next_node (dawg, edge), ch, + direction, is_last, eow); + + if (edge - node > MAX_NODE_EDGES) return; + } edge_loop (dawg, edge); + + if (edge_occupied (dawg, edge) && backward_edge (dawg, edge)) { + do { + if (forward_edge (dawg, edge)) direction = forward_string; + else direction = backward_string; + + if (last_edge (dawg, edge)) is_last = last_string; + else is_last = not_last_string; + + if (end_of_word (dawg, edge)) eow = eow_string; + else eow = not_eow_string; + + ch = edge_letter (dawg, edge); + cprintf ("%7d : next = %7d, char = '%c', %s %s %s\n", + (int) edge, (int) next_node (dawg, edge), ch, + direction, is_last, eow); + + if (edge - node > MAX_NODE_EDGES) return; + } edge_loop (dawg, edge); + } + } + else { + cprintf ("%5d : no edges in this node\n", node); + } + new_line(); +} + + +/********************************************************************** + * read_squished_dawg + * + * Write the DAWG out to a file + **********************************************************************/ +void read_squished_dawg(char *filename, EDGE_ARRAY dawg, INT32 max_num_edges) { + FILE *file; + EDGE_REF edge; + INT32 num_edges; + INT32 node_count = 0; + + if (debug) print_string ("read_debug"); + + clear_all_edges(dawg, edge, max_num_edges); + + #ifdef __UNIX__ + file = open_file (filename, "r"); + #else + file = open_file (filename, "rb"); + #endif + fseek(file, 0, SEEK_END); + long fsize = ftell(file); + rewind(file); + fread (&num_edges, sizeof (int), 1, file); + // Auto-detect relative endianness of file and OS as future DAWG + // files may be little-endian. + long diff1 = sizeof(EDGE_RECORD)*num_edges + sizeof(int) - fsize; + reverse32(&num_edges); + long diff2 = sizeof(EDGE_RECORD)*num_edges + sizeof(int) - fsize; + reverse32(&num_edges); + // One of diff1 and diff2 should now be 0, but find the smallest + // just in case. + if (diff1 < 0) diff1 = -diff1; + if (diff2 < 0) diff2 = -diff2; + bool swap = diff2 < diff1; + if (swap) + reverse32(&num_edges); + fread (&dawg[0], sizeof (EDGE_RECORD), num_edges, file); + fclose(file); + if (swap) + for (edge=0;edge +#include "general.h" + +/*---------------------------------------------------------------------- + T y p e s +----------------------------------------------------------------------*/ +#define MAX_WERD_LENGTH (INT32) 40 +#define MAX_NODE_EDGES (INT32) 100 +#define LAST_FLAG (INT32) 1 +#define DIRECTION_FLAG (INT32) 2 +#define WERD_END_FLAG (INT32) 4 + +#define FLAG_START_BIT 21 +#define LETTER_START_BIT 24 + +#define NO_EDGE (INT32) 0x1fffff + +typedef UINT32 EDGE_RECORD; +typedef EDGE_RECORD *EDGE_ARRAY; +typedef INT32 EDGE_REF; +typedef INT32 NODE_REF; + +/*--------------------------------------------------------------------- + V a r i a b l e s +----------------------------------------------------------------------*/ +extern INT32 case_sensative; +extern INT32 debug; + +/*---------------------------------------------------------------------- + M a c r o s +----------------------------------------------------------------------*/ +/********************************************************************** + * next_node + * + * The next node visited in the DAWG by following this edge. + **********************************************************************/ + +#define next_node(edges,e) \ +((edges)[e] & NO_EDGE) + +/********************************************************************** + * set_next_edge + * + * Set the next node link for this edge in the DAWG. + **********************************************************************/ + +#define set_next_edge(edges,e,value) \ +((edges)[e] = ((edges)[e] & (INT32) 0xffe00000) |\ + (value & NO_EDGE)) + +/********************************************************************** + * set_empty_edge + * + * Return TRUE if this edge spot in this location is unoccupied. + **********************************************************************/ + +#define set_empty_edge(edges,e) \ +((edges)[e] = NO_EDGE) + +/********************************************************************** + * clear_all_edges + * + * Go through all the edges in the DAWG and clear out each one. + **********************************************************************/ + +#define clear_all_edges(dawg,edge,max_num_edges) \ +for (edge=0; edge> LETTER_START_BIT) + +/********************************************************************** + * last_edge + * + * Return TRUE if this edge is the last edge in the sequence. This is + * TRUE for the last one in both the forward and backward part. + **********************************************************************/ + +#define last_edge(edges,e) \ +((edges)[e] & (LAST_FLAG << FLAG_START_BIT)) + +/********************************************************************** + * end_of_word + * + * Return TRUE if this edge marks the end of a word. + **********************************************************************/ + +#define end_of_word(edges,e) \ +((edges)[e] & (WERD_END_FLAG << FLAG_START_BIT)) + +/********************************************************************** + * forward_edge + * + * Return TRUE if this edge is in the forward direction. + **********************************************************************/ + +#define forward_edge(edges,e) \ +((edges)[e] & (DIRECTION_FLAG << FLAG_START_BIT) && \ + edge_occupied (edges,e)) + +/********************************************************************** + * backward_edge + * + * Return TRUE if this edge is in the backward direction. + **********************************************************************/ + +#define backward_edge(edges,e) \ +(! ((edges)[e] & (DIRECTION_FLAG << FLAG_START_BIT)) && \ + edge_occupied (edges,e)) + +/********************************************************************** + * edge_loop + * + * Loop for each of the edges in the forward direction. This macro + * can be used in the following way: + *********************************************************************/ + +#define edge_loop(edges,e) \ +while (! last_edge (edges,e++)) + +/********************************************************************** + * case_is_okay + * + * Check the case of this character in the character string to make + * sure that there is not a problem with the case. + **********************************************************************/ + +#define case_is_okay(word,i) \ +(i ? \ + ((isupper(word[i]) && islower(word[i-1])) ? \ + FALSE : \ + ((islower(word[i]) && isupper(word[i-1]) && \ + i>1 && isalpha (word[i-2])) ? \ + FALSE : \ + TRUE)) : \ + TRUE) + +/********************************************************************** + * trailing_punc + * + * Check for leading punctuation. + **********************************************************************/ + +#define trailing_punc(ch) \ +((ch == '}' ) || \ + (ch == ':' ) || \ + (ch == ';' ) || \ + (ch == '-' ) || \ + (ch == ']' ) || \ + (ch == '!' ) || \ + (ch == '?' ) || \ + (ch == '`' ) || \ + (ch == ',' ) || \ + (ch == '.' ) || \ + (ch == ')' ) || \ + (ch == '\"' ) || \ + (ch == '\'' )) + +/********************************************************************** + * leading_punc + * + * Check for leading punctuation. + **********************************************************************/ + +#define leading_punc(ch) \ +((ch == '\"' ) || \ + (ch == '(' ) || \ + (ch == '{' ) || \ + (ch == '[' ) || \ + (ch == '`' ) || \ + (ch == '\'' )) + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +EDGE_REF edge_char_of(EDGE_ARRAY dawg, + NODE_REF node, + int character, + int word_end); + +INT32 edges_in_node(EDGE_ARRAY dawg, NODE_REF node); + +INT32 letter_is_okay(EDGE_ARRAY dawg, + NODE_REF *node, + INT32 char_index, + char prevchar, + const char *word, + INT32 word_end); + +INT32 num_forward_edges(EDGE_ARRAY dawg, NODE_REF node); + +void print_dawg_node(EDGE_ARRAY dawg, NODE_REF node); + +void read_squished_dawg(char *filename, EDGE_ARRAY dawg, INT32 max_num_edges); + +INT32 verify_trailing_punct(EDGE_ARRAY dawg, char *word, INT32 char_index); + +INT32 word_in_dawg(EDGE_ARRAY dawg, const char *string); + +/* +#if defined(__STDC__) || defined(__cplusplus) || MAC_OR_DOS +# define _ARGS(s) s +#else +# define _ARGS(s) () +#endif*/ + +/* dawg.c +EDGE_REF edge_char_of + _ARGS((EDGE_ARRAY dawg, + NODE_REF node, + int character, + int word_end)); + +INT32 edges_in_node + _ARGS((EDGE_ARRAY dawg, + NODE_REF node)); + +INT32 letter_is_okay + _ARGS((EDGE_ARRAY dawg, + NODE_REF *node, + INT32 char_index, + char *word, + INT32 word_end)); + +INT32 num_forward_edges + _ARGS((EDGE_ARRAY dawg, + NODE_REF node)); + +void print_dawg_node + _ARGS((EDGE_ARRAY dawg, + NODE_REF node)); + +void read_squished_dawg + _ARGS((char *filename, + EDGE_ARRAY dawg, + INT32 max_num_edges)); + +INT32 verify_trailing_punct + _ARGS((EDGE_ARRAY dawg, + char *word, + INT32 char_index)); + +INT32 word_in_dawg + _ARGS((EDGE_ARRAY dawg, + char *string)); + +#undef _ARGS +*/ +#endif diff --git a/dict/hyphen.cpp b/dict/hyphen.cpp new file mode 100644 index 0000000000..1fcc2e8a8b --- /dev/null +++ b/dict/hyphen.cpp @@ -0,0 +1,70 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: hyphen.c (Formerly hyphen.c) + * Description: + * 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. + * + *********************************************************************************/ +/*---------------------------------------------------------------------- + I n c l u d e s +----------------------------------------------------------------------*/ +#include "const.h" +#include "hyphen.h" +#include "tordvars.h" +#include "callcpp.h" +#include + +/*---------------------------------------------------------------------- + V a r i a b l e s +----------------------------------------------------------------------*/ +int last_word_on_line = 0; +char *hyphen_string = 0; +float hyphen_rating = MAXFLOAT; +int hyphen_state = 0; + +/*---------------------------------------------------------------------- + F u n c t i o n s +---------------------------------------------------------------------*/ +/********************************************************************** + * set_hyphen_word + * + * If this hyphenated word choice is better than the last one then add + * it as the new word choice. This string can be used on the next + * line to permute the other half of the word. + **********************************************************************/ +void set_hyphen_word(char *word, float rating, int state) { + int char_index = strlen (word) - 1; + + if (display_ratings) + cprintf ("set hyphen word = %s\n", word); + + if (hyphen_rating > rating && char_index > 0) { + word[char_index] = '\0'; + + if (hyphen_string) + strfree(hyphen_string); + hyphen_string = strsave (word); + + hyphen_state = state; + hyphen_rating = rating; + + word[char_index] = '-'; + } +} diff --git a/dict/hyphen.h b/dict/hyphen.h new file mode 100644 index 0000000000..ea978e5858 --- /dev/null +++ b/dict/hyphen.h @@ -0,0 +1,114 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: hyphen.h (Formerly hyphen.h) + * Description: + * Author: Mark Seaman, SW Productivity + * Created: Fri Oct 16 14:37:00 1987 + * Modified: Mon Jan 14 17:52:50 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 HYPHEN_H +#define HYPHEN_H + +/*---------------------------------------------------------------------- + I n c l u d e s +----------------------------------------------------------------------*/ +#include "choices.h" + +/*---------------------------------------------------------------------- + V a r i a b l e s +----------------------------------------------------------------------*/ +extern int last_word_on_line; +extern char *hyphen_string; +extern float hyphen_rating; +extern int hyphen_state; + +/*---------------------------------------------------------------------- + M a c r o s +----------------------------------------------------------------------*/ +/********************************************************************** + * set_last_word + * + * Set the flag that indicated that this is the last word on a line. + **********************************************************************/ + +#define set_last_word() \ +last_word_on_line = TRUE + +/********************************************************************** + * reset_hyphen_word + * + * Erase the hyphenation word that my have been stored at this location. + **********************************************************************/ + +#define reset_hyphen_word() \ +if (last_word_on_line == FALSE) { \ + if (hyphen_string) strfree (hyphen_string); \ + hyphen_string = NULL; \ + hyphen_rating = MAX_FLOAT32; \ + hyphen_state = 0; \ +} \ + + +/********************************************************************** + * reset_last_word + * + * Reset the flag that indicated that this is the last word on a line. + **********************************************************************/ + +#define reset_last_word() \ +last_word_on_line = FALSE + +/********************************************************************** + * is_last_word + * + * Test the flag that indicated that this is the last word on a line. + **********************************************************************/ + +#define is_last_word() \ +(last_word_on_line) + +/********************************************************************** + * hyphen_base_size + * + * Size of the base word (the part on the line before) of a hyphenated + * coumpound word. + **********************************************************************/ + +#define hyphen_base_size() \ +((! is_last_word () && hyphen_string) ? \ + (strlen (hyphen_string)) : \ + (0)) \ + + +/********************************************************************** + * hyphen_tail + * + * Return the a pointer to the part of the word that was not on the + * previous line. This routine is used for words that were split + * between lines and hyphenated. + **********************************************************************/ + +#define hyphen_tail(word) \ +(& word [hyphen_base_size()]) \ + +/*---------------------------------------------------------------------- + Public Function Prototypes +----------------------------------------------------------------------*/ +void set_hyphen_word(char *word, float rating, int state); +#endif diff --git a/dict/matchdefs.h b/dict/matchdefs.h new file mode 100644 index 0000000000..0620f0c5f9 --- /dev/null +++ b/dict/matchdefs.h @@ -0,0 +1,143 @@ +/****************************************************************************** + ** 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 "general.h" +#include + +/* define the maximum number of classes defined for any matcher + and the maximum class id for any matcher */ +#define MAX_NUM_CLASSES 100 +#define MAX_CLASS_ID 255 + +/* a CLASS_ID is the ascii character to be associated with a class */ +typedef UINT8 CLASS_ID; +#define NO_CLASS 0 + +/* define a type for the index (rather than the class id) of a class. + Class indexes are sequentially defined, while class id's are defined + by the ascii character set. */ +typedef INT16 CLASS_INDEX; +typedef CLASS_INDEX CLASS_TO_INDEX[MAX_CLASS_ID + 1]; +typedef CLASS_ID INDEX_TO_CLASS[MAX_NUM_CLASSES]; +#define ILLEGAL_CLASS (-1) + +/* 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. + +void InitClassifier (); + +void InitClassifierVars (); + +int TweekClassifier (char *Params); + +void InitQuickGuess (FEATURE_STRUCT *CharFeatures); + +CLASS_ID NextQuickGuess (); + +void MatchCharToClass (CLASS_ID + ClassID, + FEATURE_STRUCT + *CharFeatures, + MATCH_RESULT + *MatchResult); + +void DebugMatch (CLASS_ID + ClassID, + FEATURE_STRUCT + *CharFeatures, + MATCH_RESULT + *MatchResult); + +*/ + +/* 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 diff --git a/dict/permdawg.cpp b/dict/permdawg.cpp new file mode 100644 index 0000000000..72f17e7edd --- /dev/null +++ b/dict/permdawg.cpp @@ -0,0 +1,352 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: permdawg.c (Formerly permdawg.c) + * Description: + * 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 "permdawg.h" +#include "debug.h" +#include "hyphen.h" +#include "permute.h" +#include "tordvars.h" +#include "context.h" +#include "stopper.h" +#include "freelist.h" +#include "globals.h" +#include "dawg.h" +#include + +/*---------------------------------------------------------------------- + T y p e s +----------------------------------------------------------------------*/ +#define FREQ_WERD 1.0 +#define GOOD_WERD 1.1 +#define OK_WERD 1.25 +#define MAX_FREQ_EDGES 1000 +#define NO_RATING -1 + +/*---------------------------------------------------------------------- + V a r i a b l e s +----------------------------------------------------------------------*/ +static EDGE_ARRAY frequent_words; +static float rating_margin; +static float rating_pad = 5.0; + +make_toggle_var (dawg_debug, 0, make_dawg_debug, +8, 10, set_dawg_debug, "DAWG Debug "); + +make_float_var (ok_word, OK_WERD, make_ok_word, +8, 17, set_ok_word, "Bad word adjustment"); + +make_float_var (good_word, GOOD_WERD, make_good_word, +8, 18, set_good_word, "Good word adjustment"); + +make_float_var (freq_word, FREQ_WERD, make_freq_word, +8, 19, set_freq_word, "Freq word adjustment"); + +//extern char *demodir; + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +/********************************************************************** + * adjust_word + * + * Assign an adjusted value to a string that is a word. The value + * that this word choice has is based on case and punctuation rules. + **********************************************************************/ +void adjust_word(A_CHOICE *best_choice, float *certainty_array) { + char *this_word; + int punct_status; + float adjust_factor; + + if (adjust_debug) + cprintf ("%s %4.2f ", + class_string (best_choice), class_probability (best_choice)); + + this_word = class_string (best_choice); + punct_status = punctuation_ok (this_word); + + class_probability (best_choice) += RATING_PAD; + if (case_ok (this_word) && punct_status != -1) { + if (punct_status < 1 && word_in_dawg (frequent_words, this_word)) { + class_probability (best_choice) *= freq_word; + class_permuter (best_choice) = FREQ_DAWG_PERM; + adjust_factor = freq_word; + if (adjust_debug) + cprintf (", F, %4.2f ", freq_word); + } + else { + class_probability (best_choice) *= good_word; + adjust_factor = good_word; + if (adjust_debug) + cprintf (", %4.2f ", good_word); + } + } + else { + class_probability (best_choice) *= ok_word; + adjust_factor = ok_word; + if (adjust_debug) { + if (!case_ok (this_word)) + cprintf (", C"); + if (punctuation_ok (this_word) == -1) + cprintf (", P"); + cprintf (", %4.2f ", ok_word); + } + } + + class_probability (best_choice) -= RATING_PAD; + + LogNewWordChoice(best_choice, adjust_factor, certainty_array); + + if (adjust_debug) + cprintf (" --> %4.2f\n", class_probability (best_choice)); +} + + +/********************************************************************** + * append_next_choice + * + * Check to see whether or not the next choice is worth appending to + * the string being generated. If so then keep going deeper into the + * word. + **********************************************************************/ +void append_next_choice( /*previous option */ + EDGE_ARRAY dawg, + NODE_REF node, + char permuter, + char *word, + CHOICES_LIST choices, + int char_index, + A_CHOICE *this_choice, + char prevchar, + float *limit, + float rating, + float certainty, + float *rating_array, + float *certainty_array, + int word_ending, + int last_word, + CHOICES *result) { + A_CHOICE *better_choice; + /* Add new character */ + word[char_index] = class_string (this_choice)[0]; + word[char_index + 1] = 0; + if (word[char_index] == 0) + word[char_index] = ' '; + certainty_array[char_index] = class_certainty (this_choice); + + rating += class_probability (this_choice); + certainty = min (class_certainty (this_choice), certainty); + + if (rating_array[char_index] == NO_RATING) { + /* Prune bad subwords */ + rating_array[char_index] = rating; + } + else { + if (rating_array[char_index] * rating_margin + rating_pad < rating) { + if (dawg_debug) + cprintf ("early pruned word (%s, rating=%4.2f, limit=%4.2f)\n", + word, rating, *limit); + return; + } + } + + /* Deal with hyphens */ + if (word_ending && last_word && word[char_index] == '-' && char_index > 0) { + *limit = rating; + if (dawg_debug) + cprintf ("new hyphen choice = %s\n", word); + + better_choice = new_choice (word, rating, certainty, -1, permuter); + adjust_word(better_choice, certainty_array); + push_on(*result, better_choice); + set_hyphen_word(word, rating, node); + } + /* Look up char in DAWG */ + else if (letter_is_okay (dawg, &node, char_index, prevchar, + word, word_ending)) { + /* Add a new word choice */ + if (word_ending) { + if (dawg_debug == 1) + cprintf ("new choice = %s\n", word); + *limit = rating; + + better_choice = new_choice (hyphen_tail (word), rating, certainty, + -1, permuter); + adjust_word (better_choice, &certainty_array[hyphen_base_size ()]); + push_on(*result, better_choice); + } + else { + /* Search the next letter */ + JOIN_ON (*result, + dawg_permute (dawg, node, permuter, + choices, char_index + 1, limit, + word, rating, certainty, + rating_array, certainty_array, last_word)); + } + } +} + + +/********************************************************************** + * dawg_permute + * + * Permute all the valid words that can be created with this starting + * point. The node (in the DAWG) and the word string define a base + * from which to start adding the remaining character choices. + **********************************************************************/ +CHOICES dawg_permute(EDGE_ARRAY dawg, + NODE_REF node, + char permuter, + CHOICES_LIST choices, + int char_index, + float *limit, + char *word, + float rating, + float certainty, + float *rating_array, + float *certainty_array, + int last_word) { + CHOICES result = NIL; + CHOICES c; + char *prevchar; + int word_ending = FALSE; + + if (dawg_debug) { + cprintf ("dawg_permute (node=%d, char_index=%d, limit=%4.2f, ", + node, char_index, *limit); + cprintf ("word=%s, rating=%4.2f, certainty=%4.2f)\n", + word, rating, certainty); + } + /* Check for EOW */ + if (1 + char_index == array_count (choices) + hyphen_base_size ()) + word_ending = TRUE; + + if (char_index < array_count (choices) + hyphen_base_size ()) { + prevchar = NULL; + iterate_list (c, + (CHOICES) array_index (choices, + char_index - hyphen_base_size ())) { + append_next_choice (dawg, node, permuter, word, choices, char_index, + (A_CHOICE *) first (c), + prevchar != NULL ? *prevchar : '\0', limit, + rating, certainty, rating_array, certainty_array, + word_ending, last_word, &result); + prevchar = best_string (c); + } + } + + if (result && (dawg_debug == 1)) + print_choices ("dawg_permute", result); + return (result); +} + + +/********************************************************************** + * dawg_permute_and_select + * + * Use a DAWG type data structure to enumerate all the valid strings + * in some gramar. Compare each of the choices against the best choice + * so far. Update the best choice if needed. + **********************************************************************/ +void dawg_permute_and_select(const char *string, + EDGE_ARRAY dawg, + char permuter, + CHOICES_LIST character_choices, + A_CHOICE *best_choice, + INT16 system_words) { + CHOICES result = NIL; + char word[MAX_WERD_LENGTH + 1]; + float certainty_array[MAX_WERD_LENGTH + 1]; + float rating_array[MAX_WERD_LENGTH + 1]; + float rating; + int char_index; + NODE_REF dawg_node = 0; + + /* Pruning margin ratio */ + rating_margin = ok_word / good_word; + + word[0] = '\0'; + rating = class_probability (best_choice); + + for (char_index = 0; char_index < MAX_WERD_LENGTH + 1; char_index++) + rating_array[char_index] = NO_RATING; + char_index = 0; + + if (!is_last_word () && hyphen_string) { + strcpy(word, hyphen_string); + char_index = strlen (hyphen_string); + if (system_words) + dawg_node = hyphen_state; + } + result = dawg_permute (dawg, dawg_node, permuter, character_choices, + char_index, &rating, word, 0.0, 0.0, + rating_array, certainty_array, is_last_word ()); + + if (display_ratings && result) + print_choices(string, result); + + while (result != NIL) { + if (best_probability (result) < class_probability (best_choice)) { + clone_choice (best_choice, first (result)); + } + free_choice (first (result)); + pop_off(result); + } +} + + +/********************************************************************** + * init_permdawg + * + * Initialize the variables needed by this file. + **********************************************************************/ +void init_permdawg() { + char name[1024]; + make_dawg_debug(); + make_ok_word(); + make_good_word(); + make_freq_word(); + + frequent_words = (EDGE_ARRAY) memalloc (sizeof (EDGE_RECORD) * + MAX_FREQ_EDGES); + strcpy(name, demodir); + strcat (name, "tessdata/freq-dawg"); + read_squished_dawg(name, frequent_words, MAX_FREQ_EDGES); +} + +void end_permdawg() { + memfree(frequent_words); + frequent_words = NULL; +} + +/********************************************************************** + * test_freq_words() + * + * Tests a word against the frequent word dawg + **********************************************************************/ +int test_freq_words(const char *word) { + return (word_in_dawg (frequent_words, word)); +} diff --git a/dict/permdawg.h b/dict/permdawg.h new file mode 100644 index 0000000000..0f41732107 --- /dev/null +++ b/dict/permdawg.h @@ -0,0 +1,94 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: permdawg.h (Formerly permdawg.h) + * Description: + * Author: Mark Seaman, SW Productivity + * Created: Fri Oct 16 14:37:00 1987 + * Modified: Mon May 20 16:45:29 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 PERMDAWG_H +#define PERMDAWG_H + +/*---------------------------------------------------------------------- + I n c l u d e s +----------------------------------------------------------------------*/ +#include "dawg.h" +#include "choices.h" +#include "choicearr.h" + +/*--------------------------------------------------------------------- + V a r i a b l e s +----------------------------------------------------------------------*/ +extern int dawg_debug; +extern float ok_word; +extern float good_word; +extern float freq_word; + +/*---------------------------------------------------------------------- + M a c r o s +----------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------- + Public Function Prototypes +----------------------------------------------------------------------*/ +void adjust_word(A_CHOICE *best_choice, float *certainty_array); + + /*previous option */ +void append_next_choice(EDGE_ARRAY dawg, + NODE_REF node, + char permuter, + char *word, + CHOICES_LIST choices, + int char_index, + A_CHOICE *this_choice, + char prevchar, + float *limit, + float rating, + float certainty, + float *rating_array, + float *certainty_array, + int word_ending, + int last_word, + CHOICES *result); + +CHOICES dawg_permute(EDGE_ARRAY dawg, + NODE_REF node, + char permuter, + CHOICES_LIST choices, + int char_index, + float *limit, + char *word, + float rating, + float certainty, + float *rating_array, + float *certainty_array, + int last_word); + +void dawg_permute_and_select(const char *string, + EDGE_ARRAY dawg, + char permuter, + CHOICES_LIST character_choices, + A_CHOICE *best_choice, + INT16 system_words); + +void init_permdawg(); +void end_permdawg(); + +int test_freq_words(const char *word); +#endif diff --git a/dict/permnum.cpp b/dict/permnum.cpp new file mode 100644 index 0000000000..2c1afbc317 --- /dev/null +++ b/dict/permnum.cpp @@ -0,0 +1,483 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: permnum.c (Formerly permnum.c) + * Description: + * Author: Mark Seaman, OCR Technology + * Created: Fri Oct 16 14:37:00 1987 + * Modified: Tue Jul 2 14:12: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. + * + *********************************************************************************/ +/*---------------------------------------------------------------------- + I n c l u d e s +----------------------------------------------------------------------*/ +#include "const.h" +#include "permnum.h" +#include "debug.h" +#include "permute.h" +#include "dawg.h" +#include "tordvars.h" +#include "stopper.h" + +#include +#include + +/*---------------------------------------------------------------------- + V a r i a b l e s +----------------------------------------------------------------------*/ +static const char *allowed_alpha_strs[] = { + "jan", "feb", "mar", "apr", "may", "jun", + "jul", "aug", "sep", "oct", "nov", "dec", NULL +}; + +static const char *allowed_char_strs[] = { + "adfjmnos", "aceopu", "bcglnrptvy" +}; + +const int kNumStates = 7; + +static int number_state_table[kNumStates][8] = { { + /* 0. Beginning of string */ + /* l d o a t 1 2 3 */ + 0, 1, 1, -99, -99, 4, -99, -99 + }, + { /* 1. After a digit or operator */ + -99, 1, 1, 3, 2, 4, 3, 3 + }, + { /* 2. After trailing punctuation */ + -99, -99, 1, -99, 2, -99, -99, -99 + }, + { /* 3. After a alpha character */ + -99, -99, 3, 3, 2, 3, 3, 3 + }, + { /* 4. After 1st char */ + -99, -1, -1, -99, -2, -99, 5, -99 + }, + { /* 5. After 2nd char */ + -99, -1, -1, -99, -2, -99, -99, 6 + }, + { /* 6. After 3rd char */ + -99, -1, -1, -99, -2, -99, -99, -99 + } +}; + +// The state is coded with its true state shifted left by kStateShift. +// A repeat count (starting with 0) is stored in the lower bits +// No state is allowed to occur more than kMaxRepeats times. +const int kStateShift = 4; +const int kRepeatMask = (1 << kStateShift) - 1; + +const int kMaxRepeats[kNumStates] = { + 3, 10, 3, 3, 3, 3, 3 +}; + +make_float_var (good_number, GOOD_NUMBER, make_good_number, +8, 15, set_good_number, "Good number adjustment"); + +make_float_var (ok_number, OK_NUMBER, make_ok_number, +8, 16, set_ok_number, "Bad number adjustment"); + +make_toggle_var (number_debug, 0, make_number_debug, +8, 23, set_number_debug, "Number debug"); + +make_int_var (number_depth, 3, make_number_depth, +8, 24, set_number_depth, "Number depth"); + +/*---------------------------------------------------------------------- + M a c r o s +----------------------------------------------------------------------*/ +/********************************************************************** + * isleading + * + * Return non-zero if this is a leading type punctuation mark for the + * numeric grammar. + **********************************************************************/ + +#define isleading(ch) \ +((ch == '{' ) || \ + (ch == '[' ) || \ + (ch == '(' ) || \ + (ch == '#' ) || \ + (ch == '@' ) || \ + (ch == '$' )) + +/********************************************************************** + * istrailing + * + * Return non-zero if this is a leading type punctuation mark for the + * numeric grammar. + **********************************************************************/ + +#define istrailing(ch) \ +((ch == '}' ) || \ + (ch == ']' ) || \ + (ch == ')' ) || \ + (ch == ';' ) || \ + (ch == ':' ) || \ + (ch == ',' ) || \ + (ch == '.' ) || \ + (ch == '%' )) + +/********************************************************************** + * isoperator + * + * Return non-zero if this is a leading type punctuation mark for the + * numeric grammar. + **********************************************************************/ + +#define isoperator(ch) \ +((ch == '*' ) || \ + (ch == '+' ) || \ + (ch == '-' ) || \ + (ch == '/' ) || \ + (ch == '.' ) || \ + (ch == ':' ) || \ + (ch == ',' )) + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +/********************************************************************** + * adjust_number + * + * Assign an adjusted value to a string that is a word. The value + * that this word choice has is based on case and punctuation rules. + **********************************************************************/ +void adjust_number(A_CHOICE *best_choice, float *certainty_array) { + float adjust_factor; + + if (adjust_debug) + cprintf ("Number: %s %4.2f ", + class_string (best_choice), class_probability (best_choice)); + + class_probability (best_choice) += RATING_PAD; + if (pure_number (class_string (best_choice))) { + class_probability (best_choice) *= good_number; + adjust_factor = good_number; + if (adjust_debug) + cprintf (", %4.2f ", good_number); + } + else { + class_probability (best_choice) *= ok_number; + adjust_factor = ok_number; + if (adjust_debug) + cprintf (", N, %4.2f ", ok_number); + } + + class_probability (best_choice) -= RATING_PAD; + LogNewWordChoice(best_choice, adjust_factor, certainty_array); + if (adjust_debug) + cprintf (" --> %4.2f\n", class_probability (best_choice)); +} + + +/********************************************************************** + * append_number_choices + * + * Check to see whether or not the next choice is worth appending to + * the string being generated. If so then keep going deeper into the + * word. + **********************************************************************/ +void append_number_choices(int state, + char *word, + CHOICES_LIST choices, + int char_index, + A_CHOICE *this_choice, + float *limit, + float rating, + float certainty, + float *certainty_array, + CHOICES *result) { + int word_ending = FALSE; + int x; + + if (char_index == (array_count (choices) - 1)) + word_ending = TRUE; + + word[char_index] = class_string (this_choice)[0]; + word[char_index + 1] = '\0'; + if (word[char_index] == '\0') + word[char_index] = ' '; + certainty_array[char_index] = class_certainty (this_choice); + + rating += class_probability (this_choice); + certainty = min (class_certainty (this_choice), certainty); + + if (rating < *limit) { + + state = number_state_change (state, word + char_index); + if (number_debug) + cprintf ("%-20s prob=%4.2f state=%d\n", word, rating, state); + + if (state != -1) { + + if ((state >> kStateShift) == 3 && + char_index + 3 < array_count (choices)) { + return; + } + + if (word_ending) { + for (x = 0; x <= char_index; x++) { + if (isdigit (word[x])) { + if (number_debug) + cprintf ("new choice = %s\n", word); + push_on (*result, new_choice (word, rating, certainty, + -1, NUMBER_PERM)); + adjust_number ((A_CHOICE *) first (*result), + certainty_array); + if (best_probability (*result) > *limit) { + free_choice (first (*result)); + pop_off(*result); + } + else { + *limit = best_probability (*result); + break; + } + } + } + } + else { + JOIN_ON (*result, + number_permute (state, choices, char_index + 1, limit, + word, rating, certainty, + certainty_array)); + } + } + } + else { + if (number_debug) + cprintf ("pruned word (%s, rating=%4.2f, limit=%4.2f)\n", + word, rating, *limit); + } +} + + +/********************************************************************** + * init_permute + * + * Initialize anything that needs to be set up for the permute + * functions. + **********************************************************************/ +void init_permnum() { + make_good_number(); + make_ok_number(); + make_number_debug(); + make_number_depth(); +} + + +/********************************************************************** + * number_character_type + * + * Decide which type of a character (with regard to the numeric state + * table) we are looking at. + **********************************************************************/ +int number_character_type( //current state + char ch, + int state) { + char lower_char = tolower (ch); + + if (isalpha (ch)) { + if (state < 4 && strchr (allowed_char_strs[0], lower_char) != NULL) + return 5; + else if (state == 4 + && strchr (allowed_char_strs[1], lower_char) != NULL) + return 6; + else if (state == 5 + && strchr (allowed_char_strs[2], lower_char) != NULL) + return 7; + return 3; + } + else if (isdigit (ch)) + return (1); + else if (isoperator (ch)) + return (2); + else if (istrailing (ch)) + return (4); + else if (isleading (ch)) + return (0); + else + return (-1); +} + + +/********************************************************************** + * number_state_change + * + * Execute a state transition according to the state table and + * additional rules. + **********************************************************************/ +int number_state_change(int state, //current state + const char *word) { //current char + int char_type; //type of char + int new_state; //state to return + int old_state = state >> kStateShift; + int repeats = state & kRepeatMask; + int index; + char copy_word[4]; //tolowered chars + + char_type = number_character_type (*word, old_state); + if (char_type == -1) + return -1; + new_state = number_state_table[old_state][char_type]; + if (new_state == old_state) { + ++repeats; + if (repeats >= kMaxRepeats[old_state]) + return -1; + } else { + repeats = 0; + } + if (new_state >= 0) + return (new_state << kStateShift) | repeats; + if (new_state == -99) + return -1; + + //now check to see if the last state-3 chars in the word + //make an allowable word. For now only 3 letter words + //are allowed + if (old_state != 6) + return -1; //only 3 letters now + copy_word[0] = tolower (word[-3]); + copy_word[1] = tolower (word[-2]); + copy_word[2] = tolower (word[-1]); + copy_word[3] = '\0'; + for (index = 0; allowed_alpha_strs[index] != NULL; index++) { + if (strcmp (copy_word, allowed_alpha_strs[index]) == 0) + return (-new_state) << kStateShift; + } + return -1; //not a good word +} + + +/********************************************************************** + * number_permute + * + * Permute all the valid string that match the 'grammar' of numbers. + * The valid syntax for numbers is encoded in a state table. The + * permuter uses this state table to enumerate all the string that + * can be produced using the input choices. + **********************************************************************/ +CHOICES number_permute(int state, + CHOICES_LIST choices, + int char_index, + float *limit, + char *word, + float rating, + float certainty, + float *certainty_array) { + CHOICES result = NIL; + CHOICES c; + int depth = 0; + + if (number_debug) { + cprintf ("number_permute (state=%d, char_index=%d, limit=%4.2f, ", + state, char_index, *limit); + cprintf ("word=%s, rating=%4.2f, certainty=%4.2f)\n", + word, rating, certainty); + } + if (char_index < array_count (choices)) { + iterate_list (c, (CHOICES) array_index (choices, char_index)) { + if (depth++ < number_depth) + append_number_choices (state, word, choices, char_index, + (A_CHOICE *) first (c), limit, rating, + certainty, certainty_array, &result); + } + } + if (result && number_debug == 1) + print_choices ("number_permute:", result); + return (result); +} + + +/********************************************************************** + * number_permute_and_select + * + * Permute all the possible valid numbers and adjust their ratings. + * Save the best rating. + **********************************************************************/ +A_CHOICE *number_permute_and_select(CHOICES_LIST char_choices, + float rating_limit) { + CHOICES result = NIL; + char word[MAX_WERD_LENGTH + 1]; + float certainty_array[MAX_WERD_LENGTH + 1]; + float rating = rating_limit; + A_CHOICE *best_choice; + + best_choice = new_choice (NULL, MAXFLOAT, -MAXFLOAT, -1, NO_PERM); + + if (array_count (char_choices) <= MAX_WERD_LENGTH) { + word[0] = '\0'; + result = number_permute (0, char_choices, 0, &rating, + word, 0.0, 0.0, certainty_array); + + if (display_ratings && result) + print_choices ("number_permuter", result); + + while (result != NIL) { + if (best_probability (result) < class_probability (best_choice)) { + clone_choice (best_choice, first (result)); + } + free_choice (first (result)); + pop_off(result); + } + } + return (best_choice); +} + + +/********************************************************************** + * pure_number + * + * Check to see if this string is a pure number (one that does not end + * with alphabetic characters). + **********************************************************************/ +int pure_number(const char *string) { + int x; + + for (x = strlen (string) - 1; x >= 0; x--) { + if (isdigit (string[x])) { + return (TRUE); + } + else if (isalpha (string[x])) + return (FALSE); + } + return (FALSE); +} + + +/********************************************************************** + * valid_number + * + * Check this string to see if it is a valid number. Return TRUE if + * it is. + **********************************************************************/ +int valid_number(const char *string) { + int state = 0; + int char_index; + int num_chars = strlen (string); + int num_digits = 0; + + for (char_index = 0; char_index < num_chars; char_index++) { + + state = number_state_change (state, string + char_index); + if (state == -1) + return (FALSE); + if (isdigit (string[char_index])) + num_digits++; + } + return num_digits > num_chars - num_digits; +} diff --git a/dict/permnum.h b/dict/permnum.h new file mode 100644 index 0000000000..0b097173fe --- /dev/null +++ b/dict/permnum.h @@ -0,0 +1,79 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: permnum.h (Formerly permnum.h) + * Description: + * Author: Mark Seaman, SW Productivity + * Created: Fri Oct 16 14:37:00 1987 + * Modified: Mon May 20 16:30:03 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 PERMNUM_H +#define PERMNUM_H + +/*---------------------------------------------------------------------- + I n c l u d e s +----------------------------------------------------------------------*/ +#include "choicearr.h" + +/*---------------------------------------------------------------------- + V a r i a b l e s +----------------------------------------------------------------------*/ +#define GOOD_NUMBER 1.1 +#define OK_NUMBER 1.4 +extern float ok_number; +extern float good_number; + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +void adjust_number(A_CHOICE *best_choice, float *certainty_array); + +void append_number_choices(int state, + char *word, + CHOICES_LIST choices, + int char_index, + A_CHOICE *this_choice, + float *limit, + float rating, + float certainty, + float *certainty_array, + CHOICES *result); + +void init_permnum(); + +int number_character_type(char ch, int state); + + //current state +int number_state_change(int state, const char *word); + +CHOICES number_permute(int state, + CHOICES_LIST choices, + int char_index, + float *limit, + char *word, + float rating, + float certainty, + float *certainty_array); + +A_CHOICE *number_permute_and_select(CHOICES_LIST char_choices, + float rating_limit); + +int pure_number(const char *string); + +int valid_number(const char *string); +#endif diff --git a/dict/permute.cpp b/dict/permute.cpp new file mode 100644 index 0000000000..75a56a9ba2 --- /dev/null +++ b/dict/permute.cpp @@ -0,0 +1,1587 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: permute.c (Formerly permute.c) + * Description: Handle the new ratings choices for Wise Owl + * Author: Mark Seaman, OCR Technology + * Created: Fri Sep 22 14:05:51 1989 + * Modified: Thu Jan 3 16:38:46 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Experimental (Do Not Distribute) + * + * (c) Copyright 1989, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + *********************************************************************************/ +/*---------------------------------------------------------------------- + I n c l u d e s +---------------------------------------------------------------------*/ +#include "permute.h" +#include "globals.h" +#include "permdawg.h" +#include "debug.h" +#include "tordvars.h" +#include "hyphen.h" +#include "stopper.h" +#include "trie.h" +#include "context.h" +#include "permnum.h" +#include "freelist.h" +#include "callcpp.h" + +#include + +int permutation_count; // Used in metrics.cpp. +/*---------------------------------------------------------------------- + V a r i a b l e s +----------------------------------------------------------------------*/ +#define MAX_NUM_EDGES 60000 +#define MAX_DOC_EDGES 250000 +#define RESERVED_DOC_EDGES 10000 +#define MAX_USER_EDGES 20000 +#define USER_RESERVED_EDGES 2000 + /* Weights for adjustment */ +#define NON_WERD 1.25 +#define GARBAGE_STRING 1.5 +#define MAX_PERM_LENGTH 128 + +static EDGE_ARRAY pending_words; +static EDGE_ARRAY document_words; +static EDGE_ARRAY user_words; +static EDGE_ARRAY word_dawg; + +make_toggle_var (adjust_debug, 0, make_adjust_debug, +8, 13, set_adjust_debug, "Adjustment Debug"); + +make_toggle_var (compound_debug, 0, make_compound_debug, +8, 14, set_compound_debug, "Compound Debug"); + +make_float_var (non_word, NON_WERD, make_non_word, +8, 20, set_non_word, "Non-word adjustment"); + +make_float_var (garbage, GARBAGE_STRING, make_garbage, +8, 21, set_garbage, "Garbage adjustment"); + +make_toggle_var (save_doc_words, 0, make_doc_words, +8, 22, set_doc_words, "Save Document Words "); + +make_toggle_var (doc_dict_enable, 1, make_doc_dict, +8, 25, set_doc_dict, "Enable Document Dictionary "); +/* PREV DEFAULT 0 */ + +int permute_only_top = 0; + + //0x0=. +static INT32 bigram_counts[256][3] = { { + 0, 0, 0 + }, + { //0x1=. + 0, 0, 0 + }, + { //0x2=. + 0, 0, 0 + }, + { //0x3=. + 0, 0, 0 + }, + { //0x4=. + 0, 0, 0 + }, + { //0x5=. + 0, 0, 0 + }, + { //0x6=. + 0, 0, 0 + }, + { //0x7=. + 0, 0, 0 + }, + { //0x8=. + 0, 0, 0 + }, + { //0x9=. + 0, 0, 0 + }, + { //0xa=. + 93, 28, 0 + }, + { //0xb=. + 0, 0, 0 + }, + { //0xc=. + 0, 0, 0 + }, + { //0xd=. + 0, 0, 0 + }, + { //0xe=. + 0, 0, 0 + }, + { //0xf=. + 0, 0, 0 + }, + { //0x10=. + 0, 0, 0 + }, + { //0x11=. + 0, 0, 0 + }, + { //0x12=. + 0, 0, 0 + }, + { //0x13=. + 0, 0, 0 + }, + { //0x14=. + 0, 0, 0 + }, + { //0x15=. + 0, 0, 0 + }, + { //0x16=. + 0, 0, 0 + }, + { //0x17=. + 0, 0, 0 + }, + { //0x18=. + 0, 0, 0 + }, + { //0x19=. + 0, 0, 0 + }, + { //0x1a=. + 0, 0, 0 + }, + { //0x1b=. + 0, 0, 0 + }, + { //0x1c=. + 0, 0, 0 + }, + { //0x1d=. + 0, 0, 0 + }, + { //0x1e=. + 0, 0, 0 + }, + { //0x1f=. + 0, 0, 0 + }, + { //0x20= + 324, 377, 2 + }, + { //0x21=! + 2, 1, 0 + }, + { //0x22=" + 2, 1, 0 + }, + { //0x23=# + 1, 0, 1 + }, + { //0x24=$ + 2, 1, 0 + }, + { //0x25=% + 2, 0, 0 + }, + { //0x26=& + 2, 1, 0 + }, + { //0x27=' + 1, 21, 8 + }, + { //0x28=( + 2, 1, 0 + }, + { //0x29=) + 19, 0, 0 + }, + { //0x2a=* + 2, 1, 0 + }, + { //0x2b=+ + 1, 0, 0 + }, + { //0x2c=, + 75, 4, 0 + }, + { //0x2d=- + 52, 7, 0 + }, + { //0x2e=. + 190, 16, 3 + }, + { //0x2f=/ + 53, 2, 0 + }, + { //0x30=0 + 399, 0, 0 + }, + { //0x31=1 + 220, 0, 0 + }, + { //0x32=2 + 226, 0, 0 + }, + { //0x33=3 + 128, 0, 0 + }, + { //0x34=4 + 147, 0, 0 + }, + { //0x35=5 + 179, 0, 1 + }, + { //0x36=6 + 173, 0, 0 + }, + { //0x37=7 + 115, 0, 0 + }, + { //0x38=8 + 107, 0, 0 + }, + { //0x39=9 + 934, 0, 1 + }, + { //0x3a=: + 27, 0, 1 + }, + { //0x3b=; + 2, 1, 0 + }, + { //0x3c=< + 2, 1, 0 + }, + { //0x3d== + 2, 1, 0 + }, + { //0x3e=> + 2, 1, 0 + }, + { //0x3f=? + 2, 1, 0 + }, + { //0x40=@ + 2, 1, 0 + }, + { //0x41=A + 3, 1, 0 + }, + { //0x42=B + 1, 73, 0 + }, + { //0x43=C + 1, 6, 0 + }, + { //0x44=D + 1, 24, 0 + }, + { //0x45=E + 1, 2, 0 + }, + { //0x46=F + 1, 19, 0 + }, + { //0x47=G + 1, 2, 0 + }, + { //0x48=H + 3, 2, 1 + }, + { //0x49=I + 0, 68, 0 + }, + { //0x4a=J + 1, 2, 0 + }, + { //0x4b=K + 1, 2, 0 + }, + { //0x4c=L + 1, 82, 0 + }, + { //0x4d=M + 10, 10, 0 + }, + { //0x4e=N + 3, 239, 0 + }, + { //0x4f=O + 1, 10, 0 + }, + { //0x50=P + 0, 1, 3 + }, + { //0x51=Q + 2, 3, 0 + }, + { //0x52=R + 1, 43, 0 + }, + { //0x53=S + 1, 53, 0 + }, + { //0x54=T + 2, 18, 0 + }, + { //0x55=U + 1, 2, 0 + }, + { //0x56=V + 1, 17, 0 + }, + { //0x57=W + 1, 5, 0 + }, + { //0x58=X + 1, 6, 0 + }, + { //0x59=Y + 1, 2, 0 + }, + { //0x5a=Z + 1, 2, 0 + }, + { //0x5b=[ + 2, 1, 0 + }, + { //0x5c=backslash + 2, 1, 0 + }, + { //0x5d=] + 2, 1, 0 + }, + { //0x5e=^ + 2, 1, 0 + }, + { //0x5f=_ + 2, 1, 0 + }, + { //0x60=` + 1, 0, 2 + }, + { //0x61=a + 0, 0, 671 + }, + { //0x62=b + 0, 1, 16 + }, + { //0x63=c + 0, 2, 1 + }, + { //0x64=d + 0, 14, 0 + }, + { //0x65=e + 0, 0, 763 + }, + { //0x66=f + 0, 186, 0 + }, + { //0x67=g + 0, 2, 1 + }, + { //0x68=h + 0, 2, 1 + }, + { //0x69=i + 0, 0, 818 + }, + { //0x6a=j + 0, 2, 1 + }, + { //0x6b=k + 0, 4, 1 + }, + { //0x6c=l + 0, 26, 3 + }, + { //0x6d=m + 0, 69, 0 + }, + { //0x6e=n + 0, 885, 0 + }, + { //0x6f=o + 0, 17, 722 + }, + { //0x70=p + 0, 1, 5 + }, + { //0x71=q + 2, 1, 0 + }, + { //0x72=r + 0, 21, 0 + }, + { //0x73=s + 3, 49, 0 + }, + { //0x74=t + 0, 219, 5 + }, + { //0x75=u + 0, 0, 56 + }, + { //0x76=v + 0, 4, 0 + }, + { //0x77=w + 0, 2, 1 + }, + { //0x78=x + 0, 2, 1 + }, + { //0x79=y + 0, 1, 23 + }, + { //0x7a=z + 0, 2, 1 + }, + { //0x7b={ + 2, 1, 0 + }, + { //0x7c=| + 59, 0, 3 + }, + { //0x7d=} + 2, 1, 0 + }, + { //0x7e=~ + 2, 1, 0 + }, + { //0x7f=. + 0, 0, 0 + }, + { //0x80=. + 0, 0, 0 + }, + { //0x81=. + 0, 0, 0 + }, + { //0x82=. + 0, 0, 0 + }, + { //0x83=. + 0, 0, 0 + }, + { //0x84=. + 0, 0, 0 + }, + { //0x85=. + 0, 0, 0 + }, + { //0x86=. + 0, 0, 0 + }, + { //0x87=. + 0, 0, 0 + }, + { //0x88=. + 0, 0, 0 + }, + { //0x89=. + 0, 0, 0 + }, + { //0x8a=. + 0, 0, 0 + }, + { //0x8b=. + 0, 0, 0 + }, + { //0x8c=. + 0, 0, 0 + }, + { //0x8d=. + 0, 0, 0 + }, + { //0x8e=. + 0, 0, 0 + }, + { //0x8f=. + 0, 0, 0 + }, + { //0x90=. + 0, 0, 0 + }, + { //0x91=. + 0, 0, 0 + }, + { //0x92=. + 0, 0, 0 + }, + { //0x93=. + 0, 0, 0 + }, + { //0x94=. + 0, 0, 0 + }, + { //0x95=. + 0, 0, 0 + }, + { //0x96=. + 0, 0, 0 + }, + { //0x97=. + 0, 0, 0 + }, + { //0x98=. + 0, 0, 0 + }, + { //0x99=. + 0, 0, 0 + }, + { //0x9a=. + 0, 0, 0 + }, + { //0x9b=. + 0, 0, 0 + }, + { //0x9c=. + 0, 0, 0 + }, + { //0x9d=. + 0, 0, 0 + }, + { //0x9e=. + 0, 0, 0 + }, + { //0x9f=. + 0, 0, 0 + }, + { //0xa0=. + 0, 0, 0 + }, + { //0xa1=. + 0, 0, 0 + }, + { //0xa2=. + 0, 0, 0 + }, + { //0xa3=. + 0, 0, 0 + }, + { //0xa4=. + 0, 0, 0 + }, + { //0xa5=. + 0, 0, 0 + }, + { //0xa6=. + 0, 0, 0 + }, + { //0xa7=. + 0, 0, 0 + }, + { //0xa8=. + 0, 0, 0 + }, + { //0xa9=. + 0, 0, 0 + }, + { //0xaa=. + 0, 0, 0 + }, + { //0xab=. + 0, 0, 0 + }, + { //0xac=. + 0, 0, 0 + }, + { //0xad=. + 0, 0, 0 + }, + { //0xae=. + 0, 0, 0 + }, + { //0xaf=. + 0, 0, 0 + }, + { //0xb0=. + 0, 0, 0 + }, + { //0xb1=. + 0, 0, 0 + }, + { //0xb2=. + 0, 0, 0 + }, + { //0xb3=. + 0, 0, 0 + }, + { //0xb4=. + 0, 0, 0 + }, + { //0xb5=. + 0, 0, 0 + }, + { //0xb6=. + 0, 0, 0 + }, + { //0xb7=. + 0, 0, 0 + }, + { //0xb8=. + 0, 0, 0 + }, + { //0xb9=. + 0, 0, 0 + }, + { //0xba=. + 0, 0, 0 + }, + { //0xbb=. + 0, 0, 0 + }, + { //0xbc=. + 0, 0, 0 + }, + { //0xbd=. + 0, 0, 0 + }, + { //0xbe=. + 0, 0, 0 + }, + { //0xbf=. + 0, 0, 0 + }, + { //0xc0=. + 0, 0, 0 + }, + { //0xc1=. + 0, 0, 0 + }, + { //0xc2=. + 0, 0, 0 + }, + { //0xc3=. + 0, 0, 0 + }, + { //0xc4=. + 0, 0, 0 + }, + { //0xc5=. + 0, 0, 0 + }, + { //0xc6=. + 0, 0, 0 + }, + { //0xc7=. + 0, 0, 0 + }, + { //0xc8=. + 0, 0, 0 + }, + { //0xc9=. + 0, 0, 0 + }, + { //0xca=. + 0, 0, 0 + }, + { //0xcb=. + 0, 0, 0 + }, + { //0xcc=. + 0, 0, 0 + }, + { //0xcd=. + 0, 0, 0 + }, + { //0xce=. + 0, 0, 0 + }, + { //0xcf=. + 0, 0, 0 + }, + { //0xd0=. + 0, 0, 0 + }, + { //0xd1=. + 0, 0, 0 + }, + { //0xd2=. + 0, 0, 0 + }, + { //0xd3=. + 0, 0, 0 + }, + { //0xd4=. + 0, 0, 0 + }, + { //0xd5=. + 0, 0, 0 + }, + { //0xd6=. + 0, 0, 0 + }, + { //0xd7=. + 0, 0, 0 + }, + { //0xd8=. + 0, 0, 0 + }, + { //0xd9=. + 0, 0, 0 + }, + { //0xda=. + 0, 0, 0 + }, + { //0xdb=. + 0, 0, 0 + }, + { //0xdc=. + 0, 0, 0 + }, + { //0xdd=. + 0, 0, 0 + }, + { //0xde=. + 0, 0, 0 + }, + { //0xdf=. + 0, 0, 0 + }, + { //0xe0=. + 0, 0, 0 + }, + { //0xe1=. + 0, 0, 0 + }, + { //0xe2=. + 0, 0, 0 + }, + { //0xe3=. + 0, 0, 0 + }, + { //0xe4=. + 0, 0, 0 + }, + { //0xe5=. + 0, 0, 0 + }, + { //0xe6=. + 0, 0, 0 + }, + { //0xe7=. + 0, 0, 0 + }, + { //0xe8=. + 0, 0, 0 + }, + { //0xe9=. + 0, 0, 0 + }, + { //0xea=. + 0, 0, 0 + }, + { //0xeb=. + 0, 0, 0 + }, + { //0xec=. + 0, 0, 0 + }, + { //0xed=. + 0, 0, 0 + }, + { //0xee=. + 0, 0, 0 + }, + { //0xef=. + 0, 0, 0 + }, + { //0xf0=. + 0, 0, 0 + }, + { //0xf1=. + 0, 0, 0 + }, + { //0xf2=. + 0, 0, 0 + }, + { //0xf3=. + 0, 0, 0 + }, + { //0xf4=. + 0, 0, 0 + }, + { //0xf5=. + 0, 0, 0 + }, + { //0xf6=. + 0, 0, 0 + }, + { //0xf7=. + 0, 0, 0 + }, + { //0xf8=. + 0, 0, 0 + }, + { //0xf9=. + 0, 0, 0 + }, + { //0xfa=. + 0, 0, 0 + }, + { //0xfb=. + 0, 0, 0 + }, + { //0xfc=. + 0, 0, 0 + }, + { //0xfd=. + 0, 0, 0 + }, + { //0xfe=. + 0, 0, 0 + }, + { //0xff=. + 0, 0, 0 + }, +}; + +//extern "C" double permuter_pending_threshold; + + /* Similarity matcher values */ +#define SIM_CERTAINTY_SCALE -10.0 + /* Similarity matcher values */ +#define SIM_CERTAINTY_OFFSET -10.0 + /* Worst E*L product to stop on */ +#define SIMILARITY_FLOOR 100.0 +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ + +/********************************************************************** + * good_choice + * + * Return TRUE if a good answer is found for the unknown blob rating. + **********************************************************************/ +int good_choice(A_CHOICE *choice) { + register float certainty; + if (choice == NULL) + return (FALSE); + if (similarity_enable) { + if ((class_probability (choice) + 1) * class_certainty (choice) > + SIMILARITY_FLOOR) + return (FALSE); + certainty = + SIM_CERTAINTY_OFFSET + + class_probability (choice) * SIM_CERTAINTY_SCALE; + } + + else { + certainty = class_certainty (choice); + } + if (certainty > certainty_threshold) { + return (TRUE); + } + + else { + return (FALSE); + } +} + + +/********************************************************************** + * add_document_word + * + * Add a word found on this document to the document specific + * dictionary. + **********************************************************************/ +void add_document_word(A_CHOICE *best_choice) { + char filename[CHARS_PER_LINE]; + FILE *doc_word_file; + char *string; + int stringlen; //length of word + + string = class_string (best_choice); + stringlen = strlen (string); + + if (!doc_dict_enable + || valid_word (string) || CurrentWordAmbig () || stringlen < 2) + return; + + if (!good_choice (best_choice) || stringlen == 2) { + if (class_certainty (best_choice) < permuter_pending_threshold) + return; + if (!word_in_dawg (pending_words, string)) { + if (stringlen > 2 || isupper (string[0]) && isupper (string[1])) + add_word_to_dawg(pending_words, + string, + MAX_DOC_EDGES, + RESERVED_DOC_EDGES); + return; + } + } + + if (save_doc_words) { + strcpy(filename, imagefile); + strcat (filename, ".doc"); + doc_word_file = open_file (filename, "a"); + fprintf (doc_word_file, "%s\n", string); + fclose(doc_word_file); + } + add_word_to_dawg(document_words, string, MAX_DOC_EDGES, RESERVED_DOC_EDGES); + case_sensative = FALSE; +} + + +/********************************************************************** + * adjust_non_word + * + * Assign an adjusted value to a string that is a non-word. The value + * that this word choice has is based on case and punctuation rules. + **********************************************************************/ +void +adjust_non_word (A_CHOICE * best_choice, float certainties[]) { + char *this_word; + float adjust_factor; + + if (adjust_debug) + cprintf ("%s %4.2f ", + class_string (best_choice), class_probability (best_choice)); + + this_word = class_string (best_choice); + + class_probability (best_choice) += RATING_PAD; + if (case_ok (this_word) && punctuation_ok (this_word) != -1) { + class_probability (best_choice) *= non_word; + adjust_factor = non_word; + if (adjust_debug) + cprintf (", %4.2f ", non_word); + } + else { + class_probability (best_choice) *= garbage; + adjust_factor = garbage; + if (adjust_debug) { + if (!case_ok (this_word)) + cprintf (", C"); + if (punctuation_ok (this_word) == -1) + cprintf (", P"); + cprintf (", %4.2f ", garbage); + } + } + + class_probability (best_choice) -= RATING_PAD; + + LogNewWordChoice(best_choice, adjust_factor, certainties); + + if (adjust_debug) + cprintf (" --> %4.2f\n", class_probability (best_choice)); +} + + +/********************************************************************** + * init_permute + * + * Initialize anything that needs to be set up for the permute + * functions. + **********************************************************************/ +void init_permute() { + char name[1024]; + make_adjust_debug(); + make_compound_debug(); + make_non_word(); + make_garbage(); + make_doc_words(); + make_doc_dict(); + + init_permdawg(); + init_permnum(); + + word_dawg = (EDGE_ARRAY) memalloc (sizeof (EDGE_RECORD) * MAX_NUM_EDGES); + strcpy(name, demodir); + strcat (name, "tessdata/word-dawg"); + read_squished_dawg(name, word_dawg, MAX_NUM_EDGES); + + document_words = + (EDGE_ARRAY) memalloc (sizeof (EDGE_RECORD) * MAX_DOC_EDGES); + initialize_dawg(document_words, MAX_DOC_EDGES); + + pending_words = + (EDGE_ARRAY) memalloc (sizeof (EDGE_RECORD) * MAX_DOC_EDGES); + initialize_dawg(pending_words, MAX_DOC_EDGES); + + user_words = (EDGE_ARRAY) memalloc (sizeof (EDGE_RECORD) * MAX_USER_EDGES); + strcpy(name, demodir); + strcat (name, "tessdata/user-words"); + read_word_list(name, user_words, MAX_USER_EDGES, USER_RESERVED_EDGES); + case_sensative = FALSE; +} + +void end_permute() { + memfree(word_dawg); + word_dawg = NULL; + memfree(document_words); + document_words = NULL; + memfree(pending_words); + pending_words = NULL; + memfree(user_words); + user_words = NULL; +} + +/********************************************************************** + * permute_all + * + * Permute all the characters together using all of the different types + * of permuters/selectors available. Each of the characters must have + * a non-NIL choice list. + **********************************************************************/ +A_CHOICE *permute_all(CHOICES_LIST char_choices, + float rating_limit, + A_CHOICE *raw_choice) { + A_CHOICE *result_1; + A_CHOICE *result_2 = NULL; + BOOL8 any_alpha; + + result_1 = permute_top_choice (char_choices, rating_limit, raw_choice, + &any_alpha); + if (result_1 == NULL) + return (NULL); + if (permute_only_top) + return result_1; + if (any_alpha && array_count (char_choices) <= 20) { + result_2 = permute_words (char_choices, rating_limit); + + if (class_probability (result_1) < class_probability (result_2) + || class_string (result_2) == NULL) { + free_choice(result_2); + } + else { + free_choice(result_1); + result_1 = result_2; + } + } + + result_2 = number_permute_and_select (char_choices, rating_limit); + + if (class_probability (result_1) < class_probability (result_2) + || class_string (result_2) == NULL) { + free_choice(result_2); + } + else { + free_choice(result_1); + result_1 = result_2; + } + + result_2 = permute_compound_words (char_choices, rating_limit); + + if (!result_2 || + class_probability (result_1) < class_probability (result_2) + || class_string (result_2) == NULL) { + free_choice(result_2); + } + else { + free_choice(result_1); + result_1 = result_2; + } + + return (result_1); +} + + +/********************************************************************** + * permute_characters + * + * Permute these characters together according to each of the different + * permuters that are enabled. + **********************************************************************/ +void permute_characters(CHOICES_LIST char_choices, + float limit, + A_CHOICE *best_choice, + A_CHOICE *raw_choice) { + A_CHOICE *this_choice; + + permutation_count++; /* Global counter */ + + this_choice = permute_all (char_choices, limit, raw_choice); + + if (this_choice && + class_probability (this_choice) < class_probability (best_choice)) { + clone_choice(best_choice, this_choice); + } + free_choice(this_choice); + + if (display_ratings) + cprintf ("permute_characters: %-15s %4.2f %4.2f\n", + class_string (best_choice), + class_probability (best_choice), class_certainty (best_choice)); +} + + +/********************************************************************** + * permute_compound_word + * + * Return the top choice for each character as the choice for the word. + **********************************************************************/ +A_CHOICE *permute_compound_words(CHOICES_LIST character_choices, + float rating_limit) { + A_CHOICE *first_choice; + A_CHOICE *best_choice = NULL; + char word[MAX_WERD_LENGTH + 1]; + float rating = 0; + float certainty = 10000; + char char_choice; + int x; + int first_index = 0; + char *ptr; + + word[0] = '\0'; + + if (array_count (character_choices) > MAX_WERD_LENGTH) { + return (new_choice (NULL, MAX_FLOAT32, -MAX_FLOAT32, -1, NO_PERM)); + } + + array_loop(character_choices, x) { + + first_choice = + (A_CHOICE *) first ((CHOICES) array_value (character_choices, x)); + + ptr = class_string (first_choice); + char_choice = ptr != NULL ? *ptr : '\0'; + if (x > first_index && (char_choice == '-' || char_choice == '/')) { + if (compound_debug) + cprintf ("Hyphenated word found\n"); + + permute_subword (character_choices, rating_limit, + first_index, x - 1, word, &rating, &certainty); + + if (rating > rating_limit) + break; + first_index = x + 1; + strcat (word, class_string (first_choice)); + rating += class_probability (first_choice); + certainty = min (class_certainty (first_choice), certainty); + } + } + + if (first_index > 0 && first_index < x && rating <= rating_limit) { + permute_subword (character_choices, rating_limit, + first_index, x - 1, word, &rating, &certainty); + + best_choice = new_choice (word, rating, certainty, -1, COMPOUND_PERM); + } + return (best_choice); +} + + +/********************************************************************** + * permute_subword + * + * Permute a part of a compound word this subword is bounded by hyphens + * and the start and end of the word. Call the standard word permute + * function on a set of choices covering only part of the original + * word. When it is done reclaim the memory that was used in the + * excercise. + **********************************************************************/ +void permute_subword(CHOICES_LIST character_choices, + float rating_limit, + int start, + int end, + char *word, + float *rating, + float *certainty) { + int x; + A_CHOICE *best_choice = NULL; + A_CHOICE raw_choice; + CHOICES_LIST subchoices; + CHOICES choices; + char this_char; + char *ptr; + + DisableChoiceAccum(); + raw_choice.string = NULL; + raw_choice.rating = MAX_INT16; + raw_choice.certainty = -MAX_INT16; + + subchoices = new_choice_list (); + for (x = start; x <= end; x++) { + choices = (CHOICES) array_value (character_choices, x); + ptr = best_string (choices); + this_char = ptr != NULL ? *ptr : '\0'; + if (this_char != '-' && this_char != '/') { + subchoices = array_push (subchoices, choices); + } else { + const char* str = best_string(choices); + strcat (word, str); + } + } + + if (array_count (subchoices)) { + if (compound_debug) + dawg_debug = TRUE; + best_choice = permute_all (subchoices, rating_limit, &raw_choice); + if (compound_debug) + dawg_debug = FALSE; + + if (best_choice && class_string (best_choice)) { + strcat (word, class_string (best_choice)); + *rating += class_probability (best_choice); + *certainty = min (class_certainty (best_choice), *certainty); + } + else { + *rating = MAX_FLOAT32; + } + } + else { + *rating = MAX_FLOAT32; + } + + free_choice_list(subchoices); + if (best_choice) + free_choice(best_choice); + + if (compound_debug && *rating < MAX_FLOAT32) { + cprintf ("Subword permuted = %s, %5.2f, %5.2f\n\n", + word, *rating, *certainty); + } + if (raw_choice.string) + strfree(raw_choice.string); + + EnableChoiceAccum(); +} + + +/********************************************************************** + * permute_top_choice + * + * Return the top choice for each character as the choice for the word. + * In addition a choice is created for the best lower and upper case + * non-words. In each character position the best lower (or upper) case + * character is substituted for the best overall character. + **********************************************************************/ +A_CHOICE *permute_top_choice(CHOICES_LIST character_choices, + float rating_limit, + A_CHOICE *raw_choice, + BOOL8 *any_alpha) { + CHOICES char_list; + A_CHOICE *first_choice; + A_CHOICE *best_choice; + A_CHOICE *other_choice; + char *ptr; + char first_char; //first choice + char second_char; //second choice + char third_char; //third choice + char prev_char = '\0'; //prev in word + char next_char = '\0'; //next in word + char next_next_char = '\0'; //after next next in word + + char word[MAX_PERM_LENGTH + 1]; + char capital_word[MAX_PERM_LENGTH + 1]; + char lower_word[MAX_PERM_LENGTH + 1]; + int x; + BOOL8 char_alpha; + + float rating = 0; + float upper_rating = 0; + float lower_rating = 0; + float first_rating = 0; + + float certainty = 10000; + float upper_certainty = 10000; + float lower_certainty = 10000; + + float certainties[MAX_PERM_LENGTH + 1]; + float lower_certainties[MAX_PERM_LENGTH + 1]; + float upper_certainties[MAX_PERM_LENGTH + 1]; + + register CHOICES this_char; + register char ch; + register INT8 lower_done; + register INT8 upper_done; + + if (any_alpha != NULL) + *any_alpha = FALSE; + + if (array_count (character_choices) > MAX_PERM_LENGTH) { + return (NULL); + } + + array_loop(character_choices, x) { + if (x + 1 < array_count (character_choices)) { + char_list = (CHOICES) array_value (character_choices, x + 1); + first_choice = (A_CHOICE *) first (char_list); + + ptr = class_string (first_choice); + next_char = (ptr != NULL && *ptr != '\0') ? *ptr : ' '; + } + else + next_char = '\0'; + if (x + 2 < array_count (character_choices)) { + char_list = (CHOICES) array_value (character_choices, x + 2); + first_choice = (A_CHOICE *) first (char_list); + + ptr = class_string (first_choice); + next_next_char = (ptr != NULL && *ptr != '\0') ? *ptr : ' '; + } + else + next_next_char = '\0'; + + char_list = (CHOICES) array_value (character_choices, x); + first_choice = (A_CHOICE *) first (char_list); + + ptr = class_string (first_choice); + word[x] = (ptr != NULL && *ptr != '\0') ? *ptr : ' '; + + lower_word[x] = word[x]; + capital_word[x] = word[x]; + first_char = word[x]; + first_rating = class_probability (first_choice); + upper_rating += class_probability (first_choice); + lower_rating += class_probability (first_choice); + lower_certainty = min (class_certainty (first_choice), lower_certainty); + upper_certainty = min (class_certainty (first_choice), upper_certainty); + + certainties[x] = class_certainty (first_choice); + lower_certainties[x] = class_certainty (first_choice); + upper_certainties[x] = class_certainty (first_choice); + + lower_done = FALSE; + upper_done = FALSE; + char_alpha = FALSE; + second_char = '\0'; + third_char = '\0'; + iterate_list(this_char, char_list) { + ptr = best_string (this_char); + ch = ptr != NULL ? *ptr : '\0'; + if (ch == 'l' && rest (this_char) != NULL + && best_probability (rest (this_char)) == first_rating) { + ptr = best_string (rest (this_char)); + if (ptr != NULL && (*ptr == '1' || *ptr == 'I')) { + second_char = *ptr; + this_char = rest (this_char); + if (rest (this_char) != NULL + && best_probability (rest (this_char)) == first_rating) { + ptr = best_string (rest (this_char)); + if (ptr != NULL && (*ptr == '1' || *ptr == 'I')) { + third_char = *ptr; + this_char = rest (this_char); + } + } + ch = choose_il1 (first_char, second_char, third_char, + prev_char, next_char, next_next_char); + if (ch != 'l' && word[x] == 'l') { + word[x] = ch; + lower_word[x] = ch; + capital_word[x] = ch; + } + } + } + /* Find lower case */ + if (!lower_done && (islower (ch) || (isupper (ch) && x == 0))) { + lower_word[x] = ch; + lower_rating += best_probability (this_char); + lower_rating -= class_probability (first_choice); + lower_certainty = min (best_certainty (this_char), lower_certainty); + lower_certainties[x] = best_certainty (this_char); + lower_done = TRUE; + } + /* Find upper case */ + if (!upper_done && isupper (ch)) { + capital_word[x] = ch; + upper_rating += best_probability (this_char); + upper_rating -= class_probability (first_choice); + upper_certainty = min (best_certainty (this_char), upper_certainty); + upper_certainties[x] = best_certainty (this_char); + upper_done = TRUE; + } + if (!char_alpha && isalpha (ch)) + char_alpha = TRUE; + if (lower_done && upper_done) + break; + } + if (char_alpha && any_alpha != NULL) + *any_alpha = TRUE; + + if (first_choice == NULL) { + cprintf ("Permuter giving up due to null choices list"); + word[x + 1] = '$'; + word[x + 2] = '\0'; + cprintf (" word=%s\n", word); + return (NULL); + } + + rating += class_probability (first_choice); + if (rating > rating_limit) + return (NULL); + + certainty = min (class_certainty (first_choice), certainty); + prev_char = word[x]; + } + + lower_word[x] = '\0'; + capital_word[x] = '\0'; + word[x] = '\0'; + + if (rating < class_probability (raw_choice)) { + if (class_string (raw_choice)) + strfree (class_string (raw_choice)); + + class_probability (raw_choice) = rating; + class_certainty (raw_choice) = certainty; + class_string (raw_choice) = strsave (word); + class_permuter (raw_choice) = TOP_CHOICE_PERM; + + LogNewRawChoice (raw_choice, 1.0, certainties); + } + + best_choice = new_choice (word, rating, certainty, -1, TOP_CHOICE_PERM); + adjust_non_word(best_choice, certainties); + + other_choice = new_choice (lower_word, lower_rating, lower_certainty, + -1, LOWER_CASE_PERM); + adjust_non_word(other_choice, lower_certainties); + if (class_probability (best_choice) > class_probability (other_choice)) { + clone_choice(best_choice, other_choice); + } + free_choice(other_choice); + + other_choice = new_choice (capital_word, upper_rating, upper_certainty, + -1, UPPER_CASE_PERM); + adjust_non_word(other_choice, upper_certainties); + if (class_probability (best_choice) > class_probability (other_choice)) { + clone_choice(best_choice, other_choice); + } + free_choice(other_choice); + + return (best_choice); +} + + +/********************************************************************** + * choose_il1 + * + * Choose between the candidate il1 chars. + **********************************************************************/ +char choose_il1(char first_char, //first choice + char second_char, //second choice + char third_char, //third choice + char prev_char, //prev in word + char next_char, //next in word + char next_next_char) { //after next next in word + INT32 type1; //1/I/l type of first choice + INT32 type2; //1/I/l type of second choice + INT32 type3; //1/I/l type of third choice + + if (first_char == 'l' && second_char != '\0') { + if (second_char == 'I' + && (isupper (prev_char) && !islower (next_char) + && !isdigit (next_char) || isupper (next_char) + && !islower (prev_char) && !isdigit (prev_char))) + first_char = second_char; //override + else if (second_char == '1' || third_char == '1') { + if (isdigit (next_char) || isdigit (prev_char) + || next_char == 'l' && isdigit (next_next_char)) { + first_char = '1'; + } + else if (!islower (prev_char) + && (!islower (next_char) || next_char == 's' + && next_next_char == 't')) { + if ((prev_char != '\'' && prev_char != '`' || next_char != '\0') + && (next_char != '\'' && next_char != '`' + || prev_char != '\0')) { + first_char = '1'; + } + } + } + if (first_char == 'l' && next_char != '\0' && !isalpha (prev_char)) { + type1 = 2; + + if (second_char == '1') + type2 = 0; + else if (second_char == 'I') + type2 = 1; + else if (second_char == 'l') + type2 = 2; + else + type2 = type1; + + if (third_char == '1') + type3 = 0; + else if (third_char == 'I') + type3 = 1; + else if (third_char == 'l') + type3 = 2; + else + type3 = type1; + + if (bigram_counts[next_char][type2] > + bigram_counts[next_char][type1]) { + first_char = second_char; + type1 = type2; + } + if (bigram_counts[next_char][type3] > + bigram_counts[next_char][type1]) { + first_char = third_char; + } + } + } + return first_char; +} + + +/********************************************************************** + * permute_words + * + * Permute all the characters together using the dawg to prune all + * but the valid words. + **********************************************************************/ +A_CHOICE *permute_words(CHOICES_LIST char_choices, float rating_limit) { + A_CHOICE *best_choice; + int hyphen_len; + + best_choice = new_choice (NULL, rating_limit, -MAX_FLOAT32, -1, NO_PERM); + + hyphen_len = hyphen_string != NULL ? strlen (hyphen_string) : 0; + if (hyphen_len + array_count (char_choices) > MAX_WERD_LENGTH) { + class_probability (best_choice) = MAX_FLOAT32; + } + else { + + dawg_permute_and_select ("system words:", word_dawg, SYSTEM_DAWG_PERM, + char_choices, best_choice, TRUE); + + dawg_permute_and_select ("document_words", document_words, + DOC_DAWG_PERM, char_choices, best_choice, + FALSE); + + dawg_permute_and_select ("user words", user_words, USER_DAWG_PERM, + char_choices, best_choice, FALSE); + case_sensative = FALSE; + } + + return (best_choice); +} + + +/********************************************************************** + * valid_word + * + * Check all the DAWGs to see if this word is in any of them. + **********************************************************************/ +int valid_word(const char *string) { + int result = NO_PERM; + + if (word_in_dawg (word_dawg, string)) + result = SYSTEM_DAWG_PERM; + else { + if (word_in_dawg (document_words, string)) + result = DOC_DAWG_PERM; + else if (word_in_dawg (user_words, string)) + result = USER_DAWG_PERM; + case_sensative = FALSE; + } + return (result); +} diff --git a/dict/permute.h b/dict/permute.h new file mode 100644 index 0000000000..d7ae6caa01 --- /dev/null +++ b/dict/permute.h @@ -0,0 +1,91 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: permute.h (Formerly permute.h) + * Description: Permute choices together + * Author: Mark Seaman, OCR Technology + * Created: Fri Sep 22 14:05:51 1989 + * Modified: Mon May 20 16:32:04 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Experimental (Do Not Distribute) + * + * (c) Copyright 1989, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + ********************************************************************************/ +#ifndef PERMUTE_H +#define PERMUTE_H + +/*---------------------------------------------------------------------- + I n c l u d e s +----------------------------------------------------------------------*/ +#include "choicearr.h" + +/*---------------------------------------------------------------------- + T y p e s +----------------------------------------------------------------------*/ +#define RATING_PAD 4.0 + +/*---------------------------------------------------------------------- + V a r i a b l e s +----------------------------------------------------------------------*/ +extern int adjust_debug; +extern float garbage; +extern float non_word; +extern int permute_only_top; + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +void add_document_word(A_CHOICE *best_choice); + +void adjust_non_word (A_CHOICE * best_choice, float certainties[]); + +void init_permute(); +void end_permute(); + +A_CHOICE *permute_all(CHOICES_LIST char_choices, + float rating_limit, + A_CHOICE *raw_choice); + +void permute_characters(CHOICES_LIST char_choices, + float limit, + A_CHOICE *best_choice, + A_CHOICE *raw_choice); + +A_CHOICE *permute_compound_words(CHOICES_LIST character_choices, + float rating_limit); + +void permute_subword(CHOICES_LIST character_choices, + float rating_limit, + int start, + int end, + char *word, + float *rating, + float *certainty); + +A_CHOICE *permute_top_choice(CHOICES_LIST character_choices, + float rating_limit, + A_CHOICE *raw_choice, + BOOL8 *any_alpha); + +char choose_il1(char first_char, //first choice + char second_char, //second choice + char third_char, //third choice + char prev_char, //prev in word + char next_char, //next in word + char next_next_char); + +A_CHOICE *permute_words(CHOICES_LIST char_choices, float rating_limit); + +int valid_word(const char *string); +#endif diff --git a/dict/states.cpp b/dict/states.cpp new file mode 100644 index 0000000000..bd7fa9f85e --- /dev/null +++ b/dict/states.cpp @@ -0,0 +1,382 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: states.c (Formerly states.c) + * Description: Representations of search states + * Author: Mark Seaman, OCR Technology + * Created: Wed May 16 15:49:34 1990 + * Modified: Mon Jun 17 17:54:41 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Experimental (Do Not Distribute) + * + * (c) Copyright 1990, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + *********************************************************************************/ +/*---------------------------------------------------------------------- + I n c l u d e s +----------------------------------------------------------------------*/ +#include "states.h" +#include "structures.h" +#include "tordvars.h" +#include "callcpp.h" + +/*------------------------------------------------------------------------- + Variables +--------------------------------------------------------------------------*/ +#define STATEBLOCK 100 /* Cells per block */ +makestructure (newstate, free_state, printstate, STATE, +freestate, STATEBLOCK, "STATE", statecount); + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +/********************************************************************** + * bin_to_chunks + * + * Convert a representation of the search state in "STATE" form to one + * in "SEARCH_STATE" form. Create the memory required to hold the + * resultant state value. + **********************************************************************/ +SEARCH_STATE bin_to_chunks(STATE *state, int num_joints) { + int x; + unsigned int mask; + int depth; + int pieces = 0; + SEARCH_STATE s; + + s = memalloc (sizeof (int) * (ones_in_state (state, num_joints) + 1)); + + depth = 1; + mask = 1 << (num_joints - 1 - 32); + for (x = num_joints; x > 32; x--) { + if (state->part1 & mask) { + s[depth++] = pieces; + pieces = 0; + } + else { + pieces++; + } + mask >>= 1; + } + + if (num_joints > 32) + mask = 1 << 31; + else + mask = 1 << (num_joints - 1); + + while (x--) { + if (state->part2 & mask) { + s[depth++] = pieces; + pieces = 0; + } + else { + pieces++; + } + mask >>= 1; + } + s[0] = depth - 1; + + return (s); +} + + +/********************************************************************** + * bin_to_pieces + * + * Convert the binary (bit vector) format of a search state to an array + * of piece counts. This array has a zero element after the last valid + * character. + **********************************************************************/ +void bin_to_pieces(STATE *state, int num_joints, PIECES_STATE pieces) { + int x; + unsigned int mask; /* Bit mask */ + INT16 num_pieces = 0; + /* Preset mask */ + if (debug_8) + print_state ("bin_to_pieces = ", state, num_joints); + + mask = ((num_joints > 32) ? + (1 << (num_joints - 1 - 32)) : (1 << (num_joints - 1))); + + pieces[num_pieces] = 0; + + for (x = num_joints - 1; x >= 0; x--) { + /* Iterate all bits */ + pieces[num_pieces]++; + + if ((x < 32) ? /* Test for 1 bit */ + ((state->part2 & mask) ? TRUE : FALSE) : + ((state->part1 & mask) ? TRUE : FALSE)) { + pieces[++num_pieces] = 0; + if (debug_8) + cprintf ("[%d]=%d ", num_pieces - 1, pieces[num_pieces - 1]); + } + /* Next mask value */ + mask = ((mask == 1) ? (1 << 31) : (mask >> 1)); + } + pieces[num_pieces]++; + pieces[++num_pieces] = 0; + ASSERT_HOST (num_pieces < MAX_NUM_CHUNKS + 2); + if (debug_8) + new_line(); +} + + +/********************************************************************** + * insert_new_chunk + * + * Add a new chunk division into this state vector at the location + * requested. + **********************************************************************/ +void insert_new_chunk(register STATE *state, + register int index, + register int num_joints) { + register unsigned int mask; + register unsigned int result; + + index = (num_joints - index); + if (index < 32) { + mask = ~0; + mask <<= index; + result = (mask & state->part2) << 1; + result |= (~mask & state->part2); + state->part1 <<= 1; + if (state->part2 & 0x80000000) + state->part1 |= 1; + state->part2 = result; + } + else { + mask = ~0; + mask <<= index - 32; + result = (mask & state->part1) << 1; + result |= (~mask & state->part1); + state->part1 = result; + } +} + + +/********************************************************************** + * new_state + * + * Create a memory space for a new state variable. Set its initial + * value according to the parameters. + **********************************************************************/ +STATE *new_state(STATE *oldstate) { + STATE *this_state; + + this_state = newstate (); + this_state->part1 = oldstate->part1; + this_state->part2 = oldstate->part2; + return (this_state); +} + + +/********************************************************************* + * ones_in_state + * + * Return the number of ones that are in this state. + **********************************************************************/ +int ones_in_state(STATE *state, int num_joints) { + INT8 num_ones = 0; + INT8 x; + unsigned int mask; + + if (num_joints > 32) /* Preset mask */ + mask = 1 << (num_joints - 1 - 32); + else + mask = 1 << (num_joints - 1); + + for (x = num_joints - 1; x >= 0; x--) { + /* Iterate all bits */ + + if (x < 32) + num_ones += ((state->part2 & mask) ? 1 : 0); + else + num_ones += ((state->part1 & mask) ? 1 : 0); + + if (mask == 1) /* Next mask value */ + mask = 1 << 31; + else + mask >>= 1; + } + + return (num_ones); +} + + +/********************************************************************** + * print_state + * + * Print out the current state variable on a line with a label. + **********************************************************************/ +void print_state(const char *label, STATE *state, int num_joints) { + int x; + unsigned int mask; /* Bit mask */ + + if (num_joints > 32) /* Preset mask */ + mask = 1 << (num_joints - 1 - 32); + else + mask = 1 << (num_joints - 1); + + cprintf ("%s ", label); + + for (x = num_joints - 1; x >= 0; x--) { + /* Iterate all bits */ + + if (x < 32) + cprintf ("%d", ((state->part2 & mask) ? 1 : 0)); + else + cprintf ("%d", ((state->part1 & mask) ? 1 : 0)); + if (x % 4 == 0) + cprintf (" "); + + if (mask == 1) /* Next mask value */ + mask = 1 << 31; + else + mask >>= 1; + } + + new_line(); +} + + +/********************************************************************** + * set_n_ones + * + * Set the first n bits in a state. + **********************************************************************/ +void set_n_ones(STATE *state, int n) { + if (n < 32) { + state->part2 = ~0; + state->part2 >>= 32 - n; + state->part1 = 0; + } + else { + state->part2 = ~0; + state->part1 = ~0; + state->part1 >>= 64 - n; + } +} + + +/********************************************************************** + * compare_states + * + * Compare the 2 states at the given blob index. Return 1 if the given + * blob is a fragment compared to reality, 2 if correct, 4 if a join, + * and 5 if both a join and a fragment. + * On return the blob index is set to the corresponding index in the + * correct string. + **********************************************************************/ +int compare_states(STATE *true_state, STATE *this_state, int *blob_index) { + int blob_count; //number found + int true_index; //index of true blob + int index; //current + int result = 0; //return value + UINT32 mask; + + if (true_state->part1 == this_state->part1 + && true_state->part2 == this_state->part2) + return 2; + if (*blob_index == 0) { + if (bits_in_states > 32) { + for (mask = 1 << bits_in_states - 33; mask != 0; mask >>= 1) { + if (this_state->part1 & mask) { + if (true_state->part1 & mask) + return 2; + else + return 1; + } + else if (true_state->part1 & mask) + return 4; + } + index = 31; + } + else + index = bits_in_states - 1; + for (mask = 1 << index; mask != 0; mask >>= 1) { + if (this_state->part2 & mask) { + if (true_state->part2 & mask) + return 2; + else + return 1; + } + else if (true_state->part2 & mask) + return 4; + } + return 2; + } + else { + blob_count = 0; + true_index = 0; + if (bits_in_states > 32) { + for (mask = 1 << bits_in_states - 33; mask != 0; mask >>= 1) { + if (true_state->part1 & mask) + true_index++; + if (this_state->part1 & mask) { + blob_count++; + if (blob_count == *blob_index) { + if ((true_state->part1 & mask) == 0) + result = 1; + break; + } + } + } + if (blob_count == *blob_index) { + for (mask >>= 1; mask != 0; mask >>= 1) { + if (this_state->part1 & mask) { + if ((true_state->part1 & mask) && result == 0) + return 2; + else + return result | 1; + } + else if (true_state->part1 & mask) + result |= 4; + } + } + index = 31; + } + else + index = bits_in_states - 1; + mask = 1 << index; + if (blob_count < *blob_index) { + for (; mask != 0; mask >>= 1) { + if (true_state->part2 & mask) + true_index++; + if (this_state->part2 & mask) { + blob_count++; + if (blob_count == *blob_index) { + if ((true_state->part2 & mask) == 0) + result = 1; + break; + } + } + } + if (blob_count != *blob_index) + return 2; + mask >>= 1; + } + *blob_index = true_index; + for (; mask != 0; mask >>= 1) { + if (this_state->part2 & mask) { + if ((true_state->part2 & mask) && result == 0) + return 2; + else + return result | 1; + } + else if (true_state->part2 & mask) + result |= 4; + } + return result == 0 ? 2 : result; + } +} diff --git a/dict/states.h b/dict/states.h new file mode 100644 index 0000000000..7d01d9745d --- /dev/null +++ b/dict/states.h @@ -0,0 +1,111 @@ +/* -*-C-*- + ******************************************************************************** + * + * File: states.h (Formerly states.h) + * Description: Representations of search states + * Author: Mark Seaman, OCR Technology + * Created: Wed May 16 15:52:40 1990 + * Modified: Tue May 21 16:26:21 1991 (Mark Seaman) marks@hpgrlt + * Language: C + * Package: N/A + * Status: Experimental (Do Not Distribute) + * + * (c) Copyright 1990, Hewlett-Packard Company. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + *********************************************************************************/ +#ifndef STATES_H +#define STATES_H + +/*---------------------------------------------------------------------- + I n c l u d e s +----------------------------------------------------------------------*/ +#include "general.h" + +/*---------------------------------------------------------------------- + T y p e s +----------------------------------------------------------------------*/ +#define MAX_NUM_CHUNKS 64 /* Limit on pieces */ + +typedef struct +{ + UINT32 part1; + UINT32 part2; +} STATE; + +typedef int *SEARCH_STATE; /* State variable for search */ + + /* State variable for search */ +typedef UINT8 PIECES_STATE[MAX_NUM_CHUNKS + 2]; + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +SEARCH_STATE bin_to_chunks(STATE *state, int num_joints); + +void bin_to_pieces(STATE *state, int num_joints, PIECES_STATE pieces); + +void insert_new_chunk(register STATE *state, + register int index, + int num_joints); + +STATE *new_state(STATE *oldstate); + +int ones_in_state(STATE *state, int num_joints); + +void print_state(const char *label, STATE *state, int num_joints); + +void set_n_ones(STATE *state, int n); + +int compare_states(STATE *true_state, STATE *this_state, int *blob_index); + +extern void free_state(STATE *); + +/* +#if defined(__STDC__) || defined(__cplusplus) +# define _ARGS(s) s +#else +# define _ARGS(s) () +#endif*/ + +/* states.c +void insert_new_chunk + _ARGS((STATE *state, + int index)); + +SEARCH_STATE bin_to_chunks + _ARGS((STATE *state, + int num_joints)); + +STATE *new_state + _ARGS((STATE *oldstate)); + +int ones_in_state + _ARGS((STATE *state, + int num_joints)); + +void print_state + _ARGS((char *label, + STATE *state, + int num_joints)); + +void set_n_ones + _ARGS((STATE *state, + int n)); +int compare_states + _ARGS(( +STATE *true_state, +STATE *this_state, +int* blob_index)); + +#undef _ARGS +*/ +#endif diff --git a/dict/stopper.cpp b/dict/stopper.cpp new file mode 100644 index 0000000000..63e4753d12 --- /dev/null +++ b/dict/stopper.cpp @@ -0,0 +1,1360 @@ +/****************************************************************************** + ** 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 Files and Type Defines +----------------------------------------------------------------------------**/ +#include "stopper.h" +#include "emalloc.h" +#include "matchdefs.h" +#include "debug.h" +#include "callcpp.h" +#include "permute.h" +#include "context.h" +#include "permnum.h" +#include "danerror.h" +#include "const.h" +#include "freelist.h" +#include "efio.h" +#include "globals.h" +#include "scanutils.h" + +#include +#include +#include +#include +#ifdef __UNIX__ +#include +#endif + +/* these are kludges - add appropriate .h file later */ +extern float CertaintyScale; /* from subfeat.h */ + +#define MAX_WERD_SIZE 100 +#define MAX_AMBIG_SIZE 3 +#define DANGEROUS_AMBIGS "tessdata/DangAmbigs" + +typedef LIST AMBIG_TABLE; + +typedef struct +{ + UINT8 Class; + UINT16 NumChunks; + float Certainty; +} + + +CHAR_CHOICE; + +typedef struct +{ + float Rating; + float Certainty; + FLOAT32 AdjustFactor; + int Length; + CHAR_CHOICE Blob[1]; +} VIABLE_CHOICE_STRUCT; +typedef VIABLE_CHOICE_STRUCT *VIABLE_CHOICE; + +typedef struct +{ + VIABLE_CHOICE Choice; + float ChunkCertainty[MAX_NUM_CHUNKS]; + UINT8 ChunkClass[MAX_NUM_CHUNKS]; +} + + +EXPANDED_CHOICE; + +/**---------------------------------------------------------------------------- + Macros +----------------------------------------------------------------------------**/ +#define BestCertainty(Choices) (((VIABLE_CHOICE) first (Choices))->Certainty) +#define BestRating(Choices) (((VIABLE_CHOICE) first (Choices))->Rating) +#define BestFactor(Choices) (((VIABLE_CHOICE) first (Choices))->AdjustFactor) + +#define AmbigThreshold(F1,F2) (((F2) - (F1)) * AmbigThresholdGain - \ + AmbigThresholdOffset) + +/*--------------------------------------------------------------------------- + Private Function Prototoypes +----------------------------------------------------------------------------*/ +void AddNewChunk(VIABLE_CHOICE Choice, int Blob); + +int AmbigsFound(char *Word, + char *CurrentChar, + const char *Tail, + LIST Ambigs, + DANGERR *fixpt); + +int ChoiceSameAs(A_CHOICE *Choice, VIABLE_CHOICE ViableChoice); + +int CmpChoiceRatings(void *arg1, //VIABLE_CHOICE Choice1, + void *arg2); //VIABLE_CHOICE Choice2); + +void ExpandChoice(VIABLE_CHOICE Choice, EXPANDED_CHOICE *ExpandedChoice); + +AMBIG_TABLE *FillAmbigTable(); + +int FreeBadChoice(void *item1, //VIABLE_CHOICE Choice, + void *item2); //EXPANDED_CHOICE *BestChoice); + +int LengthOfShortestAlphaRun(register char *Word); + +VIABLE_CHOICE NewViableChoice (A_CHOICE * Choice, +FLOAT32 AdjustFactor, float Certainties[]); + +void PrintViableChoice(FILE *File, const char *Label, VIABLE_CHOICE Choice); + +void ReplaceDuplicateChoice (VIABLE_CHOICE OldChoice, +A_CHOICE * NewChoice, +FLOAT32 AdjustFactor, float Certainties[]); + +int StringSameAs(const char *String, VIABLE_CHOICE ViableChoice); + +int UniformCertainties(CHOICES_LIST Choices, A_CHOICE *BestChoice); + +/**---------------------------------------------------------------------------- + Global Data Definitions and Declarations +----------------------------------------------------------------------------**/ +/* Name of file containing potentially dangerous ambiguities */ +static const char *DangerousAmbigs = DANGEROUS_AMBIGS; + +/* Word for which stopper debug information should be printed to stdout */ +static char *WordToDebug = NULL; + +/* flag used to disable accumulation of word choices during compound word + permutation */ +BOOL8 KeepWordChoices = TRUE; + +/* additional certainty padding allowed before a word is rejected */ +static FLOAT32 RejectOffset = 0.0; + +/* structures to keep track of viable word choices */ +static VIABLE_CHOICE BestRawChoice = NULL; +static LIST BestChoices = NIL; +static PIECES_STATE CurrentSegmentation; + +make_float_var (NonDictCertainty, -2.50, MakeNonDictCertainty, +17, 2, SetNonDictCertainty, +"Certainty threshold for non-dict words"); + +make_float_var (RejectCertaintyOffset, 1.0, MakeRejectCertaintyOffset, +17, 3, SetRejectCertaintyOffset, "Reject certainty offset"); + +make_int_var (SmallWordSize, 2, MakeSmallWordSize, +17, 4, SetSmallWordSize, +"Size of dict word to be treated as non-dict word"); + +make_float_var (CertaintyPerChar, -0.50, MakeCertaintyPerChar, +17, 5, SetCertaintyPerChar, +"Certainty to add for each dict char above SmallWordSize"); + +make_float_var (CertaintyVariation, 3.0, MakeCertaintyVariation, +17, 6, SetCertaintyVariation, +"Max certaintly variation allowed in a word (in sigma)"); + +make_int_var (StopperDebugLevel, 0, MakeStopperDebugLevel, +17, 7, SetStopperDebugLevel, "Stopper debug level"); + +make_float_var (AmbigThresholdGain, 8.0, MakeAmbigThresholdGain, +17, 8, SetAmbigThresholdGain, +"Gain factor for ambiguity threshold"); + +make_float_var (AmbigThresholdOffset, 1.5, MakeAmbigThresholdOffset, +17, 9, SetAmbigThresholdOffset, +"Certainty offset for ambiguity threshold"); + +//extern char *demodir; +extern int first_pass; +INT_VAR (tessedit_truncate_wordchoice_log, 10, "Max words to keep in list"); + +/**---------------------------------------------------------------------------- + Public Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +int AcceptableChoice(CHOICES_LIST Choices, + A_CHOICE *BestChoice, + A_CHOICE *RawChoice, + DANGERR *fixpt) { +/* + ** Parameters: + ** Choices choices for current segmentation + ** BestChoice best choice for current segmentation + ** RawChoice best raw choice for current segmentation + ** Globals: + ** NonDictCertainty certainty for a non-dict word + ** SmallWordSize size of word to be treated as non-word + ** CertaintyPerChar certainty to add for each dict char + ** Operation: Return TRUE if the results from this segmentation are + ** good enough to stop. Otherwise return FALSE. + ** Return: TRUE or FALSE. + ** Exceptions: none + ** History: Mon Apr 29 14:57:32 1991, DSJ, Created. + */ + float CertaintyThreshold = NonDictCertainty; + int WordSize; + + if (fixpt != NULL) + fixpt->index = -1; + if ((BestChoice == NULL) || (class_string (BestChoice) == NULL)) + return (FALSE); + + if (StopperDebugLevel >= 1) + cprintf ("\nStopper: %s (word=%c, case=%c, punct=%c)\n", + class_string (BestChoice), + (valid_word (class_string (BestChoice)) ? 'y' : 'n'), + (case_ok (class_string (BestChoice)) ? 'y' : 'n'), + ((punctuation_ok (class_string (BestChoice)) != + -1) ? 'y' : 'n')); + + if (valid_word (class_string (BestChoice)) && + case_ok (class_string (BestChoice)) && + punctuation_ok (class_string (BestChoice)) != -1) { + WordSize = LengthOfShortestAlphaRun (class_string (BestChoice)); + WordSize -= SmallWordSize; + if (WordSize < 0) + WordSize = 0; + CertaintyThreshold += WordSize * CertaintyPerChar; + } + else if (stopper_numbers_on && valid_number (class_string (BestChoice))) { + CertaintyThreshold += stopper_numbers_on * CertaintyPerChar; + } + + if (StopperDebugLevel >= 1) + cprintf ("Stopper: Certainty = %4.1f, Threshold = %4.1f\n", + class_certainty (BestChoice), CertaintyThreshold); + + if (NoDangerousAmbig (class_string (BestChoice), fixpt) + && class_certainty (BestChoice) > CertaintyThreshold && + UniformCertainties (Choices, BestChoice)) + return (TRUE); + else + return (FALSE); + +} /* AcceptableChoice */ + + +/*---------------------------------------------------------------------------*/ +int AcceptableResult(A_CHOICE *BestChoice, A_CHOICE *RawChoice) { +/* + ** Parameters: + ** BestChoice best choice for current word + ** RawChoice best raw choice for current word + ** Globals: + ** NonDictCertainty certainty for a non-dict word + ** SmallWordSize size of word to be treated as non-word + ** CertaintyPerChar certainty to add for each dict char + ** BestChoices list of all good choices found + ** RejectOffset allowed offset before a word is rejected + ** Operation: Return 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. + ** Return: TRUE or FALSE. + ** Exceptions: none + ** History: Thu May 9 14:05:05 1991, DSJ, Created. + */ + float CertaintyThreshold = NonDictCertainty - RejectOffset; + int WordSize; + + if (StopperDebugLevel >= 1) + cprintf ("\nRejecter: %s (word=%c, case=%c, punct=%c, unambig=%c)\n", + class_string (BestChoice), + (valid_word (class_string (BestChoice)) ? 'y' : 'n'), + (case_ok (class_string (BestChoice)) ? 'y' : 'n'), + ((punctuation_ok (class_string (BestChoice)) != -1) ? 'y' : 'n'), + ((rest (BestChoices) != NIL) ? 'n' : 'y')); + + if ((BestChoice == NULL) || + (class_string (BestChoice) == NULL) || CurrentWordAmbig ()) + return (FALSE); + + if (valid_word (class_string (BestChoice)) && + case_ok (class_string (BestChoice)) && + punctuation_ok (class_string (BestChoice)) != -1) { + WordSize = LengthOfShortestAlphaRun (class_string (BestChoice)); + WordSize -= SmallWordSize; + if (WordSize < 0) + WordSize = 0; + CertaintyThreshold += WordSize * CertaintyPerChar; + } + + if (StopperDebugLevel >= 1) + cprintf ("Rejecter: Certainty = %4.1f, Threshold = %4.1f ", + class_certainty (BestChoice), CertaintyThreshold); + + if (class_certainty (BestChoice) > CertaintyThreshold) { + if (StopperDebugLevel >= 1) + cprintf ("ACCEPTED\n"); + return (TRUE); + } + else { + if (StopperDebugLevel >= 1) + cprintf ("REJECTED\n"); + return (FALSE); + } +} /* AcceptableResult */ + + +/*---------------------------------------------------------------------------*/ +int AlternativeChoicesWorseThan(FLOAT32 Threshold) { +/* + ** Parameters: + ** Threshold minimum adjust factor for alternative choices + ** Globals: + ** BestChoices alternative choices for current word + ** Operation: This routine returns TRUE if there are no alternative + ** choices for the current word OR if all alternatives have + ** an adjust factor worse than Threshold. + ** Return: TRUE or FALSE. + ** Exceptions: none + ** History: Mon Jun 3 09:36:31 1991, DSJ, Created. + */ + LIST Alternatives; + VIABLE_CHOICE Choice; + + Alternatives = rest (BestChoices); + iterate(Alternatives) { + Choice = (VIABLE_CHOICE) first (Alternatives); + if (Choice->AdjustFactor <= Threshold) + return (FALSE); + } + + return (TRUE); + +} /* AlternativeChoicesWorseThan */ + + +/*---------------------------------------------------------------------------*/ +int CurrentBestChoiceIs(const char *Word) { +/* + ** Parameters: + ** Word string to compare to current best choice + ** Globals: + ** BestChoices set of best choices for current word + ** Operation: Returns TRUE if Word is the same as the current best + ** choice, FALSE otherwise. + ** Return: TRUE or FALSE + ** Exceptions: none + ** History: Thu May 30 14:44:22 1991, DSJ, Created. + */ + return (BestChoices != NIL && + StringSameAs (Word, (VIABLE_CHOICE) first (BestChoices))); + +} /* CurrentBestChoiceIs */ + + +/*---------------------------------------------------------------------------*/ +FLOAT32 CurrentBestChoiceAdjustFactor() { +/* + ** Parameters: none + ** Globals: + ** BestChoices set of best choices for current word + ** Operation: Return the adjustment factor for the best choice for + ** the current word. + ** Return: Adjust factor for current best choice. + ** Exceptions: none + ** History: Thu May 30 14:48:24 1991, DSJ, Created. + */ + VIABLE_CHOICE BestChoice; + + if (BestChoices == NIL) + return (MAX_FLOAT32); + + BestChoice = (VIABLE_CHOICE) first (BestChoices); + return (BestChoice->AdjustFactor); + +} /* CurrentBestChoiceAdjustFactor */ + + +/*---------------------------------------------------------------------------*/ +int CurrentWordAmbig() { +/* + ** Parameters: none + ** Globals: + ** BestChoices set of best choices for current word + ** Operation: This routine returns TRUE if there are multiple good + ** choices for the current word and FALSE otherwise. + ** Return: TRUE or FALSE + ** Exceptions: none + ** History: Wed May 22 15:38:38 1991, DSJ, Created. + */ + return (rest (BestChoices) != NIL); + +} /* CurrentWordAmbig */ + + +/*---------------------------------------------------------------------------*/ +void DebugWordChoices() { +/* + ** Parameters: none + ** Globals: + ** BestRawChoice + ** BestChoices + ** Operation: Print the current choices for this word to stdout. + ** Return: none + ** Exceptions: none + ** History: Wed May 15 13:52:08 1991, DSJ, Created. + */ + LIST Choices; + int i; + char LabelString[80]; + + if (StopperDebugLevel >= 1 || + WordToDebug && BestChoices && + StringSameAs (WordToDebug, (VIABLE_CHOICE) first (BestChoices))) { + if (BestRawChoice) + PrintViableChoice (stdout, "\nBest Raw Choice: ", BestRawChoice); + + i = 1; + Choices = BestChoices; + if (Choices) + cprintf ("\nBest Cooked Choices:\n"); + iterate(Choices) { + sprintf (LabelString, "Cooked Choice #%d: ", i); + PrintViableChoice (stdout, LabelString, + (VIABLE_CHOICE) first (Choices)); + i++; + } + } +} /* DebugWordChoices */ + + +/*---------------------------------------------------------------------------*/ +void FilterWordChoices() { +/* + ** Parameters: none + ** Globals: + ** BestChoices set of choices for current word + ** Operation: This routine removes from BestChoices all choices which + ** are not within a reasonable range of the best choice. + ** Return: none + ** Exceptions: none + ** History: Wed May 15 13:08:24 1991, DSJ, Created. + */ + EXPANDED_CHOICE BestChoice; + + if (BestChoices == NIL || second (BestChoices) == NIL) + return; + + /* compute certainties and class for each chunk in best choice */ + ExpandChoice ((VIABLE_CHOICE_STRUCT *) first (BestChoices), &BestChoice); + + set_rest (BestChoices, delete_d (rest (BestChoices), + &BestChoice, FreeBadChoice)); + +} /* FilterWordChoices */ + + +/*---------------------------------------------------------------------------*/ +void +FindClassifierErrors (FLOAT32 MinRating, +FLOAT32 MaxRating, +FLOAT32 RatingMargin, FLOAT32 Thresholds[]) { +/* + ** Parameters: + ** MinRating limits how tight to make a template + ** MaxRating limits how loose to make a template + ** RatingMargin amount of margin to put in template + ** Thresholds[] place to put error thresholds + ** Globals: none + ** Operation: This routine compares the best choice for the current + ** word to the best raw choice to determine which characters + ** were classified incorrectly by the classifier. It then + ** places a separate threshold into Thresholds for each + ** character in the word. If the classifier was correct, + ** MaxRating is placed into Thresholds. If the + ** classifier was incorrect, the avg. match rating (error + ** percentage) of the classifier's incorrect choice minus + ** some margin is + ** placed into thresholds. This can then be used by the + ** caller to try to create a new template for the desired + ** class that will classify the character with a rating better + ** than the threshold value. The match rating placed into + ** Thresholds is never allowed to be below MinRating in order + ** to prevent trying to make overly tight templates. + ** Return: none (results are placed in Thresholds) + ** Exceptions: none + ** History: Fri May 31 16:02:57 1991, DSJ, Created. + */ + EXPANDED_CHOICE BestRaw; + VIABLE_CHOICE Choice; + int i, j, Chunk; + FLOAT32 AvgRating; + int NumErrorChunks; + + assert (BestChoices != NIL); + assert (BestRawChoice != NULL); + + ExpandChoice(BestRawChoice, &BestRaw); + Choice = (VIABLE_CHOICE) first (BestChoices); + + for (i = 0, Chunk = 0; i < Choice->Length; i++, Thresholds++) { + AvgRating = 0.0; + NumErrorChunks = 0; + + for (j = 0; j < Choice->Blob[i].NumChunks; j++, Chunk++) + if (Choice->Blob[i].Class != BestRaw.ChunkClass[Chunk]) { + AvgRating += BestRaw.ChunkCertainty[Chunk]; + NumErrorChunks++; + } + + if (NumErrorChunks > 0) { + AvgRating /= NumErrorChunks; + *Thresholds = (AvgRating / -CertaintyScale) * (1.0 - RatingMargin); + } + else + *Thresholds = MaxRating; + + if (*Thresholds > MaxRating) + *Thresholds = MaxRating; + if (*Thresholds < MinRating) + *Thresholds = MinRating; + } +} /* FindClassifierErrors */ + + +/*---------------------------------------------------------------------------*/ +void InitStopperVars() { +/* + ** Parameters: none + ** Globals: none + ** Operation: Initializes the control variables used by the stopper. + ** Return: none + ** Exceptions: none + ** History: Thu May 9 10:06:04 1991, DSJ, Created. + */ + VALUE dummy; + + string_variable (DangerousAmbigs, "DangerousAmbigs", DANGEROUS_AMBIGS); + string_variable (WordToDebug, "WordToDebug", ""); + + MakeNonDictCertainty(); + MakeRejectCertaintyOffset(); + MakeSmallWordSize(); + MakeCertaintyPerChar(); + MakeCertaintyVariation(); + MakeStopperDebugLevel(); + MakeAmbigThresholdGain(); + MakeAmbigThresholdOffset(); +} /* InitStopperVars */ + + +/*---------------------------------------------------------------------------*/ +void InitChoiceAccum() { +/* + ** Parameters: none + ** Globals: none + ** Operation: This routine initializes the data structures used to + ** keep track the good word choices found for a word. + ** Return: none + ** Exceptions: none + ** History: Fri May 17 07:59:00 1991, DSJ, Created. + */ + BLOB_WIDTH *BlobWidth, *End; + + if (BestRawChoice) + memfree(BestRawChoice); + + if (BestChoices) + destroy_nodes(BestChoices, memfree); + + BestRawChoice = NULL; + BestChoices = NIL; + EnableChoiceAccum(); + + for (BlobWidth = CurrentSegmentation, + End = CurrentSegmentation + MAX_NUM_CHUNKS; + BlobWidth < End; *BlobWidth++ = 1); + +} /* InitChoiceAccum */ + + +/*---------------------------------------------------------------------------*/ +void +LogNewRawChoice (A_CHOICE * Choice, FLOAT32 AdjustFactor, float Certainties[]) { +/* + ** Parameters: + ** Choice new raw choice for current word + ** AdjustFactor adjustment factor which was applied to choice + ** Certainties certainties for each char in new choice + ** Globals: + ** BestRawChoice best raw choice so far for current word + ** Operation: This routine compares Choice to the best raw (non-dict) + ** choice so far and replaces it if the new choice is better. + ** Return: none + ** Exceptions: none + ** History: Wed May 15 09:57:19 1991, DSJ, Created. + */ + if (!KeepWordChoices) + return; + + if (!BestRawChoice) + BestRawChoice = NewViableChoice (Choice, AdjustFactor, Certainties); + else if (class_probability (Choice) < BestRawChoice->Rating) { + if (ChoiceSameAs (Choice, BestRawChoice)) + ReplaceDuplicateChoice(BestRawChoice, Choice, AdjustFactor, Certainties); + else { + memfree(BestRawChoice); + BestRawChoice = NewViableChoice (Choice, AdjustFactor, Certainties); + } + } +} /* LogNewRawChoice */ + + +/*---------------------------------------------------------------------------*/ +void LogNewSegmentation(PIECES_STATE BlobWidth) { +/* + ** Parameters: + ** BlobWidth[] number of chunks in each blob in segmentation + ** Globals: + ** CurrentSegmentation blob widths for current segmentation + ** Operation: This routine updates the blob widths in CurrentSegmentation + ** to be the same as provided in BlobWidth. + ** Return: none + ** Exceptions: none + ** History: Mon May 20 11:52:26 1991, DSJ, Created. + */ + BLOB_WIDTH *Segmentation; + + for (Segmentation = CurrentSegmentation; *BlobWidth != 0; + BlobWidth++, Segmentation++) + *Segmentation = *BlobWidth; + *Segmentation = 0; + +} /* LogNewSegmentation */ + + +/*---------------------------------------------------------------------------*/ +void LogNewSplit(int Blob) { +/* + ** Parameters: + ** Blob index of blob that was split + ** Globals: + ** BestRawChoice current best raw choice + ** BestChoices list of best choices found so far + ** Operation: This routine adds 1 chunk to the specified blob for each + ** choice in BestChoices and for the BestRawChoice. + ** Return: none + ** Exceptions: none + ** History: Mon May 20 11:38:56 1991, DSJ, Created. + */ + LIST Choices; + + if (BestRawChoice) { + AddNewChunk(BestRawChoice, Blob); + } + + Choices = BestChoices; + iterate(Choices) { + AddNewChunk ((VIABLE_CHOICE) first (Choices), Blob); + } + +} /* LogNewSplit */ + + +/*---------------------------------------------------------------------------*/ +void +LogNewWordChoice (A_CHOICE * Choice, +FLOAT32 AdjustFactor, float Certainties[]) { +/* + ** Parameters: + ** Choice new choice for current word + ** AdjustFactor adjustment factor which was applied to choice + ** Certainties certainties for each char in new choice + ** Globals: + ** BestChoices best choices so far for current word + ** Operation: This routine adds Choice to BestChoices if the + ** adjusted certainty for Choice is within a reasonable range + ** of the best choice in BestChoices. The BestChoices + ** list is kept in sorted order by rating. Duplicates are + ** removed. + ** Return: none + ** Exceptions: none + ** History: Wed May 15 09:57:19 1991, DSJ, Created. + */ + VIABLE_CHOICE NewChoice; + LIST Choices; + FLOAT32 Threshold; + + if (!KeepWordChoices) + return; + + /* throw out obviously bad choices to save some work */ + if (BestChoices != NIL) { + Threshold = AmbigThreshold (BestFactor (BestChoices), AdjustFactor); + if (Threshold > -AmbigThresholdOffset) + Threshold = -AmbigThresholdOffset; + if (class_certainty (Choice) - BestCertainty (BestChoices) < Threshold) + return; + } + + /* see if a choice with the same text string has already been found */ + NewChoice = NULL; + Choices = BestChoices; + iterate(Choices) { + if (ChoiceSameAs (Choice, (VIABLE_CHOICE) first (Choices))) + if (class_probability (Choice) < BestRating (Choices)) + NewChoice = (VIABLE_CHOICE) first (Choices); + else + return; + } + + if (NewChoice) { + ReplaceDuplicateChoice(NewChoice, Choice, AdjustFactor, Certainties); + BestChoices = delete_d (BestChoices, NewChoice, is_same_node); + } + else { + NewChoice = NewViableChoice (Choice, AdjustFactor, Certainties); + } + + BestChoices = s_adjoin (BestChoices, NewChoice, CmpChoiceRatings); + if (StopperDebugLevel >= 2) + PrintViableChoice (stdout, "New Word Choice: ", NewChoice); + if (count (BestChoices) > tessedit_truncate_wordchoice_log) { + Choices = + (LIST) nth_cell (BestChoices, tessedit_truncate_wordchoice_log); + destroy_nodes (rest (Choices), Efree); + set_rest(Choices, NIL); + } + +} /* LogNewWordChoice */ + + +/*---------------------------------------------------------------------------*/ +static AMBIG_TABLE *AmbigFor = NULL; + +int NoDangerousAmbig(const char *Word, DANGERR *fixpt) { +/* + ** Parameters: + ** Word word to check for dangerous ambiguities + ** Globals: none + ** Operation: This word checks each letter in word against a list + ** of potentially ambiguous characters. If a match is found + ** that letter is replaced with its ambiguity and tested in + ** the dictionary. If the ambiguous word is found in the + ** dictionary, FALSE is returned. Otherwise, the search + ** continues for other ambiguities. If no ambiguities that + ** match in the dictionary are found, TRUE is returned. + ** Return: TRUE if Word contains no dangerous ambiguities. + ** Exceptions: none + ** History: Mon May 6 16:28:56 1991, DSJ, Created. + */ + + char NewWord[MAX_WERD_SIZE]; + char *NextNewChar; + int bad_index = 0; + + if (!AmbigFor) + AmbigFor = FillAmbigTable (); + + NextNewChar = NewWord; + while (*Word) + if (AmbigsFound (NewWord, NextNewChar, Word + 1, AmbigFor[*Word], fixpt)) { + if (fixpt != NULL) + fixpt->index = bad_index; + return (FALSE); + } + else { + *NextNewChar++ = *Word++; + bad_index++; + } + + return (TRUE); + +} /* NoDangerousAmbig */ + +void EndDangerousAmbigs() { + if (AmbigFor != NULL) { + for (int i = 0; i <= MAX_CLASS_ID; ++i) { + destroy_nodes(AmbigFor[i], Efree); + } + Efree(AmbigFor); + AmbigFor = NULL; + } +} + +/*---------------------------------------------------------------------------*/ +void SettupStopperPass1() { +/* + ** Parameters: none + ** Globals: + ** RejectOffset offset allowed before word is rejected + ** Operation: This routine performs any settup of stopper variables + ** that is needed in preparation for the first pass. + ** Return: none + ** Exceptions: none + ** History: Mon Jun 3 12:32:00 1991, DSJ, Created. + */ + RejectOffset = 0.0; +} /* SettupStopperPass1 */ + + +/*---------------------------------------------------------------------------*/ +void SettupStopperPass2() { +/* + ** Parameters: none + ** Globals: + ** RejectOffset offset allowed before word is rejected + ** Operation: This routine performs any settup of stopper variables + ** that is needed in preparation for the second pass. + ** Return: none + ** Exceptions: none + ** History: Mon Jun 3 12:32:00 1991, DSJ, Created. + */ + RejectOffset = RejectCertaintyOffset; +} /* SettupStopperPass2 */ + + +/**---------------------------------------------------------------------------- + Private Code +----------------------------------------------------------------------------**/ +/*---------------------------------------------------------------------------*/ +void AddNewChunk(VIABLE_CHOICE Choice, int Blob) { +/* + ** Parameters: + ** Choice choice to add a new chunk to + ** Blob index of blob being split + ** Globals: none + ** Operation: This routine increments the chunk count of the character + ** in Choice which corresponds to Blob. + ** Return: none + ** Exceptions: none + ** History: Mon May 20 11:43:27 1991, DSJ, Created. + */ + int i, LastChunk; + + for (i = 0, LastChunk = 0; i < Choice->Length; i++) { + LastChunk += Choice->Blob[i].NumChunks; + if (Blob < LastChunk) { + (Choice->Blob[i].NumChunks)++; + return; + } + } + mem_tidy (1); + cprintf ("AddNewChunk failed:Choice->Length=%d, LastChunk=%d, Blob=%d\n", + Choice->Length, LastChunk, Blob); + assert(FALSE); /* this should never get executed */ + +} /* AddNewChunk */ + + +/*---------------------------------------------------------------------------*/ +int AmbigsFound(char *Word, + char *CurrentChar, + const char *Tail, + LIST Ambigs, + DANGERR *fixpt) { +/* + ** Parameters: + ** Word word being tested for ambiguities + ** CurrentChar position in Word to put ambig replacement + ** Tail end of word to place after ambiguity + ** Ambigs list of ambiguities to test at this position + ** Globals: none + ** Operation: For each ambiguity in Ambigs, see if the remainder of + ** the test string matches the start of Tail. If it does, + ** construct a word consisting of the contents of Word up to, + ** but not including, CurrentChar followed by the replacement + ** string for the ambiguity followed by the unmatched + ** contents of Tail. Then test this word to see if it + ** is a dictionary word. If it is return TRUE. If none of + ** the ambiguities result in a dictionary word, return FALSE. + ** Return: TRUE if the Word is ambiguous at the specified position + ** Exceptions: none + ** History: Thu May 9 10:10:28 1991, DSJ, Created. + */ + char *AmbigSpec; + const char *UnmatchedTail; + int Matches; + int bad_length; + + iterate(Ambigs) { + AmbigSpec = (char *) first (Ambigs); + bad_length = 1; + UnmatchedTail = Tail; + Matches = TRUE; + + while (*AmbigSpec != ' ' && Matches) + if (*AmbigSpec == *UnmatchedTail) { + AmbigSpec++; + UnmatchedTail++; + bad_length++; + } + else + Matches = FALSE; + + if (Matches) { + AmbigSpec++; /* skip over the space */ + /* insert replacement string */ + strcpy(CurrentChar, AmbigSpec); + /* add tail */ + strcat(Word, UnmatchedTail); + if (valid_word (Word)) { + if (StopperDebugLevel >= 1) + cprintf ("Stopper: Possible ambiguous word = %s\n", Word); + if (fixpt != NULL) { + fixpt->good_length = strlen (AmbigSpec); + fixpt->bad_length = bad_length; + } + return (TRUE); + } + } + } + return (FALSE); + +} /* AmbigsFound */ + + +/*---------------------------------------------------------------------------*/ +int ChoiceSameAs(A_CHOICE *Choice, VIABLE_CHOICE ViableChoice) { +/* + ** Parameters: + ** Choice choice to compare to ViableChoice + ** ViableChoice viable choice to compare to Choice + ** Globals: none + ** Operation: This routine compares the corresponding strings of + ** Choice and ViableChoice and returns TRUE if they are the + ** same, FALSE otherwise. + ** Return: TRUE or FALSE. + ** Exceptions: none + ** History: Fri May 17 08:48:04 1991, DSJ, Created. + */ + return (StringSameAs (class_string (Choice), ViableChoice)); + +} /* ChoiceSameAs */ + + +/*---------------------------------------------------------------------------*/ +int CmpChoiceRatings(void *arg1, //VIABLE_CHOICE Choice1, + void *arg2) { //VIABLE_CHOICE Choice2) +/* + ** Parameters: + ** Choice1, Choice2 choices to compare ratings for + ** Globals: none + ** Operation: Return -1 if the rating for Choice1 is less than the + ** rating for Choice2, otherwise return (1). + ** Return: -1 or 1 + ** Exceptions: none + ** History: Wed May 15 13:02:37 1991, DSJ, Created. + */ + float R1, R2; + VIABLE_CHOICE Choice1 = (VIABLE_CHOICE) arg1; + VIABLE_CHOICE Choice2 = (VIABLE_CHOICE) arg2; + + R1 = Choice1->Rating; + R2 = Choice2->Rating; + + if (R1 < R2) + return (-1); + else + return (1); + +} /* CmpChoiceRatings */ + + +/*---------------------------------------------------------------------------*/ +void ExpandChoice(VIABLE_CHOICE Choice, EXPANDED_CHOICE *ExpandedChoice) { +/* + ** Parameters: + ** Choice choice to be expanded + ** ExpandedChoice place to put resulting expanded choice + ** Globals: none + ** Operation: This routine expands Choice and places the results + ** in ExpandedChoice. The primary function of expansion + ** is to create an two arrays, one which holds the corresponding + ** certainty for each chunk in Choice, and one which holds + ** the class for each chunk. + ** Return: none (results are placed in ExpandedChoice) + ** Exceptions: none + ** History: Fri May 31 15:21:57 1991, DSJ, Created. + */ + int i, j, Chunk; + + ExpandedChoice->Choice = Choice; + for (i = 0, Chunk = 0; i < Choice->Length; i++) + for (j = 0; j < Choice->Blob[i].NumChunks; j++, Chunk++) { + ExpandedChoice->ChunkCertainty[Chunk] = Choice->Blob[i].Certainty; + ExpandedChoice->ChunkClass[Chunk] = Choice->Blob[i].Class; + } +} /* ExpandChoice */ + + +/*---------------------------------------------------------------------------*/ +AMBIG_TABLE *FillAmbigTable() { +/* + ** Parameters: none + ** Globals: + ** DangerousAmbigs filename of dangerous ambig info + ** Operation: This routine allocates a new ambiguity table and fills + ** it in from the file specified by DangerousAmbigs. An + ** ambiguity table is an array of lists. The array is indexed + ** by a class id. Therefore, each entry in the table provides + ** a list of potential ambiguities which can start with the + ** corresponding character. Each potential ambiguity is + ** described by a string which contains the remainder of the + ** test string followed by a space followed by the replacement + ** string. For example the ambiguity "rn -> m", would be + ** located in the table at index 'r'. The string corresponding + ** to this ambiguity would be "n m". + ** Return: Pointer to new ambiguity table. + ** Exceptions: none + ** History: Thu May 9 09:20:57 1991, DSJ, Created. + */ + FILE *AmbigFile; + AMBIG_TABLE *NewTable; + int i; + char TestString[256]; + char ReplacementString[256]; + char name[1024]; + char *AmbigSpec; + int AmbigSize; + + strcpy(name, demodir); + strcat(name, DangerousAmbigs); + AmbigFile = Efopen (name, "r"); + NewTable = (AMBIG_TABLE *) Emalloc (sizeof (LIST) * (MAX_CLASS_ID + 1)); + + for (i = 0; i <= MAX_CLASS_ID; i++) + NewTable[i] = NIL; + + while (fscanf (AmbigFile, "%s", TestString) == 1 && + fscanf (AmbigFile, "%s", ReplacementString) == 1) { + if (strlen (TestString) > MAX_AMBIG_SIZE || + strlen (ReplacementString) > MAX_AMBIG_SIZE) + DoError (0, "Illegal ambiguity specification!"); + + AmbigSize = strlen (TestString) + strlen (ReplacementString) + 1; + AmbigSpec = (char *) Emalloc (sizeof (char) * AmbigSize); + + strcpy (AmbigSpec, &(TestString[1])); + strcat (AmbigSpec, " "); + strcat(AmbigSpec, ReplacementString); + NewTable[TestString[0]] = + push_last (NewTable[TestString[0]], AmbigSpec); + } + fclose(AmbigFile); + return (NewTable); + +} /* FillAmbigTable */ + + +/*---------------------------------------------------------------------------*/ +int FreeBadChoice(void *item1, //VIABLE_CHOICE Choice, + void *item2) { //EXPANDED_CHOICE *BestChoice) +/* + ** Parameters: + ** Choice choice to be tested + ** BestChoice best choice found + ** Globals: + ** AmbigThresholdGain + ** AmbigThresholdOffset + ** Operation: If the certainty of any chunk in Choice is not ambiguous + ** with the corresponding chunk in the best choice, free + ** Choice and return TRUE. Otherwise, return FALSE. + ** Return: TRUE or FALSE. + ** Exceptions: none + ** History: Wed May 15 13:20:26 1991, DSJ, Created. + */ + int i, j, Chunk; + FLOAT32 Threshold; + VIABLE_CHOICE Choice; + EXPANDED_CHOICE *BestChoice; + + Choice = (VIABLE_CHOICE) item1; + BestChoice = (EXPANDED_CHOICE *) item2; + + Threshold = AmbigThreshold (BestChoice->Choice->AdjustFactor, + Choice->AdjustFactor); + + for (i = 0, Chunk = 0; i < Choice->Length; i++) + for (j = 0; j < Choice->Blob[i].NumChunks; j++, Chunk++) + if (Choice->Blob[i].Class != BestChoice->ChunkClass[Chunk] && + Choice->Blob[i].Certainty - BestChoice->ChunkCertainty[Chunk] < + Threshold) { + memfree(Choice); + return (TRUE); + } + + return (FALSE); + +} /* FreeBadChoice */ + + +/*---------------------------------------------------------------------------*/ +int LengthOfShortestAlphaRun(register char *Word) { +/* + ** Parameters: + ** Word word to be tested + ** Globals: none + ** Operation: Return the length of the shortest alpha run in Word. + ** Return: Return the length of the shortest alpha run in Word. + ** Exceptions: none + ** History: Tue May 14 07:50:45 1991, DSJ, Created. + */ + register int Shortest = MAXINT; + register int Length; + + for (; *Word; Word++) + if (isalpha (*Word)) { + for (Length = 1, Word++; isalpha (*Word); Word++, Length++); + if (Length < Shortest) + Shortest = Length; + + if (*Word == 0) + break; + } + if (Shortest == MAXINT) + Shortest = 0; + + return (Shortest); + +} /* LengthOfShortestAlphaRun */ + + +/*---------------------------------------------------------------------------*/ +VIABLE_CHOICE +NewViableChoice (A_CHOICE * Choice, FLOAT32 AdjustFactor, float Certainties[]) { +/* + ** Parameters: + ** Choice choice to be converted to a viable choice + ** AdjustFactor factor used to adjust ratings for Choice + ** Certainties certainty for each character in Choice + ** Globals: + ** CurrentSegmentation segmentation corresponding to Choice + ** Operation: Allocate a new viable choice data structure, copy + ** Choice, Certainties, and CurrentSegmentation into it, + ** and return a pointer to it. + ** Return: Ptr to new viable choice. + ** Exceptions: none + ** History: Thu May 16 15:28:29 1991, DSJ, Created. + */ + VIABLE_CHOICE NewChoice; + int Length; + char *Word; + CHAR_CHOICE *NewChar; + BLOB_WIDTH *BlobWidth; + + Length = strlen (class_string (Choice)); + assert (Length <= MAX_NUM_CHUNKS && Length > 0); + + NewChoice = (VIABLE_CHOICE) Emalloc (sizeof (VIABLE_CHOICE_STRUCT) + + (Length - 1) * sizeof (CHAR_CHOICE)); + + NewChoice->Rating = class_probability (Choice); + NewChoice->Certainty = class_certainty (Choice); + NewChoice->AdjustFactor = AdjustFactor; + NewChoice->Length = Length; + + for (Word = class_string (Choice), + NewChar = &(NewChoice->Blob[0]), + BlobWidth = CurrentSegmentation; + *Word; Word++, NewChar++, Certainties++, BlobWidth++) { + NewChar->Class = *Word; + NewChar->NumChunks = *BlobWidth; + NewChar->Certainty = *Certainties; + } + + return (NewChoice); + +} /* NewViableChoice */ + + +/*---------------------------------------------------------------------------*/ +void PrintViableChoice(FILE *File, const char *Label, VIABLE_CHOICE Choice) { +/* + ** Parameters: + ** File open text file to print Choice to + ** Label text label to be printed with Choice + ** Choice choice to be printed + ** Globals: none + ** Operation: This routine dumps a text representation of the + ** specified Choice to File. + ** Return: none + ** Exceptions: none + ** History: Mon May 20 11:16:44 1991, DSJ, Created. + */ + int i, j; + + fprintf (File, "%s", Label); + + fprintf (File, "(R=%5.1f, C=%4.1f, F=%4.2f) ", + Choice->Rating, Choice->Certainty, Choice->AdjustFactor); + + for (i = 0; i < Choice->Length; i++) + fprintf (File, "%c", Choice->Blob[i].Class); + fprintf (File, "\n"); + + for (i = 0; i < Choice->Length; i++) { + fprintf (File, " %c", Choice->Blob[i].Class); + for (j = 0; j < Choice->Blob[i].NumChunks - 1; j++) + fprintf (File, " "); + } + fprintf (File, "\n"); + + for (i = 0; i < Choice->Length; i++) { + for (j = 0; j < Choice->Blob[i].NumChunks; j++) + fprintf (File, "%3d", (int) (Choice->Blob[i].Certainty * -10.0)); + } + fprintf (File, "\n"); + +} /* PrintViableChoice */ + + +/*---------------------------------------------------------------------------*/ +void +ReplaceDuplicateChoice (VIABLE_CHOICE OldChoice, +A_CHOICE * NewChoice, +FLOAT32 AdjustFactor, float Certainties[]) { +/* + ** Parameters: + ** OldChoice existing viable choice to be replaced + ** NewChoice choice to replace OldChoice with + ** AdjustFactor factor used to adjust ratings for OldChoice + ** Certainties certainty for each character in OldChoice + ** Globals: + ** CurrentSegmentation segmentation for NewChoice + ** Operation: This routine is used whenever a better segmentation (or + ** contextual interpretation) is found for a word which already + ** exists. The OldChoice is updated with the relevant + ** information from the new choice. The text string itself + ** does not need to be copied since, by definition, has not + ** changed. + ** Return: none + ** Exceptions: none + ** History: Fri May 17 13:35:58 1991, DSJ, Created. + */ + char *Word; + CHAR_CHOICE *NewChar; + BLOB_WIDTH *BlobWidth; + + OldChoice->Rating = class_probability (NewChoice); + OldChoice->Certainty = class_certainty (NewChoice); + OldChoice->AdjustFactor = AdjustFactor; + + for (Word = class_string (NewChoice), + NewChar = &(OldChoice->Blob[0]), + BlobWidth = CurrentSegmentation; + *Word; Word++, NewChar++, Certainties++, BlobWidth++) { + NewChar->NumChunks = *BlobWidth; + NewChar->Certainty = *Certainties; + } +} /* ReplaceDuplicateChoice */ + + +/*---------------------------------------------------------------------------*/ +int StringSameAs(const char *String, VIABLE_CHOICE ViableChoice) { +/* + ** Parameters: + ** String string to compare to ViableChoice + ** ViableChoice viable choice to compare to String + ** Globals: none + ** Operation: This routine compares String to ViableChoice and + ** returns TRUE if they are the same, FALSE otherwise. + ** Return: TRUE or FALSE. + ** Exceptions: none + ** History: Fri May 17 08:48:04 1991, DSJ, Created. + */ + CHAR_CHOICE *Char; + int i; + + for (Char = &(ViableChoice->Blob[0]), i = 0; + i < ViableChoice->Length; String++, Char++, i++) + if (*String != Char->Class) + return (FALSE); + + if (*String == 0) + return (TRUE); + else + return (FALSE); + +} /* StringSameAs */ + + +/*---------------------------------------------------------------------------*/ +int UniformCertainties(CHOICES_LIST Choices, A_CHOICE *BestChoice) { +/* + ** Parameters: + ** Choices choices for current segmentation + ** BestChoice best choice for current segmentation + ** Globals: + ** CertaintyVariation max allowed certainty variation + ** Operation: This routine 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. + ** Return: TRUE or FALSE. + ** Exceptions: none + ** History: Tue May 14 08:23:21 1991, DSJ, Created. + */ + int i; + CHOICES CharChoices; + float Certainty; + float WorstCertainty = MAX_FLOAT32; + float CertaintyThreshold; + FLOAT64 TotalCertainty; + FLOAT64 TotalCertaintySquared; + FLOAT64 Variance; + FLOAT32 Mean, StdDev; + int WordLength; + + WordLength = array_count (Choices); + if (WordLength < 3) + return (TRUE); + + TotalCertainty = TotalCertaintySquared = 0.0; + for_each_choice(Choices, i) { + CharChoices = (CHOICES) array_index (Choices, i); + Certainty = best_certainty (CharChoices); + TotalCertainty += Certainty; + TotalCertaintySquared += Certainty * Certainty; + if (Certainty < WorstCertainty) + WorstCertainty = Certainty; + } + + /* subtract off worst certainty from statistics */ + WordLength--; + TotalCertainty -= WorstCertainty; + TotalCertaintySquared -= WorstCertainty * WorstCertainty; + + Mean = TotalCertainty / WordLength; + Variance = ((WordLength * TotalCertaintySquared - + TotalCertainty * TotalCertainty) / + (WordLength * (WordLength - 1))); + if (Variance < 0.0) + Variance = 0.0; + StdDev = sqrt (Variance); + + CertaintyThreshold = Mean - CertaintyVariation * StdDev; + if (CertaintyThreshold > NonDictCertainty) + CertaintyThreshold = NonDictCertainty; + + if (class_certainty (BestChoice) < CertaintyThreshold) { + if (StopperDebugLevel >= 1) + cprintf + ("Stopper: Non-uniform certainty = %4.1f (m=%4.1f, s=%4.1f, t=%4.1f)\n", + class_certainty (BestChoice), Mean, StdDev, CertaintyThreshold); + return (FALSE); + } + else + return (TRUE); + +} /* UniformCertainties */ diff --git a/dict/stopper.h b/dict/stopper.h new file mode 100644 index 0000000000..5101c378d9 --- /dev/null +++ b/dict/stopper.h @@ -0,0 +1,101 @@ +/****************************************************************************** + ** 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 "choicearr.h" +#include "states.h" + +typedef UINT8 BLOB_WIDTH; + +typedef struct +{ + INT16 index; + unsigned bad_length:8; + unsigned good_length:8; +} DANGERR; + +/*--------------------------------------------------------------------------- + Variables +---------------------------------------------------------------------------*/ +extern float CertaintyPerChar; +extern float NonDictCertainty; +extern float RejectCertaintyOffset; +extern int StopperDebugLevel; + +/**---------------------------------------------------------------------------- + Macros +----------------------------------------------------------------------------**/ +#define DisableChoiceAccum() (KeepWordChoices = FALSE) +#define EnableChoiceAccum() (KeepWordChoices = TRUE) + +/**---------------------------------------------------------------------------- + Public Function Prototypes +----------------------------------------------------------------------------**/ +int AcceptableChoice(CHOICES_LIST Choices, + A_CHOICE *BestChoice, + A_CHOICE *RawChoice, + DANGERR *fixpt); + +int AcceptableResult(A_CHOICE *BestChoice, A_CHOICE *RawChoice); + +int AlternativeChoicesWorseThan(FLOAT32 Threshold); + +int CurrentBestChoiceIs(const char *Word); + +FLOAT32 CurrentBestChoiceAdjustFactor(); + +int CurrentWordAmbig(); + +void DebugWordChoices(); + +void FilterWordChoices(); + +void FindClassifierErrors (FLOAT32 MinRating, +FLOAT32 MaxRating, +FLOAT32 RatingMargin, FLOAT32 Thresholds[]); + +void InitStopperVars(); + +void InitChoiceAccum(); + +void LogNewRawChoice (A_CHOICE * Choice, +FLOAT32 AdjustFactor, float Certainties[]); + +void LogNewSegmentation(PIECES_STATE BlobWidth); + +void LogNewSplit(int Blob); + +void LogNewWordChoice (A_CHOICE * Choice, +FLOAT32 AdjustFactor, float Certainties[]); + +int NoDangerousAmbig(const char *Word, DANGERR *fixpt); +void EndDangerousAmbigs(); + +void SettupStopperPass1(); + +void SettupStopperPass2(); + +/**---------------------------------------------------------------------------- + Global Data Definitions and Declarations +----------------------------------------------------------------------------**/ +extern BOOL8 KeepWordChoices; +#endif diff --git a/dict/trie.cpp b/dict/trie.cpp new file mode 100644 index 0000000000..6dfea67cee --- /dev/null +++ b/dict/trie.cpp @@ -0,0 +1,463 @@ +/* -*-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 +----------------------------------------------------------------------*/ +#include "trie.h" +#include "callcpp.h" + +#ifdef __UNIX__ +#include +#endif +#include + +/*---------------------------------------------------------------------- + V a r i a b l e s +----------------------------------------------------------------------*/ +static INT32 move_counter = 0; +static INT32 new_counter = 0; +static INT32 edge_counter = 0; + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +/********************************************************************** + * add_edge_linkage + * + * Add a single edge linkage to between the two nodes. This function + * can be used to add either forward or backward links. + **********************************************************************/ +void add_edge_linkage(EDGE_ARRAY dawg, + NODE_REF node1, + NODE_REF node2, + INT32 direction, + char character, + INT32 word_end) { + EDGE_REF edge1 = node1; + EDGE_REF edge2; + INT32 num_edges = edges_in_node (dawg, node1); + INT32 last_one; + + word_end = (word_end ? WERD_END_FLAG : 0); + + if (num_edges == 0) { /* No edges yet */ + direction = ((direction == FORWARD_EDGE) ? DIRECTION_FLAG : 0); + link_edge (dawg, edge1, node2, character, + LAST_FLAG | direction | word_end); + } + else { + /* Forward links */ + if (direction == FORWARD_EDGE) { + last_one = (forward_edge (dawg, edge1) ? 0 : LAST_FLAG); + if (debug) + cprintf ("moving edges (nodes = %ld, %dl, num = %ld)\n", + edge1, edge1+1, num_edges); + copy_edges (dawg, edge1, edge1+1, num_edges); + link_edge (dawg, edge1, node2, character, + last_one | DIRECTION_FLAG | word_end); + } + else { /* Backward links */ + + if (forward_edge (dawg, edge1)) + edge_loop(dawg, edge1); + + /* Existing back edges */ + if (backward_edge (dawg,edge1)) { + num_edges = 0; + edge2 = edge1; + do { num_edges++; } + edge_loop(dawg, edge2); + + if (debug) + cprintf ("moving edges (nodes = %ld, %ld, num = %ld)\n", + edge1, edge1+1, num_edges); + copy_edges (dawg, edge1, edge1+1, num_edges); + link_edge(dawg, edge1, node2, character, word_end); + } + else { /* First back edge */ + link_edge (dawg, edge1, node2, character, + LAST_FLAG | word_end); + } + } + } +} + + +/********************************************************************** + * add_new_edge + * + * Add an edge between two nodes in the DAWG. Link the nodes both ways. + **********************************************************************/ +void add_new_edge(EDGE_ARRAY dawg, + NODE_REF *node1, + NODE_REF *node2, + char character, + INT32 word_end, + INT32 max_num_edges, + INT32 reserved_edges) { + int direction; + + if (debug) + cprintf ("add_new_edge (nodes = %ld, %ld, ch = '%c', end = %d)\n", + *node1, *node2, character, word_end); + edge_counter++; + + move_node_if_needed(dawg, *node1, max_num_edges, reserved_edges); + move_node_if_needed(dawg, *node2, max_num_edges, reserved_edges); + + direction = (int) FORWARD_EDGE; + + add_edge_linkage(dawg, *node1, *node2, direction, character, word_end); + + direction = (int) BACKWARD_EDGE; + add_edge_linkage(dawg, *node2, *node1, direction, character, word_end); +} + + +/********************************************************************** + * add_word_to_dawg + * + * Add in a word by creating the necessary nodes and edges. + **********************************************************************/ +void add_word_to_dawg(EDGE_ARRAY dawg, + char *string, + INT32 max_num_edges, + INT32 reserved_edges) { + EDGE_REF edge; + NODE_REF last_node = 0; + NODE_REF the_next_node; + INT32 i; + INT32 still_finding_chars = TRUE; + INT32 word_end = FALSE; + + for (i=0; i reserved_edges) { + cprintf ("error: Not enough room in root node, %d\n", + edges_in_node (dawg, 0)); + exit (1); + } +} + + +/********************************************************************** + * initialize_dawg + * + * Initialize the DAWG data structure for further used. Reset each of + * the edge cells to NO_EDGE. + **********************************************************************/ +void initialize_dawg(EDGE_ARRAY dawg, INT32 max_num_edges) { + INT32 x; + + clear_all_edges(dawg, x, max_num_edges); +} + + +/********************************************************************** + * move_node + * + * Move the location in the edge array of this node in the DAWG. + **********************************************************************/ +NODE_REF move_node(EDGE_ARRAY dawg, + NODE_REF node, + INT32 max_num_edges, + INT32 reserved_edges) { + NODE_REF this_new_node; + EDGE_REF edge; + INT32 num_edges = edges_in_node (dawg, node); + + if (debug) + print_dawg_node(dawg, node); + + this_new_node = new_dawg_node (dawg, num_edges + EDGE_NUM_MARGIN, max_num_edges, reserved_edges); + + if (debug) + cprintf ("move_node (from = %ld, to = %ld, num = %ld)\n", + node, this_new_node, num_edges); + + move_edges(dawg, node, this_new_node, num_edges); + + if (debug) + print_dawg_node(dawg, this_new_node); + + edge = this_new_node; + if (forward_edge (dawg, edge)) { + do { + relocate_edge (dawg, next_node (dawg, edge), node, this_new_node); + } edge_loop (dawg, edge); + } + if (backward_edge (dawg, edge)) { + do { + relocate_edge (dawg, next_node (dawg, edge), node, this_new_node); + } edge_loop (dawg, edge); + } + + move_counter++; + return (this_new_node); +} + + +/********************************************************************** + * new_dawg_node + * + * Create a space within the DAWG data structure to house a node that + * consists of the requested number of edges. + **********************************************************************/ +NODE_REF new_dawg_node(EDGE_ARRAY dawg, + INT32 num_edges, + INT32 max_num_edges, + INT32 reserved_edges) { + INT32 i; + INT32 n; + INT32 edge_index; + INT32 edge_collision; + /* Try several times */ + for (i=0; i %ld)\n", node, old_node, new_node); + + edge = node; + if (forward_edge (dawg, edge)) { + do { + if (next_node (dawg, edge) == old_node) { + if (debug) + cprintf ("forward assign (%ld, %ld ==> %ld)\n", edge, old_node, new_node); + + set_next_edge(dawg, edge, new_node); + } + } edge_loop (dawg, edge); + } + + if (backward_edge (dawg, edge)) { + do { + if (next_node (dawg, edge) == old_node) { + if (debug) + cprintf ("backward assign (%ld, %ld ==> %ld)\n", edge, old_node, new_node); + + set_next_edge(dawg, edge, new_node); + } + } + edge_loop(dawg, edge); + } +} + + +/********************************************************************** + * remove_edge + * + * Add a single edge linkage to between the two nodes. This function + * can be used to add either forward or backward links. + **********************************************************************/ +void remove_edge(EDGE_ARRAY dawg, + NODE_REF node1, + NODE_REF node2, + char character, + INT32 word_end) { + remove_edge_linkage(dawg, node1, node2, FORWARD_EDGE, character, word_end); + + remove_edge_linkage(dawg, node2, node1, BACKWARD_EDGE, character, word_end); +} + + +/********************************************************************** + * remove_edge_linkage + * + * Remove this edge reference from this node. Move the edge entries in + * this node to fill the gap. + **********************************************************************/ +void remove_edge_linkage(EDGE_ARRAY dawg, + NODE_REF node, + NODE_REF next, + INT32 direction, + char character, + INT32 word_end) { + INT32 forward_edges; + INT32 num_edges; + INT32 e = node; + INT32 last_flag; + + forward_edges = num_forward_edges (dawg, node); + num_edges = edges_in_node (dawg, node); + + for (e=node; e=0; i--) { \ + copy_edge(dawg,from+i,to+i); \ + } \ +} \ + + +/********************************************************************** + * move_node_if_needed + * + * Move the node location if there is a need to do it because there is + * not enough room to add the new edges. + **********************************************************************/ + +#define move_node_if_needed(dawg,node,max_num_edges,reserved_edges) \ +if (! room_in_node (dawg, node)) { \ + node = move_node (dawg, node, max_num_edges, reserved_edges); \ +} \ + + +/*---------------------------------------------------------------------- + F u n c t i o n s +----------------------------------------------------------------------*/ +void add_edge_linkage(EDGE_ARRAY dawg, + NODE_REF node1, + NODE_REF node2, + INT32 direction, + char character, + INT32 word_end); + +void add_new_edge(EDGE_ARRAY dawg, + NODE_REF *node1, + NODE_REF *node2, + char character, + INT32 word_end, + INT32 max_num_edges, + INT32 reserved_edges); + +void add_word_to_dawg(EDGE_ARRAY dawg, + char *string, + INT32 max_num_edges, + INT32 reserved_edges); + +void initialize_dawg(EDGE_ARRAY dawg, INT32 max_num_edges); + +NODE_REF move_node(EDGE_ARRAY dawg, + NODE_REF node, + INT32 max_num_edges, + INT32 reserved_edges); + +NODE_REF new_dawg_node(EDGE_ARRAY dawg, + INT32 num_edges, + INT32 max_num_edges, + INT32 reserved_edges); + +void read_word_list(char *filename, + EDGE_ARRAY dawg, + INT32 max_num_edges, + INT32 reserved_edges); + +void relocate_edge(EDGE_ARRAY dawg, + NODE_REF node, + NODE_REF old_node, + NODE_REF new_node); + +void remove_edge(EDGE_ARRAY dawg, + NODE_REF node1, + NODE_REF node2, + char character, + INT32 word_end); + +void remove_edge_linkage(EDGE_ARRAY dawg, + NODE_REF node, + NODE_REF next, + INT32 direction, + char character, + INT32 word_end); + +INT32 room_in_node(EDGE_ARRAY dawg, NODE_REF node); + +/* +#if defined(__STDC__) || defined(__cplusplus) +# define _ARGS(s) s +#else +# define _ARGS(s) () +#endif*/ + +/* trie.c * +void add_edge_linkage + _ARGS((EDGE_ARRAY dawg, + NODE_REF node1, + NODE_REF node2, + INT32 direction, + int character, + INT32 word_end)); + +void add_new_edge + _ARGS((EDGE_ARRAY dawg, + NODE_REF *node1, + NODE_REF *node2, + int character, + INT32 word_end, + INT32 max_num_edges, + INT32 reserved_edges)); + +void add_word_to_dawg + _ARGS((EDGE_ARRAY dawg, + char *string, + INT32 max_num_edges, + INT32 reserved_edges)); + +void initialize_dawg + _ARGS((EDGE_ARRAY dawg, + INT32 max_num_edges)); + +NODE_REF move_node + _ARGS((EDGE_ARRAY dawg, + NODE_REF node, + INT32 max_num_edges, + INT32 reserved_edges)); + +NODE_REF new_dawg_node + _ARGS((EDGE_ARRAY dawg, + INT32 num_edges, + INT32 max_num_edges, + INT32 reserved_edges)); + +void read_word_list + _ARGS((char *filename, + EDGE_ARRAY dawg, + INT32 max_num_edges, + INT32 reserved_edges)); + +void relocate_edge + _ARGS((EDGE_ARRAY dawg, + NODE_REF node, + NODE_REF old_node, + NODE_REF new_node)); + +void remove_edge + _ARGS((EDGE_ARRAY dawg, + NODE_REF node1, + NODE_REF node2, + int character, + INT32 word_end)); + +void remove_edge_linkage + _ARGS((EDGE_ARRAY dawg, + NODE_REF node, + NODE_REF next, + INT32 direction, + int character, + INT32 word_end)); + +INT32 room_in_node + _ARGS((EDGE_ARRAY dawg, + NODE_REF node)); + +#undef _ARGS +*/ +#endif diff --git a/display/Makefile.am b/display/Makefile.am new file mode 100644 index 0000000000..b9a8e3a744 --- /dev/null +++ b/display/Makefile.am @@ -0,0 +1,14 @@ +SUBDIRS = +AM_CPPFLAGS = \ + -I$(top_srcdir)/image -I$(top_srcdir)/ccstruct \ + -I$(top_srcdir)/viewer -I$(top_srcdir)/textord \ + -I$(top_srcdir)/ccutil + +EXTRA_DIST = \ + cmndwin.h pagewalk.h pgedit.h pgeditx.h sbdmenu.h submen.h \ + tessio.h varabled.h varblmen.h varblwin.h + +noinst_LIBRARIES = libtesseract_display.a +libtesseract_display_a_SOURCES = \ + cmndwin.cpp pagewalk.cpp pgedit.cpp sbdmenu.cpp varabled.cpp \ + varblmen.cpp varblwin.cpp diff --git a/display/Makefile.in b/display/Makefile.in new file mode 100644 index 0000000000..90e8144c1c --- /dev/null +++ b/display/Makefile.in @@ -0,0 +1,539 @@ +# Makefile.in generated by automake 1.9.6 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005 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@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = .. +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +INSTALL = @INSTALL@ +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@ +subdir = display +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \ + $(top_srcdir)/config/ac_define_versionlevel.m4 \ + $(top_srcdir)/config/acinclude_custom.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(SHELL) $(top_srcdir)/config/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config_auto.h +CONFIG_CLEAN_FILES = +LIBRARIES = $(noinst_LIBRARIES) +AR = ar +ARFLAGS = cru +libtesseract_display_a_AR = $(AR) $(ARFLAGS) +libtesseract_display_a_LIBADD = +am_libtesseract_display_a_OBJECTS = cmndwin.$(OBJEXT) \ + pagewalk.$(OBJEXT) pgedit.$(OBJEXT) sbdmenu.$(OBJEXT) \ + varabled.$(OBJEXT) varblmen.$(OBJEXT) varblwin.$(OBJEXT) +libtesseract_display_a_OBJECTS = $(am_libtesseract_display_a_OBJECTS) +DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/config/depcomp +am__depfiles_maybe = depfiles +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +CXXLD = $(CXX) +CXXLINK = $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) \ + -o $@ +SOURCES = $(libtesseract_display_a_SOURCES) +DIST_SOURCES = $(libtesseract_display_a_SOURCES) +RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \ + html-recursive info-recursive install-data-recursive \ + install-exec-recursive install-info-recursive \ + install-recursive installcheck-recursive installdirs-recursive \ + pdf-recursive ps-recursive uninstall-info-recursive \ + uninstall-recursive +ETAGS = etags +CTAGS = ctags +DIST_SUBDIRS = $(SUBDIRS) +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMDEP_FALSE = @AMDEP_FALSE@ +AMDEP_TRUE = @AMDEP_TRUE@ +AMTAR = @AMTAR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CXXRPOFLAGS = @CXXRPOFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +GNUWIN32_DIR = @GNUWIN32_DIR@ +HAVE_GNUWIN32_FALSE = @HAVE_GNUWIN32_FALSE@ +HAVE_GNUWIN32_TRUE = @HAVE_GNUWIN32_TRUE@ +HAVE_LIBTIFF_FALSE = @HAVE_LIBTIFF_FALSE@ +HAVE_LIBTIFF_TRUE = @HAVE_LIBTIFF_TRUE@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTIFF_CFLAGS = @LIBTIFF_CFLAGS@ +LIBTIFF_LIBS = @LIBTIFF_LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAINTAINER_MODE_FALSE = @MAINTAINER_MODE_FALSE@ +MAINTAINER_MODE_TRUE = @MAINTAINER_MODE_TRUE@ +MAKEINFO = @MAKEINFO@ +OBJEXT = @OBJEXT@ +OPTS = @OPTS@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_DATE = @PACKAGE_DATE@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PACKAGE_YEAR = @PACKAGE_YEAR@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +RPO_NO = @RPO_NO@ +RPO_YES = @RPO_YES@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +USING_CL_FALSE = @USING_CL_FALSE@ +USING_CL_TRUE = @USING_CL_TRUE@ +VERSION = @VERSION@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_RANLIB = @ac_ct_RANLIB@ +ac_ct_STRIP = @ac_ct_STRIP@ +am__fastdepCC_FALSE = @am__fastdepCC_FALSE@ +am__fastdepCC_TRUE = @am__fastdepCC_TRUE@ +am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@ +am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@ +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@ +datadir = @datadir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +SUBDIRS = +AM_CPPFLAGS = \ + -I$(top_srcdir)/image -I$(top_srcdir)/ccstruct \ + -I$(top_srcdir)/viewer -I$(top_srcdir)/textord \ + -I$(top_srcdir)/ccutil + +EXTRA_DIST = \ + cmndwin.h pagewalk.h pgedit.h pgeditx.h sbdmenu.h submen.h \ + tessio.h varabled.h varblmen.h varblwin.h + +noinst_LIBRARIES = libtesseract_display.a +libtesseract_display_a_SOURCES = \ + cmndwin.cpp pagewalk.cpp pgedit.cpp sbdmenu.cpp varabled.cpp \ + varblmen.cpp varblwin.cpp + +all: all-recursive + +.SUFFIXES: +.SUFFIXES: .cpp .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 \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu display/Makefile'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu display/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 + +clean-noinstLIBRARIES: + -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) +libtesseract_display.a: $(libtesseract_display_a_OBJECTS) $(libtesseract_display_a_DEPENDENCIES) + -rm -f libtesseract_display.a + $(libtesseract_display_a_AR) libtesseract_display.a $(libtesseract_display_a_OBJECTS) $(libtesseract_display_a_LIBADD) + $(RANLIB) libtesseract_display.a + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmndwin.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pagewalk.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pgedit.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sbdmenu.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/varabled.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/varblmen.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/varblwin.Po@am__quote@ + +.cpp.o: +@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ $< + +.cpp.obj: +@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` +uninstall-info-am: + +# 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. +$(RECURSIVE_TARGETS): + @failcom='exit 1'; \ + for f in x $$MAKEFLAGS; do \ + case $$f in \ + *=* | --[!k]*);; \ + *k*) failcom='fail=yes';; \ + esac; \ + done; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + list='$(SUBDIRS)'; 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; \ + (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" + +mostlyclean-recursive clean-recursive distclean-recursive \ +maintainer-clean-recursive: + @failcom='exit 1'; \ + for f in x $$MAKEFLAGS; do \ + case $$f in \ + *=* | --[!k]*);; \ + *k*) failcom='fail=yes';; \ + esac; \ + done; \ + dot_seen=no; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + rev=''; for subdir in $$list; do \ + if test "$$subdir" = "."; then :; else \ + rev="$$subdir $$rev"; \ + fi; \ + done; \ + rev="$$rev ."; \ + target=`echo $@ | sed s/-recursive//`; \ + for subdir in $$rev; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done && test -z "$$fail" +tags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \ + done +ctags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \ + done + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + 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 || \ + tags="$$tags $$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique; \ + fi +ctags: CTAGS +CTAGS: ctags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(CTAGS_ARGS)$$tags$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +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)'; for file in $$list; do \ + case $$file in \ + $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \ + $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \ + esac; \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + dir="/$$dir"; \ + $(mkdir_p) "$(distdir)$$dir"; \ + else \ + dir=''; \ + fi; \ + if test -d $$d/$$file; then \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$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 \ + test -d "$(distdir)/$$subdir" \ + || $(mkdir_p) "$(distdir)/$$subdir" \ + || exit 1; \ + distdir=`$(am__cd) $(distdir) && pwd`; \ + top_distdir=`$(am__cd) $(top_distdir) && pwd`; \ + (cd $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$top_distdir" \ + distdir="$$distdir/$$subdir" \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-recursive +all-am: Makefile $(LIBRARIES) +installdirs: installdirs-recursive +installdirs-am: +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: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_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-recursive + +clean-am: clean-generic clean-noinstLIBRARIES mostlyclean-am + +distclean: distclean-recursive + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +info: info-recursive + +info-am: + +install-data-am: + +install-exec-am: + +install-info: install-info-recursive + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-compile mostlyclean-generic + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: uninstall-info-am + +uninstall-info: uninstall-info-recursive + +.PHONY: $(RECURSIVE_TARGETS) CTAGS GTAGS all all-am check check-am \ + clean clean-generic clean-noinstLIBRARIES clean-recursive \ + ctags ctags-recursive distclean distclean-compile \ + distclean-generic distclean-recursive distclean-tags distdir \ + dvi dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-exec install-exec-am \ + install-info install-info-am install-man install-strip \ + installcheck installcheck-am installdirs installdirs-am \ + maintainer-clean maintainer-clean-generic \ + maintainer-clean-recursive mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-recursive pdf pdf-am ps ps-am \ + tags tags-recursive uninstall uninstall-am uninstall-info-am + +# 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: diff --git a/display/cmndwin.cpp b/display/cmndwin.cpp new file mode 100644 index 0000000000..9ca62f5391 --- /dev/null +++ b/display/cmndwin.cpp @@ -0,0 +1,449 @@ +/********************************************************************** + * File: cmndwin.cpp (Formerly cmdwin.c) + * Description: Command Window class + * 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 "mfcpch.h" +#include +#include "evnts.h" +#include "cmndwin.h" + +#define BACKSPACE_KEY '\010' +#define RETURN_KEY '\015' + +#define BORDER_HEIGHT (menu_char_height / 2) + +#define MSG_AREA_TOP_LEFT_X 0 +#define MSG_AREA_TOP_LEFT_Y 0 +#define MSG_AREA_HEIGHT (int)(menu_char_height * 1.5 ) +#define MSG_TEXT_START_X (menu_char_height / 2) +#define MSG_TEXT_START_Y (MSG_AREA_TOP_LEFT_Y - menu_char_height) + +#define PROMPT_AREA_TOP_LEFT_X 0 +#define PROMPT_AREA_TOP_LEFT_Y \ + (MSG_AREA_TOP_LEFT_Y - MSG_AREA_HEIGHT - BORDER_HEIGHT) +#define PROMPT_AREA_HEIGHT MSG_AREA_HEIGHT +#define PROMPT_TEXT_START_X (menu_char_height / 2) +#define PROMPT_TEXT_START_Y (PROMPT_AREA_TOP_LEFT_Y - menu_char_height) + +#define MENU_AREA_TOP_LEFT_X 0 +#define MENU_AREA_TOP_LEFT_Y \ + (PROMPT_AREA_TOP_LEFT_Y - PROMPT_AREA_HEIGHT - BORDER_HEIGHT) + +#define MIN_WINDOW_WIDTH (PROMPT_TEXT_START_X + 80 * menu_char_width) + +#define EXTERN + +EXTERN INT_VAR (editor_cmdwin_width, 950, "CmdWin max non scrollable width"); +EXTERN INT_VAR (editor_cmdwin_height, 550, +"CmdWin max non scrollable height"); +EXTERN INT_VAR (editor_cmdwin_xpos1, 20, "X pos of first command window"); +EXTERN INT_VAR (editor_cmdwin_ypos1, 20, "Y pos of first command window"); +EXTERN INT_VAR (editor_cmdwin_xoffset, 30, "X offset between command windws"); +EXTERN INT_VAR (editor_cmdwin_yoffset, 30, "Y offset between command windws"); + +INT16 +COMMAND_WINDOW::next_win_x_pos = 0; +INT16 +COMMAND_WINDOW::next_win_y_pos = 0; + +/********************************************************************** + * COMMAND_WINDOW::COMMAND_WINDOW() + * + * COMMAND_WINDOW constructor + **********************************************************************/ + + // window name +COMMAND_WINDOW::COMMAND_WINDOW(const char *name, + MENU_ROOT *menu_ptr // root of menu + ) { + BOX menu_box; + INT16 window_height; + INT8 window_type = FULLSIZEWIN;//Default + INT16 xsize; + INT16 ysize; + + message_str[0] = '\0'; + prompt_str[0] = '\0'; + strcpy(win_name, name); + + menu_root = menu_ptr; + menu_box = menu_root->recalc_bounding_box (MENU_AREA_TOP_LEFT_X, + MENU_AREA_TOP_LEFT_Y); + + if (menu_box.width () > MIN_WINDOW_WIDTH) { + window_width = menu_box.width (); + } + else { + window_width = MIN_WINDOW_WIDTH; + } + + window_height = MSG_AREA_TOP_LEFT_Y - menu_box.bottom (); + + xsize = window_width; + ysize = window_height; + + if (window_width > editor_cmdwin_width) { + window_type = SCROLLINGWIN; + xsize = editor_cmdwin_width; + } + + if (window_height > editor_cmdwin_height) { + window_type = SCROLLINGWIN; + ysize = editor_cmdwin_height; + } + + if ((next_win_x_pos == 0) && (next_win_x_pos == 0)) { + //Init for 1st win + next_win_x_pos = editor_cmdwin_xpos1; + next_win_y_pos = editor_cmdwin_ypos1; + } + x_pos = next_win_x_pos; + y_pos = next_win_y_pos; + + //min x + fd = create_window (name, window_type, x_pos, y_pos, xsize, ysize, MSG_AREA_TOP_LEFT_X, + window_width, //max x + menu_box.bottom (), //min y + MSG_AREA_TOP_LEFT_Y, //max_y + TRUE, //mouse DOWN + FALSE, FALSE, TRUE); //key press + + vdc_extent (fd, 0, 0, xsize, ysize); + next_win_x_pos += editor_cmdwin_xoffset; + next_win_y_pos += editor_cmdwin_yoffset; + + plot(); +} + + +/********************************************************************** + * COMMAND_WINDOW::event() + * + * COMMAND_WINDOW event handler + **********************************************************************/ + +void COMMAND_WINDOW::event( //Process event //Command event type + GRAPHICS_EVENT &g_event, + INT32 *cmd_event, + char *new_value //of menu item + ) { + message_str[0] = '\0'; + prompt_str[0] = '\0'; + *cmd_event = UNIDENTIFIED_COMMAND; + new_value[0] = '\0'; + + switch (g_event.type) { + case DOWN_EVENT: + { + menu_root->event (this, FCOORD (g_event.x, g_event.y), + cmd_event, new_value); + if (*cmd_event == UNIDENTIFIED_COMMAND) + *cmd_event = NULL_COMMAND; + break; + } + case KEYPRESS_EVENT: + { + if (g_event.key == 3) { //Control C + exit (0); + } + } + default: + { + // sprintf( message_str, "ERROR: Unrecognised graphics event %d", + // g_event.type ); + *cmd_event = NULL_COMMAND; + break; + } + } + plot(); +} + + +/********************************************************************** + * COMMAND_WINDOW::msg() + * + * COMMAND_WINDOW message display + **********************************************************************/ + +void COMMAND_WINDOW::msg( //Display message + const char *msg_str //Text to display + ) { + strcpy(message_str, msg_str); + plot_msg_area(); + overlap_picture_ops(TRUE); +} + + +/********************************************************************** + * COMMAND_WINDOW::plot() + * + * COMMAND_WINDOW (re) paint the window + **********************************************************************/ + +void COMMAND_WINDOW::plot() { + clear_view_surface(fd); + text_font_index (fd, 1); + character_height(fd, menu_char_height); + + plot_msg_area(); + plot_prompt_area(); + + menu_root->plot (fd); +} + + +/********************************************************************** + * COMMAND_WINDOW::plot_msg_area() + * + **********************************************************************/ + +void COMMAND_WINDOW::plot_msg_area() { + fill_color_index(fd, DARK_SLATE_BLUE); + interior_style(fd, INT_SOLID, FALSE); + + rectangle (fd, + MSG_AREA_TOP_LEFT_X, MSG_AREA_TOP_LEFT_Y, + MSG_AREA_TOP_LEFT_X + window_width - 1, + MSG_AREA_TOP_LEFT_Y - MSG_AREA_HEIGHT); + + text_color_index(fd, WHITE); + text2d (fd, MSG_TEXT_START_X, MSG_TEXT_START_Y, message_str, 0, FALSE); +} + + +/********************************************************************** + * COMMAND_WINDOW::plot_prompt_area() + * + **********************************************************************/ + +void COMMAND_WINDOW::plot_prompt_area() { + INT8 i; + INT8 prompt_len; + char char_str[2]; + + fill_color_index(fd, DARK_SLATE_BLUE); + interior_style(fd, INT_SOLID, FALSE); + + rectangle (fd, + PROMPT_AREA_TOP_LEFT_X, PROMPT_AREA_TOP_LEFT_Y, + PROMPT_AREA_TOP_LEFT_X + window_width - 1, + PROMPT_AREA_TOP_LEFT_Y - MSG_AREA_HEIGHT); + + text_color_index(fd, WHITE); + prompt_len = strlen (prompt_str); + char_str[1] = '\0'; + + for (i = 0; i < prompt_len; i++) { + char_str[0] = prompt_str[i]; + text2d (fd, + PROMPT_TEXT_START_X + (i * menu_char_width), + PROMPT_TEXT_START_Y, char_str, 0, FALSE); + } +} + + +/********************************************************************** + * COMMAND_WINDOW::internal_prompt() + * + * COMMAND_WINDOW Prompt for and read a response + * WITHOUT DOING a PLOT! + **********************************************************************/ + +BOOL8 COMMAND_WINDOW::internal_prompt( //Prompt user + const char *msg_str, //Prompt message + char *response_str //Response & Default + ) { + GRAPHICS_EVENT event; + INT8 pos; + BOOL8 ok = TRUE; + + strcpy(message_str, msg_str); + strcpy(prompt_str, response_str); + plot_msg_area(); + plot_prompt_area(); + overlap_picture_ops(TRUE); + + /* MODE THE UI SO THAT IT ONLY RESPONDS TO KEYPRESSes IN THE COMMAND WINDOW */ + + event.key = '\0'; + + while (event.key != RETURN_KEY) { + await_event(fd, //just cmd window + TRUE, //wait for event + ANY_EVENT, //ONLY keypresses + &event); + + if (event.type != KEYPRESS_EVENT) { + ok = FALSE; + response_str[0] = '\0'; + break; + } + + pos = strlen (response_str); + if (isprint (event.key) && pos < MAX_CHARS) { + response_str[pos] = event.key; + response_str[pos + 1] = '\0'; + text2d (fd, + PROMPT_TEXT_START_X + (pos * menu_char_width), + PROMPT_TEXT_START_Y, response_str + pos, 0, FALSE); + } + else { + switch (event.key) { + case BACKSPACE_KEY: + if (pos > 0) { + response_str[pos - 1] = '\0'; + fill_color_index(fd, DARK_SLATE_BLUE); + interior_style(fd, INT_SOLID, FALSE); + + rectangle (fd, + PROMPT_TEXT_START_X + (pos - + 1) * menu_char_width, + PROMPT_AREA_TOP_LEFT_Y, + PROMPT_AREA_TOP_LEFT_X + window_width - 1, + PROMPT_AREA_TOP_LEFT_Y - PROMPT_AREA_HEIGHT); + } + break; + case RETURN_KEY: + break; + default: + sprintf (message_str, "NON PRINTING CHAR: %o", event.key); + plot_msg_area(); + } + } + overlap_picture_ops(TRUE); + } + message_str[0] = '\0'; + prompt_str[0] = '\0'; + #ifdef __UNIX__ + // clear_event_queue(0); //clear ALL win events + #endif + return ok; +} + + +/********************************************************************** + * COMMAND_WINDOW::press_radio_button() + * + * COMMAND_WINDOW Change the selected button in a radio set + **********************************************************************/ + +void COMMAND_WINDOW::press_radio_button( //of this radio set + RADIO_MENU *radio_sub_menu_item, //This button + RADIO_MENU_LEAF *button_menu_item) { + radio_sub_menu_item->press_radio_button (button_menu_item); + plot(); +} + + +/********************************************************************** + * COMMAND_WINDOW::update_menu_tree() + * + * Following a CHANGE to the menu tree, the + * boundng boxes must be recalculated, this may require the window to be + * re-created, the window must then be re-displayed. + **********************************************************************/ + +void COMMAND_WINDOW::update_menu_tree() { + BOX menu_box; + INT16 window_height; + INT8 window_type = FULLSIZEWIN;//Default + INT16 xsize; + INT16 ysize; + + menu_box = menu_root->recalc_bounding_box (MENU_AREA_TOP_LEFT_X, + MENU_AREA_TOP_LEFT_Y); + if (menu_box.width () > MIN_WINDOW_WIDTH) { + window_width = menu_box.width (); + } + else { + window_width = MIN_WINDOW_WIDTH; + } + + window_height = MSG_AREA_TOP_LEFT_Y - menu_box.bottom (); + + xsize = window_width; + ysize = window_height; + + if (window_width > editor_cmdwin_width) { + window_type = SCROLLINGWIN; + xsize = editor_cmdwin_width; + } + + if (window_height > editor_cmdwin_height) { + window_type = SCROLLINGWIN; + ysize = editor_cmdwin_height; + } + destroy_window(fd); + //min x + fd = create_window (win_name, window_type, x_pos, y_pos, xsize, ysize, MSG_AREA_TOP_LEFT_X, + window_width, //max x + menu_box.bottom (), //min y + MSG_AREA_TOP_LEFT_Y, //max_y + TRUE, //mouse DOWN + FALSE, FALSE, TRUE); //key press + + vdc_extent (fd, 0, 0, xsize, ysize); + plot(); +} + + +/********************************************************************** + * COMMAND_WINDOW::prompt() + * + * COMMAND_WINDOW Prompt for and read a response + **********************************************************************/ + +BOOL8 COMMAND_WINDOW::prompt( //Prompt user + const char *msg_str, //Prompt message + char *response_str //Response & Default + ) { + BOOL8 ok; + + ok = internal_prompt (msg_str, response_str); + plot(); + return ok; +} + + +/********************************************************************** + * COMMAND_WINDOW::replace_menu_text() + * + * COMMAND_WINDOW Change the label of a specified menu item + **********************************************************************/ + +void COMMAND_WINDOW::replace_menu_text( //for this item + LEAF_MENU_NODE *menu_item, + const char *new_label //New label + ) { + menu_item->new_label (new_label); + menu_item->plot (fd); +} + + +/********************************************************************** + * COMMAND_WINDOW::set_toggle() + * + * COMMAND_WINDOW Change the value of a specified menu item + **********************************************************************/ + +void COMMAND_WINDOW::set_toggle( //for this item + TOGGLE_MENU_LEAF *menu_item, + BOOL8 new_value) { + menu_item->set_toggle (new_value); + menu_item->plot (fd); +} diff --git a/display/cmndwin.h b/display/cmndwin.h new file mode 100644 index 0000000000..af66a8ccfe --- /dev/null +++ b/display/cmndwin.h @@ -0,0 +1,111 @@ +/********************************************************************** + * File: cmndwin.h (Formerly cmdwin.h) + * Description: Command Window class + * 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. + * + **********************************************************************/ + +#ifndef CMNDWIN_H +#define CMNDWIN_H + +#include +#include "hosthplb.h" +#include "grphics.h" +#include "evnts.h" +#include "sbdmenu.h" +#include "notdll.h" + +#define MAX_CHARS 80 + //still decoding event +#define UNIDENTIFIED_COMMAND -1 +#define NULL_COMMAND 0 //no processing reqd + +class COMMAND_WINDOW +{ + friend class VARIABLE_MENU_LEAF; + friend class DBL_VAR_MENU_LEAF; + friend class INT_VAR_MENU_LEAF; + friend class STR_VAR_MENU_LEAF; + + INT16 window_width; //Displable width + //Message display area + char message_str[MAX_CHARS + 1]; + char prompt_str[MAX_CHARS + 1];//Prompt display area + INT16 x_pos; //Current win pos + INT16 y_pos; //Current win pos + char win_name[MAX_CHARS + 1]; //Window name + static INT16 next_win_x_pos; //Posn of next cmd win + static INT16 next_win_y_pos; //Posn of next cmd win + + protected: + WINDOW fd; //Cmd Window handle + MENU_ROOT *menu_root; //Root of menu tree + + //Prompt user(No plot) + virtual BOOL8 internal_prompt(const char *prompt_str, //Prompt message + char *response); //Response & Default + + public: + COMMAND_WINDOW( //Constructor + const char *name, //window name + MENU_ROOT *menu_ptr); + + void event( //Process event //Command event type + GRAPHICS_EVENT &g_event, + INT32 *c_event, + char *new_value); //of menu item + + void msg( //Display message + const char *message_string); //Text to display + + void plot(); //(re)paint the window + + void plot_msg_area(); + + void plot_prompt_area(); + + void press_radio_button( //Change selected item //of this radio set + RADIO_MENU *radio_sub_menu_item, //This button + RADIO_MENU_LEAF *button_menu_item); + + void update_menu_tree(); //Re calc BBoxes etc + + BOOL8 prompt( //Prompt user(& plot) + const char *prompt_str, //Prompt message + char *response); //Response & Default + + void replace_menu_text( //Change & show label + LEAF_MENU_NODE *menu_item, //for this item + const char *new_label); //New label + + void set_toggle( //Change value & show + TOGGLE_MENU_LEAF *menu_item, //for this item + BOOL8 new_value); + + WINDOW window() { //Return window handle + return fd; + } +}; +extern INT_VAR_H (editor_cmdwin_width, 950, +"CmdWin max non scrollable width"); +extern INT_VAR_H (editor_cmdwin_height, 550, +"CmdWin max non scrollable height"); +extern INT_VAR_H (editor_cmdwin_xpos1, 20, "X pos of first command window"); +extern INT_VAR_H (editor_cmdwin_ypos1, 20, "Y pos of first command window"); +extern INT_VAR_H (editor_cmdwin_xoffset, 30, +"X offset between command windws"); +extern INT_VAR_H (editor_cmdwin_yoffset, 30, +"Y offset between command windws"); +#endif diff --git a/display/pagewalk.cpp b/display/pagewalk.cpp new file mode 100644 index 0000000000..cce0cf3fda --- /dev/null +++ b/display/pagewalk.cpp @@ -0,0 +1,666 @@ +/********************************************************************** + * 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 "mfcpch.h" +#include "pagewalk.h" + +#define EXTERN + +EXTERN BOOL_VAR (current_word_quit, FALSE, "Stop processing this word"); +DLLSYM BOOL_VAR (selection_quit, FALSE, "Stop processing this selection"); + +/********************************************************************** + * block_list_bounding_box() + * + * Scan block list to find the bounding box of all blocks. + **********************************************************************/ + +BOX block_list_bounding_box( //find bounding box + BLOCK_LIST *block_list //of this block list + ) { + BLOCK_IT block_it(block_list); + BOX enclosing_box; + + for (block_it.mark_cycle_pt (); !block_it.cycled_list (); + block_it.forward ()) + enclosing_box += block_it.data ()->bounding_box (); + return enclosing_box; +} + + +/********************************************************************** + * block_list_compress() + * + * Pack a block list to occupy a smaller space by compressing each block and + * moving the compressed blocks one above the other. + * The compressed block list has the same top left point as the uncompressed + * first. Blocks are reordered so that the source names are in alphabetic + * order. (This gathers together, but does not combine, blocks from the same + * file.) + * The enclosing box of the compressed block list is returned. + **********************************************************************/ + +const BOX block_list_compress( //shuffle up blocks + BLOCK_LIST *block_list) { + BLOCK_IT block_it(block_list); + BLOCK *block; + ICOORD initial_top_left; + ICOORD block_spacing (0, BLOCK_SPACING); + BOX enclosing_box; //for full display + + initial_top_left = block_it.data ()->bounding_box ().topleft (); + //group srcfile blks + block_it.sort (block_name_order); + + /* Compress the target block list into an area starting from the top left of + the first block on the list */ + + enclosing_box = BOX (initial_top_left, initial_top_left); + enclosing_box.move_bottom_edge (BLOCK_SPACING); + + for (block_it.mark_cycle_pt (); + !block_it.cycled_list (); block_it.forward ()) { + block = block_it.data (); + block->compress (enclosing_box.botleft () - block_spacing - + block->bounding_box ().topleft ()); + enclosing_box += block->bounding_box (); + } + return enclosing_box; +} + + +/********************************************************************** + * block_list_move() + * + * Move all the blocks in the list by a vector + **********************************************************************/ + +void block_list_move( //move + BLOCK_LIST *block_list, //this list + ICOORD vec //by this vector + ) { + BLOCK_IT block_it(block_list); + + for (block_it.mark_cycle_pt (); !block_it.cycled_list (); + block_it.forward ()) + block_it.data ()->move (vec); +} + + +/********************************************************************** + * block_name_order() + * + * Block comparator used to sort a block list so that blocks from the same + * filename are located together, and blocks from the same file are ordered + * by vertical position. + **********************************************************************/ + +int block_name_order( //sort blocks + const void *block1p, //ptr to ptr to block1 + const void *block2p //ptr to ptr to block2 + ) { + int result; + BLOCK *block1 = *(BLOCK **) block1p; + BLOCK *block2 = *(BLOCK **) block2p; + + result = strcmp (block1->name (), block2->name ()); + if (result == 0) + result = block2->bounding_box ().top () - block1->bounding_box ().top (); + return result; +} + + +/********************************************************************** + * process_all_blobs() + * + * Walk the current block list applying the specified blob processor function + * to all blobs + **********************************************************************/ + +void +process_all_blobs ( //process blobs +BLOCK_LIST * block_list, //blocks to check +BOOL8 blob_processor ( //function to call + //function to call +BLOCK *, ROW *, WERD *, PBLOB *), BOOL8 c_blob_processor ( +BLOCK +*, +ROW +*, +WERD +*, +C_BLOB +*)) { + BLOCK_IT block_it(block_list); + BLOCK *block; + ROW_IT row_it; + ROW *row; + WERD_IT word_it; + WERD *word; + PBLOB_IT blob_it; + PBLOB *blob; + C_BLOB_IT c_blob_it; + C_BLOB *c_blob; + + for (block_it.mark_cycle_pt (); + !block_it.cycled_list (); block_it.forward ()) { + block = block_it.data (); + row_it.set_to_list (block->row_list ()); + for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) { + row = row_it.data (); + word_it.set_to_list (row->word_list ()); + for (word_it.mark_cycle_pt (); + !word_it.cycled_list (); word_it.forward ()) { + word = word_it.data (); + if (word->flag (W_POLYGON)) { + if (blob_processor != NULL) { + blob_it.set_to_list (word->blob_list ()); + for (blob_it.mark_cycle_pt (); + !blob_it.cycled_list (); blob_it.forward ()) { + blob = blob_it.data (); + if (!blob_processor (block, row, word, blob) || + selection_quit) + return; + } + } + } + else { + if (c_blob_processor != NULL) { + c_blob_it.set_to_list (word->cblob_list ()); + for (c_blob_it.mark_cycle_pt (); + !c_blob_it.cycled_list (); c_blob_it.forward ()) { + c_blob = c_blob_it.data (); + if (!c_blob_processor (block, row, word, c_blob) || + selection_quit) + return; + } + } + } + } + } + } +} + + +/********************************************************************** + * process_selected_blobs() + * + * Walk the current block list applying the specified blob processor function + * to each selected blob + **********************************************************************/ + +void +process_selected_blobs ( //process blobs +BLOCK_LIST * block_list, //blocks to check + //function to call +BOX & selection_box, BOOL8 blob_processor ( + //function to call +BLOCK *, ROW *, WERD *, PBLOB *), BOOL8 c_blob_processor ( +BLOCK +*, +ROW +*, +WERD +*, +C_BLOB +*)) { + BLOCK_IT block_it(block_list); + BLOCK *block; + ROW_IT row_it; + ROW *row; + WERD_IT word_it; + WERD *word; + PBLOB_IT blob_it; + PBLOB *blob; + C_BLOB_IT c_blob_it; + C_BLOB *c_blob; + + for (block_it.mark_cycle_pt (); + !block_it.cycled_list (); block_it.forward ()) { + block = block_it.data (); + if (block->bounding_box ().overlap (selection_box)) { + row_it.set_to_list (block->row_list ()); + for (row_it.mark_cycle_pt (); + !row_it.cycled_list (); row_it.forward ()) { + row = row_it.data (); + if (row->bounding_box ().overlap (selection_box)) { + word_it.set_to_list (row->word_list ()); + for (word_it.mark_cycle_pt (); + !word_it.cycled_list (); word_it.forward ()) { + word = word_it.data (); + if (word->bounding_box ().overlap (selection_box)) { + if (word->flag (W_POLYGON)) { + if (blob_processor != NULL) { + blob_it.set_to_list (word->blob_list ()); + for (blob_it.mark_cycle_pt (); + !blob_it.cycled_list (); + blob_it.forward ()) { + blob = blob_it.data (); + if (blob->bounding_box (). + overlap (selection_box)) { + if (!blob_processor + (block, row, word, blob) + || selection_quit) + return; + } + } + } + } + else { + if (c_blob_processor != NULL) { + c_blob_it.set_to_list (word->cblob_list ()); + for (c_blob_it.mark_cycle_pt (); + !c_blob_it.cycled_list (); + c_blob_it.forward ()) { + c_blob = c_blob_it.data (); + if (c_blob-> + bounding_box (). + overlap (selection_box)) { + if (!c_blob_processor + (block, row, word, c_blob) + || selection_quit) + return; + } + } + } + } + } + } + } + } + } + } +} + + +/********************************************************************** + * process_all_words() + * + * Walk the current block list applying the specified word processor function + * to all words + **********************************************************************/ + +void +process_all_words ( //process words +BLOCK_LIST * block_list, //blocks to check +BOOL8 word_processor ( //function to call +BLOCK *, ROW *, WERD *)) { + BLOCK_IT block_it(block_list); + BLOCK *block; + ROW_IT row_it; + ROW *row; + WERD_IT word_it; + WERD *word; + + for (block_it.mark_cycle_pt (); + !block_it.cycled_list (); block_it.forward ()) { + block = block_it.data (); + row_it.set_to_list (block->row_list ()); + for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) { + row = row_it.data (); + word_it.set_to_list (row->word_list ()); + for (word_it.mark_cycle_pt (); + !word_it.cycled_list (); word_it.forward ()) { + word = word_it.data (); + if (!word_processor (block, row, word) || selection_quit) + return; + } + } + } +} + + +/********************************************************************** + * process_selected_words() + * + * Walk the current block list applying the specified word processor function + * to each word selected. + **********************************************************************/ + +void +process_selected_words ( //process words +BLOCK_LIST * block_list, //blocks to check + //function to call +BOX & selection_box, BOOL8 word_processor ( +BLOCK *, +ROW *, +WERD *)) { + BLOCK_IT block_it(block_list); + BLOCK *block; + ROW_IT row_it; + ROW *row; + WERD_IT word_it; + WERD *word; + + for (block_it.mark_cycle_pt (); + !block_it.cycled_list (); block_it.forward ()) { + block = block_it.data (); + if (block->bounding_box ().overlap (selection_box)) { + row_it.set_to_list (block->row_list ()); + for (row_it.mark_cycle_pt (); + !row_it.cycled_list (); row_it.forward ()) { + row = row_it.data (); + if (row->bounding_box ().overlap (selection_box)) { + word_it.set_to_list (row->word_list ()); + for (word_it.mark_cycle_pt (); + !word_it.cycled_list (); word_it.forward ()) { + word = word_it.data (); + if (word->bounding_box ().overlap (selection_box)) { + if (!word_processor (block, row, word) || + selection_quit) + return; + } + } + } + } + } + } +} + + +/********************************************************************** + * process_all_words_it() PASS ITERATORS + * + * Walk the current block list applying the specified word processor function + * to all words + **********************************************************************/ + +void +process_all_words_it ( //process words +BLOCK_LIST * block_list, //blocks to check +BOOL8 word_processor ( //function to call +BLOCK *, +ROW *, +WERD *, +BLOCK_IT &, +ROW_IT &, WERD_IT &)) { + BLOCK_IT block_it(block_list); + BLOCK *block; + ROW_IT row_it; + ROW *row; + WERD_IT word_it; + WERD *word; + + for (block_it.mark_cycle_pt (); + !block_it.cycled_list (); block_it.forward ()) { + block = block_it.data (); + row_it.set_to_list (block->row_list ()); + for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) { + row = row_it.data (); + word_it.set_to_list (row->word_list ()); + for (word_it.mark_cycle_pt (); + !word_it.cycled_list (); word_it.forward ()) { + word = word_it.data (); + if (!word_processor + (block, row, word, block_it, row_it, word_it) + || selection_quit) + return; + } + } + } +} + + +/********************************************************************** + * process_selected_words_it() PASS ITERATORS + * + * Walk the current block list applying the specified word processor function + * to each word selected. + **********************************************************************/ + +void +process_selected_words_it ( //process words +BLOCK_LIST * block_list, //blocks to check + //function to call +BOX & selection_box, BOOL8 word_processor ( +BLOCK +*, +ROW *, +WERD +*, +BLOCK_IT +&, +ROW_IT +&, +WERD_IT +&)) { + BLOCK_IT block_it(block_list); + BLOCK *block; + ROW_IT row_it; + ROW *row; + WERD_IT word_it; + WERD *word; + + for (block_it.mark_cycle_pt (); + !block_it.cycled_list (); block_it.forward ()) { + block = block_it.data (); + if (block->bounding_box ().overlap (selection_box)) { + row_it.set_to_list (block->row_list ()); + for (row_it.mark_cycle_pt (); + !row_it.cycled_list (); row_it.forward ()) { + row = row_it.data (); + if (row->bounding_box ().overlap (selection_box)) { + word_it.set_to_list (row->word_list ()); + for (word_it.mark_cycle_pt (); + !word_it.cycled_list (); word_it.forward ()) { + word = word_it.data (); + if (word->bounding_box ().overlap (selection_box)) { + if (!word_processor (block, row, word, + block_it, row_it, word_it) || + selection_quit) + return; + } + } + } + } + } + } +} + + +/********************************************************************** + * process_all_blocks() + * + * Walk the current block list applying the specified block processor function + * to each block. + **********************************************************************/ + +void +process_all_blocks ( //process blocks +BLOCK_LIST * block_list, //blocks to check +BOOL8 block_processor ( //function to call +BLOCK *)) { + BLOCK_IT block_it(block_list); + BLOCK *block; + + for (block_it.mark_cycle_pt (); + !block_it.cycled_list (); block_it.forward ()) { + block = block_it.data (); + if (!block_processor (block) || selection_quit) + return; + } +} + + +/********************************************************************** + * process_selected_blocks() + * + * Walk the current block list applying the specified block processor function + * to each block selected. + **********************************************************************/ + +void +process_selected_blocks ( //process blocks +BLOCK_LIST * block_list, //blocks to check + //function to call +BOX & selection_box, BOOL8 block_processor ( +BLOCK +*)) { + BLOCK_IT block_it(block_list); + BLOCK *block; + + for (block_it.mark_cycle_pt (); + !block_it.cycled_list (); block_it.forward ()) { + block = block_it.data (); + if (block->bounding_box ().overlap (selection_box)) { + if (!block_processor (block) || selection_quit) + return; + } + } +} + + +/********************************************************************** + * process_all_rows() + * + * Walk the current block list applying the specified row processor function + * to all rows + **********************************************************************/ + +void +process_all_rows ( //process words +BLOCK_LIST * block_list, //blocks to check +BOOL8 row_processor ( //function to call +BLOCK *, ROW *)) { + BLOCK_IT block_it(block_list); + BLOCK *block; + ROW_IT row_it; + ROW *row; + + for (block_it.mark_cycle_pt (); + !block_it.cycled_list (); block_it.forward ()) { + block = block_it.data (); + row_it.set_to_list (block->row_list ()); + for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) { + row = row_it.data (); + if (!row_processor (block, row) || selection_quit) + return; + } + } +} + + +/********************************************************************** + * process_selected_rows() + * + * Walk the current block list applying the specified row processor function + * to each row selected. + **********************************************************************/ + +void +process_selected_rows ( //process rows +BLOCK_LIST * block_list, //blocks to check + //function to call +BOX & selection_box, BOOL8 row_processor ( +BLOCK *, +ROW *)) { + BLOCK_IT block_it(block_list); + BLOCK *block; + ROW_IT row_it; + ROW *row; + + for (block_it.mark_cycle_pt (); + !block_it.cycled_list (); block_it.forward ()) { + block = block_it.data (); + if (block->bounding_box ().overlap (selection_box)) { + row_it.set_to_list (block->row_list ()); + for (row_it.mark_cycle_pt (); + !row_it.cycled_list (); row_it.forward ()) { + row = row_it.data (); + if (row->bounding_box ().overlap (selection_box)) { + if (!row_processor (block, row) || selection_quit) + return; + } + } + } + } +} + + +/********************************************************************** + * process_all_rows_it() PASS ITERATORS + * + * Walk the current block list applying the specified row processor function + * to all rows + **********************************************************************/ + +void +process_all_rows_it ( //process words +BLOCK_LIST * block_list, //blocks to check +BOOL8 row_processor ( //function to call +BLOCK *, +ROW *, BLOCK_IT &, ROW_IT &)) { + BLOCK_IT block_it(block_list); + BLOCK *block; + ROW_IT row_it; + ROW *row; + + for (block_it.mark_cycle_pt (); + !block_it.cycled_list (); block_it.forward ()) { + block = block_it.data (); + row_it.set_to_list (block->row_list ()); + for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) { + row = row_it.data (); + if (!row_processor (block, row, block_it, row_it) || selection_quit) + return; + } + } +} + + +/********************************************************************** + * process_selected_rows_it() PASS ITERATORS + * + * Walk the current block list applying the specified row processor function + * to each row selected. + **********************************************************************/ + +void +process_selected_rows_it ( //process rows +BLOCK_LIST * block_list, //blocks to check + //function to call +BOX & selection_box, BOOL8 row_processor ( +BLOCK *, +ROW *, +BLOCK_IT +&, +ROW_IT +&)) { + BLOCK_IT block_it(block_list); + BLOCK *block; + ROW_IT row_it; + ROW *row; + + for (block_it.mark_cycle_pt (); + !block_it.cycled_list (); block_it.forward ()) { + block = block_it.data (); + if (block->bounding_box ().overlap (selection_box)) { + row_it.set_to_list (block->row_list ()); + for (row_it.mark_cycle_pt (); + !row_it.cycled_list (); row_it.forward ()) { + row = row_it.data (); + if (row->bounding_box ().overlap (selection_box)) { + if (!row_processor (block, row, block_it, row_it) || + selection_quit) + return; + } + } + } + } +} diff --git a/display/pagewalk.h b/display/pagewalk.h new file mode 100644 index 0000000000..0dee31ab0f --- /dev/null +++ b/display/pagewalk.h @@ -0,0 +1,155 @@ +/********************************************************************** + * File: pagewalk.h (Formerly walkers.h) + * Description: Structure 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. + * + **********************************************************************/ + +#ifndef PAGEWALK_H +#define PAGEWALK_H + +#include "ocrblock.h" +#include "ocrrow.h" +#include "werd.h" +#include "polyblob.h" +#include "stepblob.h" +#include "rect.h" +#include "varable.h" +#include "notdll.h" + +#define BLOCK_SPACING 20 + +extern BOOL_VAR_H (current_word_quit, FALSE, "Stop processing this word"); +extern DLLSYM BOOL_VAR_H (selection_quit, FALSE, +"Stop processing this selection"); +BOX block_list_bounding_box( //find bounding box + BLOCK_LIST *block_list //of this block list + ); +const BOX block_list_compress( //shuffle up blocks + BLOCK_LIST *block_list); +void block_list_move( //move + BLOCK_LIST *block_list, //this list + ICOORD vec //by this vector + ); +int block_name_order( //sort blocks + const void *block1p, //ptr to ptr to block1 + const void *block2p //ptr to ptr to block2 + ); +void process_all_blobs ( //process blobs +BLOCK_LIST * block_list, //blocks to check +BOOL8 blob_processor ( //function to call + //function to call +BLOCK *, ROW *, WERD *, PBLOB *), BOOL8 c_blob_processor ( +BLOCK +*, +ROW +*, +WERD +*, +C_BLOB +*)); +void process_selected_blobs ( //process blobs +BLOCK_LIST * block_list, //blocks to check + //function to call +BOX & selection_box, BOOL8 blob_processor ( + //function to call +BLOCK *, ROW *, WERD *, PBLOB *), BOOL8 c_blob_processor ( +BLOCK +*, +ROW +*, +WERD +*, +C_BLOB +*)); +void process_all_words ( //process words +BLOCK_LIST * block_list, //blocks to check +BOOL8 word_processor ( //function to call +BLOCK *, ROW *, WERD *)); +void process_selected_words ( //process words +BLOCK_LIST * block_list, //blocks to check + //function to call +BOX & selection_box, BOOL8 word_processor ( +BLOCK +*, +ROW +*, +WERD +*)); +void process_all_words_it ( //process words +BLOCK_LIST * block_list, //blocks to check +BOOL8 word_processor ( //function to call +BLOCK *, +ROW *, +WERD *, +BLOCK_IT &, +ROW_IT &, WERD_IT &)); +void process_selected_words_it ( //process words +BLOCK_LIST * block_list, //blocks to check + //function to call +BOX & selection_box, BOOL8 word_processor ( +BLOCK +*, +ROW +*, +WERD +*, +BLOCK_IT +&, +ROW_IT +&, +WERD_IT +&)); +void process_all_blocks ( //process blocks +BLOCK_LIST * block_list, //blocks to check +BOOL8 block_processor ( //function to call +BLOCK *)); +void process_selected_blocks ( //process blocks +BLOCK_LIST * block_list, //blocks to check + //function to call +BOX & selection_box, BOOL8 block_processor ( +BLOCK +*)); +void process_all_rows ( //process words +BLOCK_LIST * block_list, //blocks to check +BOOL8 row_processor ( //function to call +BLOCK *, ROW *)); +void process_selected_rows ( //process rows +BLOCK_LIST * block_list, //blocks to check + //function to call +BOX & selection_box, BOOL8 row_processor ( +BLOCK +*, +ROW +*)); +void process_all_rows_it ( //process words +BLOCK_LIST * block_list, //blocks to check +BOOL8 row_processor ( //function to call +BLOCK *, +ROW *, +BLOCK_IT &, ROW_IT &)); +void process_selected_rows_it ( //process rows +BLOCK_LIST * block_list, //blocks to check + //function to call +BOX & selection_box, BOOL8 row_processor ( +BLOCK +*, +ROW +*, +BLOCK_IT +&, +ROW_IT +&)); +#endif diff --git a/display/pgedit.cpp b/display/pgedit.cpp new file mode 100644 index 0000000000..dd60348bb8 --- /dev/null +++ b/display/pgedit.cpp @@ -0,0 +1,1938 @@ +/********************************************************************** + * 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. + * + **********************************************************************/ + +#include "mfcpch.h" +#include +#include +#include "cmndwin.h" +#include "genblob.h" +#include "pgedit.h" +#include "pgeditx.h" +#include "tessio.h" +#include "tessout.h" +#include "submen.h" +#include "varblwin.h" +#include "tordmain.h" +#include "statistc.h" +#include "debugwin.h" +#include "showim.h" +#include "mainblk.h" +#include "evnts.h" + +#define ASC_HEIGHT (2 * bln_baseline_offset + bln_x_height) +#define X_HEIGHT (bln_baseline_offset + bln_x_height) +#define BL_HEIGHT bln_baseline_offset +#define DESC_HEIGHT 0 +#define MAXSPACING 128 /*max expected spacing in pix */ + +const ERRCODE EMPTYBLOCKLIST = "No blocks to edit"; +extern IMAGE page_image; + +enum CMD_EVENTS +{ + NULL_CMD_EVENT, + DELETE_CMD_EVENT, + COPY_CMD_EVENT, + CHANGE_DISP_CMD_EVENT, + CHANGE_TEXT_CMD_EVENT, + TOGGLE_SEG_CMD_EVENT, + DUMP_WERD_CMD_EVENT, + SHOW_POINT_CMD_EVENT, + ROW_SPACE_STAT_CMD_EVENT, + BLOCK_SPACE_STAT_CMD_EVENT, + SHOW_BLN_WERD_CMD_EVENT, + SEGMENT_WERD_CMD_EVENT, + BOUNDING_BOX_CMD_EVENT, + CORRECT_TEXT_CMD_EVENT, + POLYGONAL_CMD_EVENT, + BL_NORM_CMD_EVENT, + BITMAP_CMD_EVENT, + TIDY_CMD_EVENT, + VIEW_CMD_EVENT, + IMAGE_CMD_EVENT, + BLOCKS_CMD_EVENT, + BASELINES_CMD_EVENT, + WRITE_CMD_EVENT, + SMD_CMD_EVENT, + NEW_SOURCE_CMD_EVENT, + UNIFORM_DISP_CMD_EVENT, + REFRESH_CMD_EVENT, + QUIT_CMD_EVENT +}; + +#define EXTENDED_MODES_BASE 1000 //for extended cmd ids +#define EXTENDED_OTHER_BASE 2000 + +/********************************************************************** + * + * Some global data + * + **********************************************************************/ + +RADIO_MENU *modes_menu_item; +RADIO_MENU_LEAF *change_display_menu_item; +SIMPLE_MENU_LEAF *view_menu_item; +RADIO_MENU_LEAF *copy_menu_item; +VARIABLE_MENU_LEAF *write_menu_item; + +#ifdef __UNIX__ +FILE *debug_window = NULL; //opened on demand +#endif + //baseline norm words +WINDOW bln_word_window = NO_WINDOW; + +CMD_EVENTS mode = CHANGE_DISP_CMD_EVENT; + //Selected words op + +BITS16 word_display_mode; +BOOL8 display_image = FALSE; +BOOL8 display_blocks = FALSE; +BOOL8 display_baselines = FALSE; +BOOL8 viewing_source = TRUE; + +BLOCK_LIST *source_block_list = NULL; //image blocks +BLOCK_LIST target_block_list; //target blocks +BLOCK_LIST *other_block_list = &target_block_list; + +BOOL8 source_changed = FALSE; //Changes not saved +BOOL8 target_changed = FALSE; //Changes not saved +BOOL8 *other_image_changed = &target_changed; + +/* Public globals */ + +#define EXTERN + + //image window +EXTERN WINDOW image_win = NO_WINDOW; +EXTERN COMMAND_WINDOW *command_window; + +EXTERN BLOCK_LIST *current_block_list = NULL; +EXTERN BOOL8 *current_image_changed = &source_changed; +EXTERN void (*show_pt_handler) (GRAPHICS_EVENT *) = NULL; + +/* Variables */ + +EXTERN STRING_VAR (editor_image_win_name, "EditorImage", +"Editor image window name"); +EXTERN INT_VAR (editor_image_xpos, 590, "Editor image X Pos"); +EXTERN INT_VAR (editor_image_ypos, 10, "Editor image Y Pos"); +EXTERN INT_VAR (editor_image_height, 680, "Editor image height"); +EXTERN INT_VAR (editor_image_width, 655, "Editor image width"); +EXTERN INT_VAR (editor_image_word_bb_color, BLUE, "Word bounding box colour"); +EXTERN INT_VAR (editor_image_blob_bb_color, YELLOW, +"Blob bounding box colour"); +EXTERN INT_VAR (editor_image_text_color, WHITE, "Correct text colour"); + +EXTERN STRING_VAR (editor_dbwin_name, "EditorDBWin", +"Editor debug window name"); +EXTERN INT_VAR (editor_dbwin_xpos, 50, "Editor debug window X Pos"); +EXTERN INT_VAR (editor_dbwin_ypos, 500, "Editor debug window Y Pos"); +EXTERN INT_VAR (editor_dbwin_height, 24, "Editor debug window height"); +EXTERN INT_VAR (editor_dbwin_width, 80, "Editor debug window width"); + +EXTERN STRING_VAR (editor_word_name, "BlnWords", "BL normalised word window"); +EXTERN INT_VAR (editor_word_xpos, 60, "Word window X Pos"); +EXTERN INT_VAR (editor_word_ypos, 510, "Word window Y Pos"); +EXTERN INT_VAR (editor_word_height, 240, "Word window height"); +EXTERN INT_VAR (editor_word_width, 655, "Word window width"); + +EXTERN double_VAR (editor_smd_scale_factor, 1.0, "Scaling for smd image"); + +/********************************************************************** + * add_word() + * + * Inserts the a word into a specified block list. The list is searched for a + * block and row of the same file as those of the word to be added, which + * contain the bounding box of the word. If such a row is found, the new + * word is inserted into the row in the correct X order. If the + * block is found, but not the row, a copy of the word's old row is added to + * the block in the correct Y order, and the word is put in that row. + * If neither the row nor the block are found, then the word's old block is + * copied with only the word's row. It is added to the block list in the + * correct Y order. + **********************************************************************/ + +void add_word( //to block list + WERD *word, //word to be added + ROW *src_row, //source row + BLOCK *src_block, //source block + BLOCK_LIST *dest_block_list //add to this + ) { + BLOCK_IT block_it(dest_block_list); + BLOCK *block; //current block + BLOCK *dest_block = NULL; //destination block + ROW_IT row_it; + ROW *row; //destination row + ROW *dest_row = NULL; //destination row + WERD_IT word_it; + BOX word_box = word->bounding_box (); + BOX insert_point_word_box; + BOOL8 seen_blocks_for_current_file = FALSE; + + block_it.mark_cycle_pt (); + while (!block_it.cycled_list () && (dest_block == NULL)) { + block = block_it.data (); + if ((block->bounding_box ().contains (word_box)) && + (strcmp (block->name (), src_block->name ()) == 0)) { + dest_block = block; //found dest block + row_it.set_to_list (block->row_list ()); + row_it.mark_cycle_pt (); + while ((!row_it.cycled_list ()) && (dest_row == NULL)) { + row = row_it.data (); + if (row->bounding_box ().contains (word_box)) + dest_row = row; //found dest row + else + row_it.forward (); + } + } + else + block_it.forward (); + } + + if (dest_block == NULL) { //make a new one + dest_block = new BLOCK; + *dest_block = *src_block; + + block_it.set_to_list (dest_block_list); + for (block_it.mark_cycle_pt (); + !block_it.cycled_list (); block_it.forward ()) { + block = block_it.data (); + + if (!seen_blocks_for_current_file && + (strcmp (block->name (), dest_block->name ()) == 0)) + seen_blocks_for_current_file = TRUE; + + if (seen_blocks_for_current_file && + ((strcmp (block->name (), dest_block->name ()) != 0) || + (block->bounding_box ().top () < + dest_block->bounding_box ().top ()))) + break; + } + + if (block_it.cycled_list ()) + //didn't find insrt pt + block_it.add_to_end (dest_block); + else + //did find insert pt + block_it.add_before_stay_put (dest_block); + } + + if (dest_row == NULL) { //make a new one + dest_row = new ROW; + *dest_row = *src_row; + + row_it.set_to_list (dest_block->row_list ()); + for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) { + if (row_it.data ()->bounding_box ().top () < + dest_row->bounding_box ().top ()) + break; + } + + if (row_it.cycled_list ()) + //didn't find insrt pt + row_it.add_to_end (dest_row); + else + //did find insert pt + row_it.add_before_stay_put (dest_row); + } + + /* dest_block and dest_row are now found or built and inserted as necessesary + so add the word to dest row */ + + word_it.set_to_list (dest_row->word_list ()); + for (word_it.mark_cycle_pt (); !word_it.cycled_list (); word_it.forward ()) { + if (word_it.data ()->bounding_box ().right () >= word_box.left ()) + break; + } + + if (word_it.cycled_list ()) + word_it.add_to_end (word); //didn't find insrt pt + else { //did find insert pt + insert_point_word_box = word_it.data ()->bounding_box (); + if (insert_point_word_box.contains (word_box) || + word_box.contains (insert_point_word_box)) + command_window-> + msg + ("Refusing to add words which obliterate, or are obliterated by, others"); + else { + if (word_it.data ()->bounding_box ().left () > + word->bounding_box ().left ()) + //infront of insert pt + word_it.add_before_stay_put (word); + else + //behind insert pt + word_it.add_after_stay_put (word); + } + } +} + + +/********************************************************************** + * bln_word_window_handle() + * + * Return a WINDOW for the word window, creating it if necessary + **********************************************************************/ + +WINDOW bln_word_window_handle() { //return handle + //not opened yet + if (bln_word_window == NO_WINDOW) { + pgeditor_msg ("Creating BLN word window..."); + // xmin, xmax + bln_word_window = create_window (editor_word_name.string (), SCROLLINGWIN, editor_word_xpos, editor_word_ypos, editor_word_width, editor_word_height, -2000.0, 2000.0, + DESC_HEIGHT - 30.0f, + ASC_HEIGHT + 30.0f, + // ymin, ymax + TRUE, FALSE, FALSE, FALSE); + // down event only + + 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(BOX page_bounding_box) { + if (image_win != NO_WINDOW) + destroy_window(image_win); + + // xmin + image_win = create_window (editor_image_win_name.string (), SCROLLINGWIN, editor_image_xpos, editor_image_ypos, editor_image_width, editor_image_height, 0.0, + (float) page_bounding_box.right () + 1, + // xmax + 0.0, // ymin + (float) page_bounding_box.top () + 1, + // ymax + TRUE, FALSE, TRUE, FALSE); //down and up only +} + + +/********************************************************************** + * build_menu() + * + * Construct the menu tree used by the command window + **********************************************************************/ + +MENU_ROOT *build_menu() { + NON_RADIO_MENU *parent_menu; + MENU_ROOT *root_menu_item; + + root_menu_item = new MENU_ROOT (); + + modes_menu_item = new RADIO_MENU ("MODES"); + root_menu_item->add_child (modes_menu_item); + + change_display_menu_item = new RADIO_MENU_LEAF ("Change Display", + CHANGE_DISP_CMD_EVENT); + modes_menu_item->add_child (change_display_menu_item); + modes_menu_item->add_child (new RADIO_MENU_LEAF ("Delete", + DELETE_CMD_EVENT)); + copy_menu_item = new RADIO_MENU_LEAF ("Copy to TARGET", COPY_CMD_EVENT); + modes_menu_item->add_child (copy_menu_item); + modes_menu_item->add_child (new RADIO_MENU_LEAF ("Change Text", + CHANGE_TEXT_CMD_EVENT)); + modes_menu_item->add_child (new RADIO_MENU_LEAF ("Toggle Correct Seg Flg", + TOGGLE_SEG_CMD_EVENT)); + modes_menu_item->add_child (new RADIO_MENU_LEAF ("Dump Word", + DUMP_WERD_CMD_EVENT)); + modes_menu_item->add_child (new RADIO_MENU_LEAF ("Show Point", + SHOW_POINT_CMD_EVENT)); + modes_menu_item->add_child (new RADIO_MENU_LEAF ("Row gaps hist", + ROW_SPACE_STAT_CMD_EVENT)); + modes_menu_item->add_child (new RADIO_MENU_LEAF ("Block gaps hist", + BLOCK_SPACE_STAT_CMD_EVENT)); + modes_menu_item->add_child (new RADIO_MENU_LEAF ("Show BL Norm Word", + SHOW_BLN_WERD_CMD_EVENT)); + modes_menu_item->add_child (new RADIO_MENU_LEAF ("Re-Segment Word", + SEGMENT_WERD_CMD_EVENT)); + + parent_menu = new NON_RADIO_MENU ("DISPLAY"); + root_menu_item->add_child (parent_menu); + + parent_menu->add_child (new TOGGLE_MENU_LEAF ("Bounding Boxes", + BOUNDING_BOX_CMD_EVENT, + TRUE)); + parent_menu->add_child (new TOGGLE_MENU_LEAF ("Correct Text", + CORRECT_TEXT_CMD_EVENT, + FALSE)); + parent_menu->add_child (new TOGGLE_MENU_LEAF ("Polygonal Approx", + POLYGONAL_CMD_EVENT, FALSE)); + parent_menu->add_child (new TOGGLE_MENU_LEAF ("Baseline Normalised", + BL_NORM_CMD_EVENT, FALSE)); + parent_menu->add_child (new TOGGLE_MENU_LEAF ("Edge Steps", + BITMAP_CMD_EVENT, FALSE)); + + parent_menu = new NON_RADIO_MENU ("OTHER"); + root_menu_item->add_child (parent_menu); + + parent_menu->add_child (new SIMPLE_MENU_LEAF ("Quit", QUIT_CMD_EVENT)); + parent_menu->add_child (new SIMPLE_MENU_LEAF ("Tidy Target", + TIDY_CMD_EVENT)); + view_menu_item = new SIMPLE_MENU_LEAF ("View TARGET", VIEW_CMD_EVENT); + parent_menu->add_child (view_menu_item); + parent_menu->add_child (new TOGGLE_MENU_LEAF ("Show Image", + IMAGE_CMD_EVENT, FALSE)); + parent_menu->add_child (new TOGGLE_MENU_LEAF ("ShowBlock Outlines", + BLOCKS_CMD_EVENT, FALSE)); + parent_menu->add_child (new TOGGLE_MENU_LEAF ("Show Baselines", + BASELINES_CMD_EVENT, FALSE)); + write_menu_item = new VARIABLE_MENU_LEAF ("Write File", + WRITE_CMD_EVENT, + imagebasename.string ()); + parent_menu->add_child (write_menu_item); + parent_menu->add_child (new SIMPLE_MENU_LEAF ("Make SMD Image", + SMD_CMD_EVENT)); + parent_menu->add_child (new VARIABLE_MENU_LEAF ("New Source File", + NEW_SOURCE_CMD_EVENT, + imagebasename.string ())); + parent_menu->add_child (new SIMPLE_MENU_LEAF ("Uniform Display", + UNIFORM_DISP_CMD_EVENT)); + parent_menu->add_child (new SIMPLE_MENU_LEAF ("Refresh Display", + REFRESH_CMD_EVENT)); + + //Call driver program + extend_menu(modes_menu_item, + EXTENDED_MODES_BASE, + parent_menu, + EXTENDED_OTHER_BASE); + return root_menu_item; +} + + +/********************************************************************** + * debug_window_handle() + * + * Return a FILE* for the debug window, creating it if necessary + **********************************************************************/ + +void debug_window_handle() { //return handle + // if ( debug_winth == NULL ) //not opened yet + // { + // pgeditor_msg( "Creating debug window..." ); + // create_debug_window(); + pgeditor_msg ("Creating debug window...Done"); + // } +} + + +/********************************************************************** + * display_bln_lines() + * + * Display normalised baseline, x-height, ascender limit and descender limit + **********************************************************************/ + +void display_bln_lines(WINDOW window, + COLOUR colour, + float scale_factor, + float y_offset, + float minx, + float maxx) { + line_color_index(window, colour); + line_type(window, SOLID); + move2d (window, minx, y_offset + scale_factor * DESC_HEIGHT); + draw2d (window, maxx, y_offset + scale_factor * DESC_HEIGHT); + move2d (window, minx, y_offset + scale_factor * BL_HEIGHT); + draw2d (window, maxx, y_offset + scale_factor * BL_HEIGHT); + move2d (window, minx, y_offset + scale_factor * X_HEIGHT); + draw2d (window, maxx, y_offset + scale_factor * X_HEIGHT); + move2d (window, minx, y_offset + scale_factor * ASC_HEIGHT); + draw2d (window, maxx, y_offset + scale_factor * ASC_HEIGHT); + +} + + +/********************************************************************** + * do_new_source() + * + * Change to another source file. Automatically tidy page first + * + **********************************************************************/ + +void do_new_source( //serialise + char *name //file name + ) { + FILE *infp; //input file + char msg_str[MAX_CHARS + 1]; + STRING name_str(name); + char response_str[MAX_CHARS + 1]; + char *token; //first response token + + if (source_changed) { + response_str[0] = '\0'; + command_window->prompt ("Source changes will be LOST. Continue? (Y/N)", + response_str); + token = strtok (response_str, " "); + if (tolower (token[0]) != 'y') + return; + } + + //if not file exists + if (!(infp = fopen (name, "r"))) { + sprintf (msg_str, "Cant open file " "%s" "", name); + command_window->msg (msg_str); + return; + } + + fclose(infp); + sprintf (msg_str, "Reading file " "%s" "...", name); + command_window->msg (msg_str); + source_block_list->clear (); + //appends to SOURCE + pgeditor_read_file(name_str, source_block_list); + source_changed = FALSE; + command_window->msg ("Doing automatic Tidy Target..."); + viewing_source = FALSE; //Force viewing source + do_tidy_cmd(); + command_window->msg ("Doing automatic Tidy Target...Done"); +} + + +/********************************************************************** + * do_re_display() + * + * Redisplay page + **********************************************************************/ + +void + //function to call +do_re_display (BOOL8 word_painter ( +BLOCK *, ROW *, WERD *)) { + BLOCK_IT block_it(current_block_list); + BLOCK *block; + int block_count = 1; + + ROW_IT row_it; + ROW *row; + + WERD_IT word_it; + WERD *word; + + clear_view_surface(image_win); + if (display_image) { + show_sub_image (&page_image, 0, 0, + page_image.get_xsize (), page_image.get_ysize (), + image_win, 0, 0); + } + + for (block_it.mark_cycle_pt (); + !block_it.cycled_list (); block_it.forward ()) { + block = block_it.data (); + row_it.set_to_list (block->row_list ()); + for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) { + row = row_it.data (); + word_it.set_to_list (row->word_list ()); + for (word_it.mark_cycle_pt (); + !word_it.cycled_list (); word_it.forward ()) { + word = word_it.data (); + word_painter(block, row, word); + } + if (display_baselines) + row->plot_baseline (image_win, GREEN); + } + if (display_blocks) + block->plot (image_win, block_count++, RED); + } +} + + +/********************************************************************** + * do_tidy_cmd() + * + * Tidy TARGET page + **********************************************************************/ + +const BOX do_tidy_cmd() { //tidy + ICOORD shift_vector; + BOX tidy_box; //Just the tidy area + BOX source_box; //source file area + + source_box = block_list_bounding_box (source_block_list); + //find src area + + if (!target_block_list.empty ()) { + tidy_box = block_list_compress (&target_block_list); + + /* Shift tidied target above the source image area. */ + + shift_vector = ICOORD (0, source_box.top () + BLOCK_SPACING) + - tidy_box.botleft (); + block_list_move(&target_block_list, shift_vector); + tidy_box.move (shift_vector); + } + source_box += tidy_box; + //big enough for both + build_image_window(source_box); + do_view_cmd(); + return tidy_box; +} + + +/********************************************************************** + * do_view_cmd() + * + * View TARGET/View SOURCE command + **********************************************************************/ + +void do_view_cmd() { + viewing_source = !viewing_source; + clear_view_surface(image_win); + if (viewing_source) { + current_block_list = source_block_list; + current_image_changed = &source_changed; + other_block_list = &target_block_list; + other_image_changed = &target_changed; + do_re_display(&word_display); + + command_window->replace_menu_text (view_menu_item, "View TARGET"); + command_window->replace_menu_text (copy_menu_item, "Copy to TARGET"); + write_menu_item->replace_value (imagebasename.string ()); + } + else { + current_block_list = &target_block_list; + current_image_changed = &target_changed; + other_block_list = source_block_list; + other_image_changed = &source_changed; + do_re_display(&word_display); + + command_window->replace_menu_text (view_menu_item, "View SOURCE"); + command_window->replace_menu_text (copy_menu_item, ""); + write_menu_item->replace_value ((imagebasename + ".bits.pg").string ()); + } +} + + +/********************************************************************** + * do_write_file() + * + * Serialise a block list to file + * + * If writing image, tidy page and move to (0,0) first + **********************************************************************/ + +void do_write_file( //serialise + char *name //file name + ) { + FILE *infp; //input file + char msg_str[MAX_CHARS + 1]; + char response_str[MAX_CHARS + 1]; + char *token; //first response token + BOX enclosing_box; + + //if file exists + if ((infp = fopen (name, "r")) != NULL) { + fclose(infp); + sprintf (msg_str, "Overwrite file " "%s" "? (Y/N)", name); + response_str[0] = '\0'; + if (!command_window->prompt (msg_str, response_str)) + return; + token = strtok (response_str, " "); + if (tolower (token[0]) != 'y') + return; // dont write + } + + infp = fopen (name, "w"); //can we write to it? + if (infp == NULL) { + sprintf (msg_str, "Cant write to file " "%s" "", name); + command_window->msg (msg_str); + return; + } + fclose(infp); + + if (!viewing_source && !target_block_list.empty ()) { + //Tidy & move to (0,0) + command_window->msg ("Automatic tidy..."); + viewing_source = TRUE; //Stay viewing target! + enclosing_box = do_tidy_cmd (); + block_list_move (&target_block_list, -enclosing_box.botleft ()); + command_window->msg ("Writing file..."); + pgeditor_write_file(name, &target_block_list); + //move back + block_list_move (&target_block_list, + enclosing_box.botleft ()); + } + else { + command_window->msg ("Writing file..."); + pgeditor_write_file(name, current_block_list); + } + command_window->msg ("Writing file...Done"); + *current_image_changed = FALSE; +} + + +void smd_cmd() { + char response_str[MAX_CHARS + 1]; + WINDOW display_window; //temp + ICOORD tr, bl; + BOX page_box = block_list_bounding_box (current_block_list); + + bl = ICOORD (0, 0); + tr = ICOORD (page_image.get_xsize () + 1, page_image.get_ysize () + 1); + page_box += BOX (bl, tr); + + strcpy (response_str, imagebasename.string ()); + strcpy (response_str + imagebasename.length (), ".smd.tif"); + command_window->prompt ("SMD File Name?", response_str); + + display_window = image_win; + // xmin + image_win = create_window (response_str, SMDWINDOW, 0, 0, (INT16) (page_box.width () * editor_smd_scale_factor), (INT16) (page_box.height () * editor_smd_scale_factor), 0.0, + page_box.width (), // xmax + 0.0, // ymin + page_box.height (), // ymax + FALSE, FALSE, FALSE, FALSE); //down and up only + do_re_display(&word_display); + destroy_window(image_win); //Dumps sbd file + image_win = display_window; +} + + +/********************************************************************** + * pgeditor_main() + * + * Top level editor operation: + * Read events and send them to the appropriate command processor. + * + **********************************************************************/ + +void pgeditor_main(BLOCK_LIST *blocks) { + GRAPHICS_EVENT event; + INT32 cmd_event = 0; + char new_value[MAX_CHARS + 1]; + BOOL8 exit = FALSE; + + source_block_list = blocks; + current_block_list = blocks; + if (current_block_list->empty ()) + return; + + command_window = new COMMAND_WINDOW ("WordEditorCmd", build_menu ()); + build_image_window (block_list_bounding_box (source_block_list)); + do_re_display(&word_display); + word_display_mode.turn_on_bit (DF_BOX); + + while (!exit) { + overlap_picture_ops(TRUE); + await_event (0, //all windows + TRUE, //wait for event + ANY_EVENT, &event); + //Command win event + if (event.fd == command_window->window ()) { + command_window->msg (""); //Clear old message + command_window->event (event, &cmd_event, new_value); + exit = process_cmd_win_event (cmd_event, new_value); + } + else { + if (event.fd == image_win) + process_image_event(event); + else + pgeditor_show_point(&event); + } + current_word_quit.set_value (FALSE); + selection_quit.set_value (FALSE); + //replot all var wins + VARIABLES_WINDOW::plot_all(); + } +} + + +/********************************************************************** + * pgeditor_msg() + * + * Display a message - in the command window if there is one, or to stdout + **********************************************************************/ + +void pgeditor_msg( //message display + const char *msg) { + if (command_window == NO_WINDOW) { + tprintf(msg); + tprintf ("\n"); + } + else + command_window->msg (msg); +} + + +/********************************************************************** + * pgeditor_read_file() + * + * Deserialise source file + **********************************************************************/ + +void pgeditor_read_file( //of serialised file + STRING &name, + BLOCK_LIST *blocks //block list to add to + ) { + int c; //input character + FILE *infp; //input file + BLOCK_IT block_it(blocks); //iterator + BLOCK *block; //current block + + ICOORD page_tr; //topright of page + + char *filename_extension; + + block_it.move_to_last (); + + // ptr to last dot + filename_extension = strrchr (name.string (), '.'); + #ifdef __UNIX__ + /* TEXTROW* tessrows; + TBLOB* tessblobs; + TPOINT tess_tr; + + if (strcmp( filename_extension, ".r" ) == 0) + { + tprintf( "Converting from .r file format.\n" ); + tessrows = get_tess_row_file( name.string(), //get the row file + &tess_tr ); + page_tr = ICOORD( tess_tr.x, tess_tr.y ); + make_blocks_from_rows( tessrows, name.string(), //reconstruct blocks + page_tr, TRUE, &block_it ); + } + else if (strcmp( filename_extension, ".b" ) == 0) + { + tprintf( "Converting from .b file format.\n" ); + tessblobs = get_tess_blob_file( name.string(), //get the blob file + &tess_tr ); + page_tr = ICOORD( tess_tr.x, tess_tr.y ); + make_blocks_from_blobs( tessblobs, name.string(), + //reconstruct blocks + page_tr, FALSE,blocks); + } + else*/ + if (strcmp (filename_extension, ".pb") == 0) { + tprintf ("Converting from .pb file format.\n"); + //construct blocks + read_and_textord (name.string (), blocks); + } + else + #endif + if ((strcmp (filename_extension, ".pg") == 0) || + // read a .pg file + // or a .sp file + (strcmp (filename_extension, ".sp") == 0)) { + tprintf ("Reading %s file format.\n", filename_extension); + infp = fopen (name.string (), "r"); + if (infp == NULL) + CANTOPENFILE.error ("pgeditor_read_file", EXIT, name.string ()); + //can't open file + + while (((c = fgetc (infp)) != EOF) && (ungetc (c, infp) != EOF)) { + //get one + block = BLOCK::de_serialise (infp); + //add to list + block_it.add_after_then_move (block); + } + fclose(infp); + } else { + edges_and_textord (name.string (), blocks); + } +} + + +/********************************************************************** + * pgeditor_show_point() + * + * Display the coordinates of a point in the command window + **********************************************************************/ + +void pgeditor_show_point( //display coords + GRAPHICS_EVENT *event) { + char msg[160]; + + sprintf (msg, "Pointing at (%f, %f)", event->x, event->y); + command_window->msg (msg); +} + + +/********************************************************************** + * pgeditor_write_file() + * + * Serialise a block list to file + * + **********************************************************************/ + +void pgeditor_write_file( //serialise + char *name, //file name + BLOCK_LIST *blocks //block list to write + ) { + FILE *infp; //input file + BLOCK_IT block_it(blocks); //block iterator + BLOCK *block; //current block + ROW_IT row_it; //row iterator + + infp = fopen (name, "w"); //create output file + if (infp == NULL) + CANTCREATEFILE.error ("pgeditor_write_file", EXIT, name); + + for (block_it.mark_cycle_pt (); + !block_it.cycled_list (); block_it.forward ()) { + block = block_it.extract (); + + row_it.set_to_list (block->row_list ()); + for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) + //ensure correct + row_it.data ()->recalc_bounding_box (); + + block->serialise (infp); //serialize non-empty + block_it.add_after_then_move (block); + } + fclose(infp); +} + + +/********************************************************************** + * process_cmd_win_event() + * + * Process a command returned from the command window + * (Just call the appropriate command handler) + **********************************************************************/ + +BOOL8 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; + char response_str[MAX_CHARS + 1]; + char *token; //first response token + + switch (cmd_event) { + case NULL_CMD_EVENT: + break; + + case VIEW_CMD_EVENT: + do_view_cmd(); + break; + case CHANGE_DISP_CMD_EVENT: + case DELETE_CMD_EVENT: + case CHANGE_TEXT_CMD_EVENT: + case TOGGLE_SEG_CMD_EVENT: + case DUMP_WERD_CMD_EVENT: + case SHOW_POINT_CMD_EVENT: + case ROW_SPACE_STAT_CMD_EVENT: + case BLOCK_SPACE_STAT_CMD_EVENT: + case SHOW_BLN_WERD_CMD_EVENT: + case SEGMENT_WERD_CMD_EVENT: + mode = (CMD_EVENTS) cmd_event; + break; + case COPY_CMD_EVENT: + mode = (CMD_EVENTS) cmd_event; + if (!viewing_source) + command_window->msg ("Can't COPY while viewing target!"); + 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); + command_window->press_radio_button (modes_menu_item, + change_display_menu_item); + 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); + command_window->press_radio_button (modes_menu_item, + change_display_menu_item); + 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); + command_window->press_radio_button (modes_menu_item, + change_display_menu_item); + 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); + command_window->press_radio_button (modes_menu_item, + change_display_menu_item); + 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); + command_window->press_radio_button (modes_menu_item, + change_display_menu_item); + mode = CHANGE_DISP_CMD_EVENT; + break; + case UNIFORM_DISP_CMD_EVENT: + do_re_display(&word_set_display); + *current_image_changed = TRUE; + break; + case WRITE_CMD_EVENT: + do_write_file(new_value); + break; + case SMD_CMD_EVENT: + smd_cmd(); + break; + case TIDY_CMD_EVENT: + if (!target_block_list.empty ()) { + viewing_source = TRUE; //Force viewing target + do_tidy_cmd(); + } + break; + case NEW_SOURCE_CMD_EVENT: + do_new_source(new_value); + break; + case IMAGE_CMD_EVENT: + display_image = (new_value[0] == 'T'); + do_re_display(&word_display); + break; + case BLOCKS_CMD_EVENT: + display_blocks = (new_value[0] == 'T'); + do_re_display(&word_display); + break; + case BASELINES_CMD_EVENT: + display_baselines = (new_value[0] == 'T'); + do_re_display(&word_display); + break; + case REFRESH_CMD_EVENT: + do_re_display(&word_display); + break; + case QUIT_CMD_EVENT: + if (source_changed || target_changed) { + response_str[0] = '\0'; + command_window->prompt ("Changes not saved. Exit anyway? (Y/N)", + response_str); + token = strtok (response_str, " "); + if (tolower (token[0]) == 'y') + exit = TRUE; + } + else + exit = TRUE; + break; + default: + if ((cmd_event >= EXTENDED_MODES_BASE) && + (cmd_event < EXTENDED_OTHER_BASE)) + mode = (CMD_EVENTS) cmd_event; + else { + if (cmd_event >= EXTENDED_OTHER_BASE) + extend_unmoded_commands (cmd_event - EXTENDED_OTHER_BASE, + new_value); + else { + sprintf (msg, "Unrecognised event " INT32FORMAT " (%s)", + cmd_event, new_value); + command_window->msg (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 process_image_event( //action in image win + GRAPHICS_EVENT event) { + static ICOORD down; + ICOORD up; + BOX selection_box; + char msg[80]; + + switch (event.type) { + case DOWN_EVENT: + down.set_x ((INT16) floor (event.x + 0.5)); + down.set_y ((INT16) floor (event.y + 0.5)); + if (mode == SHOW_POINT_CMD_EVENT) + show_point (current_block_list, event.x, event.y); + break; + + case UP_EVENT: + case SELECT_EVENT: + if (event.type == SELECT_EVENT) { + down.set_x ((INT16) floor (event.xmax + 0.5)); + down.set_y ((INT16) floor (event.ymax + 0.5)); + if (mode == SHOW_POINT_CMD_EVENT) + show_point (current_block_list, event.x, event.y); + } + if (mode != SHOW_POINT_CMD_EVENT) + command_window->msg ("");//Clear old message + up.set_x ((INT16) floor (event.x + 0.5)); + up.set_y ((INT16) floor (event.y + 0.5)); + selection_box = BOX (up, down); + + switch (mode) { + case CHANGE_DISP_CMD_EVENT: + process_selected_words(current_block_list, + selection_box, + &word_blank_and_set_display); + break; + case COPY_CMD_EVENT: + if (!viewing_source) + command_window->msg ("Can't COPY while viewing target!"); + else + process_selected_words(current_block_list, + selection_box, + &word_copy); + break; + case DELETE_CMD_EVENT: + process_selected_words_it(current_block_list, + selection_box, + &word_delete); + break; + case CHANGE_TEXT_CMD_EVENT: + process_selected_words(current_block_list, + selection_box, + &word_change_text); + break; + case TOGGLE_SEG_CMD_EVENT: + process_selected_words(current_block_list, + selection_box, + &word_toggle_seg); + break; + case DUMP_WERD_CMD_EVENT: + process_selected_words(current_block_list, + selection_box, + &word_dumper); + break; + case SHOW_BLN_WERD_CMD_EVENT: + process_selected_words(current_block_list, + selection_box, + &word_bln_display); + break; + case SEGMENT_WERD_CMD_EVENT: + re_segment_word(current_block_list, selection_box); + break; + case ROW_SPACE_STAT_CMD_EVENT: + row_space_stat(current_block_list, selection_box); + break; + case BLOCK_SPACE_STAT_CMD_EVENT: + block_space_stat(current_block_list, selection_box); + break; + case SHOW_POINT_CMD_EVENT: + break; //ignore up event + default: + if ((mode >= EXTENDED_MODES_BASE) && (mode < EXTENDED_OTHER_BASE)) + extend_moded_commands (mode - EXTENDED_MODES_BASE, selection_box); + else { + sprintf (msg, "Mode %d not yet implemented", mode); + command_window->msg (msg); + } + break; + } + default: + break; + } +} + + +/********************************************************************** + * re_scale_and_move_bln_word() + * + * Scale and move a bln word so that it fits in a specified bounding box. + * Scale by width or height to generate the largest image + **********************************************************************/ + +float re_scale_and_move_bln_word( //put bln word in box + WERD *norm_word, //BL normalised word + const BOX &box //destination box + ) { + BOX norm_box = norm_word->bounding_box (); + float width_scale_factor; + float height_scale_factor; + float selected_scale_factor; + + width_scale_factor = box.width () / (float) norm_box.width (); + height_scale_factor = box.height () / (float) ASC_HEIGHT; + + if ((ASC_HEIGHT * width_scale_factor) <= box.height ()) + selected_scale_factor = width_scale_factor; + else + selected_scale_factor = height_scale_factor; + + norm_word->scale (selected_scale_factor); + norm_word->move (ICOORD ((box.left () + box.width () / 2), box.bottom ())); + return selected_scale_factor; +} + + +/********************************************************************** + * re_segment_word() + * + * If all selected blobs are in the same row, remove them from their current + * word(s) and put them in a new word. Insert the new word in the row at the + * appropriate point. Delete any empty words. + * + **********************************************************************/ + +void re_segment_word( //break/join words + BLOCK_LIST *block_list, //blocks to check + BOX &selection_box) { + BLOCK_IT block_it(block_list); + BLOCK *block; + BLOCK *block_to_process = NULL; + ROW_IT row_it; + ROW *row; + ROW *row_to_process = NULL; + WERD_IT word_it; + WERD *word; + WERD *new_word = NULL; + BOOL8 polyg = false; + PBLOB_IT blob_it; + PBLOB_LIST dummy; // Just to initialize new_blob_it. + PBLOB_IT new_blob_it = &dummy; + PBLOB *blob; + + /* Find row to process - error if selections from more than one row */ + + for (block_it.mark_cycle_pt (); + !block_it.cycled_list (); block_it.forward ()) { + block = block_it.data (); + if (block->bounding_box ().overlap (selection_box)) { + row_it.set_to_list (block->row_list ()); + for (row_it.mark_cycle_pt (); + !row_it.cycled_list (); row_it.forward ()) { + row = row_it.data (); + if (row->bounding_box ().overlap (selection_box)) { + if (row_to_process == NULL) { + block_to_process = block; + row_to_process = row; + } + else { + command_window-> + msg ("Cant resegment words in more than one row"); + return; + } + } + } + } + } + /* Continue with row_to_process */ + + word_it.set_to_list (row_to_process->word_list ()); + for (word_it.mark_cycle_pt (); !word_it.cycled_list (); word_it.forward ()) { + word = word_it.data (); + polyg = word->flag (W_POLYGON); + if (word->bounding_box ().overlap (selection_box)) { + blob_it.set_to_list (word->gblob_list ()); + for (blob_it.mark_cycle_pt (); + !blob_it.cycled_list (); blob_it.forward ()) { + blob = blob_it.data (); + if (gblob_bounding_box (blob, polyg).overlap (selection_box)) { + if (new_word == NULL) { + new_word = word->shallow_copy (); + new_blob_it.set_to_list (new_word->gblob_list ()); + } + new_blob_it.add_to_end (blob_it.extract ()); + //move blob + } + } + if (blob_it.empty ()) { //no blobs in word + //so delete word + delete word_it.extract (); + } + } + } + if (new_word != NULL) { + gblob_sort_list (new_word->gblob_list (), polyg); + word_it.add_to_end (new_word); + word_it.sort (word_comparator); + row_to_process->bounding_box ().plot (image_win, + INT_SOLID, FALSE, BLACK, BLACK); + word_it.set_to_list (row_to_process->word_list ()); + for (word_it.mark_cycle_pt (); + !word_it.cycled_list (); word_it.forward ()) + word_display (block_to_process, row_to_process, word_it.data ()); + *current_image_changed = TRUE; + } +} + + +void block_space_stat( //show space stats + BLOCK_LIST *block_list, //blocks to check + BOX &selection_box) { + BLOCK_IT block_it(block_list); + BLOCK *block; + ROW_IT row_it; + ROW *row; + int block_idx = 0; + STATS all_gap_stats (0, MAXSPACING); + WERD_IT word_it; + WERD *word; + PBLOB_IT blob_it; + PBLOB *blob; + C_BLOB_IT cblob_it; + C_BLOB *cblob; + BOX box; + INT16 prev_box_right; + INT16 gap_width; + INT16 min_inter_word_gap; + INT16 max_inter_char_gap; + + /* Find blocks to process */ + + for (block_it.mark_cycle_pt (); + !block_it.cycled_list (); block_it.forward ()) { + block_idx++; + block = block_it.data (); + if (block->bounding_box ().overlap (selection_box)) { + /* Process a block */ + tprintf ("\nBlock %d\n", block_idx); + min_inter_word_gap = 3000; + max_inter_char_gap = 0; + all_gap_stats.clear (); + row_it.set_to_list (block->row_list ()); + for (row_it.mark_cycle_pt (); + !row_it.cycled_list (); row_it.forward ()) { + row = row_it.data (); + prev_box_right = -1; + word_it.set_to_list (row->word_list ()); + for (word_it.mark_cycle_pt (); + !word_it.cycled_list (); word_it.forward ()) { + word = word_it.data (); + if (word->flag (W_POLYGON)) { + blob_it.set_to_list (word->blob_list ()); + for (blob_it.mark_cycle_pt (); + !blob_it.cycled_list (); blob_it.forward ()) { + blob = blob_it.data (); + box = blob->bounding_box (); + if (prev_box_right > -1) { + gap_width = box.left () - prev_box_right; + all_gap_stats.add (gap_width, 1); + if (blob_it.at_first ()) { + if (gap_width < min_inter_word_gap) + min_inter_word_gap = gap_width; + } + else { + if (gap_width > max_inter_char_gap) + max_inter_char_gap = gap_width; + } + } + prev_box_right = box.right (); + } + } + else { + cblob_it.set_to_list (word->cblob_list ()); + for (cblob_it.mark_cycle_pt (); + !cblob_it.cycled_list (); cblob_it.forward ()) { + cblob = cblob_it.data (); + box = cblob->bounding_box (); + if (prev_box_right > -1) { + gap_width = box.left () - prev_box_right; + all_gap_stats.add (gap_width, 1); + if (cblob_it.at_first ()) { + if (gap_width < min_inter_word_gap) + min_inter_word_gap = gap_width; + } + else { + if (gap_width > max_inter_char_gap) + max_inter_char_gap = gap_width; + } + } + prev_box_right = box.right (); + } + } + } + } + tprintf ("Max inter char gap = %d.\nMin inter word gap = %d.\n", + max_inter_char_gap, min_inter_word_gap); + all_gap_stats.short_print (NULL, TRUE); + all_gap_stats.smooth (2); + tprintf ("SMOOTHED DATA...\n"); + all_gap_stats.short_print (NULL, TRUE); + } + } +} + + +void row_space_stat( //show space stats + BLOCK_LIST *block_list, //blocks to check + BOX &selection_box) { + BLOCK_IT block_it(block_list); + BLOCK *block; + ROW_IT row_it; + ROW *row; + int block_idx = 0; + int row_idx; + STATS all_gap_stats (0, MAXSPACING); + WERD_IT word_it; + WERD *word; + PBLOB_IT blob_it; + PBLOB *blob; + C_BLOB_IT cblob_it; + C_BLOB *cblob; + BOX box; + INT16 prev_box_right; + INT16 gap_width; + INT16 min_inter_word_gap; + INT16 max_inter_char_gap; + + /* Find rows to process */ + + for (block_it.mark_cycle_pt (); + !block_it.cycled_list (); block_it.forward ()) { + block_idx++; + block = block_it.data (); + if (block->bounding_box ().overlap (selection_box)) { + row_it.set_to_list (block->row_list ()); + row_idx = 0; + for (row_it.mark_cycle_pt (); + !row_it.cycled_list (); row_it.forward ()) { + row_idx++; + row = row_it.data (); + if (row->bounding_box ().overlap (selection_box)) { + /* Process a row */ + + tprintf ("\nBlock %d Row %d\n", block_idx, row_idx); + min_inter_word_gap = 3000; + max_inter_char_gap = 0; + prev_box_right = -1; + all_gap_stats.clear (); + word_it.set_to_list (row->word_list ()); + for (word_it.mark_cycle_pt (); + !word_it.cycled_list (); word_it.forward ()) { + word = word_it.data (); + if (word->flag (W_POLYGON)) { + blob_it.set_to_list (word->blob_list ()); + for (blob_it.mark_cycle_pt (); + !blob_it.cycled_list (); blob_it.forward ()) { + blob = blob_it.data (); + box = blob->bounding_box (); + if (prev_box_right > -1) { + gap_width = box.left () - prev_box_right; + all_gap_stats.add (gap_width, 1); + if (blob_it.at_first ()) { + if (gap_width < min_inter_word_gap) + min_inter_word_gap = gap_width; + } + else { + if (gap_width > max_inter_char_gap) + max_inter_char_gap = gap_width; + } + } + prev_box_right = box.right (); + } + } + else { + cblob_it.set_to_list (word->cblob_list ()); + for (cblob_it.mark_cycle_pt (); + !cblob_it.cycled_list (); cblob_it.forward ()) { + cblob = cblob_it.data (); + box = cblob->bounding_box (); + if (prev_box_right > -1) { + gap_width = box.left () - prev_box_right; + all_gap_stats.add (gap_width, 1); + if (cblob_it.at_first ()) { + if (gap_width < min_inter_word_gap) + min_inter_word_gap = gap_width; + } + else { + if (gap_width > max_inter_char_gap) + max_inter_char_gap = gap_width; + } + } + prev_box_right = box.right (); + } + } + } + tprintf + ("Max inter char gap = %d.\nMin inter word gap = %d.\n", + max_inter_char_gap, min_inter_word_gap); + all_gap_stats.short_print (NULL, TRUE); + all_gap_stats.smooth (2); + tprintf ("SMOOTHED DATA...\n"); + all_gap_stats.short_print (NULL, TRUE); + } + } + } + } +} + + +/********************************************************************** + * show_point() + * + * Show coords of point, blob bounding box, word bounding box and offset from + * row baseline + **********************************************************************/ + +void show_point( //display posn of bloba word + BLOCK_LIST *block_list, //blocks to check + float x, + float y) { + FCOORD pt(x, y); + BOX box; + BLOCK_IT block_it(block_list); + BLOCK *block; + ROW_IT row_it; + ROW *row; + WERD_IT word_it; + WERD *word; + PBLOB_IT blob_it; + PBLOB *blob; + C_BLOB_IT cblob_it; + C_BLOB *cblob; + + char msg[160]; + char *msg_ptr = msg; + + msg_ptr += sprintf (msg_ptr, "Pt:(%0.3f, %0.3f) ", x, y); + + for (block_it.mark_cycle_pt (); + !block_it.cycled_list (); block_it.forward ()) { + block = block_it.data (); + if (block->bounding_box ().contains (pt)) { + row_it.set_to_list (block->row_list ()); + for (row_it.mark_cycle_pt (); + !row_it.cycled_list (); row_it.forward ()) { + row = row_it.data (); + if (row->bounding_box ().contains (pt)) { + msg_ptr += sprintf (msg_ptr, "BL(x)=%0.3f ", + row->base_line (x)); + + word_it.set_to_list (row->word_list ()); + for (word_it.mark_cycle_pt (); + !word_it.cycled_list (); word_it.forward ()) { + word = word_it.data (); + box = word->bounding_box (); + if (box.contains (pt)) { + msg_ptr += sprintf (msg_ptr, + "Wd(%d, %d)/(%d, %d) ", + box.left (), box.bottom (), + box.right (), box.top ()); + + if (word->flag (W_POLYGON)) { + blob_it.set_to_list (word->blob_list ()); + for (blob_it.mark_cycle_pt (); + !blob_it.cycled_list (); + blob_it.forward ()) { + blob = blob_it.data (); + box = blob->bounding_box (); + if (box.contains (pt)) { + msg_ptr += sprintf (msg_ptr, + "Blb(%d, %d)/(%d, %d) ", + box.left (), + box.bottom (), + box.right (), + box.top ()); + } + } + } + else { + cblob_it.set_to_list (word->cblob_list ()); + for (cblob_it.mark_cycle_pt (); + !cblob_it.cycled_list (); + cblob_it.forward ()) { + 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 ()); + } + } + } + } + } + } + } + } + } + command_window->msg (msg); +} + + +/********************************************************************** + * WERD PROCESSOR FUNCTIONS + * ======================== + * + * These routines are invoked by one or mode 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 + **********************************************************************/ + +BOOL8 word_blank_and_set_display( //display a word + BLOCK *block, //block holding word + ROW *row, //row holding word + WERD *word //word to be processed + ) { + word->bounding_box ().plot (image_win, INT_SOLID, FALSE, BLACK, BLACK); + return word_set_display (block, row, word); +} + + +/********************************************************************** + * word_bln_display() + * + * Normalise word and display in word window + **********************************************************************/ + +BOOL8 word_bln_display( //bln & display + BLOCK *, //block holding word + ROW *row, //row holding word + WERD *word //word to be processed + ) { + WERD *bln_word; + + bln_word = word->poly_copy (row->x_height ()); + bln_word->baseline_normalise (row); + clear_view_surface (bln_word_window_handle ()); + display_bln_lines (bln_word_window_handle (), CYAN, 1.0, 0.0f, -1000.0f, + 1000.0f); + bln_word->plot (bln_word_window_handle (), RED); + delete bln_word; + return TRUE; +} + + +/********************************************************************** + * word_change_text() + * + * Change the correct text of a word + **********************************************************************/ + +BOOL8 word_change_text( //change correct text + BLOCK *block, //block holding word + ROW *row, //row holding word + WERD *word //word to be processed + ) { + char response_str[MAX_CHARS + 1]; + + strcpy (response_str, word->text ()); + if (!command_window-> + prompt ("Enter/edit the correct text and press <>", + response_str)) + return FALSE; + else + word->set_text (response_str); + + if (word_display_mode.bit (DF_TEXT) || word->display_flag (DF_TEXT)) { + word_blank_and_set_display(block, row, word); + overlap_picture_ops(TRUE); + } + + *current_image_changed = TRUE; + return TRUE; +} + + +/********************************************************************** + * word_copy() + * + * Copy a word to other display list + **********************************************************************/ + +BOOL8 word_copy( //copy a word + BLOCK *block, //block holding word + ROW *row, //row holding word + WERD *word //word to be processed + ) { + WERD *copy_word = new WERD; + + *copy_word = *word; + add_word(copy_word, row, block, other_block_list); + *other_image_changed = TRUE; + return TRUE; +} + + +/********************************************************************** + * word_delete() + * + * Delete a word + **********************************************************************/ + +BOOL8 word_delete( //delete a word + BLOCK *block, //block holding word + ROW *row, //row holding word + WERD *word, //word to be processed + BLOCK_IT &block_it, //block list iterator + ROW_IT &row_it, //row list iterator + WERD_IT &word_it //word list iterator + ) { + word_it.extract (); + word->bounding_box ().plot (image_win, INT_SOLID, FALSE, BLACK, BLACK); + delete(word); + + if (word_it.empty ()) { //no words left in row + //so delete row + row_it.extract (); + row->bounding_box ().plot (image_win, INT_SOLID, FALSE, BLACK, BLACK); + delete(row); + + if (row_it.empty ()) { //no rows left in blk + //so delete block + block_it.extract (); + block->bounding_box ().plot (image_win, INT_SOLID, FALSE, + BLACK, BLACK); + delete(block); + } + } + *current_image_changed = TRUE; + return TRUE; +} + + +/********************************************************************** + * word_display() Word Processor + * + * Display a word according to its display modes + **********************************************************************/ + +BOOL8 word_display( // display a word + BLOCK *, //block holding word + ROW *row, //row holding word + WERD *word //word to be processed + ) { + BOX word_bb; //word bounding box + int word_height; //ht of word BB + BOOL8 displayed_something = FALSE; + BOOL8 displayed_rainbow = FALSE; + float shift; //from bot left + PBLOB_IT it; //blob iterator + C_BLOB_IT c_it; //cblob iterator + WERD *word_ptr; //poly copy + WERD temp_word; + float scale_factor; //for BN_POLYGON + + /* + 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, INT_HOLLOW, TRUE, + (COLOUR) ((INT32) + editor_image_word_bb_color), + (COLOUR) ((INT32) + editor_image_word_bb_color)); + + perimeter_color_index (image_win, + (COLOUR) ((INT32) editor_image_blob_bb_color)); + if (word->flag (W_POLYGON)) { + it.set_to_list (word->blob_list ()); + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) + it.data ()->bounding_box ().plot (image_win); + } + else { + 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) && + !word->flag (W_POLYGON)) { //edgesteps available + word->plot (image_win); //rainbow colors + displayed_something = TRUE; + displayed_rainbow = TRUE; + } + + //display poly approx + if (word->display_flag (DF_POLYGONAL)) { + //need to convert + if (!word->flag (W_POLYGON)) { + word_ptr = word->poly_copy (row->x_height ()); + + /* CALL POLYGONAL APPROXIMATOR WHEN AVAILABLE - on a temp_word */ + + if (displayed_rainbow) + //ensure its visible + word_ptr->plot (image_win, WHITE); + else + //rainbow colors + word_ptr->plot (image_win); + delete word_ptr; + } + else { + if (displayed_rainbow) + //ensure its visible + word->plot (image_win, WHITE); + else + word->plot (image_win); //rainbow colors + } + + displayed_rainbow = TRUE; + displayed_something = TRUE; + } + + //disp BN poly approx + if (word->display_flag (DF_BN_POLYGONAL)) { + //need to convert + if (!word->flag (W_POLYGON)) { + word_ptr = word->poly_copy (row->x_height ()); + temp_word = *word_ptr; + delete word_ptr; + + /* CALL POLYGONAL APPROXIMATOR WHEN AVAILABLE - on a temp_word */ + + } + else + temp_word = *word; //copy word + word_bb = word->bounding_box (); + if (!temp_word.flag (W_NORMALIZED)) + temp_word.baseline_normalise (row); + + scale_factor = re_scale_and_move_bln_word (&temp_word, word_bb); + display_bln_lines (image_win, CYAN, scale_factor, word_bb.bottom (), + word_bb.left (), word_bb.right ()); + + if (displayed_rainbow) + //ensure its visible + temp_word.plot (image_win, WHITE); + else + temp_word.plot (image_win);//rainbow colors + + displayed_rainbow = TRUE; + displayed_something = TRUE; + } + + //display correct text + if (word->display_flag (DF_TEXT)) { + word_bb = word->bounding_box (); + text_color_index (image_win, + (COLOUR) ((INT32) editor_image_text_color)); + text_font_index (image_win, 1); + word_height = word_bb.height (); + character_height (image_win, 0.75 * word_height); + + if (word_height < word_bb.width ()) + shift = 0.25 * word_height; + else + shift = 0.0f; + + text2d (image_win, + word_bb.left () + shift, + word_bb.bottom () + 0.25 * word_height, + word->text (), 0, FALSE); + if (strlen (word->text ()) > 0) + displayed_something = TRUE; + } + + if (!displayed_something) //display BBox anyway + word->bounding_box ().plot (image_win, INT_HOLLOW, TRUE, + (COLOUR) ((INT32) editor_image_word_bb_color), + (COLOUR) ((INT32) + editor_image_word_bb_color)); + return TRUE; +} + + +/********************************************************************** + * word_dumper() + * + * Dump members to the debug window + **********************************************************************/ + +BOOL8 word_dumper( //dump word + BLOCK *block, //block holding word + ROW *row, //row holding word + WERD *word //word to be processed + ) { + + tprintf ("\nBlock data...\n"); + block->print (NULL, FALSE); + tprintf ("\nRow data...\n"); + row->print (NULL); + tprintf ("\nWord data...\n"); + word->print (NULL); + return TRUE; +} + + +/********************************************************************** + * word_set_display() Word processor + * + * Display word according to current display mode settings + **********************************************************************/ + +BOOL8 word_set_display( //display a word + BLOCK *block, //block holding word + ROW *row, //row holding word + WERD *word //word to be processed + ) { + BOX word_bb; //word bounding box + + 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)); + *current_image_changed = TRUE; + return word_display (block, row, word); +} + + +/********************************************************************** + * word_toggle_seg() + * + * Toggle the correct segmentation flag + **********************************************************************/ + +BOOL8 word_toggle_seg( //toggle seg flag + BLOCK *, //block holding word + ROW *, //row holding word + WERD *word //word to be processed + ) { + word->set_flag (W_SEGMENTED, !word->flag (W_SEGMENTED)); + *current_image_changed = TRUE; + return TRUE; +} + + +/* DEBUG ONLY */ + +void do_check_mem( //do it + INT32 level) { + check_mem ("Doing it", level); +} diff --git a/display/pgedit.h b/display/pgedit.h new file mode 100644 index 0000000000..b03c5dc6a9 --- /dev/null +++ b/display/pgedit.h @@ -0,0 +1,176 @@ +/********************************************************************** + * File: pgedit.h (Formerly pgeditor.h) + * 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. + * + **********************************************************************/ + +#ifndef PGEDIT_H +#define PGEDIT_H + +#include "ocrblock.h" +#include "ocrrow.h" +#include "werd.h" +#include "rect.h" +#include "pagewalk.h" +//#include "basefile.h" +#include "evnts.h" +#include "sbdmenu.h" +#include "varable.h" +#include "notdll.h" + +extern WINDOW image_win; +extern COMMAND_WINDOW *command_window; +extern BLOCK_LIST *current_block_list; +extern BOOL8 *current_image_changed; +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"); +void add_word( //to block list + WERD *word, //word to be added + ROW *src_row, //source row + BLOCK *src_block, //source block + BLOCK_LIST *dest_block_list //add to this + ); +WINDOW bln_word_window_handle(); //return handle +void build_image_window(BOX page_bounding_box); +MENU_ROOT *build_menu(); +void debug_window_handle(); //return handle +void display_bln_lines(WINDOW window, + COLOUR colour, + float scale_factor, + float y_offset, + float minx, + float maxx); +void do_new_source( //serialise + char *name //file name + ); + //function to call +void do_re_display (BOOL8 word_painter ( +BLOCK *, ROW *, WERD *)); +const BOX do_tidy_cmd(); //tidy +void do_view_cmd(); +void do_write_file( //serialise + char *name //file name + ); +void smd_cmd(); +void pgeditor_main(BLOCK_LIST *blocks); +void pgeditor_msg( //message display + const char *msg); + //of serialised file +void pgeditor_read_file(STRING &name, + BLOCK_LIST *blocks //block list to add to + ); +void pgeditor_show_point( //display coords + GRAPHICS_EVENT *event); +void pgeditor_write_file( //serialise + char *name, //file name + BLOCK_LIST *blocks //block list to write + ); +BOOL8 process_cmd_win_event( //UI command semantics + INT32 cmd_event, //which menu item? + char *new_value //any prompt data + ); +void process_image_event( //action in image win + GRAPHICS_EVENT event); + //put bln word in box +float re_scale_and_move_bln_word(WERD *norm_word, //BL normalised word + const BOX &box //destination box + ); +void re_segment_word( //break/join words + BLOCK_LIST *block_list, //blocks to check + BOX &selection_box); +void block_space_stat( //show space stats + BLOCK_LIST *block_list, //blocks to check + BOX &selection_box); +void row_space_stat( //show space stats + BLOCK_LIST *block_list, //blocks to check + BOX &selection_box); +void show_point( //display posn of bloba word + BLOCK_LIST *block_list, //blocks to check + float x, + float y); + //display a word +BOOL8 word_blank_and_set_display(BLOCK *block, //block holding word + ROW *row, //row holding word + WERD *word //word to be processed + ); +BOOL8 word_bln_display( //bln & display + BLOCK *, //block holding word + ROW *row, //row holding word + WERD *word //word to be processed + ); +BOOL8 word_change_text( //change correct text + BLOCK *block, //block holding word + ROW *row, //row holding word + WERD *word //word to be processed + ); +BOOL8 word_copy( //copy a word + BLOCK *block, //block holding word + ROW *row, //row holding word + WERD *word //word to be processed + ); +BOOL8 word_delete( //delete a word + BLOCK *block, //block holding word + ROW *row, //row holding word + WERD *word, //word to be processed + BLOCK_IT &block_it, //block list iterator + ROW_IT &row_it, //row list iterator + WERD_IT &word_it //word list iterator + ); +BOOL8 word_display( // display a word + BLOCK *, //block holding word + ROW *row, //row holding word + WERD *word //word to be processed + ); +BOOL8 word_dumper( //dump word + BLOCK *block, //block holding word + ROW *row, //row holding word + WERD *word //word to be processed + ); +BOOL8 word_set_display( //display a word + BLOCK *block, //block holding word + ROW *row, //row holding word + WERD *word //word to be processed + ); +BOOL8 word_toggle_seg( //toggle seg flag + BLOCK *, //block holding word + ROW *, //row holding word + WERD *word //word to be processed + ); +void do_check_mem( //do it + INT32 level); +#endif diff --git a/display/pgeditx.h b/display/pgeditx.h new file mode 100644 index 0000000000..4d1f52d0fb --- /dev/null +++ b/display/pgeditx.h @@ -0,0 +1,20 @@ +/************************************************************************* + * The following functions are the standard external calls that pgeditor makes + * to its client programs. They are included explicitly here to avoid making + * pgeditor.c dependent on any/all of its clients + ***************************************************************************/ + + //handle for "MODES" +void extend_menu(RADIO_MENU *modes_menu, + INT16 modes_id_base, //mode cmd ids offset + NON_RADIO_MENU *other_menu, //handle for "OTHER" + INT16 other_id_base //mode cmd ids offset + ); + //current mode +void extend_moded_commands(INT32 mode, + BOX selection_box //area selected + ); + //current mode +void extend_unmoded_commands(INT32 cmd_event, + char *new_value //changed value if any + ); diff --git a/display/sbdmenu.cpp b/display/sbdmenu.cpp new file mode 100644 index 0000000000..15bcac87cb --- /dev/null +++ b/display/sbdmenu.cpp @@ -0,0 +1,520 @@ +/********************************************************************** + * File: sbdmenu.cpp (Formerly menu.c) + * Description: Command Window MENU class + * 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 "mfcpch.h" +#include "sbdmenu.h" + +#define MENU_COLUMN_SPACING (menu_char_height / 2) +#define MENU_ITEM_HEIGHT (int)(menu_char_height * 1.5) +#define MENU_SEPARATOR_HEIGHT MENU_COLUMN_SPACING +#define MENU_TEXT_X_OFFSET (menu_char_height / 2) +#define MENU_TEXT_Y_OFFSET menu_char_height/2 + +#include "notdll.h" +ELISTIZE(MENU_L); + +const ERRCODE NOT_SUBMENU_NODE = "Button is not leaf of radio submenu"; +const ERRCODE SHOULDNT_BE_CALLED = "Should never get to here!!"; + +INT_VAR (menu_char_width, 8, "Width of characters in menu text"); +INT_VAR (menu_char_height, 14, "Height characters in menu text"); + +/********************************************************************** + *********************************************************************** + * + * LEAF_MENU_NODE MEMBER FUNCTIONS + * + *********************************************************************** + **********************************************************************/ + +/********************************************************************** + * LEAF_MENU_NODE::plotx( ) + * + * The real plot. Positions assumed already calculated. + **********************************************************************/ + +void LEAF_MENU_NODE::plotx( //draw it + WINDOW window //in this window + ) { + box.plot (window); + + text2d (window, + box.left () + MENU_TEXT_X_OFFSET, + box.top () - MENU_TEXT_Y_OFFSET, name.string (), 0, FALSE); +} + + +/********************************************************************** + *********************************************************************** + * + * MENU_NODE MEMBER FUNCTIONS + * + *********************************************************************** + **********************************************************************/ + +/********************************************************************** + * MENU_NODE::plot( ) + * + * Initialise, then call plotx to do the real work + * (This means that we can initialise plotting once only REGARDLESS of what menu + * subclass we start plotting. (EG - we don't always start with a MENU_ROOT.) + **********************************************************************/ + +void MENU_NODE::plot( //draw it + WINDOW window //window to draw in + ) { + character_height(window, menu_char_height); + interior_style(window, INT_SOLID, TRUE); + perimeter_color_index(window, LIGHT_GREY); + text_color_index(window, WHITE); + fill_color_index(window, GREY); + plotx(window); +} + + +/********************************************************************** + * MENU_NODE::recalc_bounding_box( ) + * + * SHOULD NEVER BE CALLED. Compiler needs it as this can't be a pure virtual + * function as not all classes provide it and the compiler will not let you do + * "new" on a class which inherits a pure virtual function which it cannot + * resolve into a real function. + **********************************************************************/ + +BOX &MENU_NODE::recalc_bounding_box( // calculate size + INT16, // start topleft x + INT16 // start topleft y + ) { + SHOULDNT_BE_CALLED.error ("MENU_ROOT::recalc_bounding_box", ABORT, NULL); + return box; +} + + +/********************************************************************** + * MENU_NODE::recalc_fixed_width_bb( ) + * + * Calculate the selectable area of the menu item and hence its display + * position. Note that the width is predetermined. + **********************************************************************/ + +BOX &MENU_NODE::recalc_fixed_width_bb( // calc BB + INT16 tl_x, // start topleft x + INT16 tl_y, // start topleft y + INT16 width // width + ) { + box = BOX (ICOORD (tl_x, tl_y - MENU_ITEM_HEIGHT), + ICOORD (tl_x + width, tl_y)); + return box; +} + + +/********************************************************************** + *********************************************************************** + * + * MENU_ROOT MEMBER FUNCTIONS + * + *********************************************************************** + **********************************************************************/ + +/********************************************************************** + * MENU_ROOT::plotx( ) + * + * The real plot - only called internally after initialisation. + * Display the menu recursively. Positions assumed already calculated. + **********************************************************************/ + +void MENU_ROOT::plotx( //draw it + WINDOW window //in this window + ) { + MENU_L_IT it(&menu_list); + + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) + it.data ()->ptr->plotx (window); +} + + +/********************************************************************** + * MENU_ROOT::recalc_bounding_box( ) + * + * Calculate the selectable areas of the menu items and hence their display + * positions. + **********************************************************************/ + +BOX &MENU_ROOT::recalc_bounding_box( // calculate size + INT16 tl_x, // start topleft x + INT16 tl_y // start topleft y + ) { + MENU_L_IT it(&menu_list); + + box = BOX (ICOORD (tl_x, tl_y), ICOORD (tl_x, tl_y)); + + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + box += it.data ()->ptr->recalc_bounding_box (box.right (), box.top ()); + box.move_right_edge (MENU_COLUMN_SPACING); + } + box.move_right_edge (-MENU_COLUMN_SPACING); + return box; +} + + +/********************************************************************** + * MENU_ROOT::write_vars( ) + * + * Just propogate down the menu tree + **********************************************************************/ + +void MENU_ROOT::write_vars( //save config vars + FILE *fp, //in this file + BOOL8 changes_only //Changed vars only? + ) { + MENU_L_IT it(&menu_list); + + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) + it.data ()->ptr->write_vars (fp, changes_only); +} + + +/********************************************************************** + *********************************************************************** + * + * NON_LEAF_MENU_NODE MEMBER FUNCTIONS + * + *********************************************************************** + **********************************************************************/ + +/********************************************************************** + * NON_LEAF_MENU_NODE::link_child( MENU_NODE* ) + * + * Add a child to the end of the menu list + **********************************************************************/ + +void NON_LEAF_MENU_NODE::link_child( //add to menu end + MENU_NODE *new_child) { + MENU_L_IT it(&menu_list); + + it.add_to_end (new MENU_L (new_child)); +} + + +/********************************************************************** + * NON_LEAF_MENU_NODE::link_child_link( ) + * + * Add a child to the end of the menu list + **********************************************************************/ + +void NON_LEAF_MENU_NODE::link_child_link( //add to menu + MENU_L *new_child) { + MENU_L_IT it(&menu_list); + + it.add_to_end (new_child); +} + + +/********************************************************************** + * NON_LEAF_MENU_NODE::event( ) + * + * Event selection + **********************************************************************/ + +void NON_LEAF_MENU_NODE::event( //User clicked... + COMMAND_WINDOW *cmd_win, //For UI, update etc + FCOORD pt, //here + INT32 *cmd_event_id, //Command selected + char *new_value //Edited value + ) { + MENU_L_IT it(&menu_list); + + if (box.contains (pt)) + for (it.mark_cycle_pt (); + (*cmd_event_id == UNIDENTIFIED_COMMAND) && !it.cycled_list (); + it.forward ()) + it.data ()->ptr->event (cmd_win, pt, cmd_event_id, new_value); +} + + +/********************************************************************** + * NON_LEAF_MENU_NODE::max_num_chars( ) + * + * Calculate the max character width of any item of this and its children + **********************************************************************/ + +INT8 NON_LEAF_MENU_NODE::max_num_chars() { //calc char width + MENU_L_IT it(&menu_list); + INT8 max_so_far = 0; + INT8 current_char_count = 0; + + max_so_far = name.length (); + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + current_char_count = it.data ()->ptr->max_num_chars (); + if (current_char_count > max_so_far) + max_so_far = current_char_count; + } + return max_so_far; +} + + +/********************************************************************** + * NON_LEAF_MENU_NODE::plotx( ) (Other than ROOT) + * + * The real plot - only called internally after initialisation. + * Display the menu recursively. Positions assumed already calculated. + **********************************************************************/ + +void NON_LEAF_MENU_NODE::plotx( //draw it + WINDOW window //in this window + ) { + MENU_L_IT it(&menu_list); + + box.plot (window); + text2d (window, + box.left () + MENU_TEXT_X_OFFSET, + box.top () - MENU_TEXT_Y_OFFSET, name.string (), 0, FALSE); + + move2d (window, + box.left (), + box.top () - MENU_ITEM_HEIGHT - MENU_SEPARATOR_HEIGHT / 2); + draw2d (window, + box.right (), + box.top () - MENU_ITEM_HEIGHT - MENU_SEPARATOR_HEIGHT / 2); + + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) + it.data ()->ptr->plotx (window); +} + + +/********************************************************************** + * NON_LEAF_MENU_NODE::recalc_bounding_box( ) + * + * Calculate the selectable areas of the menu items and hence their display + * positions. + **********************************************************************/ + +BOX &NON_LEAF_MENU_NODE::recalc_bounding_box( // calc BB + INT16 tl_x, // start topleft x + INT16 tl_y // start topleft y + ) { + MENU_L_IT it(&menu_list); + INT16 + menu_width = max_num_chars () * menu_char_width + 2 * MENU_TEXT_X_OFFSET; + + box = BOX (ICOORD (tl_x, tl_y - MENU_ITEM_HEIGHT - MENU_SEPARATOR_HEIGHT), + ICOORD (tl_x + menu_width, tl_y)); + + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + box += it.data ()->ptr->recalc_fixed_width_bb (box.left (), + box.bottom (), + box.width ()); + } + return box; +} + + +/********************************************************************** + *********************************************************************** + * + * RADIO_MENU MEMBER FUNCTIONS + * + *********************************************************************** + **********************************************************************/ + +/********************************************************************** + * RADIO_MENU::add_child( ) + * + * Add a radio button + **********************************************************************/ + +void RADIO_MENU::add_child( //Add button + RADIO_MENU_LEAF *new_child) { //item to add + if (menu_list.empty ()) { //Init 1st choice ON + on_button = new_child; + new_child->state = TRUE; + } + link_child(new_child); +} + + +/********************************************************************** + * RADIO_MENU::event( ) + * + * Event selection + **********************************************************************/ + +void RADIO_MENU::event( //User clicked... + COMMAND_WINDOW *cmd_win, //For UI, update etc + FCOORD pt, //here + INT32 *cmd_event_id, //Command selected + char *new_value //Edited value + ) { + MENU_L_IT it(&menu_list); + + if (box.contains (pt)) { + if (!on_button->box.contains (pt)) { + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + it.data ()->ptr->event (cmd_win, pt, cmd_event_id, new_value); + if (*cmd_event_id != UNIDENTIFIED_COMMAND) { + on_button->state = FALSE; + on_button = (RADIO_MENU_LEAF *) it.data ()->ptr; + break; + } + } + } + } +} + + +/********************************************************************** + * RADIO_MENU::press_radio_button() + * + * Change the selected button in a radio menu + **********************************************************************/ + +void RADIO_MENU::press_radio_button( //Chnge selctd butn + RADIO_MENU_LEAF *button //to this one + ) { + MENU_L_IT it(&menu_list); + + if (button != on_button) { + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + if (it.data ()->ptr == button) + break; + } + + if (it.cycled_list ()) //couldnt find it + NOT_SUBMENU_NODE.error ("MENU::press_radio_button", ABORT, NULL); + + on_button->state = FALSE; + button->state = TRUE; + on_button = button; + } +} + + +/********************************************************************** + *********************************************************************** + * + * SIMPLE_MENU_LEAF MEMBER FUNCTIONS + * + *********************************************************************** + **********************************************************************/ + +/********************************************************************** + * SIMPLE_MENU_LEAF::event( ) + * + * Event selection + **********************************************************************/ + +void SIMPLE_MENU_LEAF::event( //User clicked... + COMMAND_WINDOW *, //For UI, update etc + FCOORD pt, //here + INT32 *cmd_event_id, //Command selected + char *new_value //Edited value + ) { + if (box.contains (pt)) { + *cmd_event_id = event_id; + new_value[0] = '\0'; + } +} + + +/********************************************************************** + *********************************************************************** + * + * TOGGLE_MENU_LEAF MEMBER FUNCTIONS + * + *********************************************************************** + **********************************************************************/ + +/********************************************************************** + * TOGGLE_MENU_LEAF::event( ) + * + * Event selection + **********************************************************************/ + +void TOGGLE_MENU_LEAF::event( //User clicked... + COMMAND_WINDOW *, //For UI, update etc + FCOORD pt, //here + INT32 *cmd_event_id, //Command selected + char *new_value //Edited value + ) { + if (box.contains (pt)) { + state = !state; + *cmd_event_id = event_id; + strcpy (new_value, state ? "T" : "F"); + } +} + + +/********************************************************************** + * TOGGLE_MENU_LEAF::plotx( ) + * + * The real plot. Positions assumed already calculated. + **********************************************************************/ + +void TOGGLE_MENU_LEAF::plotx( //draw it + WINDOW window //in this window + ) { + if (state) { + text_color_index(window, BLACK); + fill_color_index(window, WHITE); + } + else { + text_color_index(window, WHITE); + fill_color_index(window, GREY); + } + + LEAF_MENU_NODE::plotx(window); + + text_color_index(window, WHITE); + fill_color_index(window, GREY); +} + + +/********************************************************************** + *********************************************************************** + * + * VARIABLE_MENU_LEAF MEMBER FUNCTIONS + * + *********************************************************************** + **********************************************************************/ + +/********************************************************************** + * VARIABLE_MENU_LEAF::event( ) + * + * Event selection + **********************************************************************/ + +void VARIABLE_MENU_LEAF::event( //User clicked... + COMMAND_WINDOW *cmd_win, //For UI, update etc + FCOORD pt, //here + INT32 *cmd_event_id, //Command selected + char *new_value //Edited value + ) { + char prompt_msg[MAX_CHARS + 1]; + + if (box.contains (pt)) { + strcpy (new_value, current_value.string ()); + sprintf (prompt_msg, "New value for " "%s" "?", name.string ()); + if (cmd_win->internal_prompt (prompt_msg, new_value)) { + current_value = new_value; + *cmd_event_id = event_id; + } + else + *cmd_event_id = NULL_COMMAND; + } +} diff --git a/display/sbdmenu.h b/display/sbdmenu.h new file mode 100644 index 0000000000..e79f1d01a5 --- /dev/null +++ b/display/sbdmenu.h @@ -0,0 +1,415 @@ +/********************************************************************** + * File: sbdmenu.h (Formerly menu.h) + * Description: Command Window MENU class + * 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. + * + **********************************************************************/ + +#ifndef SBDMENU_H +#define SBDMENU_H + +#include "hosthplb.h" +#include "elst.h" +#include "strngs.h" +#include "grphics.h" +#include "varable.h" +#include "rect.h" +#include "points.h" +#include "notdll.h" + +extern INT_VAR_H (menu_char_width, 8, "Width of characters in menu text"); +extern INT_VAR_H (menu_char_height, 14, "Height characters in menu text"); + +class MENU_L; //Forward Declaration +ELISTIZEH(MENU_L); + +class COMMAND_WINDOW; //Fwd Decl #inc at end + +/* Forward declarations showing the inheritance tree of the MENU_NODE + subclasses */ + +class MENU_NODE; +class LEAF_MENU_NODE; +class SIMPLE_MENU_LEAF; +class TOGGLE_MENU_LEAF; +class RADIO_MENU_LEAF; +class VARIABLE_MENU_LEAF; +class NON_LEAF_MENU_NODE; +class MENU_ROOT; +class NON_RADIO_MENU; +class RADIO_MENU; + +/********************************************************************** + +Class MENU_NODE - Generic menu item - an abstract class. + +**********************************************************************/ + +class MENU_NODE +{ + friend class MENU_ROOT; + friend class NON_LEAF_MENU_NODE; + friend class LEAF_MENU_NODE; + friend class RADIO_MENU; + protected: + STRING name; //Text of option + BOX box; //Display area + + MENU_NODE( //constructor + const char *txt) { + name = STRING (txt); + } + + virtual void event ( //User clicked... + COMMAND_WINDOW * cmd_win, //For UI, update etc + FCOORD pt, //here + INT32 * cmd_event_id, //Command selected + char *new_value) = 0; //Edited value + + //char width reqd + virtual INT8 max_num_chars () = 0; + + virtual void plotx ( //draw it + WINDOW window) = 0; //in this window + + //build box + virtual BOX &recalc_bounding_box(INT16 tl_x, //top left x + INT16 tl_y); //top left y + + private: + BOX &recalc_fixed_width_bb( //build box + INT16 tl_x, //top left x + INT16 tl_y, //top left y + INT16 width); //required width + + public: + //return ptr to name + virtual const char *cmp_str() { + return name.string (); + } + + void plot( //draw it + WINDOW window); //in this window + + void new_label( //replace name + const char *label) { + name = label; + } + + virtual void write_vars( //save config vars + FILE *, //in this file + BOOL8) { //Changed vars only? + //default do nothing + } +}; + +/********************************************************************** + +Class LEAF_MENU_NODE - Generic menu leaf + +**********************************************************************/ + +class LEAF_MENU_NODE:public MENU_NODE +{ + protected: + LEAF_MENU_NODE ( //constructor + const char *menu_text):MENU_NODE (menu_text) { + } + + INT8 max_num_chars() { //char width reqd + return (INT8) name.length (); + } + + virtual void plotx( //draw it + WINDOW window); //in this window +}; + +/********************************************************************** + +Class SIMPLE_MENU_LEAF - Just returns its event code when selected + +**********************************************************************/ + +class SIMPLE_MENU_LEAF:public LEAF_MENU_NODE +{ + INT32 event_id; //Return event code + + private: + void event( //User clicked... + COMMAND_WINDOW *cmd_win, //For UI, update etc + FCOORD pt, //here + INT32 *cmd_event_id, //Command selected + char *new_value); //Edited value + + public: + SIMPLE_MENU_LEAF ( //constructor + const char *menu_text, + INT32 code):LEAF_MENU_NODE (menu_text) { + event_id = code; + } +}; + +/********************************************************************** + +Class TOGGLE_MENU_LEAF - Boolean toggle leaf + +**********************************************************************/ + +class TOGGLE_MENU_LEAF:public LEAF_MENU_NODE +{ + friend class RADIO_MENU; + + INT32 event_id; //Return event code + BOOL8 state; //ON/OFF + + private: + void event( //User clicked... + COMMAND_WINDOW *cmd_win, //For UI, update etc + FCOORD pt, //here + INT32 *cmd_event_id, //Command selected + char *new_value); //Edited value + + void plotx( //draw it + WINDOW window); //in this window + + public: + TOGGLE_MENU_LEAF ( //constructor + const char *menu_text, + INT32 code, + BOOL8 initial_state):LEAF_MENU_NODE (menu_text) { + event_id = code; + state = initial_state; + } + + void set_toggle( //Explicit override + BOOL8 value) { + state = value; + } +}; + +/********************************************************************** + +Class RADIO_MENU_LEAF - Press the radio button and return the event code +on selection + +**********************************************************************/ + +class RADIO_MENU_LEAF:public TOGGLE_MENU_LEAF +{ + public: + RADIO_MENU_LEAF ( //constructor + const char *menu_text, + INT32 code):TOGGLE_MENU_LEAF (menu_text, code, FALSE) { + } +}; + +/********************************************************************** + +Class VARIABLE_MENU_LEAF - Prompt for new value and return it + +**********************************************************************/ + +class VARIABLE_MENU_LEAF:public LEAF_MENU_NODE +{ + INT32 event_id; //Return event code + STRING current_value; //Variable value + + void event( //User clicked... + COMMAND_WINDOW *cmd_win, //For UI, update etc + FCOORD pt, //here + INT32 *cmd_event_id, //Command selected + char *new_value); //Edited value + + public: + VARIABLE_MENU_LEAF ( //constructor + const char *menu_text, + INT32 code, + const char *initial_value):LEAF_MENU_NODE (menu_text) { + event_id = code; + current_value = STRING (initial_value); + } + + void replace_value( //change current_value + const char *new_value) { + current_value = new_value; + } +}; + +/********************************************************************** + +Class NON_LEAF_MENU_NODE - Generic menu item - an abstract class. + +**********************************************************************/ + +class NON_LEAF_MENU_NODE:public MENU_NODE +{ + public: + void clear_children() { + menu_list.clear (); + }; + + protected: + MENU_L_LIST menu_list; + + NON_LEAF_MENU_NODE ( //constructor + const char *menu_text):MENU_NODE (menu_text) { + } + + void link_child( //add to sub-menu end + MENU_NODE *new_child); //item to add + + void link_child_link( //add to sub-menu end + MENU_L *new_child); //item to add + + virtual void event( //User clicked... + COMMAND_WINDOW *cmd_win, //For UI, update etc + FCOORD pt, //here + INT32 *cmd_event_id, //Command selected + char *new_value); //Edited value + + INT8 max_num_chars(); //char width reqd + + virtual void plotx( //draw it + WINDOW window); //in this window + + //build box + virtual BOX &recalc_bounding_box(INT16 tl_x, //top left x + INT16 tl_y); //top left y + +}; + +/********************************************************************** + +Class MENU_ROOT - The root of a menu tree + +**********************************************************************/ + +class MENU_ROOT:public NON_LEAF_MENU_NODE +{ + private: + void plotx( //draw it + WINDOW window); //in this window + + public: + MENU_ROOT ():NON_LEAF_MENU_NODE ("") { + } + //cnstrctr + + void add_child( //add to sub-menu end //item to add + NON_LEAF_MENU_NODE *new_child) { + link_child(new_child); + } + + /* Public defn of event - at menu_root only*/ + void event( //User clicked... + COMMAND_WINDOW *cmd_win, //For UI, update etc + FCOORD pt, //here + INT32 *cmd_event_id, //Command selected + char *new_value) { //Edited value + NON_LEAF_MENU_NODE::event(cmd_win, pt, cmd_event_id, new_value); + } + + BOX &recalc_bounding_box( //build box + INT16 tl_x, //top left x + INT16 tl_y); //top left y + + /* Public defn of write_vars - at menu_root only*/ + void write_vars( //save config vars + FILE *fp, //in this file + BOOL8 changes_only); //Changed vars only? +}; + +/********************************************************************** + +Class NON_RADIO_MENU - The root of a menu tree + +**********************************************************************/ + +class NON_RADIO_MENU:public NON_LEAF_MENU_NODE +{ + public: + NON_RADIO_MENU ( //constructor + const char *menu_text):NON_LEAF_MENU_NODE (menu_text) { + } + + void add_child( //add to sub-menu end //item to add + SIMPLE_MENU_LEAF *new_child) { + link_child(new_child); + } + + void add_child( //add to sub-menu end //item to add + TOGGLE_MENU_LEAF *new_child) { + link_child(new_child); + } + + void add_child( //add to sub-menu end //item to add + VARIABLE_MENU_LEAF *new_child) { + link_child(new_child); + } + +}; + +/********************************************************************** + +Class RADIO_MENU - The root of a radio sub menu + +**********************************************************************/ + +class RADIO_MENU:public NON_LEAF_MENU_NODE +{ + private: + RADIO_MENU_LEAF * on_button; //which one is on? + + void event( //User clicked... + COMMAND_WINDOW *cmd_win, //For UI, update etc + FCOORD pt, //here + INT32 *cmd_event_id, //Command selected + char *new_value); //Edited value + + public: + RADIO_MENU ( //constructor + const char *menu_text):NON_LEAF_MENU_NODE (menu_text) { + } + + void add_child( //add to sub-menu end + RADIO_MENU_LEAF *new_child); //item to add + + void press_radio_button( //Change selected butn + RADIO_MENU_LEAF *button); //to this one +}; + +/********************************************************************** + +Class MENU_L - Generic menu list element for heterogeneous list. + +**********************************************************************/ + +class MENU_L:public ELIST_LINK +{ + public: + MENU_NODE * ptr; //Generic menu item + + MENU_L() { + } //copy list constrctr + + MENU_L( //normal constructor + MENU_NODE *p) { + ptr = p; + } +}; + +#include "cmndwin.h" +#include "notdll.h" +#endif diff --git a/display/submen.h b/display/submen.h new file mode 100644 index 0000000000..40eadf8e2c --- /dev/null +++ b/display/submen.h @@ -0,0 +1,62 @@ +/********************************************************************** + * File: submen.h (Formerly submenu.h) + * Description: Variables submenu subclasses + * Author: Phil Cheatle + * Created: Wed Nov 13 11:17: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 SUBMEN_H +#define SUBMEN_H + +/********************************************************************** + +Class VAR_SUB_MENU - A leaf in the top level variable editor window + +**********************************************************************/ + +class VAR_SUB_MENU:public LEAF_MENU_NODE +{ + private: + MENU_ROOT * root; //Sub window menu + COMMAND_WINDOW *sub_window; //Sub window window + + void event( //User clicked... + COMMAND_WINDOW *cmd_win, //For UI, update etc + FCOORD pt, //here + INT32 *cmd_event_id, //Command selected + char *new_value); //Edited value + + void write_vars( //save config vars + FILE *fp, //in this file + BOOL8 changes_only) { //Changed vars only? + fprintf (fp, "\n"); + //propogate to submenu + root->write_vars (fp, changes_only); + } + + public: + VAR_SUB_MENU ( //constructor + //variable to edit + const char *menu_text, MENU_ROOT * sub_menu + ):LEAF_MENU_NODE (menu_text) { + root = sub_menu; + sub_window = NULL; + } + + void child_closed() { + sub_window = NULL; + } +}; +#endif diff --git a/display/tessio.h b/display/tessio.h new file mode 100644 index 0000000000..a63e7a376a --- /dev/null +++ b/display/tessio.h @@ -0,0 +1,110 @@ +/********************************************************************** + * File: tessio.h (Formerly tessread.h) + * Description: Read/write Tesseract format row files. + * Author: Ray Smith + * Created: Wed Oct 09 15:02:46 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 TESSIO_H +#define TESSIO_H + +#include +#include "tessclas.h" +#include "notdll.h" + +TEXTROW *get_tess_row_file( //open read & close + const char *name, //file name + TPOINT *topright //corner + ); +TBLOB *get_tess_blob_file( //open read & close + const char *name, //file name + TPOINT *topright //corner + ); +TEXTROW *readrows( //read row file + int gphfd, /*file to read */ + int count, /*number expected */ + TPOINT *imagesize //size of image + ); +TWERD *readwords( //read some words + int gphfd, /*file to read */ + int count, /*number expected */ + TEXTROW *row, /*row it comes from */ + TPOINT *imagesize /*size of image */ + ); +TBLOB *readblobs( //read some blobs + int gphfd, /*file to read */ + int count, /*number expected */ + TPOINT *imagesize /*size of image */ + ); +char *readratings( //get a string + int gphfd, /*file to read */ + int ratingspace /*size to read */ + ); +void readoutlines( //read some outlines + int gphfd, /*file to read */ + TESSLINE **outlines, /*array of ptrs */ + int outlinecount /*no to read */ + ); +int readgph( //read with testing + int fd, /*file to read */ + void *start, /*buffer to write */ + int size, /*amount to write */ + int checkeof /*give error on eof? */ + ); +void write_row( //write a row + FILE *name, //file to write + TEXTROW *row /*row to write */ + ); +void write_error_row( //write special row + FILE *name, /*file name */ + TEXTROW *row, /*row to write */ + int wordcount /*no of words to go */ + ); +void write_error_blob( //write special blob + FILE *name, /*file name */ + TBLOB *blob, /*blob to write */ + char *charlist, /*true chars */ + int charcount /*no of true chars */ + ); +void write_error_word( //write special word + FILE *name, /*file name */ + TWERD *word, /*word to write */ + char *charlist, /*true chars */ + int charcount /*no of true chars */ + ); +void writeblob( //write a blob + FILE *name, /*file to write */ + TBLOB *blob /*blob to write */ + ); +void serial_outlines( //serialize + FILE *name, /*file to write to */ + TBLOB *blob, /*current blob */ + register TESSLINE *outline, /*current outline */ + int *outlineno /*current serial no */ + ); +int countloop( //count loopsize + register BYTEVEC *vector /*vectors to count */ + ); +int outlineserial( //get serial no + register TESSLINE *outline, /*start of serach */ + register TESSLINE *target, /*outline to find */ + int serial /*serial no so far */ + ); +void writegph( //interface to fwrite + FILE *name, /*file to write */ + void *start, /*buffer to write */ + int size /*amount to write */ + ); +#endif diff --git a/display/varabled.cpp b/display/varabled.cpp new file mode 100644 index 0000000000..aea8973e21 --- /dev/null +++ b/display/varabled.cpp @@ -0,0 +1,283 @@ +/********************************************************************** + * File: varabled.cpp (Formerly varedit.c) + * Description: Variables Editor + * Author: Phil Cheatle + * Created: Mon Nov 11 09:51:58 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 "mfcpch.h" +#include +#include "varable.h" +#include "varblmen.h" +#include "varblwin.h" +#include "varabled.h" +#include "cmndwin.h" +#include "mainblk.h" + +#define VARDIR "configs/" /*variables files */ + +const ERRCODE NO_VARIABLES_TO_EDIT = "No Variables defined to edit"; + +/********************************************************************** + * build_list_of_all_leaves() + * + * Generate a list of menu leaves for each of the variables. The list is + * heterogeneous, different menu leaf types for each variable type. + **********************************************************************/ + +MENU_L_LIST *build_list_of_all_leaves() { //find all variables + INT_VARIABLE_C_IT int_it (INT_VARIABLE::get_head ()); + BOOL_VARIABLE_C_IT bool_it (BOOL_VARIABLE::get_head ()); + STRING_VARIABLE_C_IT str_it (STRING_VARIABLE::get_head ()); + double_VARIABLE_C_IT dbl_it (double_VARIABLE::get_head ()); + + MENU_L_LIST *all_leaves_list = new MENU_L_LIST; + MENU_L_IT it; + + it.set_to_list (all_leaves_list); + + for (int_it.mark_cycle_pt (); !int_it.cycled_list (); int_it.forward ()) + it.add_to_end (new MENU_L (new INT_VAR_MENU_LEAF (int_it.data ()))); + + for (bool_it.mark_cycle_pt (); !bool_it.cycled_list (); bool_it.forward ()) + it.add_to_end (new MENU_L (new BOOL_VAR_MENU_LEAF (bool_it.data ()))); + + for (str_it.mark_cycle_pt (); !str_it.cycled_list (); str_it.forward ()) + it.add_to_end (new MENU_L (new STR_VAR_MENU_LEAF (str_it.data ()))); + + for (dbl_it.mark_cycle_pt (); !dbl_it.cycled_list (); dbl_it.forward ()) + it.add_to_end (new MENU_L (new DBL_VAR_MENU_LEAF (dbl_it.data ()))); + + return all_leaves_list; +} + + +/********************************************************************** + * build_main_var_menu() + * + * Extract each element from the sorted, non empty list of menu leaves. Add + * each to the appropriate menu. Note that ALL menus for ALL variable editor + * windows are built at this stage. The VAR_SUB_MENU menu leaves used in the + * leaves of the top level window hold the root menus for the sub windows for + * use if and when that window is created. A little wastful on space perhaps, + * but easier than reconstructing/sorting/etc the all_leaves_list. + * + **********************************************************************/ + +MENU_ROOT *build_main_var_menu( //build menus + MENU_L_LIST *all_leaves_list) { + MENU_L_IT it; + MENU_L_LIST sub_window_list; + MENU_L_LIST sub_menu_list; + + MENU_ROOT *main_root; + VAR_NON_RADIO_MENU *main_menu; + NON_RADIO_MENU *std_menu; + MENU_ROOT *main_misc_root = NULL; + VAR_NON_RADIO_MENU *main_misc_menu = NULL; + MENU_ROOT *sub_window_root; + VAR_NON_RADIO_MENU *current_sub_win_menu; + VAR_NON_RADIO_MENU *sub_window_misc_menu; + MENU_L *leaf; + const char *full_name; + char first_word[80]; + char first_two_words[80]; + int header_len; + STRING varfile; //save config filename + + main_root = new MENU_ROOT; + main_menu = new VAR_NON_RADIO_MENU ("SubMenus"); + main_root->add_child (main_menu); + + while (!all_leaves_list->empty ()) { + // Whatever is left + it.set_to_list (all_leaves_list); + full_name = (it.data ()->ptr)->cmp_str (); + // 1st word delim by _ + get_first_words (full_name, 1, first_word); + extract_sublist(all_leaves_list, first_word, &sub_window_list); + + //addto main misc menu + if (sub_window_list.singleton ()) { + if (main_misc_root == NULL) { + main_misc_root = new MENU_ROOT; + main_misc_menu = new VAR_NON_RADIO_MENU ("Miscellaneous"); + main_misc_root->add_child (main_misc_menu); + } + it.set_to_list (&sub_window_list); + leaf = it.extract (); + leaf->ptr->new_label (full_name); + main_misc_menu->add_child (leaf); + } + else { //build subwindow menu + sub_window_root = new MENU_ROOT; + main_menu->add_child (new VAR_SUB_MENU (first_word, + sub_window_root)); + sub_window_misc_menu = new VAR_NON_RADIO_MENU ("Miscellaneous"); + sub_window_misc_menu-> + add_child (new + SIMPLE_MENU_LEAF ("Kill Sub Window", KILL_WINDOW_CMD)); + while (!sub_window_list.empty ()) { + it.set_to_list (&sub_window_list); + full_name = it.data ()->ptr->cmp_str (); + get_first_words (full_name, 2, first_two_words); + extract_sublist(&sub_window_list, first_two_words, &sub_menu_list); + + it.set_to_list (&sub_menu_list); + //addto subwin misc + if (sub_menu_list.singleton ()) { + leaf = it.extract (); + leaf->ptr->new_label (full_name); + sub_window_misc_menu->add_child (leaf); + } + else { + //build sub menu + current_sub_win_menu = + new VAR_NON_RADIO_MENU(first_two_words); + sub_window_root->add_child (current_sub_win_menu); + header_len = strlen (first_two_words); + while (!sub_menu_list.empty ()) { + it.set_to_list (&sub_menu_list); + leaf = it.extract (); + leaf->ptr->new_label (leaf->ptr->cmp_str () + + header_len); + current_sub_win_menu->add_child (leaf); + } + } + } + sub_window_root->add_child (sub_window_misc_menu); + } + } + if (main_misc_root != NULL) + main_root->add_child (main_misc_root); + + varfile = datadir; + varfile += VARDIR; /*variables dir */ + varfile += "edited"; /*actual name */ + + std_menu = new NON_RADIO_MENU ("Build Config File"); + main_root->add_child (std_menu); + std_menu->add_child (new VARIABLE_MENU_LEAF ("All Variables", + WRITE_ALL_CMD, + varfile.string ())); + std_menu->add_child (new VARIABLE_MENU_LEAF ("Changed Variables Only", + WRITE_CHANGED_CMD, + varfile.string ())); + return main_root; +} + + +/********************************************************************** + * extract_sublist() + * + * Extract a sublist containing elements whose name initially matches the + * specified string + * + **********************************************************************/ + +void extract_sublist( //remove initial items + MENU_L_LIST *source_list, //source list + char *leading_str, //string to match + MENU_L_LIST *extracted_list //extracted list + ) { + MENU_L_IT start_it(source_list); + MENU_L_IT end_it(source_list); + int match_len = strlen (leading_str); + + while (!end_it.at_last () && + (strncmp (leading_str, end_it.data_relative (1)->ptr->cmp_str (), + match_len) == 0)) + end_it.forward (); + extracted_list->assign_to_sublist (&start_it, &end_it); +} + + +/********************************************************************** + * get_first_words() + * + * Copy the first N words from the source string to the target string. + * Words are delimited by "_" + * + **********************************************************************/ + +void get_first_words( //copy first N words + 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 +} + + +/********************************************************************** + * menu_item_sorter() LIST SORT COMPARATOR + * + * Given two MENU_L items, sort them on the basis of their cmp_str s + * + **********************************************************************/ + +int menu_item_sorter( //sorter + const void *item1, + const void *item2) { + MENU_L *menu_l_1 = (*(MENU_L **) item1); + MENU_L *menu_l_2 = (*(MENU_L **) item2); + + MENU_NODE *node1; + MENU_NODE *node2; + + const char *str1; + const char *str2; + + node1 = menu_l_1->ptr; + node2 = menu_l_2->ptr; + + str1 = node1->cmp_str (); + str2 = node2->cmp_str (); + + return strcmp (str1, str2); +} + + +/********************************************************************** + * start_variables_editor() + * + * Create the top level variables editor window. Build all the menus for all + * the subwindows in the process. + **********************************************************************/ + +void start_variables_editor() { //create top level win + MENU_L_LIST *all_leaves_list = new MENU_L_LIST; + MENU_L_IT it; + + all_leaves_list = build_list_of_all_leaves (); + it.set_to_list (all_leaves_list); + if (it.empty ()) { + NO_VARIABLES_TO_EDIT.error ("start_variables_editor", LOG, NULL); + } + it.sort (&menu_item_sorter); + + new VARIABLES_WINDOW ("VarEditorMAIN", + build_main_var_menu (all_leaves_list), NULL); +} diff --git a/display/varabled.h b/display/varabled.h new file mode 100644 index 0000000000..fa8b6b6555 --- /dev/null +++ b/display/varabled.h @@ -0,0 +1,41 @@ +/********************************************************************** + * File: varabled.h (Formerly varedit.h) + * Description: Variables Editor + * Author: Phil Cheatle + * Created: Mon Nov 11 09:51:58 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 VARABLED_H +#define VARABLED_H + //find all variables +MENU_L_LIST *build_list_of_all_leaves(); +MENU_ROOT *build_main_var_menu( //build menus + MENU_L_LIST *all_leaves_list); + +void extract_sublist( //remove initial items + MENU_L_LIST *source_list, //source list + char *leading_str, //string to match + MENU_L_LIST *extracted_list //extracted list + ); +void get_first_words( //copy first N words + const char *s, //source string + int n, //number of words + char *t //target string + ); +int menu_item_sorter( //sorter + const void *item1, + const void *item2); +void start_variables_editor(); //create top level win +#endif diff --git a/display/varblmen.cpp b/display/varblmen.cpp new file mode 100644 index 0000000000..7cab2d4ac0 --- /dev/null +++ b/display/varblmen.cpp @@ -0,0 +1,329 @@ +/********************************************************************** + * File: varblmen.cpp (Formerly varmenu.c) + * Description: Variables menu subclasses + * Author: Phil Cheatle + * Created: Wed Nov 13 11:17: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 "mfcpch.h" +#include "varblmen.h" +#include "varblwin.h" + +/********************************************************************** + *********************************************************************** + * + * BOOL_VAR_MENU_LEAF MEMBER FUNCTIONS + * + *********************************************************************** + **********************************************************************/ + +/********************************************************************** + * BOOL_VAR_MENU_LEAF::event( ) + * + * Event selection - Toggle value + **********************************************************************/ + +void BOOL_VAR_MENU_LEAF::event( //User clicked... + COMMAND_WINDOW *cmd_win, //For UI, update etc + FCOORD pt, //here + INT32 *, //Command selected + char * //Edited value + ) { + char msg[MAX_CHARS + 1]; + + if (box.contains (pt)) { + var->set_value (!(BOOL8) * var); + sprintf (msg, "TOGGLED: %s", var->info_str ()); + cmd_win->msg (msg); + changed = TRUE; + } +} + + +/********************************************************************** + * BOOL_VAR_MENU_LEAF::plotx( ) + * + * The real plot. Positions assumed already calculated. + **********************************************************************/ + +void BOOL_VAR_MENU_LEAF::plotx( //draw it + WINDOW window //in this window + ) { + if ((BOOL8) * var) { + text_color_index(window, BLACK); + fill_color_index(window, WHITE); + } + + LEAF_MENU_NODE::plotx(window); + + text_color_index(window, WHITE); + fill_color_index(window, GREY); +} + + +/********************************************************************** + * BOOL_VAR_MENU_LEAF::write_vars( ) + * + **********************************************************************/ + +void BOOL_VAR_MENU_LEAF::write_vars( //save config vars + FILE *fp, //in this file + BOOL8 changes_only //Changed vars only? + ) { + if (!changes_only || changed) { + fprintf (fp, "%-25s %-12s # %s\n", + var->name_str (), + (BOOL8) * var ? "TRUE" : "FALSE", var->info_str ()); + } +} + + +/********************************************************************** + *********************************************************************** + * + * DBL_VAR_MENU_LEAF MEMBER FUNCTIONS + * + *********************************************************************** + **********************************************************************/ + +/********************************************************************** + * DBL_VAR_MENU_LEAF::event( ) + * + * Event selection - Toggle value + **********************************************************************/ + +void DBL_VAR_MENU_LEAF::event( //User clicked... + COMMAND_WINDOW *cmd_win, //For UI, update etc + FCOORD pt, //here + INT32 *, //Command selected + char * //Edited value + ) { + char ascii_new_value[MAX_CHARS + 1]; + const char *prompt_msg; + double new_value; + + if (box.contains (pt)) { + sprintf (ascii_new_value, "%g", (double) *var); + prompt_msg = var->info_str (); + while (cmd_win->internal_prompt (prompt_msg, ascii_new_value)) { + if (sscanf (ascii_new_value, "%lf", &new_value) == 1) { + if (new_value != (double) *var) { + var->set_value (new_value); + cmd_win->msg ("Value changed"); + changed = TRUE; + return; + } + else { + cmd_win->msg ("Value unchanged"); + return; + } + } + else + prompt_msg = "Invalid value for double - try again or click"; + } + } +} + + +/********************************************************************** + * DBL_VAR_MENU_LEAF::write_vars( ) + * + **********************************************************************/ + +void DBL_VAR_MENU_LEAF::write_vars( //save config vars + FILE *fp, //in this file + BOOL8 changes_only //Changed vars only? + ) { + if (!changes_only || changed) { + fprintf (fp, "%-25s %-12e # %s\n", + var->name_str (), (double) *var, var->info_str ()); + } +} + + +/********************************************************************** + *********************************************************************** + * + * INT_VAR_MENU_LEAF MEMBER FUNCTIONS + * + *********************************************************************** + **********************************************************************/ + +/********************************************************************** + * INT_VAR_MENU_LEAF::event( ) + * + * Event selection - Toggle value + **********************************************************************/ + +void INT_VAR_MENU_LEAF::event( //User clicked... + COMMAND_WINDOW *cmd_win, //For UI, update etc + FCOORD pt, //here + INT32 *, //Command selected + char * //Edited value + ) { + char ascii_new_value[MAX_CHARS + 1]; + const char *prompt_msg; + INT32 new_value; + + if (box.contains (pt)) { + sprintf (ascii_new_value, INT32FORMAT, (INT32) * var); + prompt_msg = var->info_str (); + while (cmd_win->internal_prompt (prompt_msg, ascii_new_value)) { + if (sscanf (ascii_new_value, INT32FORMAT, &new_value) == 1) { + if (new_value != (INT32) * var) { + var->set_value (new_value); + cmd_win->msg ("Value changed"); + changed = TRUE; + return; + } + else { + cmd_win->msg ("Value unchanged"); + return; + } + } + else + prompt_msg = "Invalid value for INT32 - try again or click"; + } + } +} + + +/********************************************************************** + * INT_VAR_MENU_LEAF::write_vars( ) + * + **********************************************************************/ + +void INT_VAR_MENU_LEAF::write_vars( //save config vars + FILE *fp, //in this file + BOOL8 changes_only //Changed vars only? + ) { + if (!changes_only || changed) { + fprintf (fp, "%-25s %-12d # %s\n", + var->name_str (), (int) *var, var->info_str ()); + } +} + + +/********************************************************************** + *********************************************************************** + * + * STR_VAR_MENU_LEAF MEMBER FUNCTIONS + * + *********************************************************************** + **********************************************************************/ + +/********************************************************************** + * STR_VAR_MENU_LEAF::event( ) + * + * Event selection - Toggle value + **********************************************************************/ + +void STR_VAR_MENU_LEAF::event( //User clicked... + COMMAND_WINDOW *cmd_win, //For UI, update etc + FCOORD pt, //here + INT32 *, //Command selected + char * //Edited value + ) { + char ascii_new_value[MAX_CHARS + 1]; + + if (box.contains (pt)) { + sprintf (ascii_new_value, "%s", ((STRING) * var).string ()); + if (cmd_win->internal_prompt (var->info_str (), ascii_new_value)) { + if (strcmp (ascii_new_value, ((STRING) * var).string ()) != 0) { + var->set_value (ascii_new_value); + cmd_win->msg ("Value changed"); + changed = TRUE; + return; + } + else { + cmd_win->msg ("Value unchanged"); + return; + } + } + } +} + + +/********************************************************************** + * STR_VAR_MENU_LEAF::write_vars( ) + * + **********************************************************************/ + +void STR_VAR_MENU_LEAF::write_vars( //save config vars + FILE *fp, //in this file + BOOL8 changes_only //Changed vars only? + ) { + if (!changes_only || changed) { + fprintf (fp, "%-25s %-12s # %s\n", + var->name_str (), + ((STRING) * var).string (), var->info_str ()); + } +} + + +/********************************************************************** + *********************************************************************** + * + * VAR_NON_RADIO_MENU MEMBER FUNCTIONS + * + *********************************************************************** + **********************************************************************/ + +/********************************************************************** + * VAR_NON_RADIO_MENU::write_vars( ) + * + * Just propogate down the menu tree + **********************************************************************/ + +void VAR_NON_RADIO_MENU::write_vars( //save config vars + FILE *fp, //in this file + BOOL8 changes_only //Changed vars only? + ) { + MENU_L_IT it(&menu_list); + + fprintf (fp, "\n"); + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) + it.data ()->ptr->write_vars (fp, changes_only); +} + + +/********************************************************************** + *********************************************************************** + * + * VAR_SUB_MENU MEMBER FUNCTIONS + * + *********************************************************************** + **********************************************************************/ + +/********************************************************************** + * VAR_SUB_MENU::event( ) + * + * Event selection - Toggle value + **********************************************************************/ + +void VAR_SUB_MENU::event( //User clicked... + COMMAND_WINDOW *win, //For UI, update etc + FCOORD pt, //here + INT32 *, //Command selected + char * //Edited value + ) { + if (box.contains (pt)) { + if (sub_window != NULL) + win->msg ("Sub menu is already open!"); + else { + sub_window = new VARIABLES_WINDOW (name.string (), root, this); + } + } +} diff --git a/display/varblmen.h b/display/varblmen.h new file mode 100644 index 0000000000..5901c0d52f --- /dev/null +++ b/display/varblmen.h @@ -0,0 +1,203 @@ +/********************************************************************** + * File: varblmen.h (Formerly varmenu.h) + * Description: Variables menu subclasses + * Author: Phil Cheatle + * Created: Wed Nov 13 11:17: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 VARBLMEN_H +#define VARBLMEN_H + +#include "sbdmenu.h" +#include "submen.h" +#include "notdll.h" + +/********************************************************************** + +Class BOOL_VAR_MENU_LEAF - Boolean Variable leaf + +**********************************************************************/ + +class BOOL_VAR_MENU_LEAF:public LEAF_MENU_NODE +{ + private: + BOOL_VARIABLE * var; //The variable + BOOL8 changed; //User modified? + + void event( //User clicked... + COMMAND_WINDOW *cmd_win, //For UI, update etc + FCOORD pt, //here + INT32 *cmd_event_id, //Command selected + char *new_value); //Edited value + + void plotx( //draw it + WINDOW window); //in this window + + const char *cmp_str() { //get ptr to name + return var->name_str (); + } + + void write_vars( //save config vars + FILE *fp, //in this file + BOOL8 changes_only); //Changed vars only? + + public: + BOOL_VAR_MENU_LEAF ( //constructor + BOOL_VARIABLE * this_var //variable to edit + ): LEAF_MENU_NODE ("") { + var = this_var; + changed = FALSE; + } +}; + +/********************************************************************** + +Class DBL_VAR_MENU_LEAF - Double Variable leaf + +**********************************************************************/ + +class DBL_VAR_MENU_LEAF:public LEAF_MENU_NODE +{ + private: + double_VARIABLE * var; //The variable + BOOL8 changed; //User modified? + + void event( //User clicked... + COMMAND_WINDOW *cmd_win, //For UI, update etc + FCOORD pt, //here + INT32 *cmd_event_id, //Command selected + char *new_value); //Edited value + + const char *cmp_str() { //get ptr to name + return var->name_str (); + } + + void write_vars( //save config vars + FILE *fp, //in this file + BOOL8 changes_only); //Changed vars only? + + public: + DBL_VAR_MENU_LEAF ( //constructor + double_VARIABLE * this_var //variable to edit + ): LEAF_MENU_NODE ("") { + var = this_var; + changed = FALSE; + } +}; + +/********************************************************************** + +Class INT_VAR_MENU_LEAF - Integer Variable leaf + +**********************************************************************/ + +class INT_VAR_MENU_LEAF:public LEAF_MENU_NODE +{ + private: + INT_VARIABLE * var; //The variable + BOOL8 changed; //User modified? + + void event( //User clicked... + COMMAND_WINDOW *cmd_win, //For UI, update etc + FCOORD pt, //here + INT32 *cmd_event_id, //Command selected + char *new_value); //Edited value + + const char *cmp_str() { //get ptr to name + return var->name_str (); + } + + void write_vars( //save config vars + FILE *fp, //in this file + BOOL8 changes_only); //Changed vars only? + + public: + INT_VAR_MENU_LEAF ( //constructor + INT_VARIABLE * this_var //variable to edit + ): LEAF_MENU_NODE ("") { + var = this_var; + changed = FALSE; + } +}; + +/********************************************************************** + +Class STR_VAR_MENU_LEAF - String Variable leaf + +**********************************************************************/ + +class STR_VAR_MENU_LEAF:public LEAF_MENU_NODE +{ + private: + STRING_VARIABLE * var; //The variable + BOOL8 changed; //User modified? + + void event( //User clicked... + COMMAND_WINDOW *cmd_win, //For UI, update etc + FCOORD pt, //here + INT32 *cmd_event_id, //Command selected + char *new_value); //Edited value + + const char *cmp_str() { //get ptr to name + return var->name_str (); + } + + void write_vars( //save config vars + FILE *fp, //in this file + BOOL8 changes_only); //Changed vars only? + + public: + STR_VAR_MENU_LEAF ( //constructor + STRING_VARIABLE * this_var //variable to edit + ): LEAF_MENU_NODE ("") { + var = this_var; + changed = FALSE; + } +}; + +/********************************************************************** + +Class VAR_NON_RADIO_MENU - A sub menu containing variables + +**********************************************************************/ + +class VAR_NON_RADIO_MENU:public NON_LEAF_MENU_NODE +{ + private: + void write_vars( //save config vars + FILE *fp, //in this file + BOOL8 changes_only); //Changed vars only? + + public: + VAR_NON_RADIO_MENU ( //constructor + const char *menu_text):NON_LEAF_MENU_NODE (menu_text) { + } + + void add_child( //add to sub-menu end //item to add + SIMPLE_MENU_LEAF *new_child) { + link_child(new_child); + } + + void add_child( //add to sub-menu end + VAR_SUB_MENU *new_child) { //item to add + link_child(new_child); + } + + void add_child( //add to sub-menu end + MENU_L *new_child) { //item to add + link_child_link(new_child); + } +}; +#endif diff --git a/display/varblwin.cpp b/display/varblwin.cpp new file mode 100644 index 0000000000..f0cd1d3b17 --- /dev/null +++ b/display/varblwin.cpp @@ -0,0 +1,279 @@ +/********************************************************************** + * File: varblwin.cpp (Formerly varwin.c) + * Description: Variables window subclass of COMMAND_WINDOW + * Author: Phil Cheatle + * Created: Thu Nov 14 15:40: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 "mfcpch.h" +#include +#include "sbdmenu.h" +#include "submen.h" +#include "varblwin.h" +#include "notdll.h" + +const ERRCODE NOT_ASSOCIATED = "Cant find associated VARIABLES_WINDOW"; + +ASSOCIATION_LIST +VARIABLES_WINDOW::win_assocs; + // for initialisation + +ELISTIZE (ASSOC) +/********************************************************************** + * + * ASSOCIATION_LIST class member functions + * + **********************************************************************/ +/********************************************************************** + * ASSOCIATION_LIST::add() + * + * Add a new association + **********************************************************************/ +void ASSOCIATION_LIST::add( //Window handle + WINDOW new_fd, + VARIABLES_WINDOW *new_var_win //Associated var windw + ) { + ASSOC_IT it(&associations); + + it.add_to_end (new ASSOC (new_fd, new_var_win)); +} + + +/********************************************************************** + * ASSOCIATION_LIST::remove() + * + * Delete an association + **********************************************************************/ + +void ASSOCIATION_LIST::remove( // delete assoc + WINDOW fd //Window handle + ) { + ASSOC_IT it(&associations); + + if (!it.empty ()) { + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + if (fd == it.data ()->fd) { + it.extract (); + return; + } + } + } + NOT_ASSOCIATED.error ("ASSOCIATION_LIST::remove", ABORT, NULL); +} + + +/********************************************************************** + * ASSOCIATION_LIST::lookup() + * + * Find an association + **********************************************************************/ + +VARIABLES_WINDOW *ASSOCIATION_LIST::lookup( //find assoc + WINDOW fd //Window handle + ) { + ASSOC_IT it(&associations); + + if (!it.empty ()) { + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + if (fd == it.data ()->fd) + return it.data ()->var_win; + } + } + NOT_ASSOCIATED.error ("ASSOCIATION_LIST::lookup", ABORT, NULL); + return NULL; +} + + +/********************************************************************** + * ASSOCIATION_LIST::plot_all() + * + * Re plot all windows ( to reflect changes to bool vars ) + **********************************************************************/ + +void ASSOCIATION_LIST::plot_all() { //plot all wins + ASSOC_IT it(&associations); + + if (!it.empty ()) { + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { + it.data ()->var_win->plot (); + } + } +} + + +/********************************************************************** + * ASSOCIATION_LIST::turn_off_interrupts() + * + * Disable interrupts for all windows of this class + **********************************************************************/ + +void ASSOCIATION_LIST::turn_off_interrupts() { + ASSOC_IT it(&associations); + + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) + //disable interrupts + set_click_handler (it.data ()->fd, NULL); +} + + +/********************************************************************** + * ASSOCIATION_LIST::turn_on_interrupts() + * + * Re-enable interrupts for all windows of this class + **********************************************************************/ + +void ASSOCIATION_LIST::turn_on_interrupts( //handler + EVENT_HANDLER interrupt_proc) { + ASSOC_IT it(&associations); + + for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) + set_click_handler (it.data ()->fd, interrupt_proc); +} + + +/********************************************************************** + * + * VARIABLES_WINDOW class member functions + * + **********************************************************************/ + +/********************************************************************** + * VARIABLES_WINDOW::event() + * + * Do non standard event handling for specified commands + **********************************************************************/ + +void VARIABLES_WINDOW::v_event( //Process event + GRAPHICS_EVENT &g_event) { + INT32 cmd_event; //Command event type + char new_value[80]; //of menu item + char buff[80]; + + COMMAND_WINDOW::event(g_event, &cmd_event, new_value); + + switch (cmd_event) { + case NULL_COMMAND: + break; + case WRITE_ALL_CMD: + write_vars(new_value, FALSE); + break; + case WRITE_CHANGED_CMD: + write_vars(new_value, TRUE); + break; + case KILL_WINDOW_CMD: + win_assocs.remove (g_event.fd); + destroy_window (g_event.fd); + my_creator->child_closed (); + delete this; + break; + default: + sprintf (buff, "UNPROCESSED EVENT code " INT32FORMAT, cmd_event); + msg(buff); + } +} + + +/********************************************************************** + * VARIABLES_WINDOW::internal_prompt() + * + * Disable interrupts during prompting + * + **********************************************************************/ + +BOOL8 VARIABLES_WINDOW::internal_prompt( //Prompt user + const char *msg_str, //Prompt message + char *response_str //Response & Default + ) { + BOOL8 result; + + win_assocs.turn_off_interrupts (); + result = COMMAND_WINDOW::internal_prompt (msg_str, response_str); + win_assocs.turn_on_interrupts (VARIABLES_WINDOW::interrupt_handler); + set_click_handler(fd, VARIABLES_WINDOW::interrupt_handler); + return result; +} + + +/********************************************************************** + * VARIABLES_WINDOW::interrupt_handler() + * + * Forward the event to the VARIABLES_WINDOW responsible for handling events + * for the window where the event occured. + **********************************************************************/ + +void VARIABLES_WINDOW::interrupt_handler( //for any windw + GRAPHICS_EVENT *g_event //event recieved + ) { + (win_assocs.lookup (g_event->fd))->v_event (*g_event); + //forward to + overlap_picture_ops(TRUE); +} + + +/********************************************************************** + * VARIABLES_WINDOW::VARIABLES_WINDOW() + * + * Constructor for a variables window + **********************************************************************/ + +VARIABLES_WINDOW::VARIABLES_WINDOW ( + //constructor +const char *name, //window name +MENU_ROOT * menu_root, //menu +VAR_SUB_MENU * creator //who created me? +): +COMMAND_WINDOW(name, menu_root) { + win_assocs.add (fd, this); + my_creator = creator; + set_click_handler(fd, VARIABLES_WINDOW::interrupt_handler); +} + + +/********************************************************************** + * VARIABLES_WINDOW::write_vars() + * + * Write the variables to a config file + **********************************************************************/ + +void VARIABLES_WINDOW::write_vars( //Build config file + char *filename, // in this file + BOOL8 changes_only // Changed vars only? + ) { + FILE *fp; //input file + char msg_str[MAX_CHARS + 1]; + char response_str[MAX_CHARS + 1]; + char *token; //first response token + + //if file exists + if ((fp = fopen (filename, "r")) != NULL) { + fclose(fp); + sprintf (msg_str, "Overwrite file " "%s" "? (Y/N)", filename); + response_str[0] = '\0'; + if (!prompt (msg_str, response_str)) + return; + token = strtok (response_str, " "); + if (tolower (token[0]) != 'y') + return; // dont write + } + + fp = fopen (filename, "w"); //can we write to it? + if (fp == NULL) { + sprintf (msg_str, "Cant write to file " "%s" "", filename); + msg(msg_str); + return; + } + menu_root->write_vars (fp, changes_only); + fclose(fp); +} diff --git a/display/varblwin.h b/display/varblwin.h new file mode 100644 index 0000000000..900473a0fc --- /dev/null +++ b/display/varblwin.h @@ -0,0 +1,131 @@ +/********************************************************************** + * File: varblwin.h (Formerly varwin.h) + * Description: Variables window subclass of COMMAND_WINDOW + * Author: Phil Cheatle + * Created: Thu Nov 14 15:40: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 VARBLWIN_H +#define VARBLWIN_H + +#include "sbdmenu.h" +#include "notdll.h" +#include "notdll.h" + +#define KILL_WINDOW_CMD 1 +#define WRITE_ALL_CMD 2 +#define WRITE_CHANGED_CMD 3 + +class VARIABLES_WINDOW; //Fwd Decl + +/********************************************************************** + * + * ASSOC class + * + * Maintain an association between a window handle and the VARIABLES_WINDOW + * which manages events in that window. + **********************************************************************/ + +class ASSOC:public ELIST_LINK +{ + friend class ASSOCIATION_LIST; + + WINDOW fd; //Window handle + VARIABLES_WINDOW *var_win; //Associated var windw + + public: + ASSOC() { + } //contstructor + + ASSOC( //contstructor + WINDOW new_fd, //Window handle //Associated var windw + VARIABLES_WINDOW *new_var_win) { + fd = new_fd; + var_win = new_var_win; + } +}; + +ELISTIZEH (ASSOC) +/********************************************************************** + * + * ASSOCIATION_LIST class + * + * Maintain a list of associations between window handles and the + * VARIABLES_WINDOWs which manages events in those windows. + **********************************************************************/ +class ASSOCIATION_LIST +{ + ASSOC_LIST associations; //List of pairs + + public: + ASSOCIATION_LIST() { + }; + + void add(WINDOW new_fd, //Window handle //Associated var windw + VARIABLES_WINDOW *new_var_win); + + //Window handle + VARIABLES_WINDOW *lookup(WINDOW fd); + + void plot_all(); //Redisplay all wins + + void remove(WINDOW fd); //Window handle + + void turn_off_interrupts(); //for all windws listd + + void turn_on_interrupts( //for all windws listd //handler + EVENT_HANDLER interrupt_proc); +}; + +/********************************************************************** + * + * VARIABLES_WINDOW class + * + * A subclass of the basic COMMAND_WINDOW which is used for variables editor + * windows. The chief difference is that this window type awaits interrupts. + **********************************************************************/ + +class VARIABLES_WINDOW:public COMMAND_WINDOW +{ + private: + //fd -> var win assocs + static ASSOCIATION_LIST win_assocs; + VAR_SUB_MENU *my_creator; //tell it when I die + + void write_vars( //Build config file + char *filename, // in this file + BOOL8 changes_only); // Changed vars only? + + //for all windows + static void interrupt_handler(GRAPHICS_EVENT *g_event); + + void v_event( //for specified window + GRAPHICS_EVENT &g_event); + + public: + VARIABLES_WINDOW( //constructor + const char *name, //window name + MENU_ROOT *menu_root, + VAR_SUB_MENU *creator); + + BOOL8 internal_prompt( //Prompt user(No plot) + const char *prompt_str, //Prompt message + char *response); //Response & Default + + static void plot_all() { //Redisplay all vars + win_assocs.plot_all (); + } +}; +#endif diff --git a/doc/main.txt b/doc/main.txt new file mode 100644 index 0000000000..16a5598bd6 --- /dev/null +++ b/doc/main.txt @@ -0,0 +1,10 @@ +/*! + +\mainpage Overview of Tesseract OCR + +\section SecIntroduction Introduction + +TO BE WRITTEN + +*/ + diff --git a/image/Makefile.am b/image/Makefile.am new file mode 100644 index 0000000000..d22c42b481 --- /dev/null +++ b/image/Makefile.am @@ -0,0 +1,10 @@ +SUBDIRS = +AM_CPPFLAGS = -I$(top_srcdir)/ccutil + +EXTRA_DIST = \ + bitstrm.h imgbmp.h imgerrs.h img.h imgio.h imgs.h \ + imgtiff.h imgunpk.h + +noinst_LIBRARIES = libtesseract_image.a +libtesseract_image_a_SOURCES = \ + imgbmp.cpp imgio.cpp imgs.cpp imgtiff.cpp bitstrm.cpp diff --git a/image/Makefile.in b/image/Makefile.in new file mode 100644 index 0000000000..82fc322933 --- /dev/null +++ b/image/Makefile.in @@ -0,0 +1,531 @@ +# Makefile.in generated by automake 1.9.6 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005 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@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = .. +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +INSTALL = @INSTALL@ +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@ +subdir = image +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \ + $(top_srcdir)/config/ac_define_versionlevel.m4 \ + $(top_srcdir)/config/acinclude_custom.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(SHELL) $(top_srcdir)/config/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config_auto.h +CONFIG_CLEAN_FILES = +LIBRARIES = $(noinst_LIBRARIES) +AR = ar +ARFLAGS = cru +libtesseract_image_a_AR = $(AR) $(ARFLAGS) +libtesseract_image_a_LIBADD = +am_libtesseract_image_a_OBJECTS = imgbmp.$(OBJEXT) imgio.$(OBJEXT) \ + imgs.$(OBJEXT) imgtiff.$(OBJEXT) bitstrm.$(OBJEXT) +libtesseract_image_a_OBJECTS = $(am_libtesseract_image_a_OBJECTS) +DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/config/depcomp +am__depfiles_maybe = depfiles +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +CXXLD = $(CXX) +CXXLINK = $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) \ + -o $@ +SOURCES = $(libtesseract_image_a_SOURCES) +DIST_SOURCES = $(libtesseract_image_a_SOURCES) +RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \ + html-recursive info-recursive install-data-recursive \ + install-exec-recursive install-info-recursive \ + install-recursive installcheck-recursive installdirs-recursive \ + pdf-recursive ps-recursive uninstall-info-recursive \ + uninstall-recursive +ETAGS = etags +CTAGS = ctags +DIST_SUBDIRS = $(SUBDIRS) +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMDEP_FALSE = @AMDEP_FALSE@ +AMDEP_TRUE = @AMDEP_TRUE@ +AMTAR = @AMTAR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CXXRPOFLAGS = @CXXRPOFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +GNUWIN32_DIR = @GNUWIN32_DIR@ +HAVE_GNUWIN32_FALSE = @HAVE_GNUWIN32_FALSE@ +HAVE_GNUWIN32_TRUE = @HAVE_GNUWIN32_TRUE@ +HAVE_LIBTIFF_FALSE = @HAVE_LIBTIFF_FALSE@ +HAVE_LIBTIFF_TRUE = @HAVE_LIBTIFF_TRUE@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTIFF_CFLAGS = @LIBTIFF_CFLAGS@ +LIBTIFF_LIBS = @LIBTIFF_LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAINTAINER_MODE_FALSE = @MAINTAINER_MODE_FALSE@ +MAINTAINER_MODE_TRUE = @MAINTAINER_MODE_TRUE@ +MAKEINFO = @MAKEINFO@ +OBJEXT = @OBJEXT@ +OPTS = @OPTS@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_DATE = @PACKAGE_DATE@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PACKAGE_YEAR = @PACKAGE_YEAR@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +RPO_NO = @RPO_NO@ +RPO_YES = @RPO_YES@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +USING_CL_FALSE = @USING_CL_FALSE@ +USING_CL_TRUE = @USING_CL_TRUE@ +VERSION = @VERSION@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_RANLIB = @ac_ct_RANLIB@ +ac_ct_STRIP = @ac_ct_STRIP@ +am__fastdepCC_FALSE = @am__fastdepCC_FALSE@ +am__fastdepCC_TRUE = @am__fastdepCC_TRUE@ +am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@ +am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@ +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@ +datadir = @datadir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +SUBDIRS = +AM_CPPFLAGS = -I$(top_srcdir)/ccutil +EXTRA_DIST = \ + bitstrm.h imgbmp.h imgerrs.h img.h imgio.h imgs.h \ + imgtiff.h imgunpk.h + +noinst_LIBRARIES = libtesseract_image.a +libtesseract_image_a_SOURCES = \ + imgbmp.cpp imgio.cpp imgs.cpp imgtiff.cpp bitstrm.cpp + +all: all-recursive + +.SUFFIXES: +.SUFFIXES: .cpp .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 \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu image/Makefile'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu image/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 + +clean-noinstLIBRARIES: + -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) +libtesseract_image.a: $(libtesseract_image_a_OBJECTS) $(libtesseract_image_a_DEPENDENCIES) + -rm -f libtesseract_image.a + $(libtesseract_image_a_AR) libtesseract_image.a $(libtesseract_image_a_OBJECTS) $(libtesseract_image_a_LIBADD) + $(RANLIB) libtesseract_image.a + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bitstrm.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imgbmp.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imgio.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imgs.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imgtiff.Po@am__quote@ + +.cpp.o: +@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ $< + +.cpp.obj: +@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` +uninstall-info-am: + +# 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. +$(RECURSIVE_TARGETS): + @failcom='exit 1'; \ + for f in x $$MAKEFLAGS; do \ + case $$f in \ + *=* | --[!k]*);; \ + *k*) failcom='fail=yes';; \ + esac; \ + done; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + list='$(SUBDIRS)'; 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; \ + (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" + +mostlyclean-recursive clean-recursive distclean-recursive \ +maintainer-clean-recursive: + @failcom='exit 1'; \ + for f in x $$MAKEFLAGS; do \ + case $$f in \ + *=* | --[!k]*);; \ + *k*) failcom='fail=yes';; \ + esac; \ + done; \ + dot_seen=no; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + rev=''; for subdir in $$list; do \ + if test "$$subdir" = "."; then :; else \ + rev="$$subdir $$rev"; \ + fi; \ + done; \ + rev="$$rev ."; \ + target=`echo $@ | sed s/-recursive//`; \ + for subdir in $$rev; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done && test -z "$$fail" +tags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \ + done +ctags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \ + done + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + 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 || \ + tags="$$tags $$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique; \ + fi +ctags: CTAGS +CTAGS: ctags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(CTAGS_ARGS)$$tags$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +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)'; for file in $$list; do \ + case $$file in \ + $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \ + $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \ + esac; \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + dir="/$$dir"; \ + $(mkdir_p) "$(distdir)$$dir"; \ + else \ + dir=''; \ + fi; \ + if test -d $$d/$$file; then \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$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 \ + test -d "$(distdir)/$$subdir" \ + || $(mkdir_p) "$(distdir)/$$subdir" \ + || exit 1; \ + distdir=`$(am__cd) $(distdir) && pwd`; \ + top_distdir=`$(am__cd) $(top_distdir) && pwd`; \ + (cd $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$top_distdir" \ + distdir="$$distdir/$$subdir" \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-recursive +all-am: Makefile $(LIBRARIES) +installdirs: installdirs-recursive +installdirs-am: +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: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_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-recursive + +clean-am: clean-generic clean-noinstLIBRARIES mostlyclean-am + +distclean: distclean-recursive + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +info: info-recursive + +info-am: + +install-data-am: + +install-exec-am: + +install-info: install-info-recursive + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-compile mostlyclean-generic + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: uninstall-info-am + +uninstall-info: uninstall-info-recursive + +.PHONY: $(RECURSIVE_TARGETS) CTAGS GTAGS all all-am check check-am \ + clean clean-generic clean-noinstLIBRARIES clean-recursive \ + ctags ctags-recursive distclean distclean-compile \ + distclean-generic distclean-recursive distclean-tags distdir \ + dvi dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-exec install-exec-am \ + install-info install-info-am install-man install-strip \ + installcheck installcheck-am installdirs installdirs-am \ + maintainer-clean maintainer-clean-generic \ + maintainer-clean-recursive mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-recursive pdf pdf-am ps ps-am \ + tags tags-recursive uninstall uninstall-am uninstall-info-am + +# 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: diff --git a/image/bitstrm.cpp b/image/bitstrm.cpp new file mode 100644 index 0000000000..28f68f665d --- /dev/null +++ b/image/bitstrm.cpp @@ -0,0 +1,157 @@ +/********************************************************************** + * File: bitstrm.c (Formerly bits.c) + * Description: Bitstream read/write class member functions. + * Author: Ray Smith + * Created: Tue Feb 19 10:59:44 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 "mfcpch.h" //precompiled headers +#ifdef __MSW32__ +#include +#else +#include +#endif +#include "fileerr.h" +#include "bitstrm.h" + +const UINT16 +R_BITSTREAM::bitmasks[17] = { + 0, 1, 3, 7, 15, 31, 63, 127, 255, + 511, 1023, 2047, 4095, 8191, 16383, 32767, 65535 +}; + +/********************************************************************** + * R_BITSTREAM::open + * + * Establish a bitstream for reading. + **********************************************************************/ + +UINT16 R_BITSTREAM::open( //open for read + int fd //file to read + ) { + bitfd = fd; + bufsize = read (fd, (char *) bitbuf, BITBUFSIZE * sizeof (UINT8)); + //fill buffer + if (bufsize < 0) { + READFAILED.error ("R_BITSTREAM::open", LOG, NULL); + return 0; + } + bitword = bitbuf[0] | (bitbuf[1] << 8); + bitindex = 2; + bitbit = 16; + return (UINT16) bitword; +} + + +/********************************************************************** + * R_BITSTREAM::read_code + * + * Remove a code from the bitstream. + **********************************************************************/ + +UINT16 R_BITSTREAM::read_code( //take code out + UINT8 length //length of code + ) { + bitbit -= length; //no of bits left + bitword >>= length; //remove bits + while (bitbit < 16) { + //get next byte + bitword |= bitbuf[bitindex++] << bitbit; + bitbit += 8; + if (bitindex >= bufsize) { + bufsize = + read (bitfd, (char *) bitbuf, BITBUFSIZE * sizeof (UINT8)); + if (bufsize < 0) { + READFAILED.error ("R_BITSTREAM::read_code", LOG, NULL); + return 0; + } + bitindex = 0; //newly filled buffer + } + } + return (UINT16) bitword; +} + + +/********************************************************************** + * R_BITSTREAM::masks + * + * Read a code from the static member. + **********************************************************************/ + +UINT16 R_BITSTREAM::masks( //take code out + INT32 index //length of code + ) { + return bitmasks[index]; +} + + +/********************************************************************** + * W_BITSTREAM::open + * + * Establish a bitstream for writing. + **********************************************************************/ + +void W_BITSTREAM::open( //open for write + int fd //file to write + ) { + bitfd = fd; + bitindex = 0; + bitword = 0; + bitbit = 0; +} + + +/********************************************************************** + * W_BITSTREAM::write_code + * + * Add a code to the bitstream. + **********************************************************************/ + +INT8 W_BITSTREAM::write_code( //take code out + UINT16 code, //code to add + UINT8 length //length of code + ) { + if (length == 0) { + //flushing + if (bitbit > 0) + //get last byte + bitbuf[bitindex++] = (UINT8) bitword; + if ((bitindex > 0) && + (write (bitfd, (char *) bitbuf, bitindex * sizeof (UINT8)) != + (INT32) (bitindex * sizeof (UINT8)))) { + WRITEFAILED.error ("W_BITSTREAM::write_code", LOG, "Flushing"); + return -1; + } + } + else { + bitword |= code << bitbit; //add new code + bitbit += length; + while (bitbit >= 8) { + //get next byte + bitbuf[bitindex++] = (UINT8) bitword; + bitbit -= 8; + bitword >>= 8; + if (bitindex >= BITBUFSIZE) { + if (write (bitfd, (char *) bitbuf, bitindex * sizeof (UINT8)) + != (INT32) (bitindex * sizeof (UINT8))) { + WRITEFAILED.error ("W_BITSTREAM::write_code", LOG, NULL); + return -1; + } + bitindex = 0; //newly filled buffer + } + } + } + return 0; //success +} diff --git a/image/bitstrm.h b/image/bitstrm.h new file mode 100644 index 0000000000..c93383e382 --- /dev/null +++ b/image/bitstrm.h @@ -0,0 +1,73 @@ +/********************************************************************** + * File: bitstrm.h (Formerly bits.h) + * Description: R_BITSTREAM and W_BITSTREAM class definitions. + * Author: Ray Smith + * Created: Tue Feb 19 10:44:22 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 BITSTRM_H +#define BITSTRM_H + +#include "host.h" + +#define BITBUFSIZE 8192 //bitstream buffer + +class DLLSYM R_BITSTREAM +{ + private: + int bitfd; //file descriptor + INT32 bitindex; //current byte + UINT32 bitword; //current word + INT32 bitbit; //current bit + INT32 bufsize; //size of buffer + UINT8 bitbuf[BITBUFSIZE]; //bitstream buffer + //for reading codes + static const UINT16 bitmasks[17]; + + public: + + R_BITSTREAM() { + }; //Null constructor + + UINT16 open( //open to read + int fd); //file to read + + UINT16 read_code( //read a code + UINT8 length); //bits to lose + UINT16 masks( //read a code + INT32 index); //bits to lose +}; + +class DLLSYM W_BITSTREAM +{ + private: + int bitfd; //file descriptor + INT32 bitindex; //current byte + UINT32 bitword; //current word + INT32 bitbit; //current bit + UINT8 bitbuf[BITBUFSIZE]; //bitstream buffer + + public: + W_BITSTREAM() { + }; //Null constructor + + void open( //open to write + int fd); //file to write + + INT8 write_code( //write a code + UINT16 code, //code to write + UINT8 length); //bits to lose +}; +#endif diff --git a/image/img.h b/image/img.h new file mode 100644 index 0000000000..7e3e2cf08c --- /dev/null +++ b/image/img.h @@ -0,0 +1,329 @@ +/********************************************************************** + * File: img.h (Formerly image.h) + * Description: Class definition for the IMAGE class. + * Author: Ray Smith + * Created: Thu Jun 07 13:42:37 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 IMG_H +#define IMG_H + +#include "memry.h" + +#define MAXIMAGEWIDTH (900*14) /*14inch * 400dpi */ + /*14inch * 400dpi */ +#define MAXIMAGEHEIGHT (900*14) + +#define COMPUTE_IMAGE_XDIM(xsize,bpp) ((bpp)>8 ? ((xsize)*(bpp)+7)/8 :((xsize)+8/(bpp)-1)/(8/(bpp))) + +typedef INT8 (*IMAGE_OPENER) (int, INT32 *, INT32 *, INT8 *, INT8 *, INT32 *); +typedef INT8 (*IMAGE_READER) (int, UINT8 *, INT32, INT32, INT8, INT32); +typedef INT8 (*IMAGE_WRITER) (int, UINT8 *, INT32, INT32, INT8, INT8, INT32); + +typedef UINT8 *COLOUR_PIX; //array of colours +enum COLOUR_PIX_NAME +{ + RED_PIX, + GREEN_PIX, + BLUE_PIX +}; + +class DLLSYM IMAGELINE; + +class DLLSYM IMAGE //encapsulated image +{ + public: + IMAGE(); //constructor + + ~IMAGE () { //destructor + destroy(); //free memory + } + + IMAGE & operator= ( //assignment + IMAGE & source); + + INT8 read_header( //get file header + const char *name); //name of image + + INT8 read( //get rest of image + INT32 buflines); //size of buffer + + INT8 write( //write image + const char *name); //name to write + + INT8 create( //create blank image + INT32 x, //x size required + INT32 y, //ysize required + INT8 bits_per_pixel); //bpp required + + INT8 capture( //capture raw image + UINT8 *pixels, //pixels to capture + INT32 x, //x size required + INT32 y, //ysize required + INT8 bits_per_pixel); //bpp required + + void destroy(); //destroy image + + INT32 get_xsize() { + return xsize; + } + //access function + INT32 get_ysize() { + return ysize; + } + //access function + INT8 get_bpp() { + return bpp; + } //access function + INT8 get_bps() { + return bps; + } //bits per sample + BOOL8 white_high() { //photo interp + return photo_interp; + } + UINT8 get_white_level() { //access function + return (1 << bpp) - 1; + } + INT32 get_res() { + return res; + } //access function + void set_res( //set resolution + INT32 resolution) { + res = resolution; + } + UINT8 *get_buffer() { + return image; + } + //access function + + UINT8 pixel( //access pixel + INT32 x, //x coord + INT32 y); //y coord + + void fast_get_line( //get image line + INT32 x, //coord to start at + INT32 y, //line to get + INT32 width, //no of pixels to get + IMAGELINE *linebuf); //line to copy to + + void get_line( //get image line + INT32 x, //coord to start at + INT32 y, //line to get + INT32 width, //no of pixels to get + IMAGELINE *linebuf, //line to copy to + INT32 margins); //size of margins + void get_column( //get image column + INT32 x, //coord to start at + INT32 y, //line to get + INT32 height, //no of pixels to get + IMAGELINE *linebuf, //line to copy to + INT32 margins); //size of margins + + void fast_put_line( //put image line + INT32 x, //coord to start at + INT32 y, //line to put + INT32 width, //no of pixels to put + IMAGELINE *linebuf); //line to copy from + + void put_line( //put image line + INT32 x, //coord to start at + INT32 y, //line to put + INT32 width, //no of pixels to put + IMAGELINE *linebuf, //line to copy from + INT32 margins); //size of margins + void put_column( //put image column + INT32 x, //coord to start at + INT32 y, //line to put + INT32 height, //no of pixels to put + IMAGELINE *linebuf, //line to copy to + INT32 margins); //size of margins + + void check_legal_access( //check coords + INT32 x, //xcoord to check + INT32 y, + INT32 xext); //ycoord to check + + void convolver ( //Map fn over window + INT32 win_width, //Window width + INT32 win_height, //Window height + void (*convolve) ( //Conv Function + UINT8 ** pixels, //Of window + UINT8 bytespp, //1 or 3 for colour + INT32 win_wd, //Window width + INT32 win_ht, //Window height + UINT8 ret_white_value, //White value to RETURN + UINT8 * result)); //Result pixel(s) + + //copy rectangle + friend DLLSYM void copy_sub_image(IMAGE *source, //source image + INT32 xstart, //start coords + INT32 ystart, + INT32 xext, //extent to copy + INT32 yext, + IMAGE *dest, //destination image + INT32 xdest, //destination coords //shift to match bpp + INT32 ydest, + BOOL8 adjust_grey); + + //enlarge rectangle + friend DLLSYM void enlarge_sub_image(IMAGE *source, //source image + INT32 xstart, //scaled coords + INT32 ystart, + IMAGE *dest, //destination image + INT32 xdest, //destination coords + INT32 ydest, + INT32 xext, //extent to copy + INT32 yext, + INT32 scale, //scale factor + BOOL8 adjust_grey); //shift to match bpp + + //reduce rectangle + friend DLLSYM void fast_reduce_sub_image(IMAGE *source, //source image + INT32 xstart, //start coords + INT32 ystart, + INT32 xext, //extent to copy + INT32 yext, + IMAGE *dest, //destination image + INT32 xdest, //destination coords + INT32 ydest, + INT32 scale, //scale factor + BOOL8 adjust_grey); //shift to match bpp + + //reduce rectangle + friend DLLSYM void reduce_sub_image(IMAGE *source, //source image + INT32 xstart, //start coords + INT32 ystart, + INT32 xext, //extent to copy + INT32 yext, + IMAGE *dest, //destination image + INT32 xdest, //destination coords + INT32 ydest, + INT32 scale, //scale factor + BOOL8 adjust_grey); //shift to match bpp + + private: + INT8 bpp; //bits per pixel + INT8 bps; //bits per sample + INT8 bytespp; //per pixel + INT8 lineskip; //waste bytes on line + BOOL8 captured; //true if buffer captured + INT8 photo_interp; //interpretation + INT32 xsize, ysize; //size of image + INT32 res; //resolution + UINT8 *image; //the actual image + INT32 xdim; //bytes per line + INT32 bufheight; //height of buffer + int fd; //open file descriptor + IMAGE_READER reader; //reading function + INT32 ymin; //bottom line in mem + INT32 ymax; //top line in mem+1 + INT8 bufread( //read some more + INT32 y); //ycoord required +}; + +class DLLSYM IMAGELINE //one line of image +{ + public: + UINT8 * pixels; //image pixels + INT8 bpp; //bits per pixel + COLOUR_PIX operator[] ( //colour pixels + INT32 index) { + return &pixels[index * 3]; //coercion access op + } + + IMAGELINE() { //default constructor + linewidth = 0; + line = NULL; + pixels = line; + bpp = 8; + } + void init( //setup size + INT32 width) { //size of line + if (width <= 0) + width = MAXIMAGEWIDTH; + if (width > linewidth) { + if (line != NULL) + free_mem(line); + linewidth = width; + line = (UINT8 *) alloc_mem (linewidth * sizeof (UINT8)); + } + pixels = line; + bpp = 8; + } + ~IMAGELINE () { //destructor + if (line != NULL) + free_mem(line); + } + + void set_bpp( //For colour + INT8 new_bpp) { + if (new_bpp <= 8) + bpp = 8; + else + bpp = 24; + } + + void init() { + if (line == NULL) + init (0); + else { + pixels = line; + bpp = 8; + } + } + + friend void IMAGE::get_line( //copies a line + INT32 x, //coord to start at + INT32 y, //line to get + INT32 width, //no of pixels to get + IMAGELINE *linebuf, //line to copy to + INT32 margins); //size of margins + //copies a column + friend void IMAGE::get_column(INT32 x, //coord to start at + INT32 y, //line to get + INT32 height, //no of pixels to get + IMAGELINE *linebuf, //line to copy to + INT32 margins); //size of margins + + friend void IMAGE::put_line( //writes a line + INT32 x, //coord to start at + INT32 y, //line to put + INT32 width, //no of pixels to put + IMAGELINE *linebuf, //line to copy from + INT32 margins); //size of margins + //writes a column + friend void IMAGE::put_column(INT32 x, //coord to start at + INT32 y, //line to put + INT32 height, //no of pixels to put + IMAGELINE *linebuf, //line to copy from + INT32 margins); //size of margins + + //may just change pointer + friend void IMAGE::fast_get_line(INT32 x, //coord to start at + INT32 y, //line to get + INT32 width, //no of pixels to get + IMAGELINE *linebuf); //line to copy to + + //may just change pointer + friend void IMAGE::fast_put_line(INT32 x, //coord to start at + INT32 y, //line to get + INT32 width, //no of pixels to get + IMAGELINE *linebuf); //line to copy to + + private: + UINT8 * line; //local buffer + INT32 linewidth; //width of buffer +}; +#endif diff --git a/image/imgbmp.cpp b/image/imgbmp.cpp new file mode 100644 index 0000000000..30bc001bbb --- /dev/null +++ b/image/imgbmp.cpp @@ -0,0 +1,223 @@ +/********************************************************************** + * File: imgbmp.c (Formerly lz.c) + * Description: bmp image reader/writer. + * Author: Ray Smith + * Created: Tue Jan 06 15:31:25 GMT 1998 + * + * (C) Copyright 1998, 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 "mfcpch.h" //precompiled headers +#ifdef __MSW32__ +#include +#else +#include +#endif +#include +#include "img.h" +#include "imgbmp.h" + +typedef struct +{ // bmfh + char bfType1; //'B' + char bfType2; //'M' +} BMPHEADER0; +typedef struct +{ // bmfh + UINT32 bfSize; //filesize + UINT16 bfReserved1; //zero + UINT16 bfReserved2; //zero + UINT32 bfOffBits; //offset to bitmap +} BMPHEADER; + +typedef struct +{ // bmih + UINT32 biSize; //size of struct + INT32 biWidth; //image width + INT32 biHeight; //image height + UINT16 biPlanes; //1 + UINT16 biBitCount; //bpp + UINT32 biCompression; //0 for uncompressed + UINT32 biSizeImage; //image size + INT32 biXPelsPerMeter; //res in pp metre + INT32 biYPelsPerMeter; + UINT32 biClrUsed; //0 or actual size of colour table + UINT32 biClrImportant; //usually 0 +} BMPHEADER2; + +typedef struct +{ // rgbq + UINT8 rgbBlue; + UINT8 rgbGreen; + UINT8 rgbRed; + UINT8 rgbReserved; //0 +} WIN32_RGBQUAD; + +/********************************************************************** + * open_bmp_image + * + * Read the header of a bmp format image and prepare to read the rest. + **********************************************************************/ + +INT8 open_bmp_image( //read header + int fd, //file to read + INT32 *xsize, //size of image + INT32 *ysize, + INT8 *bpp, //bits per pixel + INT8 *photo, + INT32 *res //resolution + ) { + UINT32 nread; //current bits + BMPHEADER0 head0; //first part of header + BMPHEADER head1; //first part of header + BMPHEADER2 head2; //first part of header + + *photo = 1; + nread = read (fd, &head0, sizeof (head0)); + if (nread != sizeof (head0)) + return -1; + nread = read (fd, &head1, sizeof (head1)); + if (nread != sizeof (head1)) + return -1; + nread = read (fd, &head2, sizeof (head2)); + if (nread != sizeof (head2)) + return -1; + + if (head0.bfType1 != 'B') + return -1; + if (head0.bfType2 != 'M') + return -1; + lseek (fd, head1.bfOffBits, SEEK_SET); + *bpp = head2.biBitCount; + *xsize = head2.biWidth; + *ysize = head2.biHeight; + *res = 300; //make up resolution + return -2; //success +} + + +/********************************************************************** + * read_bmp_image + * + * Read a whole lz format image and close the file. + **********************************************************************/ + +INT8 read_bmp_image( //read header + int fd, //file to read + UINT8 *pixels, //pixels of image + INT32 xsize, //size of image + INT32 ysize, + INT8 bpp, //bits per pixel + INT32 //bytes per line + ) { + UINT32 bpl; //bytes per line + UINT32 wpl; //words per line + UINT32 nread; //current bits + INT32 index; //to cols + + bpl = (xsize * bpp + 7) / 8; //bytes per line + wpl = (bpl + 3) / 4; + wpl *= 4; + for (index = 0; index < ysize; index++) { + nread = read (fd, pixels + bpl * (ysize - 1 - index), bpl); + if (nread != bpl) + return -1; + if (wpl != bpl) + lseek (fd, wpl - bpl, SEEK_CUR); + } + return 0; +} + + +/********************************************************************** + * write_bmp_image + * + * Write a whole lz format image and close the file. + **********************************************************************/ + +INT8 write_bmp_image( //write whole image + int fd, //file to write on + UINT8 *pixels, //image pixels + INT32 xsize, //size of image + INT32 ysize, + INT8 bpp, //bits per pixel + INT8, + INT32 res //resolution + ) { + UINT32 bpl; //bytes per line + UINT32 wpl; //words per line + UINT32 nread; //current bits + INT32 cols; //entries in table + INT32 index; //to cols + BMPHEADER0 head0; //first part of header + BMPHEADER head1; //first part of header + BMPHEADER2 head2; //first part of header + WIN32_RGBQUAD coltab[256]; //colour table + + if (bpp == 24) + cols = 0; + else + cols = 1 << bpp; //size of colour table + bpl = (xsize * bpp + 7) / 8; //bytes per line + wpl = (bpl + 3) / 4; + + head2.biSize = sizeof (head2); //size of struct + head2.biWidth = xsize; //image width + head2.biHeight = ysize; //image height + head2.biPlanes = 1; //1 + head2.biBitCount = bpp; //bpp + head2.biCompression = 0; //0 for uncompressed + //image size + head2.biSizeImage = wpl * 4 * ysize; + //res in pp metre + head2.biXPelsPerMeter = (UINT32) (res * 39.37); + head2.biYPelsPerMeter = (UINT32) (res * 39.37); + head2.biClrUsed = cols; //0 or actual size of colour table + head2.biClrImportant = 0; //usually 0 + + head0.bfType1 = 'B'; + head0.bfType2 = 'M'; + head1.bfReserved1 = 0; //zero + head1.bfReserved2 = 0; //zero + //offset to bitmap + head1.bfOffBits = sizeof (head0) + sizeof (head1) + sizeof (head2) + sizeof (WIN32_RGBQUAD) * cols; + //filesize + head1.bfSize = head1.bfOffBits + head2.biSizeImage; + + for (index = 0; index < cols; index++) { + coltab[index].rgbBlue = index * 255 / (cols - 1); + coltab[index].rgbGreen = coltab[index].rgbBlue; + coltab[index].rgbRed = coltab[index].rgbBlue; + coltab[index].rgbReserved = 0; + } + + nread = write (fd, &head0, sizeof (head0)); + if (nread != sizeof (head0)) + return -1; + nread = write (fd, &head1, sizeof (head1)); + if (nread != sizeof (head1)) + return -1; + nread = write (fd, &head2, sizeof (head2)); + if (nread != sizeof (head2)) + return -1; + nread = write (fd, coltab, cols * sizeof (WIN32_RGBQUAD)); + if (nread != cols * sizeof (WIN32_RGBQUAD)) + return -1; + for (index = 0; index < ysize; index++) { + nread = write (fd, pixels + bpl * (ysize - 1 - index), wpl * 4); + if (nread != wpl * 4) + return -1; + } + close(fd); //done it + return 0; +} diff --git a/image/imgbmp.h b/image/imgbmp.h new file mode 100644 index 0000000000..69f08a8f81 --- /dev/null +++ b/image/imgbmp.h @@ -0,0 +1,50 @@ +/********************************************************************** + * File: imgbmp.c (Formerly lz.c) + * Description: bmp image reader/writer. + * Author: Ray Smith + * Created: Tue Jan 06 20:15:52 GMT 1998 + * + * (C) Copyright 1998, 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 IMGBMP_H +#define IMGBMP_H + +#include "host.h" + +INT8 open_bmp_image( //read header + int fd, //file to read + INT32 *xsize, //size of image + INT32 *ysize, + INT8 *bpp, //bits per pixel + INT8 *photo, + INT32 *res //resolution + ); +INT8 read_bmp_image( //read header + int fd, //file to read + UINT8 *pixels, //pixels of image + INT32 xsize, //size of image + INT32 ysize, + INT8 bpp, //bits per pixel + INT32 //bytes per line + ); +INT8 write_bmp_image( //write whole image + int fd, //file to write on + UINT8 *pixels, //image pixels + INT32 xsize, //size of image + INT32 ysize, + INT8 bpp, //bits per pixel + INT8, + INT32 //resolution + ); +#endif diff --git a/image/imgerrs.h b/image/imgerrs.h new file mode 100644 index 0000000000..00e9f6672f --- /dev/null +++ b/image/imgerrs.h @@ -0,0 +1,35 @@ +/********************************************************************** + * File: imgerrs.h (Formerly imgerr.h) + * Description: Definitions of errors related to IMAGE operations. + * Author: Ray Smith + * Created: Tue Aug 14 10:10:53 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 IMGERRS_H +#define IMGERRS_H + +#include "errcode.h" + +const ERRCODE BADIMAGETYPE = "Unrecognized image type"; +const ERRCODE CANTREADIMAGETYPE = "Can't read this image type"; +const ERRCODE CANTWRITEIMAGETYPE = "Can't write this image type"; +const ERRCODE IMAGEUNDEFINED = "Attempt to operate on undefined image"; +const ERRCODE BADIMAGECOORDS = "Coordinates in image out of bounds"; +const ERRCODE BADIMAGESEEK = "Can't seek backwards in a buffered image!"; +const ERRCODE BADIMAGESIZE = "Illegal image size"; +const ERRCODE BADIMAGEFORMAT = "Illegal image format"; +const ERRCODE BADBPP = "Only 1,2,4,5,6,8 bpp are supported"; +const ERRCODE BADWINDOW = "Convolution window must have odd dimensions"; +#endif diff --git a/image/imgio.cpp b/image/imgio.cpp new file mode 100644 index 0000000000..49054cd47b --- /dev/null +++ b/image/imgio.cpp @@ -0,0 +1,321 @@ +/********************************************************************** + * File: imgio.c (Formerly imageio.c) + * Description: Controls image input/output and selection of format. + * Author: Ray Smith + * Created: Mon Jun 11 11:47:26 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 "mfcpch.h" //precompiled headers +#ifdef __MSW32__ +#include +#else +#include +#endif +#include +#include +#include +#include +#include "scanutils.h" +#include "stderr.h" +#include "fileerr.h" +#include "imgerrs.h" +#include "memry.h" +#include "imgs.h" +#include "imgbmp.h" +#include "imgtiff.h" +#include "imgio.h" + +#define DEFAULTIMAGETYPE "tif" //default to im files + +typedef struct +{ + const char *string; //extension + IMAGE_OPENER opener; //opening function + IMAGE_READER reader; //reading function + IMAGE_WRITER writer; //writing function +} IMAGETYPE; //image type record + +static IMAGETYPE imagetypes[] = { { + "TIF", + open_tif_image, + read_tif_image, + write_moto_tif + }, + { + "itf", + open_tif_image, + read_tif_image, + write_inverse_tif + }, + { + "tif", + open_tif_image, + read_tif_image, + write_intel_tif + }, + { + "bmp", + open_bmp_image, + read_bmp_image, + write_bmp_image + }, +}; //image readers/writers + +#define MAXIMAGETYPES (sizeof(imagetypes)/sizeof(IMAGETYPE)) + +/********************************************************************** + * name_to_image_type + * + * Convert a file name to an image type, picking defaults if it is + * has no extension, and complaining if the extension is not supported. + **********************************************************************/ + +static INT8 name_to_image_type( //get image type + const char *name //name of image + ) { + const char *nametype; //type part of name + INT8 type; //imagetypes index + + nametype = strrchr (name, '.');//find extension + if (nametype != NULL) + nametype++; //ptr to extension + else + nametype = DEFAULTIMAGETYPE; //had none + + //find type of image + for (type = 0; type < MAXIMAGETYPES && strcmp (imagetypes[type].string, nametype); type++); + if (type >= MAXIMAGETYPES) { + //unrecognized type + BADIMAGETYPE.error ("name_to_image_type", LOG, name); + return -1; + } + return type; +} + + +/********************************************************************** + * read_header + * + * Read the header of an image, typed according to the extension of + * the name. Return is 0 for success, -1 for failure. + **********************************************************************/ + +INT8 IMAGE::read_header( //get file header + const char *name //name of image + ) { + INT8 type; //image type + + destroy(); //destroy old image + //get type + type = name_to_image_type (name); + if (type < 0 || imagetypes[type].opener == NULL) { + CANTREADIMAGETYPE.error ("IMAGE::read_header", LOG, name); + return -1; //read not supported + } + #ifdef __UNIX__ + if ((fd = open (name, O_RDONLY)) < 0) + #endif + #if defined (__MSW32__) || defined (__MAC__) + if ((fd = open (name, O_RDONLY | O_BINARY)) < 0) + #endif + { + CANTOPENFILE.error ("IMAGE::read_header", LOG, name); + return -1; //failed + } + lineskip = + (*imagetypes[type].opener) (fd, &xsize, &ysize, &bpp, &photo_interp, + &res); + if (lineskip == -1) { + //get header + bpp = 0; //still empty + close(fd); + fd = -1; + return -1; //failed + } + if (res <= 0) + res = image_default_resolution; + // fprintf(stderr,"Image size=(%d,%d), bpp=%d\n", + // xsize,ysize,bpp); + //bytes per line + xdim = COMPUTE_IMAGE_XDIM (xsize, bpp); + bps = bpp == 24 ? 8 : bpp; + bytespp = (bpp + 7) / 8; + //funtion to read with + reader = imagetypes[type].reader; + return 0; //success +} + + +/********************************************************************** + * read + * + * Read a previously opened image file into memory. + * If buflines is 0, the whole image is read in one go. + * If buflines>0, memory space is reserved for reading just that many + * lines at once. + * As soon as a request is made to get a line past the end of the buffer, + * the buffer is re-read with a 50% overlap. + * Backward seeks are not allowed. + * Read returns -1 in case of failure or 0 if successful. + **********************************************************************/ + +INT8 IMAGE::read( //get rest of image + INT32 buflines //size of buffer + ) { + INT32 row; //image row + BOOL8 failed; //read failed + + if (fd < 0 || image != NULL) + IMAGEUNDEFINED.error ("IMAGE::read", ABORT, NULL); + + if (buflines <= 0 || buflines > ysize || reader == NULL) + buflines = ysize; //default to all + bufheight = buflines; + image = + (UINT8 *) alloc_big_mem ((size_t) (xdim * bufheight * sizeof (UINT8))); + if (image == NULL) { + MEMORY_OUT.error ("IMAGE::read", LOG, NULL); + destroy(); + return -1; + } + captured = FALSE; + ymax = ysize; + ymin = ysize - buflines; //amount of image read + if (reader != NULL && lineskip < 0) + failed = (*reader) (fd, image, xsize, ysize, bpp, xdim) < 0; + else { + if (lineskip == 0) + failed =::read (fd, (char *) image, + (size_t) (xdim * bufheight)) != xdim * bufheight; + else { + for (failed = FALSE, row = 0; row < bufheight && !failed; row++) { + failed =::read (fd, (char *) image + row * xdim, + (size_t) xdim) != xdim; + failed |= lseek (fd, lineskip, SEEK_CUR) < 0; + } + } + } + if (failed) { + READFAILED.error ("IMAGE::read", LOG, NULL); + destroy(); + return -1; //read failed + } + if (ymin <= 0) { + close(fd); //finished reading + fd = -1; //not open now + } + return 0; //success +} + + +/********************************************************************** + * bufread + * + * Read a bit more of an image into the buffer. + **********************************************************************/ + +INT8 IMAGE::bufread( //read more into buffer + INT32 y //required coord + ) { + INT32 readtop; //no of lines copied + INT32 linestoread; //no of lines to read + INT32 row; //row to read + BOOL8 failed; //read failed + + //copy needed? + if (y + bufheight / 2 >= ymin) { + //no of lines to move + readtop = y + bufheight / 2 - ymin + 1; + //copy inside it + copy_sub_image (this, 0, ymin, xsize, readtop, this, 0, ymax - readtop, TRUE); + } + else + readtop = 0; + ymax = y + bufheight / 2; //new top of image + ymin = ymax - bufheight; //possible bottom + if (ymin < 0) + ymin = 0; //clip to image size + linestoread = ymax - ymin - readtop; + if (lineskip == 0) + failed =::read (fd, (char *) (image + xdim * readtop), + (size_t) (xdim * linestoread)) != xdim * linestoread; + else { + for (failed = FALSE, row = 0; row < linestoread && !failed; row++) { + failed =::read (fd, (char *) (image + (readtop + row) * xdim), + (size_t) xdim) != xdim; + failed |= lseek (fd, lineskip, SEEK_CUR) < 0; + } + } + if (failed) { + READFAILED.error ("IMAGE::bufread", LOG, NULL); + return -1; //read failed + } + if (ymin <= 0) { + close(fd); //finished reading + fd = -1; //not open now + } + return 0; //success +} + + +/********************************************************************** + * write + * + * Write an image to a file in a format determined by the name. + **********************************************************************/ + +INT8 IMAGE::write( //write image + const char *name //name to write + ) { + INT8 type; //type of image + + if (bpp == 0 || image == NULL || bufheight != ysize) + IMAGEUNDEFINED.error ("IMAGE::write", ABORT, NULL); + if (fd >= 0) { + close(fd); //close old file + fd = -1; //no longer open + } + //get image type + type = name_to_image_type (name); + if (type < 0 || imagetypes[type].writer == NULL) { + CANTWRITEIMAGETYPE.error ("IMAGE::write", LOG, name); + return -1; //write not supported + } + #ifdef __UNIX__ + if ((fd = creat (name, 0666)) < 0) + #endif + #ifdef __MSW32__ + if ((fd = open (name, _O_CREAT | _O_WRONLY | _O_BINARY, _S_IWRITE)) < 0) + #endif + #ifdef __MAC__ + if ((fd = creat (name, O_WRONLY | O_BINARY)) < 0) + #endif + { + CANTCREATEFILE.error ("IMAGE::write", LOG, name); + return -1; //failed + } + if (res <= 0) + res = image_default_resolution; + if ((*imagetypes[type].writer) (fd, image, xsize, ysize, bpp, photo_interp, + res) < 0) { + //get header + //write failed + WRITEFAILED.error ("IMAGE::write", LOG, name); + close(fd); + fd = -1; + return -1; //failed + } + return 0; //success +} diff --git a/image/imgio.h b/image/imgio.h new file mode 100644 index 0000000000..5c304517cc --- /dev/null +++ b/image/imgio.h @@ -0,0 +1,22 @@ +/********************************************************************** + * File: imgio.h (Formerly imageio.h) + * Description: Header file for image input/output functions. + * Author: Ray Smith + * Created: Mon Jun 11 12:55:34 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 IMGIO_H +#define IMGIO_H +#endif diff --git a/image/imgs.cpp b/image/imgs.cpp new file mode 100644 index 0000000000..c5d3a7c298 --- /dev/null +++ b/image/imgs.cpp @@ -0,0 +1,1619 @@ +/********************************************************************** + * File: imgs.c (Formerly images.c) + * Description: Main image manipulation functions. + * Author: Ray Smith + * Created: Thu Jun 07 16:25:02 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 "mfcpch.h" //precompiled headers +#ifdef __MSW32__ +#include +#else +#include +#endif +#include +#ifdef __UNIX__ +#include +#endif +#include "stderr.h" +#include "tprintf.h" +#include "imgerrs.h" +#include "memry.h" +#include "imgs.h" +#include "imgio.h" +#include "imgunpk.h" + +#define FIXED_COLOURS 32 /*number of fixed colours */ +#define MIN_4BIT 48 /*4bpp range */ +#define MAX_4BIT 64 +#define MIN_6BIT 64 /*6bpp range */ +#define MAX_6BIT 128 +#define BLACK_PIX 0 + +static UINT8 grey_scales[FIXED_COLOURS] = { + 0, 255, 76, 227, 151, 179, 28, 104, + 149, 72, 215, 67, 53, 44, 156, 137, + 110, 153, 79, 181, 166, 218, 55, 81, + 129, 105, 179, 149, 168, 69, 84, 126 +}; + +#define EXTERN + +EXTERN INT_VAR (image_default_resolution, 300, "Image resolution dpi"); + +/********************************************************************** + * IMAGE + * + * Contructor for an IMAGE class. Makes the image definitely illegal. + **********************************************************************/ + +IMAGE::IMAGE() { //construct an image + bpp = 0; //all illegal + fd = -1; + image = NULL; + photo_interp = 1; + res = image_default_resolution; +} + + +/********************************************************************** + * IMAGE::operator= + * + * Assign an IMAGE to another. The dest becomes the owner of the memory. + **********************************************************************/ + +IMAGE & IMAGE::operator= ( //assignment +IMAGE & source //source image +) { + destroy(); + bpp = source.bpp; + photo_interp = source.photo_interp; + bps = source.bps; + bytespp = (bpp + 7) / 8; + lineskip = source.lineskip; //copy everything + captured = source.captured; + xsize = source.xsize; + ysize = source.ysize; + res = source.res; + image = source.image; + xdim = source.xdim; + bufheight = source.bufheight; + fd = source.fd; + reader = source.reader; + ymin = source.ymin; + ymax = source.ymax; + + source.captured = TRUE; //source now captured + source.fd = -1; + + return *this; +} + + +/********************************************************************** + * create + * + * Create an image (allocate memory) of a specific size and bpp. + **********************************************************************/ + +INT8 IMAGE::create( //get rest of image + INT32 x, //x size required + INT32 y, //ysize required + INT8 bits_per_pixel //bpp required + ) { + UINT8 *pixels; //memory for image + + xdim = check_legal_image_size (x, y, bits_per_pixel); + if (xdim < 0) + return -1; + pixels = (UINT8 *) alloc_big_zeros ((size_t) (xdim * y * sizeof (UINT8))); + if (pixels == NULL) { + MEMORY_OUT.error ("IMAGE::create", ABORT, "Size=(%d,%d)", xdim, y); + return -1; + } + //allocate to image + this->capture (pixels, x, y, bits_per_pixel); + captured = FALSE; + res = image_default_resolution; + return 0; //success +} + + +/********************************************************************** + * destroy + * + * Destroy an image, freeing memory and closing any open file. + **********************************************************************/ + +void IMAGE::destroy() { //get rid of image + if (image != NULL && !captured) { + free_big_mem(image); + } + image = NULL; + if (fd >= 0) { + close(fd); + fd = -1; + } + bpp = 0; +} + + +/********************************************************************** + * capture + * + * Assign a given memory area to an image to use as an image of + * given size and bpp. + **********************************************************************/ + +INT8 IMAGE::capture( //get rest of image + UINT8 *pixels, //image memory + INT32 x, //x size required + INT32 y, //ysize required + INT8 bits_per_pixel //bpp required + ) { + destroy(); + xdim = check_legal_image_size (x, y, bits_per_pixel); + if (xdim < 0) + return -1; + xsize = x; + ysize = y; + bufheight = y; + bpp = bits_per_pixel; + bps = bpp == 24 ? 8 : bpp; + photo_interp = 1; + bytespp = (bpp + 7) / 8; + image = pixels; //assign image area + ymin = 0; + ymax = bufheight; //read it all + captured = TRUE; + res = image_default_resolution; + return 0; //success +} + + +/********************************************************************** + * pixel + * + * Get a single pixel out of the image. + **********************************************************************/ + +UINT8 IMAGE::pixel( //get rest of image + INT32 x, //x coord + INT32 y //y coord + ) { + if (x < 0) + x = 0; //silently clip + else if (x >= xsize) + x = xsize - 1; + if (y < 0) + y = 0; + else if (y >= ysize) + y = ysize - 1; + check_legal_access (x, y, 1); + switch (bpp) { + case 5: + case 6: + case 8: + return image[(ymax - 1 - y) * xdim + x]; + case 4: + return bpp4table[image[(ymax - 1 - y) * xdim + x / 2]][x & 1]; + case 2: + return bpp2table[image[(ymax - 1 - y) * xdim + x / 4]][x & 3]; + case 1: + return bpp1table[image[(ymax - 1 - y) * xdim + x / 8]][x & 7]; + default: + tprintf ("Unexpected bits per pixel %d\n", bpp); + return 0; + } +} + + +/********************************************************************** + * check_legal_image_size + * + * Check that the supplied image sizes are legal. If they are, + * the xdim is returned, else -1. + **********************************************************************/ + +INT32 check_legal_image_size( //get rest of image + INT32 x, //x size required + INT32 y, //ysize required + INT8 bits_per_pixel //bpp required + ) { + if (x <= 0 || y <= 0) { + BADIMAGESIZE.error ("check_legal_image_size", LOG, "(%d,%d)", x, y); + return -1; //failed + } + if (bits_per_pixel != 1 && bits_per_pixel != 2 + && bits_per_pixel != 4 && bits_per_pixel != 5 + && bits_per_pixel != 6 && bits_per_pixel != 8 && bits_per_pixel != 24) { + BADBPP.error ("check_legal_image_size", LOG, "%d", bits_per_pixel); + return -1; + } + //bytes per line + return COMPUTE_IMAGE_XDIM (x, bits_per_pixel); +} + + +/********************************************************************** + * copy_sub_image + * + * Copy a portion of one image to a portion of another image. + * If the bpps are different, the position of the most significant + * bit is preserved. + **********************************************************************/ + +DLLSYM void copy_sub_image( //copy rectangle + IMAGE *source, //source image + INT32 xstart, //start coords + INT32 ystart, + INT32 xext, //extent to copy + INT32 yext, + IMAGE *dest, //destination image + INT32 xdest, //destination coords + INT32 ydest, + BOOL8 adjust_grey //shift to new bpp + ) { + IMAGELINE copyline; //copy of line + UINT8 *copy; //source pointer + INT8 shift; //shift factor + INT32 pixel; //pixel index + INT32 y; //line index + INT32 yoffset; //current adjusted offset + INT32 bytesize; //no of bytes to copy + INT32 srcppb; //pixels per byte + BOOL8 aligned; + + if (xstart < 0 || ystart < 0 || xdest < 0 || ydest < 0) + return; + if (xext <= 0) + xext = source->xsize; //default to all + if (xext > source->xsize - xstart) + //clip to smallest + xext = source->xsize - xstart; + if (xext > dest->xsize - xdest) + xext = dest->xsize - xdest; + if (yext <= 0) + yext = source->ysize; //default to all + if (yext > source->ysize - ystart) + //clip to smallest + yext = source->ysize - ystart; + if (yext > dest->ysize - ydest) + yext = dest->ysize - ydest; + if (xext <= 0 || yext <= 0) + return; //nothing to do + + srcppb = 8 / source->bpp; //pixels per byte + if (source->bpp == dest->bpp || !adjust_grey) + shift = 0; //no adjustment + else { + shift = source->bps - dest->bps; + if (shift < 0) + shift = -shift; //keep positive + } + aligned = source->bpp == dest->bpp; + if (aligned && srcppb != 0) { + aligned = xstart % srcppb == 0 + && xdest % srcppb == 0 + && (xext % srcppb == 0 || xdest + xext == dest->xsize); + } + for (y = 0; y < yext; y++) { + if (ystart >= ydest) + yoffset = y; //top down + else + yoffset = yext - y - 1; //bottom up + source->check_legal_access (xstart, ystart + yoffset, xext); + dest->check_legal_access (xdest, ydest + yoffset, xext); + if (aligned) { + bytesize = COMPUTE_IMAGE_XDIM (xext, source->bpp); + //get bytes per line + if (srcppb == 0) + //do cheap move + memmove (dest->image + (dest->ymax - 1 - ydest - yoffset) * dest->xdim + xdest * 3, source->image + (source->ymax - 1 - ystart - yoffset) * source->xdim + xstart * 3, (unsigned) bytesize); + else + //do cheap move + memmove (dest->image + (dest->ymax - 1 - ydest - yoffset) * dest->xdim + xdest / srcppb, source->image + (source->ymax - 1 - ystart - yoffset) * source->xdim + xstart / srcppb, (unsigned) bytesize); + } + else { + if (shift == 0) { + source->fast_get_line (xstart, ystart + yoffset, xext, + ©line); + } + else if (source->bpp < dest->bpp) { + source->get_line (xstart, ystart + yoffset, xext, ©line, 0); + if (source->bpp <= shift + && (source->bpp == 1 || source->bpp == 4)) { + if (source->bpp == 1) { + for (pixel = 0, copy = copyline.pixels; pixel < xext; + pixel++, copy++) + if (*copy) + *copy = 0xff; + } + else { + for (pixel = 0, copy = copyline.pixels; pixel < xext; + pixel++, copy++) + //scale up + *copy = (*copy << shift) | *copy; + } + } + else { + for (pixel = 0, copy = copyline.pixels; pixel < xext; + pixel++) + *copy++ <<= shift; //scale up + } + } + else { + source->get_line (xstart, ystart + yoffset, xext, ©line, 0); + if (source->bpp == 24) { + for (pixel = 0, copy = copyline.pixels + 1; pixel < xext; + pixel++) { + *copy >>= shift; + copy += 3; + } + } + else { + for (pixel = 0, copy = copyline.pixels; pixel < xext; + pixel++) + *copy++ >>= shift; //scale down + } + } + dest->put_line (xdest, ydest + yoffset, xext, ©line, 0); + } + } +} + + +/********************************************************************** + * enlarge_sub_image + * + * Enlarge a portion of one image to a portion of another image. + * If the bpps are different, the position of the most significant + * bit is preserved. + **********************************************************************/ + +DLLSYM void enlarge_sub_image( //enlarge rectangle + IMAGE *source, //source image + INT32 xstart, //scaled start coords + INT32 ystart, + IMAGE *dest, //destination image + INT32 xdest, //dest coords + INT32 ydest, + INT32 xext, //destination extent + INT32 yext, + INT32 scale, //scale factor + BOOL8 adjust_grey //shift to new bpp + ) { + INT8 shift; //shift factor + UINT8 pixel; //current pixel + INT32 srcext; //source extent + INT32 xoffset; //column index + INT32 yoffset; //line index + INT32 xindex, yindex; //index in super pixel + INT32 startxindex; //initial x index + INT32 xscale; //x scale factor + UINT8 *src; //source pixels + UINT8 *destpix; //dest pixels + IMAGELINE copyline; //copy of line + IMAGELINE bigline; //expanded line + + if (xstart < 0 || ystart < 0 || xdest < 0 || ydest < 0) + return; + + if (xext <= 0) + xext = dest->xsize; //default to all + if (xext > source->xsize * scale - xstart) + //clip to smallest + xext = source->xsize * scale - xstart; + if (xext > dest->xsize - xdest) + xext = dest->xsize - xdest; + if (yext <= 0) + yext = dest->ysize; //default to all + if (yext > source->ysize * scale - ystart) + yext = source->ysize * scale - ystart; + if (yext > dest->ysize - ydest) + yext = dest->ysize - ydest; + if (xext <= 0 || yext <= 0) + return; //nothing to do + + xindex = xstart % scale; //offset in super pixel + startxindex = xindex; + yindex = ystart % scale; + //no of source pixels + srcext = (xext + xindex + scale - 1) / scale; + xstart /= scale; //actual start + ystart /= scale; + if (adjust_grey) { + shift = dest->bps - source->bps; + } + else + shift = 0; //no adjustment + bigline.init (xext * 3); + bigline.bpp = dest->bpp == 24 ? source->bpp : dest->bpp; + + for (yoffset = 0; yoffset < yext; ystart++) { + source->check_legal_access (xstart, ystart, srcext); + dest->check_legal_access (xdest, ydest + yoffset, xext); + source->fast_get_line (xstart, ystart, srcext, ©line); + src = copyline.pixels; + destpix = bigline.pixels; + xscale = scale; //enlargement factor + if (source->bpp == 24 && dest->bpp == 24) { + for (xoffset = 0, xindex = startxindex; xoffset < xext; + src += source->bytespp) { + xoffset += xscale - xindex; + if (xoffset > xext) + xscale -= xoffset - xext; + for (; xindex < xscale; xindex++) { + *destpix++ = *src; + *destpix++ = *(src + 1); + *destpix++ = *(src + 2); + } + xindex = 0; + } + } + else { + if (source->bpp == 24) + src++; + for (xoffset = 0, xindex = startxindex; xoffset < xext; + src += source->bytespp) { + xoffset += xscale - xindex; + if (xoffset > xext) + //clip to dest limit + xscale -= xoffset - xext; + if (shift == 0) + pixel = *src; + else if (shift > 0) + pixel = *src << shift; + else + pixel = *src >> (-shift); + for (; xindex < xscale; xindex++) + *destpix++ = pixel; //duplicate pixel + xindex = 0; + } + } + for (; yoffset < yext && yindex < scale; yindex++, yoffset++) { + dest->put_line (xdest, ydest + yoffset, xext, &bigline, 0); + } + yindex = 0; + } +} + + +/********************************************************************** + * fast_reduce_sub_image + * + * Reduce a portion of one image to a portion of another image. + * If the bpps are different, the position of the most significant + * bit is preserved. + * This is a fast but dirty version, which simply sub-samples. + * It does not smooth as it reduces. + **********************************************************************/ + +DLLSYM void fast_reduce_sub_image( //reduce rectangle + IMAGE *source, //source image + INT32 xstart, //start coords + INT32 ystart, + INT32 xext, //extent to copy + INT32 yext, + IMAGE *dest, //destination image + INT32 xdest, //destination coords + INT32 ydest, + INT32 scale, //reduction factor + BOOL8 adjust_grey //shift to new bpp + ) { + INT8 shift; //shift factor + INT32 xfactor; //run on x coord + INT32 divisor; //total cell area + INT32 xindex, yindex; //into averaging square + INT32 xcoord; //current x coord + INT32 destext; //destination size + INT32 yoffset; //current adjusted offset + UINT8 *pixel; //ptr to source pixels + INT32 *sums; //ptr to sums array + IMAGELINE copyline; //copy of line + INT32 *linesums; //averaging sums + + if (xstart < 0 || ystart < 0 || xdest < 0 || ydest < 0) + return; + if (xext <= 0) + xext = source->xsize; //default to all + if (xext > source->xsize - xstart) + //clip to smallest + xext = source->xsize - xstart; + if (xext > (dest->xsize - xdest) * scale) + xext = (dest->xsize - xdest) * scale; + if (yext <= 0) + yext = source->ysize; //default to all + if (yext > source->ysize - ystart) + //clip to smallest + yext = source->ysize - ystart; + if (yext > (dest->ysize - ydest) * scale) + yext = (dest->ysize - ydest) * scale; + if (xext <= 0 || yext <= 0) + return; //nothing to do + + xfactor = xext % scale; //left overs + if (xfactor == 0) + xfactor = scale; + //destination pixels + destext = (xext + scale - 1) / scale; + if (adjust_grey) + //shift factor + shift = dest->bps - source->bps; + else + shift = 0; //no adjustment + linesums = new INT32[destext * source->bytespp]; + + for (yoffset = 0; yoffset < yext; ydest++) { + source->check_legal_access (xstart, ystart + yoffset, xext); + dest->check_legal_access (xdest, ydest, destext); + for (xindex = destext * source->bytespp - 1; xindex >= 0; xindex--) + linesums[xindex] = 0; //zero sums + for (yindex = 0; yindex < scale + && ystart + yoffset < source->ysize; yindex += 3) { + source->fast_get_line (xstart, ystart + yoffset, xext, ©line); + pixel = copyline.pixels; //start of line + if (source->bpp == 24) { + for (xcoord = 1, sums = linesums; xcoord < destext; + xcoord++, sums += 3) { + for (xindex = 0; xindex < scale; xindex += 2) { + *sums += *pixel++; + *(sums + 1) += *pixel++; + *(sums + 2) += *pixel++; + pixel += 3; + } + if (scale & 1) + pixel -= 3; //correct position + } + for (xindex = 0; xindex < xfactor; xindex += 2) { + *sums += *pixel++; + *(sums + 1) += *pixel++; + *(sums + 2) += *pixel++; + pixel += 3; + } + } + else { + for (xcoord = 1, sums = linesums; xcoord < destext; + xcoord++, sums++) { + for (xindex = 0; xindex < scale; xindex += 2) { + *sums += *pixel; + pixel += 2; + } + if (scale & 1) + pixel--; //correct position + } + for (xindex = 0; xindex < xfactor; xindex += 2) { + *sums += *pixel; + pixel += 2; + } + } + yoffset += 3; //every 3 lines + } + if (yindex > scale) + yoffset -= yindex - scale; //back on right scale + copyline.init (); //set pixels back to array + copyline.bpp = source->bpp; + pixel = copyline.pixels; + //pixels in block + divisor = ((yindex + 2) / 3) * ((scale + 1) / 2); + if (shift <= 0) { + divisor <<= (-shift); //do greyscale correction + for (sums = linesums, xindex = (destext - 1) * source->bytespp; + xindex > 0; xindex--) + //turn to destination value + *pixel++ = (UINT8) (*sums++ / divisor); + for (xindex = source->bytespp; xindex > 0; xindex--) + *pixel++ = *sums++ + / (((yindex + 2) / 3) * ((xfactor + 1) / 2) << (-shift)); + //lastone different + } + else { + for (sums = linesums, xindex = (destext - 1) * source->bytespp; + xindex > 0; xindex--) + *pixel++ = (UINT8) ((*sums++ << shift) / divisor); + //destination value + for (xindex = source->bytespp; xindex > 0; xindex--) + //last one different + *pixel++ = (*(sums++) << shift) / (((yindex + 2) / 3) * ((xfactor + 1) / 2)); + } + //put in destination + dest->put_line (xdest, ydest, destext, ©line, 0); + } + delete linesums; +} + + +/********************************************************************** + * reduce_sub_image + * + * Reduce a portion of one image to a portion of another image. + * If the bpps are different, the position of the most significant + * bit is preserved. + **********************************************************************/ + +DLLSYM void reduce_sub_image( //reduce rectangle + IMAGE *source, //source image + INT32 xstart, //start coords + INT32 ystart, + INT32 xext, //extent to copy + INT32 yext, + IMAGE *dest, //destination image + INT32 xdest, //destination coords + INT32 ydest, + INT32 scale, //reduction factor + BOOL8 adjust_grey //shift to new bpp + ) { + INT8 shift; //shift factor + INT32 xfactor; //run on x coord + INT32 divisor; //total cell area + INT32 div2; //total cell area divided by 2 + INT32 xindex, yindex; //into averaging square + INT32 xcoord; //current x coord + INT32 destext; //destination size + INT32 yoffset; //current adjusted offset + UINT8 *pixel; //ptr to source pixels + INT32 *sums; //ptr to sums array + IMAGELINE copyline; //copy of line + INT32 *linesums; //averaging sums + + if (xstart < 0 || ystart < 0 || xdest < 0 || ydest < 0) + return; + if (xext <= 0) + xext = source->xsize; //default to all + if (xext > source->xsize - xstart) + //clip to smallest + xext = source->xsize - xstart; + if (xext > (dest->xsize - xdest) * scale) + xext = (dest->xsize - xdest) * scale; + if (yext <= 0) + yext = source->ysize; //default to all + if (yext > source->ysize - ystart) + //clip to smallest + yext = source->ysize - ystart; + if (yext > (dest->ysize - ydest) * scale) + yext = (dest->ysize - ydest) * scale; + if (xext <= 0 || yext <= 0) + return; //nothing to do + + xfactor = xext % scale; //left overs + if (xfactor == 0) + xfactor = scale; + //destination pixels + destext = (xext + scale - 1) / scale; + if (adjust_grey) + //shift factor + shift = dest->bps - source->bps; + else + shift = 0; //no adjustment + linesums = new INT32[destext * source->bytespp]; + + for (yoffset = 0; yoffset < yext; ydest++) { + source->check_legal_access (xstart, ystart + yoffset, xext); + dest->check_legal_access (xdest, ydest, destext); + for (xindex = 0; xindex < (destext) * source->bytespp; xindex++) + linesums[xindex] = 0; //zero sums + for (yindex = 0; yindex < scale && ystart + yoffset < source->ysize; + yindex++) { + source->fast_get_line (xstart, ystart + yoffset, xext, ©line); + pixel = copyline.pixels; //start of line + if (source->bpp == 24) { + for (xcoord = 1, sums = linesums; xcoord < destext; + xcoord++, sums += 3) { + for (xindex = 0; xindex < scale; xindex++) { + *sums += *pixel++; + *(sums + 1) += *pixel++; + *(sums + 2) += *pixel++; + } + } + for (xindex = 0; xindex < xfactor; xindex++) { + *sums += *pixel++; + *(sums + 1) += *pixel++; + *(sums + 2) += *pixel++; + } + } + else { + for (xcoord = 1, sums = linesums; xcoord < destext; + xcoord++, sums++) { + for (xindex = 0; xindex < scale; xindex++) + *sums += *pixel++; + } + for (xindex = 0; xindex < xfactor; xindex++) + *sums += *pixel++; + } + yoffset++; //next line + } + copyline.init (); //set pixels back to array + copyline.set_bpp (source->bpp); + pixel = copyline.pixels; + divisor = yindex * scale; + if (divisor == 0) { + tprintf + ("Impossible:divisor=0!, yindex=%d, scale=%d, yoffset=%d,yext=%d\n", + yindex, scale, yoffset, yext); + break; + } + if (shift <= 0) { + divisor <<= (-shift); //do greyscale correction + div2 = divisor / 2; + for (sums = linesums, xindex = (destext - 1) * source->bytespp; + xindex > 0; xindex--) + *pixel++ = (UINT8) ((div2 + *sums++) / divisor); + //turn to destination value + div2 = (yindex * xfactor << (-shift)) / 2; + for (xindex = source->bytespp; xindex > 0; xindex--) + *pixel++ = + (UINT8) ((div2 + *sums++) / (yindex * xfactor << (-shift))); + //lastone different + } + else { + div2 = divisor / 2; + for (sums = linesums, xindex = (destext - 1) * source->bytespp; + xindex > 0; xindex--) + *pixel++ = (UINT8) ((div2 + (*sums++ << shift)) / divisor); + //destination value + div2 = (yindex * xfactor) / 2; + for (xindex = source->bytespp; xindex > 0; xindex--) + *pixel++ = + (UINT8) ((div2 + (*sums++ << shift)) / (yindex * xfactor)); + //last one different + } + //put in destination + dest->put_line (xdest, ydest, destext, ©line, 0); + } + delete linesums; +} + + +/********************************************************************** + * invert_image + * + * Invert the given image (the slow way.) + **********************************************************************/ + +DLLSYM void invert_image( /*invert the image */ + IMAGE *image /*image ot invert */ + ) { + UINT8 mask; //bit mask + UINT8 bytespp; //bytes per pixel + INT32 xsize, ysize; /*size of image */ + INT32 xindex, yindex; /*index into image */ + UINT8 *pixel; /*current pixel */ + IMAGELINE line; /*line of image */ + + bytespp = image->get_bpp () == 24 ? 3 : 1; + xsize = image->get_xsize (); /*find sizes */ + ysize = image->get_ysize (); + //pixel mask + mask = (1 << image->get_bpp ()) - 1; + /*do each line */ + for (yindex = ysize - 1; yindex >= 0; yindex--) { + image->fast_get_line (0, yindex, xsize, &line); + for (pixel = line.pixels, xindex = xsize * bytespp; xindex > 0; + xindex--) { + *pixel = (*pixel) ^ mask; //invert image only + ++pixel; + } + /*put it back */ + image->fast_put_line (0, yindex, xsize, &line); + } +} + + +/********************************************************************** + * bias_sub_image + * + * Add a constant to a portion of an image. + **********************************************************************/ + +DLLSYM void bias_sub_image( //bias rectangle + IMAGE *source, //source image + INT32 xstart, //start coords + INT32 ystart, + INT32 xext, //extent to copy + INT32 yext, + UINT8 bias //number to add + ) { + IMAGELINE copyline; //copy of line + UINT8 *copy; //source pointer + INT32 pixel; //pixel index + INT32 y; //line index + UINT8 bytespp; //bytes per pixel + + if (xstart < 0 || ystart < 0) + return; + if (xext <= 0) + xext = source->get_xsize (); //default to all + if (xext > source->get_xsize () - xstart) + //clip to smallest + xext = source->get_xsize () - xstart; + if (yext <= 0) + yext = source->get_ysize (); //default to all + if (yext > source->get_ysize () - ystart) + //clip to smallest + yext = source->get_ysize () - ystart; + if (xext <= 0 || yext <= 0) + return; //nothing to do + + bytespp = source->get_bpp () == 24 ? 3 : 1; + for (y = 0; y < yext; y++) { + source->check_legal_access (xstart, ystart + y, xext); + source->fast_get_line (xstart, ystart + y, xext, ©line); + for (pixel = xext * bytespp, copy = copyline.pixels; pixel > 0; + pixel--, copy++) + *copy += bias; //add bias + + source->fast_put_line (xstart, ystart + y, xext, ©line); + } +} + + +/********************************************************************** + * starbase_to_normal + * + * Copy a portion of one image to a portion of another image. + * This function maps the colour tables used on the screen to + * greyscale values in the way "normally" expected. + **********************************************************************/ + +DLLSYM void starbase_to_normal( //copy rectangle + IMAGE *source, //source image + INT32 xstart, //start coords + INT32 ystart, + INT32 xext, //extent to copy + INT32 yext, + IMAGE *dest, //destination image + INT32 xdest, //destination coords + INT32 ydest, + BOOL8 preserve_grey //shift to new bpp + ) { + IMAGELINE copyline; //copy of line + UINT8 *copy; //source pointer + INT8 shift4; //shift factor + INT8 shift6; //shift factor + INT8 colour_shift; //shift of colours + UINT8 white_level; //dest white value + INT32 pixel; //pixel index + INT32 y; //line index + INT32 yoffset; //current adjusted offset + INT8 srcppb; //pixels per byte + + if (xstart < 0 || ystart < 0 || xdest < 0 || ydest < 0) + return; + if (xext <= 0) + xext = source->get_xsize (); //default to all + if (xext > source->get_xsize () - xstart) + //clip to smallest + xext = source->get_xsize () - xstart; + if (xext > dest->get_xsize () - xdest) + xext = dest->get_xsize () - xdest; + if (yext <= 0) + yext = source->get_ysize (); //default to all + if (yext > source->get_ysize () - ystart) + //clip to smallest + yext = source->get_ysize () - ystart; + if (yext > dest->get_ysize () - ydest) + yext = dest->get_ysize () - ydest; + if (xext <= 0 || yext <= 0) + return; //nothing to do + + //pixels per byte + srcppb = 8 / source->get_bpp (); + shift4 = 4 - dest->get_bpp (); //for different bpps + shift6 = 6 - dest->get_bpp (); + //for grey preserve + colour_shift = 8 - dest->get_bpp (); + white_level = dest->get_white_level (); + for (y = 0; y < yext; y++) { + if (ystart >= ydest) + yoffset = y; //top down + else + yoffset = yext - y - 1; //bottom up + source->check_legal_access (xstart, ystart + yoffset, xext); + dest->check_legal_access (xdest, ydest + yoffset, xext); + source->get_line (xstart, ystart + yoffset, xext, ©line, 0); + for (pixel = 0, copy = copyline.pixels; pixel < xext; pixel++) { + if (*copy < FIXED_COLOURS && preserve_grey) + *copy = grey_scales[*copy] >> colour_shift; + else if (*copy < FIXED_COLOURS) { + if (*copy == BLACK_PIX) + *copy = white_level; //black->white + else + *copy = 0; //others->black + } + else if (*copy >= MIN_4BIT && *copy < MAX_4BIT) { + if (shift4 < 0) + *copy = (*copy - MIN_4BIT) << (-shift4); + else + *copy = (*copy - MIN_4BIT) >> shift4; + } + else if (*copy >= MIN_6BIT && *copy < MAX_6BIT) { + if (shift6 < 0) + *copy = (*copy - MIN_6BIT) << (-shift6); + else + *copy = (*copy - MIN_6BIT) >> shift6; + } + else { + *copy = white_level; //white the rest + } + copy++; + } + dest->put_line (xdest, ydest + yoffset, xext, ©line, 0); + } +} + + +/********************************************************************** + * fast_get_line + * + * Get a line of image into the supplied image line buffer. + * The image is converted to 8bpp by simple assignment. + * If the image is aleady 8 or 6bpp, no copy is done and a pointer + * to the correct image section is put in the line buffer. + **********************************************************************/ + +void IMAGE::fast_get_line( //get image line + INT32 x, //coord to start at + INT32 y, //line to get + INT32 width, //no of pixels to get + IMAGELINE *linebuf //line to copy to + ) { + if (width > 0 && bpp > 4) { + check_legal_access(x, y, width); + //get pointer only + linebuf->pixels = image + xdim * (ymax - 1 - y) + x * bytespp; + } + else + //just copy it + this->get_line (x, y, width, linebuf, 0); + linebuf->bpp = bpp; +} + + +/********************************************************************** + * get_line + * + * Get a line of image into the supplied image line buffer. + * The image is converted to 8bpp by simple assignment. + **********************************************************************/ + +void IMAGE::get_line( //get image line + INT32 x, //coord to start at + INT32 y, //line to get + INT32 width, //no of pixels to get + IMAGELINE *linebuf, //line to copy to + INT32 margins //size of margins + ) { + UINT8 *src; //source pointer + UINT8 *dest; //destination pointer + UINT8 *unpacksrc; //unpacking pointer + INT8 bit; //bit index + INT8 pixperbyte; //pixels per byte + UINT8 white; //white colour + INT32 pixel; //pixel index + + //test coords + this->check_legal_access (x, y, width); + if (width > xsize - x) + width = xsize - x; //clip to image + width *= bytespp; + linebuf->init (width + margins * bytespp * 2); + linebuf->bpp = bpp; + //start of line + src = image + xdim * (ymax - 1 - y); + dest = linebuf->line; //destination line + linebuf->pixels = dest; + white = (1 << bpp) - 1; //max value of pixel + for (pixel = margins * bytespp; pixel > 0; pixel--) { + *dest++ = white; //margins are white + } + if (width > 0) { + if (bpp > 4) { + src += x; //offset + //easy way + memmove (dest, src, (unsigned) width); + } + else if (bpp == 4) { + src += x / 2; //offset on line + if (x & 1) { + //get coded nibble + *dest++ = bpp4table[*src++][1]; + width--; + } + while (width >= 2) { + //get coded bits + unpacksrc = bpp4table[*src++]; + *dest++ = *unpacksrc++; + *dest++ = *unpacksrc++; //copy nibbles + width -= 2; + } + if (width) { + //get coded nibble + *dest++ = bpp4table[*src++][0]; + } + } + else if (bpp == 2) { + pixperbyte = 4; + src += x / 4; //offset on line + bit = (INT8) (x % 4); //offset in byte + width += bit; + while (width > 0) { //until all done + if (width < pixperbyte) + //less on last byte + pixperbyte = (INT8) width; + //get coded bits + unpacksrc = &bpp2table[*src++][bit]; + for (; bit < pixperbyte; bit++) + *dest++ = *unpacksrc++;//copy bytes + width -= pixperbyte; + bit = 0; + } + } + else { + pixperbyte = 8; + src += x / 8; //offset on line + bit = (INT8) (x % 8); //offset in byte + width += bit; + while (width > 0) { //until all done + if (width < pixperbyte) + //less on last byte + pixperbyte = (INT8) width; + //get coded bits + unpacksrc = &bpp1table[*src++][bit]; + for (; bit < pixperbyte; bit++) + *dest++ = *unpacksrc++;//copy bytes + width -= pixperbyte; + bit = 0; + } + } + } + for (pixel = margins * bytespp; pixel > 0; pixel--) { + *dest++ = white; //margins are white + } +} + + +/********************************************************************** + * get_column + * + * Get a column of image into the supplied image line buffer. + * The image is converted to 8bpp by simple assignment. + **********************************************************************/ + +void IMAGE::get_column( //get image column + INT32 x, //coord to start at + INT32 y, //line to get + INT32 height, //no of pixels to get + IMAGELINE *linebuf, //line to copy to + INT32 margins //size of margins + ) { + UINT8 *src; //source pointer + UINT8 *dest; //destination pointer + INT8 bit; //bit index + INT8 pixperbyte; //pixels per byte + UINT8 white; //white colour + INT32 pixel; //pixel index + + //test coords + this->check_legal_access (x, y, 1); + //test coords + this->check_legal_access (x, y + height - 1, 1); + if (height > ysize - y) + height = ysize - y; //clip to image + linebuf->init (height * bytespp + margins * bytespp * 2); + //start of line + src = image + xdim * (ymax - 1 - y); + dest = linebuf->line; //destination line + linebuf->pixels = dest; + white = (1 << bpp) - 1; //max value of pixel + for (pixel = margins * bytespp; pixel > 0; pixel--) { + *dest++ = white; //margins are white + } + if (height > 0) { + if (bpp == 24) { + src += x * bytespp; //offset + for (; height > 0; --height) { + *dest++ = *src; //copy bytes + *dest++ = *(src + 1); + *dest++ = *(src + 2); + src -= xdim; + } + } + else if (bpp > 4) { + src += x; + for (; height > 0; --height) { + *dest++ = *src; //copy bytes + src -= xdim; + } + } + else if (bpp == 4) { + src += x / 2; //offset on line + if (x & 1) { + for (; height > 0; --height) { + //get coded nibble + *dest++ = bpp4table[*src][1]; + src -= xdim; + } + } + else { + for (; height > 0; --height) { + //get coded nibble + *dest++ = bpp4table[*src][0]; + src -= xdim; + } + } + } + else if (bpp == 2) { + pixperbyte = 4; + src += x / 4; //offset on line + bit = (INT8) (x % 4); //offset in byte + for (; height > 0; --height) { + //get coded bits + *dest++ = bpp2table[*src][bit]; + src -= xdim; + } + } + else { + pixperbyte = 8; + src += x / 8; //offset on line + bit = (INT8) (x % 8); //offset in byte + for (; height > 0; --height) { + //get coded bits + *dest++ = bpp1table[*src][bit]; + src -= xdim; + } + } + } + for (pixel = margins * bytespp; pixel > 0; pixel--) { + *dest++ = white; //margins are white + } +} + + +/********************************************************************** + * fast_put_line + * + * Put a line buffer back into the image. + * If the line buffer merely points back into the image, nothing is done. + * Otherwise, put_line is used to copy the line back. + **********************************************************************/ + +void IMAGE::fast_put_line( //put image line + INT32 x, //coord to start at + INT32 y, //line to get + INT32 width, //no of pixels to put + IMAGELINE *linebuf //line to copy to + ) { + if (width > 0 && (bpp <= 4 || linebuf->pixels == linebuf->line)) + //just copy it + put_line (x, y, width, linebuf, 0); +} + + +/********************************************************************** + * put_line + * + * Put the supplied line buffer into the image. + * The image is converted from 8bpp by simple assignment. + **********************************************************************/ + +void IMAGE::put_line( //put image line + INT32 x, //coord to start at + INT32 y, //line to get + INT32 width, //no of pixels to get + IMAGELINE *linebuf, //line to copy to + INT32 margins //margins in buffer + ) { + UINT8 *src; //source pointer + UINT8 *dest; //destination pointer + INT8 bit; //bit index + UINT8 pixel; //collected bits + INT8 pixperbyte; //pixels in a byte + INT8 bytesperpix; //in source + + this->check_legal_access (x, y, width); + if (width > xsize - x) + width = xsize - x; //clip to image + if (width <= 0) + return; //nothing to do + //source line + src = linebuf->pixels + margins; + //start of line + dest = image + xdim * (ymax - 1 - y); + + if (linebuf->bpp == 24) { + src++; + bytesperpix = 3; + } + else + bytesperpix = 1; + if (bpp == 24 && linebuf->bpp == 24) { + dest += x * bytespp; + width *= bytespp; + memmove (dest, src - 1, (unsigned) width); + } + else if (bpp == 24) { + src--; + dest += x * bytespp; + while (width > 0) { + pixel = *src++; + *dest++ = pixel; + *dest++ = pixel; + *dest++ = pixel; + width--; + } + } + else if (bpp > 4) { + dest += x; //offset + if (linebuf->bpp == 24) { + while (width > 0) { + *dest++ = *src; + src += 3; + width--; + } + } + else + //easy way + memmove (dest, src, (unsigned) width); + } + else if (bpp == 4) { + dest += x / 2; //offset on line + if (x & 1) { + *dest &= 0xf0; //clean odd byte + *dest++ |= *src & 0x0f; //and copy it + src += bytesperpix; + width--; + } + while (width >= 2) { + pixel = *src << 4; //left pixel + src += bytesperpix; + pixel |= *src & 0x0f; //right pixel + src += bytesperpix; + *dest++ = pixel; + width -= 2; + } + if (width) { + *dest &= 0x0f; //clean odd byte + *dest |= *src << 4; + } + } + else if (bpp == 2) { + pixperbyte = 4; + dest += x / 4; //offset on line + bit = (INT8) (x % 4); //offset in byte + width += bit; + pixel = *dest >> (8 - bit - bit); + while (width >= 4) { //until all done + for (; bit < 4; bit++) { + pixel <<= 2; //make space for new one + pixel |= *src & 3; + src += bytesperpix; + } + *dest++ = pixel; //new pixel + width -= 4; + bit = 0; + } + if (width > 0) { //until all done + for (bit = 0; bit < width; bit++) { + pixel <<= 2; //make space for new one + pixel |= *src & 3; + src += bytesperpix; + } + pixel <<= (8 - bit - bit); //shift rest + //keep trainling bits + pixel |= *dest & ((1 << (8 - bit - bit)) - 1); + *dest++ = pixel; //new pixel + } + } + else { + pixperbyte = 8; + dest += x / 8; //offset on line + bit = (INT8) (x % 8); //offset in byte + width += bit; + pixel = *dest >> (8 - bit); + while (width >= 8) { //until all done + for (; bit < 8; bit++) { + pixel <<= 1; //make space for new one + pixel |= *src & 1; + src += bytesperpix; + } + *dest++ = pixel; //new pixel + width -= 8; + bit = 0; + } + width -= bit; + if (width > 0) { //until all done + while (width > 0) { + pixel <<= 1; //make space for new one + pixel |= *src & 1; + src += bytesperpix; + bit++; + width--; + } + pixel <<= (8 - bit); //shift rest + //keep trainling bits + pixel |= *dest & ((1 << (8 - bit)) - 1); + *dest++ = pixel; //new pixel + } + } +} + + +/********************************************************************** + * put_column + * + * Put the supplied column buffer into the image. + * The image is converted from 8bpp by simple assignment. + **********************************************************************/ + +void IMAGE::put_column( //put image column + INT32 x, //coord to start at + INT32 y, //line to get + INT32 height, //no of pixels to get + IMAGELINE *linebuf, //line to copy to + INT32 margins //margins in buffer + ) { + UINT8 *src; //source pointer + UINT8 *dest; //destination pointer + INT8 bit; //bit index + UINT8 pixel; //collected bits + INT8 bytesperpix; //in source + + this->check_legal_access (x, y, 1); + this->check_legal_access (x, y + height - 1, 1); + if (height > ysize - y) + height = ysize - y; //clip to image + if (height <= 0) + return; //nothing to do + //source line + src = linebuf->pixels + margins; + //start of line + dest = image + xdim * (ymax - 1 - y); + + if (linebuf->bpp == 24) { + src++; + bytesperpix = 3; + } + else + bytesperpix = 1; + + if (bpp == 24 && linebuf->bpp == 24) { + dest += x * bytesperpix; + src--; + for (; height > 0; --height) { + *dest = *src++; + *(dest + 1) = *src++; + *(dest + 2) = *src++; + dest -= xdim; + } + } + else if (bpp == 24) { + src--; + dest += x * bytesperpix; + for (; height > 0; --height) { + pixel = *src++; + *dest = pixel; + *(dest + 1) = pixel; + *(dest + 2) = pixel; + dest -= xdim; + } + } + else if (bpp > 4) { + dest += x; //offset + for (; height > 0; --height) { + *dest = *src; + src += bytesperpix; + dest -= xdim; + } + } + else if (bpp == 4) { + dest += x / 2; //offset on line + if (x & 1) { + for (; height > 0; --height) { + *dest &= 0xf0; //clean odd byte + *dest |= *src & 0x0f; //and copy it + src += bytesperpix; + dest -= xdim; + } + } + else { + for (; height > 0; --height) { + *dest &= 0x0f; //clean odd byte + *dest |= *src << 4; + src += bytesperpix; + dest -= xdim; + } + } + } + else if (bpp == 2) { + dest += x / 4; //offset on line + bit = (INT8) (x % 4); //offset in byte + bit = 6 - bit - bit; //bit shift + pixel = ~(3 << bit); //mask + for (; height > 0; --height) { + //change 2 bits + *dest = (*dest & pixel) | ((*src & 3) << bit); + src += bytesperpix; + dest -= xdim; + } + } + else { + dest += x / 8; //offset on line + bit = (INT8) (x % 8); //offset in byte + bit = 7 - bit; + pixel = ~(1 << bit); + for (; height > 0; --height) { + //change 1 bit + *dest = (*dest & pixel) | ((*src & 1) << bit); + src += bytesperpix; + dest -= xdim; + } + } +} + + +/********************************************************************** + * check_legal_access + * + * Check that x,y are within the bounds of the image. + * Call bufread if necessary to get the image into memory. + **********************************************************************/ + +void IMAGE::check_legal_access( //check coords are legal + INT32 x, //coords to check + INT32 y, + INT32 xext //xextent + ) { + if (x < 0 || x >= xsize || y < 0 || y >= ysize || x + xext > xsize) + BADIMAGECOORDS.error ("IMAGE::check_legal_access", + ABORT, "(%d+%d,%d)", x, xext, y); + if (y >= ymax) + BADIMAGESEEK.error ("IMAGE::check_legal_access", ABORT, "(%d,%d)", x, y); + if (y < ymin) + bufread(y); //read some more +} + + +/************************************************************************* + * convolver() + * + * Calls the specified function for each pixel in the image, passing in an m x n + * window of the image, centred on the pixel. The convolution function returns + * a new value for the pixel, based on the window. + * + * At the edges of the image, the window is padded to white pixels. + *************************************************************************/ + +void +IMAGE::convolver ( //Map fn over window +INT32 win_width, //Window width +INT32 win_height, //Window height +void (*convolve) ( //Conv Function +UINT8 ** pixels, //Of window +UINT8 bytespp, //1 or 3 for colour +INT32 win_wd, //Window width +INT32 win_ht, //Window height +UINT8 ret_white_value, //White value to RETURN +UINT8 * result) //Ptr to result pix +) { + IMAGELINE new_row; //Replacement pixels + IMAGELINE *old_rows; //Rows being processed + INT32 oldest_imline; //Next imline to replace + UINT8 **window; //ptrs to pixel rows + UINT8 **winmax; //ptrs to pixel rows + UINT8 **win; //ptrs to pixel rows + INT32 current_row; //Row being calculated + INT32 current_col; //Col being calculated + INT32 row = 0; //Next row to get + + INT32 i, j; + UINT8 *pix; + UINT8 *max; + INT32 xmargin = win_width / 2; + INT32 ymargin = win_height / 2; + UINT8 white = get_white_level (); + const UINT8 max_white = 255; + float white_scale = (float) 255 / get_white_level (); + + if (((win_width % 2) == 0) || + ((win_height % 2) == 0) || + (win_height < 3) || + (win_width < 3) || (win_height > ysize / 2) || (win_width > xsize / 2)) + BADWINDOW.error ("IMAGE::convolver", + ABORT, "(%d x %d)", win_width, win_height); + + new_row.init (xsize * bytespp); + new_row.set_bpp (bpp); + old_rows = new IMAGELINE[win_height]; + for (i = 0; i < win_height; i++) { + old_rows[i].init ((xsize + 2 * xmargin) * bytespp); + old_rows[i].set_bpp (bpp); + } + + window = (UINT8 **) alloc_mem (win_height * sizeof (UINT8 *)); + winmax = window + win_height; + + /* Make bottom border */ + for (oldest_imline = 0; oldest_imline < ymargin; oldest_imline++) { + pix = old_rows[oldest_imline].pixels; + max = pix + (xsize + 2 * xmargin) * bytespp; + while (pix < max) + *pix++ = max_white; + } + /* Initialise remaining rows but one*/ + for (; oldest_imline < win_height - 1; oldest_imline++) { + get_line (0, row++, xsize, &old_rows[oldest_imline], xmargin); + if (max_white != white) { + pix = old_rows[oldest_imline].pixels; + max = pix + (xsize + 2 * xmargin) * bytespp; + while (pix < max) { + *pix = (UINT8) (*pix * white_scale); + ++pix; + } + } + } + + /* Image Processing */ + + for (current_row = 0; current_row < ysize;) { + /* Get next row and re-initialise window array */ + if (row < ysize) { + get_line (0, row++, xsize, &old_rows[oldest_imline], xmargin); + if (max_white != white) { + pix = old_rows[oldest_imline].pixels; + max = pix + (xsize + 2 * xmargin) * bytespp; + while (pix < max) { + *pix = (UINT8) (*pix * white_scale); + ++pix; + } + } + } + else { + pix = old_rows[oldest_imline].pixels; + max = pix + (xsize + 2 * xmargin) * bytespp; + while (pix < max) + *pix++ = max_white; + } + oldest_imline++; + if (oldest_imline >= win_height) + oldest_imline = 0; + + /* Process line */ + pix = new_row.pixels; + for (current_col = 0; current_col < xsize;) { + /* Set up window ptrs */ + if (current_col == 0) { + j = oldest_imline; + for (i = 0; i < win_height; i++) { + window[i] = old_rows[j++].pixels; + if (j >= win_height) + j = 0; + } + } + else { + for (win = window; win < winmax; (*win++) += bytespp); + //Move along rows + } + + convolve(window, bytespp, win_width, win_height, white, pix); + pix += bytespp; + current_col++; + } + + put_line (0, current_row, xsize, &new_row, 0); + new_row.init (); + new_row.set_bpp (bpp); + current_row++; + } +} diff --git a/image/imgs.h b/image/imgs.h new file mode 100644 index 0000000000..e023dc7257 --- /dev/null +++ b/image/imgs.h @@ -0,0 +1,102 @@ +/********************************************************************** + * File: imgs.h (Formerly images.h) + * Description: Header file for IMAGE member functions. + * Author: Ray Smith + * Created: Mon Jun 11 15:32:53 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 IMGS_H +#define IMGS_H + +#include "img.h" +#include "varable.h" + +extern INT_VAR_H (image_default_resolution, 300, "Image resolution dpi"); + +INT32 check_legal_image_size( //get rest of image + INT32 x, //x size required + INT32 y, //ysize required + INT8 bits_per_pixel //bpp required + ); + //copy rectangle +extern DLLSYM void copy_sub_image(IMAGE *source, //source image + INT32 xstart, //start coords + INT32 ystart, + INT32 xext, //extent to copy + INT32 yext, + IMAGE *dest, //destination image + INT32 xdest, //destination coords + INT32 ydest, + BOOL8 adjust_grey //shift to new bpp + ); + //enlarge rectangle +extern DLLSYM void enlarge_sub_image(IMAGE *source, //source image + INT32 xstart, //scaled start coords + INT32 ystart, + IMAGE *dest, //destination image + INT32 xdest, //dest coords + INT32 ydest, + INT32 xext, //destination extent + INT32 yext, + INT32 scale, //scale factor + BOOL8 adjust_grey //shift to new bpp + ); + //reduce rectangle +extern DLLSYM void fast_reduce_sub_image(IMAGE *source, //source image + INT32 xstart, //start coords + INT32 ystart, + INT32 xext, //extent to copy + INT32 yext, + IMAGE *dest, //destination image + INT32 xdest, //destination coords + INT32 ydest, + INT32 scale, //reduction factor + BOOL8 adjust_grey //shift to new bpp + ); + //reduce rectangle +extern DLLSYM void reduce_sub_image(IMAGE *source, //source image + INT32 xstart, //start coords + INT32 ystart, + INT32 xext, //extent to copy + INT32 yext, + IMAGE *dest, //destination image + INT32 xdest, //destination coords + INT32 ydest, + INT32 scale, //reduction factor + BOOL8 adjust_grey //shift to new bpp + ); +extern DLLSYM void invert_image( /*invert the image */ + IMAGE *image /*image ot invert */ + ); + //bias rectangle +extern DLLSYM void bias_sub_image(IMAGE *source, //source image + INT32 xstart, //start coords + INT32 ystart, + INT32 xext, //extent to copy + INT32 yext, + UINT8 bias //number to add + ); + //copy rectangle +extern DLLSYM void starbase_to_normal(IMAGE *source, //source image + INT32 xstart, //start coords + INT32 ystart, + INT32 xext, //extent to copy + INT32 yext, + IMAGE *dest, //destination image + INT32 xdest, //destination coords + INT32 ydest, + BOOL8 preserve_grey //shift to new bpp + ); +#endif diff --git a/image/imgtiff.cpp b/image/imgtiff.cpp new file mode 100644 index 0000000000..d76dafcf4e --- /dev/null +++ b/image/imgtiff.cpp @@ -0,0 +1,705 @@ +/********************************************************************** + * File: imgtiff.c (Formerly tiff.c) + * Description: Max format image reader/writer. + * Author: Ray Smith + * Created: Mon Jun 11 14:00:21 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 "mfcpch.h" //precompiled headers +#ifdef __MSW32__ +#include +#else +#include +#endif +#include "fileerr.h" +#include "imgerrs.h" +#include "img.h" +#include "bitstrm.h" +#include "tprintf.h" +#include "serialis.h" +#include "imgtiff.h" + +#define INTEL 0x4949 +#define MOTO 0x4d4d + +/************************************************************************* + * NOTE ON BIG-ENDIAN vs LITTLE-ENDIAN + * + * Intel machines store numbers with LSByte in the left position. + * Motorola (and PA_RISC) machines use the opposite byte ordering. + * + * This code is written so that: + * a) it will compile and run on EITHER machine type AND + * b) the program (on either machine) will process tiff file written in either + * Motorola or Intel format. + * + * The code is compiled with a __NATIVE__ define which is either MOTO or INTEL. + * MOTO and INTEL are defined (above) to be the value of the first two bytes of + * a tiff file in either format. (This identifies the filetype). + * + * Subsequent reads and writes normally just reverse the byte order if the + * machine type (__NATIVE__) is not equal to the filetype determined from the + * first two bytes of the tiff file. + * + * A special case is the "value" field of the tag structure. This can contain + * EITHER a 16bit or a 32bit value. According to the "type" field. The 4 cases + * of machine type / file type combinations need to be treated differently in + * the case of 16 bit values + *************************************************************************/ + +#define ENTRIES 19 /*no of entries */ +#define START 8 /*start of tag table */ + +typedef struct +{ + UINT16 tag; //entry tag + UINT16 type; + UINT32 length; + INT32 value; +} TIFFENTRY; //tiff tag entry + +typedef struct myrational +{ + INT32 top; + INT32 bottom; +} MYRATIONAL; //type 5 + +//statics for the run length codes +#define EOL_CODE 0x800 +#define EOL_MASK 0xfff +#define EOL_LENGTH 12 //12 bits +#define SHORT_CODE_SIZE 64 //no of short codes +#define LONG_CODE_SIZE 40 //no of long codes + +static UINT16 short_white_codes[SHORT_CODE_SIZE] = { + 0xac, 0x38, 0xe, 0x1, 0xd, 0x3, 0x7, 0xf, + 0x19, 0x5, 0x1c, 0x2, 0x4, 0x30, 0xb, 0x2b, + 0x15, 0x35, 0x72, 0x18, 0x8, 0x74, 0x60, 0x10, + 0xa, 0x6a, 0x64, 0x12, 0xc, 0x40, 0xc0, 0x58, + 0xd8, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, 0x14, + 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x20, 0xa0, 0x50, + 0xd0, 0x4a, 0xca, 0x2a, 0xaa, 0x24, 0xa4, 0x1a, + 0x9a, 0x5a, 0xda, 0x52, 0xd2, 0x4c, 0xcc, 0x2c +}; +static UINT8 short_white_lengths[SHORT_CODE_SIZE] = { + 8, 6, 4, 4, 4, 4, 4, 4, + 5, 5, 5, 5, 6, 6, 6, 6, + 6, 6, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8 +}; +static UINT16 short_black_codes[SHORT_CODE_SIZE] = { + 0x3b0, 0x2, 0x3, 0x1, 0x6, 0xc, 0x4, 0x18, + 0x28, 0x8, 0x10, 0x50, 0x70, 0x20, 0xe0, 0x30, + 0x3a0, 0x60, 0x40, 0x730, 0xb0, 0x1b0, 0x760, 0xa0, + 0x740, 0xc0, 0x530, 0xd30, + 0x330, 0xb30, 0x160, 0x960, + 0x560, 0xd60, 0x4b0, 0xcb0, + 0x2b0, 0xab0, 0x6b0, 0xeb0, + 0x360, 0xb60, 0x5b0, 0xdb0, + 0x2a0, 0xaa0, 0x6a0, 0xea0, + 0x260, 0xa60, 0x4a0, 0xca0, + 0x240, 0xec0, 0x1c0, 0xe40, + 0x140, 0x1a0, 0x9a0, 0xd40, + 0x340, 0x5a0, 0x660, 0xe60 +}; +static UINT8 short_black_lengths[SHORT_CODE_SIZE] = { + 10, 3, 2, 2, 3, 4, 4, 5, + 6, 6, 7, 7, 7, 8, 8, 9, + 10, 10, 10, 11, 11, 11, 11, 11, + 11, 11, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12 +}; +static UINT16 long_white_codes[LONG_CODE_SIZE] = { + 0x1b, 0x9, 0x3a, 0x76, 0x6c, 0xec, 0x26, 0xa6, + 0x16, 0xe6, 0x66, 0x166, 0x96, 0x196, 0x56, 0x156, + 0xd6, 0x1d6, 0x36, 0x136, 0xb6, 0x1b6, 0x32, 0x132, + 0xb2, 0x6, 0x1b2, + 0x80, 0x180, 0x580, 0x480, 0xc80, + 0x280, 0xa80, 0x680, 0xe80, 0x380, 0xb80, 0x780, 0xf80 +}; +static UINT8 long_white_lengths[LONG_CODE_SIZE] = { + 5, 5, 6, 7, 8, 8, 8, 8, + 8, 8, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, + 9, 6, 9, 11, 11, 11, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12 +}; +static UINT16 long_black_codes[LONG_CODE_SIZE] = { + 0x3c0, 0x130, 0x930, 0xda0, + 0xcc0, 0x2c0, 0xac0, 0x6c0, + 0x16c0, 0xa40, 0x1a40, 0x640, + 0x1640, 0x9c0, 0x19c0, 0x5c0, + 0x15c0, 0xdc0, 0x1dc0, 0x940, + 0x1940, 0x540, 0x1540, 0xb40, + 0x1b40, 0x4c0, 0x14c0, + 0x80, 0x180, 0x580, 0x480, 0xc80, + 0x280, 0xa80, 0x680, 0xe80, 0x380, 0xb80, 0x780, 0xf80 +}; +static UINT8 long_black_lengths[LONG_CODE_SIZE] = { + 10, 12, 12, 12, 12, 12, 12, 13, + 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 11, 11, 11, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12 +}; + +/********************************************************************** + * open_tif_image + * + * Read the header of a tif format image and prepare to read the rest. + **********************************************************************/ + +INT8 open_tif_image( //read header + int fd, //file to read + INT32 *xsize, //size of image + INT32 *ysize, + INT8 *bpp, //bits per pixel + INT8 *photo, //interpretation + INT32 *res //resolution + ) { + INT16 filetype; + INT32 start; //start of tiff directory + INT16 entries; //no of tiff entries + INT32 imagestart; //location of image in file + INT32 resoffset; //location of res + TIFFENTRY tiffentry; //tag table entry + BOOL8 compressed; //compression control + MYRATIONAL resinfo; //resolution + BOOL8 strips = false; //if in strips + + *xsize = -1; //illegal values + *ysize = -1; + *bpp = -1; + *res = -1; + resoffset = -1; + if (read (fd, (char *) &filetype, sizeof filetype) != sizeof filetype + || filetype != INTEL && filetype != MOTO) { + BADIMAGEFORMAT.error ("read_tif_image", LOG, "Filetype"); + return -1; + } + lseek (fd, 4L, 0); + if (read (fd, (char *) &start, sizeof start) != sizeof start) { + READFAILED.error ("read_tif_image", LOG, "Start of tag table"); + return -1; + } + + if (filetype != __NATIVE__) + start = reverse32 (start); + if (start <= 0) { + BADIMAGEFORMAT.error ("read_tif_image", LOG, "Start of tag table"); + return -1; + } + lseek (fd, start, 0); + if (read (fd, (char *) &entries, sizeof (INT16)) != sizeof (INT16)) { + BADIMAGEFORMAT.error ("read_tif_image", LOG, "Size of tag table"); + return -1; + } + if (filetype != __NATIVE__) + entries = reverse16 (entries); + // printf("No of tiff directory entries=%d\n",entries); + imagestart = 0; + compressed = FALSE; + for (; entries-- > 0;) { + if (read (fd, (char *) &tiffentry, sizeof tiffentry) != + sizeof tiffentry) { + BADIMAGEFORMAT.error ("read_tif_image", LOG, "Tag table entry"); + return -1; + } + if (filetype != __NATIVE__) { + tiffentry.type = reverse16 (tiffentry.type); + tiffentry.tag = reverse16 (tiffentry.tag); + tiffentry.length = reverse32 (tiffentry.length); + } + if (tiffentry.type != 3) { //Full 32bit value + if (filetype != __NATIVE__) + tiffentry.value = reverse32 (tiffentry.value); + } + else { + /* A 16bit value in 4 bytes - handle with care. SEE NOTE at start of file */ + if (__NATIVE__ == MOTO) { + if (filetype == MOTO) //MOTO file on MOTO Machine + tiffentry.value = tiffentry.value >> 16; + else //INTEL file on MOTO Machine + tiffentry.value = reverse32 (tiffentry.value); + } + else { //INTEL Machine + if (filetype == MOTO) //MOTO file on INTEL Machine + tiffentry.value = reverse16 ((UINT16) tiffentry.value); + //INTEL file on INTEL Machine NO ACTION NEEDED + } + //Clear top 2 MSBytes + tiffentry.value &= 0x0000ffff; + } + + // printf("Tag=%x, Type=%x, Length=%x, value=%x\n", + // tiffentry.tag,tiffentry.type,tiffentry.length,tiffentry.value); + switch (tiffentry.tag) { + case 0x101: + *ysize = tiffentry.value; + break; + case 0x100: + *xsize = tiffentry.value; + break; + case 0x102: + if (tiffentry.length == 1) + *bpp = (INT8) tiffentry.value; + else + *bpp = 24; + break; + case 0x111: + imagestart = tiffentry.value; + strips = tiffentry.length > 1; + break; + case 0x103: + if (tiffentry.value == 3) { + compressed = TRUE; + } + else if (tiffentry.value != 1) { + BADIMAGEFORMAT.error ("read_tif_image", LOG, "Compression"); + return -1; + } + break; + case 0x11a: + case 0x11b: + //resolution + resoffset = tiffentry.value; + break; + case 0x106: + *photo = (INT8) tiffentry.value; + break; + } //endswitch + } + if (*xsize <= 0 || *ysize <= 0 || *bpp > 24 || imagestart <= 0) { + BADIMAGEFORMAT.error ("read_tif_image", LOG, "Vital tag"); + return -1; + } + tprintf ("Image has %d bit%c per pixel and size (%d,%d)\n", + *bpp, *bpp == 1 ? ' ' : 's', *xsize, *ysize); + if (resoffset >= 0) { + lseek (fd, resoffset, 0); + if (read (fd, (char *) &resinfo, sizeof (resinfo)) != sizeof (resinfo)) { + READFAILED.error ("read_tif_image", LOG, "Resolution"); + return -1; + } + if (filetype != __NATIVE__) { + resinfo.top = reverse32 (resinfo.top); + resinfo.bottom = reverse32 (resinfo.bottom); + } + *res = resinfo.top / resinfo.bottom; + tprintf ("Resolution=%d\n", *res); + } + lseek (fd, (long) imagestart, 0); + if (strips) { + if (read (fd, (char *) &imagestart, sizeof (imagestart)) != + sizeof (imagestart)) { + READFAILED.error ("read_tif_image", LOG, "Strip offset"); + return -1; + } + if (filetype != __NATIVE__) + imagestart = reverse32 (imagestart); + //indirection + lseek (fd, (long) imagestart, 0); + } + return compressed ? -2 : 0; +} + + +/********************************************************************** + * read_tif_image + * + * Read a whole tif image into memory. + **********************************************************************/ + +INT8 read_tif_image( //read whole image + int fd, //file to read + UINT8 *pixels, //pixels of image + INT32 xsize, //size of image + INT32 ysize, + INT8 bpp, //bits per pixel + INT32 //bytes per line + ) { + INT32 xindex; //indices in image + INT32 yindex; + INT32 length; //short length + INT32 biglength; //extender + UINT8 *lengths; //current lengths + UINT16 *codes; //current codes + UINT16 codeword; //current code word + IMAGELINE imageline; //current line + IMAGE image; //dummy image + R_BITSTREAM bits; //read bitstream + UINT8 colour; //current colour + + image.capture (pixels, xsize, ysize, bpp); + codeword = bits.open (fd); //open bitstream + read_eol(&bits, codeword); //find end of line + for (yindex = ysize - 1; yindex >= 0; yindex--) { + imageline.init (); + colour = TRUE; + for (xindex = 0; xindex < xsize;) { + if (colour) { + lengths = long_white_lengths; + codes = long_white_codes; + } + else { + lengths = long_black_lengths; + codes = long_black_codes; + } + for (biglength = 0; biglength < LONG_CODE_SIZE + && (codeword & bits.masks (*lengths)) + != *codes; codes++, lengths++, biglength++); + if (biglength < LONG_CODE_SIZE) { + codeword = bits.read_code (*lengths); + biglength++; + biglength *= SHORT_CODE_SIZE; + } + else + biglength = 0; + if (colour) { + lengths = short_white_lengths; + codes = short_white_codes; + } + else { + lengths = short_black_lengths; + codes = short_black_codes; + } + for (length = 0; length < SHORT_CODE_SIZE + && (codeword & bits.masks (*lengths)) + != *codes; codes++, lengths++, length++); + if (length < SHORT_CODE_SIZE) { + codeword = bits.read_code (*lengths); + for (length += biglength; length > 0; length--, xindex++) + imageline.pixels[xindex] = colour; + colour = !colour; + } + else + break; + } + if (xindex < xsize) { + tprintf ("%d pixels short on line %d", xsize - xindex, yindex); + tprintf (", unknown code=%x\n", codeword); + } + xindex = read_eol (&bits, codeword); + if (xindex > 0) + tprintf ("Discarding %d bits on line %d\n", xindex, yindex); + image.put_line (0, yindex, xsize, &imageline, 0); + } + return 0; +} + + +/********************************************************************** + * read_eol + * + * Take bits out of the stream until and end-of-line code is hit. + **********************************************************************/ + +INT32 read_eol( //read end of line + R_BITSTREAM *bits, //bitstream to read + UINT16 &code //current code + ) { + BOOL8 anyones; //any 1 bits skipped + INT32 bitcount; //total bits skipped + + anyones = FALSE; + bitcount = 0; + while ((code & EOL_MASK) != EOL_CODE) { + if (code & 1) + anyones = TRUE; //discarded one bit + bitcount++; //total discarded bits + code = bits->read_code (1); //take single bits + } + //extract EOL code + code = bits->read_code (EOL_LENGTH); + + if (!anyones) + bitcount = 0; //ignore filler bits + return bitcount; +} + + +/********************************************************************** + * write_moto_tif + * + * Write a whole tif format image and close the file. + **********************************************************************/ + +INT8 write_moto_tif( //write whole image + int fd, //file to write on + UINT8 *pixels, //image pixels + INT32 xsize, //size of image + INT32 ysize, + INT8 bpp, //bits per pixel + INT8 photo, + INT32 res //resolution + ) { + return write_tif_image (fd, pixels, xsize, ysize, bpp, res, MOTO, photo); + //use moto format +} + + +/********************************************************************** + * write_intel_tif + * + * Write a whole tif format image and close the file. + **********************************************************************/ + +INT8 write_intel_tif( //write whole image + int fd, //file to write on + UINT8 *pixels, //image pixels + INT32 xsize, //size of image + INT32 ysize, + INT8 bpp, //bits per pixel + INT8 photo, + INT32 res //resolution + ) { + return write_tif_image (fd, pixels, xsize, ysize, bpp, res, INTEL, photo); + //use intel format +} + + +/********************************************************************** + * write_inverse_tif + * + * Write a whole tif format image and close the file. + **********************************************************************/ + +INT8 write_inverse_tif( //write whole image + int fd, //file to write on + UINT8 *pixels, //image pixels + INT32 xsize, //size of image + INT32 ysize, + INT8 bpp, //bits per pixel + INT8 photo, + INT32 res //resolution + ) { + return write_tif_image (fd, pixels, xsize, ysize, bpp, res, INTEL, + 1 - photo); + //use intel format +} + + +/********************************************************************** + * write_tif_image + * + * Write a whole tif format image and close the file. + **********************************************************************/ + +INT8 write_tif_image( //write whole image + int fd, //file to write on + UINT8 *pixels, //image pixels + INT32 xsize, //size of image + INT32 ysize, + INT8 bpp, //bits per pixel + INT32 res, //resolution + INT16 type, //format type + INT16 photo //metric interp + ) { + INT32 size; //line/image size + INT16 entries; //no of tiff entries + INT32 start; //start of tag table + INT32 zero = 0; + MYRATIONAL resolution; //resolution + TIFFENTRY entry; //current entry + + static TIFFENTRY tags[ENTRIES] = { + {0xfe, 4, 1, 0}, + {0x100, 3, 1, 0}, + {0x101, 3, 1, 0}, + {0x102, 3, 1, 0}, + {0x103, 3, 1, 1}, + {0x106, 3, 1, 1}, + { /*line art */ + 0x107, 3, 1, 1 + }, + {0x10a, 3, 1, 1}, + { + 0x111, 4, 1, START + ENTRIES * sizeof (TIFFENTRY) + + sizeof (INT32) + sizeof (short) + sizeof (MYRATIONAL) * 2 + } + , + {0x112, 3, 1, 1} + , + {0x115, 3, 1, 1} + , + {0x116, 4, 1, 0} + , + {0x117, 4, 1, 0} + , + {0x118, 3, 1, 0} + , + {0x119, 3, 1, 1} + , + { + 0x11a, 5, 1, START + ENTRIES * sizeof (TIFFENTRY) + + sizeof (INT32) + sizeof (short) + }, + { + 0x11b, 5, 1, START + ENTRIES * sizeof (TIFFENTRY) + + sizeof (INT32) + sizeof (short) + sizeof (MYRATIONAL) + } + , + {0x11c, 3, 1, 1} + , + {0x128, 3, 1, 2} + }; + + resolution.top = res; + resolution.bottom = 1; + if (write (fd, (char *) &type, sizeof type) != sizeof type + || type != INTEL && type != MOTO) { + WRITEFAILED.error ("write_tif_image", LOG, "Filetype"); + return -1; + } + start = START; + entries = 0x002a; + if (type != __NATIVE__) + entries = reverse16 (entries); + if (write (fd, (char *) &entries, sizeof entries) != sizeof entries) { + WRITEFAILED.error ("write_tif_image", LOG, "Version"); + return -1; + } + if (type != __NATIVE__) + start = reverse32 (start); + if (write (fd, (char *) &start, sizeof start) != sizeof start) { + WRITEFAILED.error ("write_tif_image", LOG, "Start"); + return -1; + } + lseek (fd, (long) START, 0); + entries = ENTRIES; + if (type != __NATIVE__) + entries = reverse16 (entries); + if (write (fd, (char *) &entries, sizeof entries) != sizeof entries) { + WRITEFAILED.error ("write_tif_image", LOG, "Entries"); + return -1; + } + //line length + size = COMPUTE_IMAGE_XDIM (xsize, bpp); + size *= ysize; //total image size + // if (photo==0) + // { + // tags[0].tag=0xfe; + // tags[0].type=4; + // tags[0].value=0; + // } + // else + // { + // tags[0].tag=0xff; + // tags[0].type=3; + // tags[0].value=1; + // } + tags[1].value = xsize; + tags[2].value = ysize; + if (bpp == 24) { + tags[3].value = 8; + tags[10].value = 3; + tags[5].value = 2; + } + else { + tags[3].value = bpp; + tags[5].value = photo; + } + tags[11].value = ysize; + tags[14].value = (1 << bpp) - 1; + tags[12].value = size; + for (entries = 0; entries < ENTRIES; entries++) { + entry = tags[entries]; //get an entry + /* NB Convert entry.value BEFORE converting entry.type!!! */ + if (entry.type != 3) { //Full 32bit value + if (type != __NATIVE__) + entry.value = reverse32 (entry.value); + } + else { + /* A 16bit value in 4 bytes - handle with care. SEE NOTE at start of file */ + entry.value &= 0x0000ffff; //Ensure top 2 MSBytes clear + if (__NATIVE__ == MOTO) { + if (type == MOTO) //MOTO file on MOTO Machine + entry.value = entry.value << 16; + else //INTEL file on MOTO Machine + entry.value = reverse32 (entry.value); + } + else { //INTEL Machine + if (type == MOTO) //MOTO file on INTEL Machine + entry.value = reverse16 ((UINT16) entry.value); + //INTEL file on INTEL Machine NO ACTION NEEDED + } + } + if (type != __NATIVE__) { + entry.tag = reverse16 (entry.tag); + entry.type = reverse16 (entry.type); + entry.length = reverse32 (entry.length); + } + if (write (fd, (char *) &entry, sizeof (TIFFENTRY)) != + sizeof (TIFFENTRY)) { + WRITEFAILED.error ("write_tif_image", LOG, "Tag Table"); + return -1; + } + } + if (write (fd, (char *) &zero, sizeof zero) != sizeof zero) { + WRITEFAILED.error ("write_tif_image", LOG, "Tag table Terminator"); + return -1; + } + if (type != __NATIVE__) { + resolution.top = reverse32 (resolution.top); + resolution.bottom = reverse32 (resolution.bottom); + } + if (write (fd, (char *) &resolution, sizeof resolution) != sizeof resolution + || write (fd, (char *) &resolution, + sizeof resolution) != sizeof resolution) { + WRITEFAILED.error ("write_tif_image", LOG, "Resolution"); + return -1; + } + if (write (fd, (char *) pixels, (size_t) size) != size) { + WRITEFAILED.error ("write_tif_image", LOG, "Image"); + return -1; + } + close(fd); + return 0; +} + + +/********************************************************************** + * reverse32 + * + * Byte swap the 32 bit number between Motorola & Intel format. + **********************************************************************/ + +//INT32 reverse32( //reverse 32 bit int +//UINT32 value //value to reverse +//) +//{ +// return (value>>24) | (value>>8) & 0xff00 +// | (value<<8) & 0xff0000 | (value<<24); +//} + +/********************************************************************** + * reverse16 + * + * Byte swap the 16 bit number between Motorola & Intel format. + **********************************************************************/ + +//INT16 reverse16( //reverse 16 bit int +//UINT16 value //value to reverse +//) +//{ +// return (value>>8) | (value<<8); +//} diff --git a/image/imgtiff.h b/image/imgtiff.h new file mode 100644 index 0000000000..58fc077507 --- /dev/null +++ b/image/imgtiff.h @@ -0,0 +1,89 @@ +/********************************************************************** + * File: imgtiff.h (Formerly tiff.h) + * Description: Header file for tiff format image reader/writer. + * Author: Ray Smith + * Created: Mon Jun 11 15:19:41 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 IMGTIFF_H +#define IMGTIFF_H + +#include "host.h" +#include "bitstrm.h" + +INT8 open_tif_image( //read header + int fd, //file to read + INT32 *xsize, //size of image + INT32 *ysize, + INT8 *bpp, //bits per pixel + INT8 *photo, //interpretation + INT32 *res //resolution + ); +INT8 read_tif_image( //read whole image + int fd, //file to read + UINT8 *pixels, //pixels of image + INT32 xsize, //size of image + INT32 ysize, + INT8 bpp, //bits per pixel + INT32 //bytes per line + ); +INT32 read_eol( //read end of line + R_BITSTREAM *bits, //bitstream to read + UINT16 &code //current code + ); +INT8 write_moto_tif( //write whole image + int fd, //file to write on + UINT8 *pixels, //image pixels + INT32 xsize, //size of image + INT32 ysize, + INT8 bpp, //bits per pixel + INT8 photo, + INT32 res //resolution + ); +INT8 write_intel_tif( //write whole image + int fd, //file to write on + UINT8 *pixels, //image pixels + INT32 xsize, //size of image + INT32 ysize, + INT8 bpp, //bits per pixel + INT8 photo, + INT32 res //resolution + ); +INT8 write_inverse_tif( //write whole image + int fd, //file to write on + UINT8 *pixels, //image pixels + INT32 xsize, //size of image + INT32 ysize, + INT8 bpp, //bits per pixel + INT8 photo, + INT32 res //resolution + ); +INT8 write_tif_image( //write whole image + int fd, //file to write on + UINT8 *pixels, //image pixels + INT32 xsize, //size of image + INT32 ysize, + INT8 bpp, //bits per pixel + INT32 res, //resolution + INT16 type, //format type + INT16 photo //metric interp + ); +//INT32 reverse32( //reverse 32 bit int +//UINT32 value //value to reverse +//); +//INT16 reverse16( //reverse 16 bit int +//UINT16 value //value to reverse +//); +#endif diff --git a/image/imgunpk.h b/image/imgunpk.h new file mode 100644 index 0000000000..d7a28e0ea1 --- /dev/null +++ b/image/imgunpk.h @@ -0,0 +1,1377 @@ +/********************************************************************** + * File: imgunpk.h (Formerly unpack.h) + * Description: Definitions of fast unpacking look-up tables for images.c. + * Author: Ray Smith + * Created: Mon Jun 11 18:06:34 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 IMGUNPK_H +#define IMGUNPK_H + +#include "host.h" + +UINT8 bpp4table[256][2] = { /*4 bit converter */ + {0, 0} + , {0, 1} + , {0, 2} + , {0, 3} + , + {0, 4} + , {0, 5} + , {0, 6} + , {0, 7} + , + {0, 8} + , {0, 9} + , {0, 10} + , {0, 11} + , + {0, 12} + , {0, 13} + , {0, 14} + , {0, 15} + , + {1, 0} + , {1, 1} + , {1, 2} + , {1, 3} + , + {1, 4} + , {1, 5} + , {1, 6} + , {1, 7} + , + {1, 8} + , {1, 9} + , {1, 10} + , {1, 11} + , + {1, 12} + , {1, 13} + , {1, 14} + , {1, 15} + , + {2, 0} + , {2, 1} + , {2, 2} + , {2, 3} + , + {2, 4} + , {2, 5} + , {2, 6} + , {2, 7} + , + {2, 8} + , {2, 9} + , {2, 10} + , {2, 11} + , + {2, 12} + , {2, 13} + , {2, 14} + , {2, 15} + , + {3, 0} + , {3, 1} + , {3, 2} + , {3, 3} + , + {3, 4} + , {3, 5} + , {3, 6} + , {3, 7} + , + {3, 8} + , {3, 9} + , {3, 10} + , {3, 11} + , + {3, 12} + , {3, 13} + , {3, 14} + , {3, 15} + , + {4, 0} + , {4, 1} + , {4, 2} + , {4, 3} + , + {4, 4} + , {4, 5} + , {4, 6} + , {4, 7} + , + {4, 8} + , {4, 9} + , {4, 10} + , {4, 11} + , + {4, 12} + , {4, 13} + , {4, 14} + , {4, 15} + , + {5, 0} + , {5, 1} + , {5, 2} + , {5, 3} + , + {5, 4} + , {5, 5} + , {5, 6} + , {5, 7} + , + {5, 8} + , {5, 9} + , {5, 10} + , {5, 11} + , + {5, 12} + , {5, 13} + , {5, 14} + , {5, 15} + , + {6, 0} + , {6, 1} + , {6, 2} + , {6, 3} + , + {6, 4} + , {6, 5} + , {6, 6} + , {6, 7} + , + {6, 8} + , {6, 9} + , {6, 10} + , {6, 11} + , + {6, 12} + , {6, 13} + , {6, 14} + , {6, 15} + , + {7, 0} + , {7, 1} + , {7, 2} + , {7, 3} + , + {7, 4} + , {7, 5} + , {7, 6} + , {7, 7} + , + {7, 8} + , {7, 9} + , {7, 10} + , {7, 11} + , + {7, 12} + , {7, 13} + , {7, 14} + , {7, 15} + , + {8, 0} + , {8, 1} + , {8, 2} + , {8, 3} + , + {8, 4} + , {8, 5} + , {8, 6} + , {8, 7} + , + {8, 8} + , {8, 9} + , {8, 10} + , {8, 11} + , + {8, 12} + , {8, 13} + , {8, 14} + , {8, 15} + , + {9, 0} + , {9, 1} + , {9, 2} + , {9, 3} + , + {9, 4} + , {9, 5} + , {9, 6} + , {9, 7} + , + {9, 8} + , {9, 9} + , {9, 10} + , {9, 11} + , + {9, 12} + , {9, 13} + , {9, 14} + , {9, 15} + , + {10, 0} + , {10, 1} + , {10, 2} + , {10, 3} + , + {10, 4} + , {10, 5} + , {10, 6} + , {10, 7} + , + {10, 8} + , {10, 9} + , {10, 10} + , {10, 11} + , + {10, 12} + , {10, 13} + , {10, 14} + , {10, 15} + , + {11, 0} + , {11, 1} + , {11, 2} + , {11, 3} + , + {11, 4} + , {11, 5} + , {11, 6} + , {11, 7} + , + {11, 8} + , {11, 9} + , {11, 10} + , {11, 11} + , + {11, 12} + , {11, 13} + , {11, 14} + , {11, 15} + , + {12, 0} + , {12, 1} + , {12, 2} + , {12, 3} + , + {12, 4} + , {12, 5} + , {12, 6} + , {12, 7} + , + {12, 8} + , {12, 9} + , {12, 10} + , {12, 11} + , + {12, 12} + , {12, 13} + , {12, 14} + , {12, 15} + , + {13, 0} + , {13, 1} + , {13, 2} + , {13, 3} + , + {13, 4} + , {13, 5} + , {13, 6} + , {13, 7} + , + {13, 8} + , {13, 9} + , {13, 10} + , {13, 11} + , + {13, 12} + , {13, 13} + , {13, 14} + , {13, 15} + , + {14, 0} + , {14, 1} + , {14, 2} + , {14, 3} + , + {14, 4} + , {14, 5} + , {14, 6} + , {14, 7} + , + {14, 8} + , {14, 9} + , {14, 10} + , {14, 11} + , + {14, 12} + , {14, 13} + , {14, 14} + , {14, 15} + , + {15, 0} + , {15, 1} + , {15, 2} + , {15, 3} + , + {15, 4} + , {15, 5} + , {15, 6} + , {15, 7} + , + {15, 8} + , {15, 9} + , {15, 10} + , {15, 11} + , + {15, 12} + , {15, 13} + , {15, 14} + , {15, 15} + , +}; + +UINT8 bpp2table[256][4] = { /*2bpp converter */ + {0, 0, 0, 0} + , + {0, 0, 0, 1} + , + {0, 0, 0, 2} + , + {0, 0, 0, 3} + , + {0, 0, 1, 0} + , + {0, 0, 1, 1} + , + {0, 0, 1, 2} + , + {0, 0, 1, 3} + , + {0, 0, 2, 0} + , + {0, 0, 2, 1} + , + {0, 0, 2, 2} + , + {0, 0, 2, 3} + , + {0, 0, 3, 0} + , + {0, 0, 3, 1} + , + {0, 0, 3, 2} + , + {0, 0, 3, 3} + , + {0, 1, 0, 0} + , + {0, 1, 0, 1} + , + {0, 1, 0, 2} + , + {0, 1, 0, 3} + , + {0, 1, 1, 0} + , + {0, 1, 1, 1} + , + {0, 1, 1, 2} + , + {0, 1, 1, 3} + , + {0, 1, 2, 0} + , + {0, 1, 2, 1} + , + {0, 1, 2, 2} + , + {0, 1, 2, 3} + , + {0, 1, 3, 0} + , + {0, 1, 3, 1} + , + {0, 1, 3, 2} + , + {0, 1, 3, 3} + , + {0, 2, 0, 0} + , + {0, 2, 0, 1} + , + {0, 2, 0, 2} + , + {0, 2, 0, 3} + , + {0, 2, 1, 0} + , + {0, 2, 1, 1} + , + {0, 2, 1, 2} + , + {0, 2, 1, 3} + , + {0, 2, 2, 0} + , + {0, 2, 2, 1} + , + {0, 2, 2, 2} + , + {0, 2, 2, 3} + , + {0, 2, 3, 0} + , + {0, 2, 3, 1} + , + {0, 2, 3, 2} + , + {0, 2, 3, 3} + , + {0, 3, 0, 0} + , + {0, 3, 0, 1} + , + {0, 3, 0, 2} + , + {0, 3, 0, 3} + , + {0, 3, 1, 0} + , + {0, 3, 1, 1} + , + {0, 3, 1, 2} + , + {0, 3, 1, 3} + , + {0, 3, 2, 0} + , + {0, 3, 2, 1} + , + {0, 3, 2, 2} + , + {0, 3, 2, 3} + , + {0, 3, 3, 0} + , + {0, 3, 3, 1} + , + {0, 3, 3, 2} + , + {0, 3, 3, 3} + , + {1, 0, 0, 0} + , + {1, 0, 0, 1} + , + {1, 0, 0, 2} + , + {1, 0, 0, 3} + , + {1, 0, 1, 0} + , + {1, 0, 1, 1} + , + {1, 0, 1, 2} + , + {1, 0, 1, 3} + , + {1, 0, 2, 0} + , + {1, 0, 2, 1} + , + {1, 0, 2, 2} + , + {1, 0, 2, 3} + , + {1, 0, 3, 0} + , + {1, 0, 3, 1} + , + {1, 0, 3, 2} + , + {1, 0, 3, 3} + , + {1, 1, 0, 0} + , + {1, 1, 0, 1} + , + {1, 1, 0, 2} + , + {1, 1, 0, 3} + , + {1, 1, 1, 0} + , + {1, 1, 1, 1} + , + {1, 1, 1, 2} + , + {1, 1, 1, 3} + , + {1, 1, 2, 0} + , + {1, 1, 2, 1} + , + {1, 1, 2, 2} + , + {1, 1, 2, 3} + , + {1, 1, 3, 0} + , + {1, 1, 3, 1} + , + {1, 1, 3, 2} + , + {1, 1, 3, 3} + , + {1, 2, 0, 0} + , + {1, 2, 0, 1} + , + {1, 2, 0, 2} + , + {1, 2, 0, 3} + , + {1, 2, 1, 0} + , + {1, 2, 1, 1} + , + {1, 2, 1, 2} + , + {1, 2, 1, 3} + , + {1, 2, 2, 0} + , + {1, 2, 2, 1} + , + {1, 2, 2, 2} + , + {1, 2, 2, 3} + , + {1, 2, 3, 0} + , + {1, 2, 3, 1} + , + {1, 2, 3, 2} + , + {1, 2, 3, 3} + , + {1, 3, 0, 0} + , + {1, 3, 0, 1} + , + {1, 3, 0, 2} + , + {1, 3, 0, 3} + , + {1, 3, 1, 0} + , + {1, 3, 1, 1} + , + {1, 3, 1, 2} + , + {1, 3, 1, 3} + , + {1, 3, 2, 0} + , + {1, 3, 2, 1} + , + {1, 3, 2, 2} + , + {1, 3, 2, 3} + , + {1, 3, 3, 0} + , + {1, 3, 3, 1} + , + {1, 3, 3, 2} + , + {1, 3, 3, 3} + , + {2, 0, 0, 0} + , + {2, 0, 0, 1} + , + {2, 0, 0, 2} + , + {2, 0, 0, 3} + , + {2, 0, 1, 0} + , + {2, 0, 1, 1} + , + {2, 0, 1, 2} + , + {2, 0, 1, 3} + , + {2, 0, 2, 0} + , + {2, 0, 2, 1} + , + {2, 0, 2, 2} + , + {2, 0, 2, 3} + , + {2, 0, 3, 0} + , + {2, 0, 3, 1} + , + {2, 0, 3, 2} + , + {2, 0, 3, 3} + , + {2, 1, 0, 0} + , + {2, 1, 0, 1} + , + {2, 1, 0, 2} + , + {2, 1, 0, 3} + , + {2, 1, 1, 0} + , + {2, 1, 1, 1} + , + {2, 1, 1, 2} + , + {2, 1, 1, 3} + , + {2, 1, 2, 0} + , + {2, 1, 2, 1} + , + {2, 1, 2, 2} + , + {2, 1, 2, 3} + , + {2, 1, 3, 0} + , + {2, 1, 3, 1} + , + {2, 1, 3, 2} + , + {2, 1, 3, 3} + , + {2, 2, 0, 0} + , + {2, 2, 0, 1} + , + {2, 2, 0, 2} + , + {2, 2, 0, 3} + , + {2, 2, 1, 0} + , + {2, 2, 1, 1} + , + {2, 2, 1, 2} + , + {2, 2, 1, 3} + , + {2, 2, 2, 0} + , + {2, 2, 2, 1} + , + {2, 2, 2, 2} + , + {2, 2, 2, 3} + , + {2, 2, 3, 0} + , + {2, 2, 3, 1} + , + {2, 2, 3, 2} + , + {2, 2, 3, 3} + , + {2, 3, 0, 0} + , + {2, 3, 0, 1} + , + {2, 3, 0, 2} + , + {2, 3, 0, 3} + , + {2, 3, 1, 0} + , + {2, 3, 1, 1} + , + {2, 3, 1, 2} + , + {2, 3, 1, 3} + , + {2, 3, 2, 0} + , + {2, 3, 2, 1} + , + {2, 3, 2, 2} + , + {2, 3, 2, 3} + , + {2, 3, 3, 0} + , + {2, 3, 3, 1} + , + {2, 3, 3, 2} + , + {2, 3, 3, 3} + , + {3, 0, 0, 0} + , + {3, 0, 0, 1} + , + {3, 0, 0, 2} + , + {3, 0, 0, 3} + , + {3, 0, 1, 0} + , + {3, 0, 1, 1} + , + {3, 0, 1, 2} + , + {3, 0, 1, 3} + , + {3, 0, 2, 0} + , + {3, 0, 2, 1} + , + {3, 0, 2, 2} + , + {3, 0, 2, 3} + , + {3, 0, 3, 0} + , + {3, 0, 3, 1} + , + {3, 0, 3, 2} + , + {3, 0, 3, 3} + , + {3, 1, 0, 0} + , + {3, 1, 0, 1} + , + {3, 1, 0, 2} + , + {3, 1, 0, 3} + , + {3, 1, 1, 0} + , + {3, 1, 1, 1} + , + {3, 1, 1, 2} + , + {3, 1, 1, 3} + , + {3, 1, 2, 0} + , + {3, 1, 2, 1} + , + {3, 1, 2, 2} + , + {3, 1, 2, 3} + , + {3, 1, 3, 0} + , + {3, 1, 3, 1} + , + {3, 1, 3, 2} + , + {3, 1, 3, 3} + , + {3, 2, 0, 0} + , + {3, 2, 0, 1} + , + {3, 2, 0, 2} + , + {3, 2, 0, 3} + , + {3, 2, 1, 0} + , + {3, 2, 1, 1} + , + {3, 2, 1, 2} + , + {3, 2, 1, 3} + , + {3, 2, 2, 0} + , + {3, 2, 2, 1} + , + {3, 2, 2, 2} + , + {3, 2, 2, 3} + , + {3, 2, 3, 0} + , + {3, 2, 3, 1} + , + {3, 2, 3, 2} + , + {3, 2, 3, 3} + , + {3, 3, 0, 0} + , + {3, 3, 0, 1} + , + {3, 3, 0, 2} + , + {3, 3, 0, 3} + , + {3, 3, 1, 0} + , + {3, 3, 1, 1} + , + {3, 3, 1, 2} + , + {3, 3, 1, 3} + , + {3, 3, 2, 0} + , + {3, 3, 2, 1} + , + {3, 3, 2, 2} + , + {3, 3, 2, 3} + , + {3, 3, 3, 0} + , + {3, 3, 3, 1} + , + {3, 3, 3, 2} + , + {3, 3, 3, 3} + , +}; + +UINT8 bpp1table[256][8] = { /*1bpp converter */ + {0, 0, 0, 0, 0, 0, 0, 0} + , + {0, 0, 0, 0, 0, 0, 0, 1} + , + {0, 0, 0, 0, 0, 0, 1, 0} + , + {0, 0, 0, 0, 0, 0, 1, 1} + , + {0, 0, 0, 0, 0, 1, 0, 0} + , + {0, 0, 0, 0, 0, 1, 0, 1} + , + {0, 0, 0, 0, 0, 1, 1, 0} + , + {0, 0, 0, 0, 0, 1, 1, 1} + , + {0, 0, 0, 0, 1, 0, 0, 0} + , + {0, 0, 0, 0, 1, 0, 0, 1} + , + {0, 0, 0, 0, 1, 0, 1, 0} + , + {0, 0, 0, 0, 1, 0, 1, 1} + , + {0, 0, 0, 0, 1, 1, 0, 0} + , + {0, 0, 0, 0, 1, 1, 0, 1} + , + {0, 0, 0, 0, 1, 1, 1, 0} + , + {0, 0, 0, 0, 1, 1, 1, 1} + , + {0, 0, 0, 1, 0, 0, 0, 0} + , + {0, 0, 0, 1, 0, 0, 0, 1} + , + {0, 0, 0, 1, 0, 0, 1, 0} + , + {0, 0, 0, 1, 0, 0, 1, 1} + , + {0, 0, 0, 1, 0, 1, 0, 0} + , + {0, 0, 0, 1, 0, 1, 0, 1} + , + {0, 0, 0, 1, 0, 1, 1, 0} + , + {0, 0, 0, 1, 0, 1, 1, 1} + , + {0, 0, 0, 1, 1, 0, 0, 0} + , + {0, 0, 0, 1, 1, 0, 0, 1} + , + {0, 0, 0, 1, 1, 0, 1, 0} + , + {0, 0, 0, 1, 1, 0, 1, 1} + , + {0, 0, 0, 1, 1, 1, 0, 0} + , + {0, 0, 0, 1, 1, 1, 0, 1} + , + {0, 0, 0, 1, 1, 1, 1, 0} + , + {0, 0, 0, 1, 1, 1, 1, 1} + , + {0, 0, 1, 0, 0, 0, 0, 0} + , + {0, 0, 1, 0, 0, 0, 0, 1} + , + {0, 0, 1, 0, 0, 0, 1, 0} + , + {0, 0, 1, 0, 0, 0, 1, 1} + , + {0, 0, 1, 0, 0, 1, 0, 0} + , + {0, 0, 1, 0, 0, 1, 0, 1} + , + {0, 0, 1, 0, 0, 1, 1, 0} + , + {0, 0, 1, 0, 0, 1, 1, 1} + , + {0, 0, 1, 0, 1, 0, 0, 0} + , + {0, 0, 1, 0, 1, 0, 0, 1} + , + {0, 0, 1, 0, 1, 0, 1, 0} + , + {0, 0, 1, 0, 1, 0, 1, 1} + , + {0, 0, 1, 0, 1, 1, 0, 0} + , + {0, 0, 1, 0, 1, 1, 0, 1} + , + {0, 0, 1, 0, 1, 1, 1, 0} + , + {0, 0, 1, 0, 1, 1, 1, 1} + , + {0, 0, 1, 1, 0, 0, 0, 0} + , + {0, 0, 1, 1, 0, 0, 0, 1} + , + {0, 0, 1, 1, 0, 0, 1, 0} + , + {0, 0, 1, 1, 0, 0, 1, 1} + , + {0, 0, 1, 1, 0, 1, 0, 0} + , + {0, 0, 1, 1, 0, 1, 0, 1} + , + {0, 0, 1, 1, 0, 1, 1, 0} + , + {0, 0, 1, 1, 0, 1, 1, 1} + , + {0, 0, 1, 1, 1, 0, 0, 0} + , + {0, 0, 1, 1, 1, 0, 0, 1} + , + {0, 0, 1, 1, 1, 0, 1, 0} + , + {0, 0, 1, 1, 1, 0, 1, 1} + , + {0, 0, 1, 1, 1, 1, 0, 0} + , + {0, 0, 1, 1, 1, 1, 0, 1} + , + {0, 0, 1, 1, 1, 1, 1, 0} + , + {0, 0, 1, 1, 1, 1, 1, 1} + , + {0, 1, 0, 0, 0, 0, 0, 0} + , + {0, 1, 0, 0, 0, 0, 0, 1} + , + {0, 1, 0, 0, 0, 0, 1, 0} + , + {0, 1, 0, 0, 0, 0, 1, 1} + , + {0, 1, 0, 0, 0, 1, 0, 0} + , + {0, 1, 0, 0, 0, 1, 0, 1} + , + {0, 1, 0, 0, 0, 1, 1, 0} + , + {0, 1, 0, 0, 0, 1, 1, 1} + , + {0, 1, 0, 0, 1, 0, 0, 0} + , + {0, 1, 0, 0, 1, 0, 0, 1} + , + {0, 1, 0, 0, 1, 0, 1, 0} + , + {0, 1, 0, 0, 1, 0, 1, 1} + , + {0, 1, 0, 0, 1, 1, 0, 0} + , + {0, 1, 0, 0, 1, 1, 0, 1} + , + {0, 1, 0, 0, 1, 1, 1, 0} + , + {0, 1, 0, 0, 1, 1, 1, 1} + , + {0, 1, 0, 1, 0, 0, 0, 0} + , + {0, 1, 0, 1, 0, 0, 0, 1} + , + {0, 1, 0, 1, 0, 0, 1, 0} + , + {0, 1, 0, 1, 0, 0, 1, 1} + , + {0, 1, 0, 1, 0, 1, 0, 0} + , + {0, 1, 0, 1, 0, 1, 0, 1} + , + {0, 1, 0, 1, 0, 1, 1, 0} + , + {0, 1, 0, 1, 0, 1, 1, 1} + , + {0, 1, 0, 1, 1, 0, 0, 0} + , + {0, 1, 0, 1, 1, 0, 0, 1} + , + {0, 1, 0, 1, 1, 0, 1, 0} + , + {0, 1, 0, 1, 1, 0, 1, 1} + , + {0, 1, 0, 1, 1, 1, 0, 0} + , + {0, 1, 0, 1, 1, 1, 0, 1} + , + {0, 1, 0, 1, 1, 1, 1, 0} + , + {0, 1, 0, 1, 1, 1, 1, 1} + , + {0, 1, 1, 0, 0, 0, 0, 0} + , + {0, 1, 1, 0, 0, 0, 0, 1} + , + {0, 1, 1, 0, 0, 0, 1, 0} + , + {0, 1, 1, 0, 0, 0, 1, 1} + , + {0, 1, 1, 0, 0, 1, 0, 0} + , + {0, 1, 1, 0, 0, 1, 0, 1} + , + {0, 1, 1, 0, 0, 1, 1, 0} + , + {0, 1, 1, 0, 0, 1, 1, 1} + , + {0, 1, 1, 0, 1, 0, 0, 0} + , + {0, 1, 1, 0, 1, 0, 0, 1} + , + {0, 1, 1, 0, 1, 0, 1, 0} + , + {0, 1, 1, 0, 1, 0, 1, 1} + , + {0, 1, 1, 0, 1, 1, 0, 0} + , + {0, 1, 1, 0, 1, 1, 0, 1} + , + {0, 1, 1, 0, 1, 1, 1, 0} + , + {0, 1, 1, 0, 1, 1, 1, 1} + , + {0, 1, 1, 1, 0, 0, 0, 0} + , + {0, 1, 1, 1, 0, 0, 0, 1} + , + {0, 1, 1, 1, 0, 0, 1, 0} + , + {0, 1, 1, 1, 0, 0, 1, 1} + , + {0, 1, 1, 1, 0, 1, 0, 0} + , + {0, 1, 1, 1, 0, 1, 0, 1} + , + {0, 1, 1, 1, 0, 1, 1, 0} + , + {0, 1, 1, 1, 0, 1, 1, 1} + , + {0, 1, 1, 1, 1, 0, 0, 0} + , + {0, 1, 1, 1, 1, 0, 0, 1} + , + {0, 1, 1, 1, 1, 0, 1, 0} + , + {0, 1, 1, 1, 1, 0, 1, 1} + , + {0, 1, 1, 1, 1, 1, 0, 0} + , + {0, 1, 1, 1, 1, 1, 0, 1} + , + {0, 1, 1, 1, 1, 1, 1, 0} + , + {0, 1, 1, 1, 1, 1, 1, 1} + , + {1, 0, 0, 0, 0, 0, 0, 0} + , + {1, 0, 0, 0, 0, 0, 0, 1} + , + {1, 0, 0, 0, 0, 0, 1, 0} + , + {1, 0, 0, 0, 0, 0, 1, 1} + , + {1, 0, 0, 0, 0, 1, 0, 0} + , + {1, 0, 0, 0, 0, 1, 0, 1} + , + {1, 0, 0, 0, 0, 1, 1, 0} + , + {1, 0, 0, 0, 0, 1, 1, 1} + , + {1, 0, 0, 0, 1, 0, 0, 0} + , + {1, 0, 0, 0, 1, 0, 0, 1} + , + {1, 0, 0, 0, 1, 0, 1, 0} + , + {1, 0, 0, 0, 1, 0, 1, 1} + , + {1, 0, 0, 0, 1, 1, 0, 0} + , + {1, 0, 0, 0, 1, 1, 0, 1} + , + {1, 0, 0, 0, 1, 1, 1, 0} + , + {1, 0, 0, 0, 1, 1, 1, 1} + , + {1, 0, 0, 1, 0, 0, 0, 0} + , + {1, 0, 0, 1, 0, 0, 0, 1} + , + {1, 0, 0, 1, 0, 0, 1, 0} + , + {1, 0, 0, 1, 0, 0, 1, 1} + , + {1, 0, 0, 1, 0, 1, 0, 0} + , + {1, 0, 0, 1, 0, 1, 0, 1} + , + {1, 0, 0, 1, 0, 1, 1, 0} + , + {1, 0, 0, 1, 0, 1, 1, 1} + , + {1, 0, 0, 1, 1, 0, 0, 0} + , + {1, 0, 0, 1, 1, 0, 0, 1} + , + {1, 0, 0, 1, 1, 0, 1, 0} + , + {1, 0, 0, 1, 1, 0, 1, 1} + , + {1, 0, 0, 1, 1, 1, 0, 0} + , + {1, 0, 0, 1, 1, 1, 0, 1} + , + {1, 0, 0, 1, 1, 1, 1, 0} + , + {1, 0, 0, 1, 1, 1, 1, 1} + , + {1, 0, 1, 0, 0, 0, 0, 0} + , + {1, 0, 1, 0, 0, 0, 0, 1} + , + {1, 0, 1, 0, 0, 0, 1, 0} + , + {1, 0, 1, 0, 0, 0, 1, 1} + , + {1, 0, 1, 0, 0, 1, 0, 0} + , + {1, 0, 1, 0, 0, 1, 0, 1} + , + {1, 0, 1, 0, 0, 1, 1, 0} + , + {1, 0, 1, 0, 0, 1, 1, 1} + , + {1, 0, 1, 0, 1, 0, 0, 0} + , + {1, 0, 1, 0, 1, 0, 0, 1} + , + {1, 0, 1, 0, 1, 0, 1, 0} + , + {1, 0, 1, 0, 1, 0, 1, 1} + , + {1, 0, 1, 0, 1, 1, 0, 0} + , + {1, 0, 1, 0, 1, 1, 0, 1} + , + {1, 0, 1, 0, 1, 1, 1, 0} + , + {1, 0, 1, 0, 1, 1, 1, 1} + , + {1, 0, 1, 1, 0, 0, 0, 0} + , + {1, 0, 1, 1, 0, 0, 0, 1} + , + {1, 0, 1, 1, 0, 0, 1, 0} + , + {1, 0, 1, 1, 0, 0, 1, 1} + , + {1, 0, 1, 1, 0, 1, 0, 0} + , + {1, 0, 1, 1, 0, 1, 0, 1} + , + {1, 0, 1, 1, 0, 1, 1, 0} + , + {1, 0, 1, 1, 0, 1, 1, 1} + , + {1, 0, 1, 1, 1, 0, 0, 0} + , + {1, 0, 1, 1, 1, 0, 0, 1} + , + {1, 0, 1, 1, 1, 0, 1, 0} + , + {1, 0, 1, 1, 1, 0, 1, 1} + , + {1, 0, 1, 1, 1, 1, 0, 0} + , + {1, 0, 1, 1, 1, 1, 0, 1} + , + {1, 0, 1, 1, 1, 1, 1, 0} + , + {1, 0, 1, 1, 1, 1, 1, 1} + , + {1, 1, 0, 0, 0, 0, 0, 0} + , + {1, 1, 0, 0, 0, 0, 0, 1} + , + {1, 1, 0, 0, 0, 0, 1, 0} + , + {1, 1, 0, 0, 0, 0, 1, 1} + , + {1, 1, 0, 0, 0, 1, 0, 0} + , + {1, 1, 0, 0, 0, 1, 0, 1} + , + {1, 1, 0, 0, 0, 1, 1, 0} + , + {1, 1, 0, 0, 0, 1, 1, 1} + , + {1, 1, 0, 0, 1, 0, 0, 0} + , + {1, 1, 0, 0, 1, 0, 0, 1} + , + {1, 1, 0, 0, 1, 0, 1, 0} + , + {1, 1, 0, 0, 1, 0, 1, 1} + , + {1, 1, 0, 0, 1, 1, 0, 0} + , + {1, 1, 0, 0, 1, 1, 0, 1} + , + {1, 1, 0, 0, 1, 1, 1, 0} + , + {1, 1, 0, 0, 1, 1, 1, 1} + , + {1, 1, 0, 1, 0, 0, 0, 0} + , + {1, 1, 0, 1, 0, 0, 0, 1} + , + {1, 1, 0, 1, 0, 0, 1, 0} + , + {1, 1, 0, 1, 0, 0, 1, 1} + , + {1, 1, 0, 1, 0, 1, 0, 0} + , + {1, 1, 0, 1, 0, 1, 0, 1} + , + {1, 1, 0, 1, 0, 1, 1, 0} + , + {1, 1, 0, 1, 0, 1, 1, 1} + , + {1, 1, 0, 1, 1, 0, 0, 0} + , + {1, 1, 0, 1, 1, 0, 0, 1} + , + {1, 1, 0, 1, 1, 0, 1, 0} + , + {1, 1, 0, 1, 1, 0, 1, 1} + , + {1, 1, 0, 1, 1, 1, 0, 0} + , + {1, 1, 0, 1, 1, 1, 0, 1} + , + {1, 1, 0, 1, 1, 1, 1, 0} + , + {1, 1, 0, 1, 1, 1, 1, 1} + , + {1, 1, 1, 0, 0, 0, 0, 0} + , + {1, 1, 1, 0, 0, 0, 0, 1} + , + {1, 1, 1, 0, 0, 0, 1, 0} + , + {1, 1, 1, 0, 0, 0, 1, 1} + , + {1, 1, 1, 0, 0, 1, 0, 0} + , + {1, 1, 1, 0, 0, 1, 0, 1} + , + {1, 1, 1, 0, 0, 1, 1, 0} + , + {1, 1, 1, 0, 0, 1, 1, 1} + , + {1, 1, 1, 0, 1, 0, 0, 0} + , + {1, 1, 1, 0, 1, 0, 0, 1} + , + {1, 1, 1, 0, 1, 0, 1, 0} + , + {1, 1, 1, 0, 1, 0, 1, 1} + , + {1, 1, 1, 0, 1, 1, 0, 0} + , + {1, 1, 1, 0, 1, 1, 0, 1} + , + {1, 1, 1, 0, 1, 1, 1, 0} + , + {1, 1, 1, 0, 1, 1, 1, 1} + , + {1, 1, 1, 1, 0, 0, 0, 0} + , + {1, 1, 1, 1, 0, 0, 0, 1} + , + {1, 1, 1, 1, 0, 0, 1, 0} + , + {1, 1, 1, 1, 0, 0, 1, 1} + , + {1, 1, 1, 1, 0, 1, 0, 0} + , + {1, 1, 1, 1, 0, 1, 0, 1} + , + {1, 1, 1, 1, 0, 1, 1, 0} + , + {1, 1, 1, 1, 0, 1, 1, 1} + , + {1, 1, 1, 1, 1, 0, 0, 0} + , + {1, 1, 1, 1, 1, 0, 0, 1} + , + {1, 1, 1, 1, 1, 0, 1, 0} + , + {1, 1, 1, 1, 1, 0, 1, 1} + , + {1, 1, 1, 1, 1, 1, 0, 0} + , + {1, 1, 1, 1, 1, 1, 0, 1} + , + {1, 1, 1, 1, 1, 1, 1, 0} + , + {1, 1, 1, 1, 1, 1, 1, 1} + , +}; +#endif diff --git a/phototest.tif b/phototest.tif new file mode 100644 index 0000000000000000000000000000000000000000..adac046dba073285f945446d5ad557894da82c9b GIT binary patch literal 38668 zcmeI5F^}U&cE=yD>@LG-kb8mcu}zbKgEJiUViO*QXdN6F*udXm77i{n8xxqoHd-af zSS5$)%+X@*D>yPHIQU8suySzd`2vgE5)@2g&u}ooaTbY$x2(mr zD6;s|t5>h89$!7qef;s?)1MHLPiy)k@^IveC)~065s_CP8(jQRea!Vl>-eZX=6iqa zRh(l~`myOzpjt}3&(#~*M|u7@n;_WBR;>t|G21s zi|B)&Tz@A2@bN#1|8p#R3FhyUtNckP|K#tn6a&;8^Bclf97$N7+ds4u++dJXg% z=rz!5pw~dJfnEc>26_$j8t669YoOOauYq0zy#{&>^cv_j&}*RAK(B#b1HA@%4fGoL z-e@4O>l~Z8M6wnst3L-s8Lg@>eEo(JZk^x9JLWnU;0asR2G3J;!e0cD#>WvK(-S)0 z<*jz)b9L54g|y1u2cA$67e$h%@li=^DBR{zL^;>n6x`+N81jlhs^6L|uoq-oy4??s`L>G}C zMkM`!#1y>zJR~tQhI!E1DnE_gCV$q8<171$qm$KV~o_zTKR1RfQR7r>~) zT>6pYWm0?&-qeKT`?S=Wd16u-dV_Sl2!@Y)aW{B=NC7@rzL>~FnS8e4t(d1H<}D0? z7w+Z78axnW>y>Ou_h>sCh%{m5%3=AK166nrC5X*Tu=Vv6q6*SiXjL-r-q`A-J)Tk?1p)@4Bsg zzY>iDFVN_Q3u<_;H0J$d0VSQvA{Appd4SQRFEII@QvlwujeGAgxDQ%- z-u=}1(E7WdG(W7nlTC9g|Bk?B(bxS=9@CU$4(V98IDLu^ z8%XwI)f-Sz5xj48@^v9ss9hDTJVSUQ(h4jHCHz!@g{Bx_ePYLRSkycz^jmA}^T>Iz z+Cm&uWchSr%U~_*Cm;4iivr&BWHOt+EmQcZ<7rW(r&#actrj^yvA~s+!Ymd=8KvyS zE(|bN_j`d1RyoJuO;!607c@B?4PT=At4Nn@Z-$j%=FBU(D zJ3YrUSs8`$QYeQbNpE#x$oDRZc@obJ3!OmszL0dL!rnP*i=y;lWs(DE2@(j14l8*+@h=)tn zUhnE7=0!Psu~mCfk$FGW&>s;zdRMyr0WJ7-e~@@d-iAlNc|1XXM2YiaU4Nh=^Bz|` z{%I~7d%41(%(J742Rrn=IenrZ)$-A5AlP0)=f!q-iu-r-WGmTdVSnN!v*u|h`m@v$N2O?lKD z+~sqPr&yK`;k>3K&B0wh4TW&cT*$S#{d>ksA5cDyl-lDIQ)W=a^v}OY1Gp~DYTvZ~ zfKQH;+RsyXrU;zsf4N!@^w7YKxnpsWQt;rJ#swCjOFP5@bq^j#O4BffXDUXEA`d1D zxSx0LU#Tiy0zY=FGlY~h*7bYv$VH7(c&1{k;P48C_3iFF7b!)1D_+7pERJ{QxyUAX zL#rTP#S{0?Uz-Py%*i)=5FWv)u6XtVcp2t{nztzLp}$RkfLH79Uj6GLrQE-Ww%5F+ z%J1I4k}7-7+5Y%}OQ`$t;B30_hVd~nh2u@{(H|~SiuT5cc8;uJ-U5DVbjRt1{Zl7j zfG1xy)Xx6wi@Wn&r1Vkv2oY4F1_%)gWiGCWhs%dxS95dyiT$4TnVZ_%uG4Do?Ux zpTif}hm)%avB=@*s|mKoyqDPMAABiS-tP=yQU0`C%OOIl!5gd+LL~$9*aY9aF>q~L zh4=S4`e{q>f^~$5CnBi9It)fH5#$=K; zBTvJ7%e**`*>44-6anm9{C2v~haBW+~?e7ci zA=u>mp+p1?9x62DgExZS(iD+d$ID}QIB0hslSFVT!zJ*F1g>qX@P1?H)jTL1JB8}Z zB=T82=Ae{GafmKqjt|~j9b(%xz;_%a;F$!jXsgsb=oj)O6eIS;Jn`&r<^lgqn2Q#V zo+{=cNMaT@^I~h-JYwFY;br3awS4r{t{g8j3%jo5gKN7bUz>;af|rZQU)J*N@CueM zTinXi8~HZ8{n5tl@Hj%s z`WwHfd0YLVd0QR*@hObyMxLiUwg*2I&k>%hc>m#eyY})3ynxK(k$zP3j@klpdn0Fm z3G!g%bBs5|_B<}m(OzeN&YrPJ!1jkrY|mr{BU9TST-#P*9(GGSQ>LeW6hw%$PZuT% zyoy(xp9VbM4E89%o8pm?$J^M%oEhWn8Bffu!aR3??a)b9)D02AHqVZl2G?WQb_1 zo3B(%g95y7Oaj-oRd|1&qn`+#Uin^$oqQP+_VmdlN8n)tA2}l6ohB#Xog$4V zb~JQ?7?4g-VPC<;n+o@fyC!d{vFH5FJpZA22!21Le8EIvhT-xs?Zxtfi2K9wu>2uq z<4~TvVgH|uIzx1Sr61bgRG8Jil_$%o@r zzabt${Z#dL)1d2^p}{gzs6{`86naYt_Pe7P{?oFJoZK%FJ1=%7o@6EN(XE3 z#&!Z%(-yxsQ{}0tJ$VVIc9+iY}mmMt#~e_G`|3EGMUFn zA}r74WG>9|Z9(ZEn2eVd?{YaUi}N%<*2AVqEMjmru>qJ$mue_gl~S6WGS8dCr!@(d zy16t<4NmNf8Gat}MaZ!;h)`ik+~AO==V>uzULwc8#d+|=1gWCfn-!C1-uxWAH21yb z1Uyb}i@XgF-a_&OPDPssbLHQmJQpKgNYNjThqPAkNS3G&V*hqI`DCV;7kaPobKDs**%G0PZF@mhi9*|oBW(Gj zJ#|6a>D1J`+MdCI!DbQc`VwZWV9%VHUh$Az?N#>YczmgAf4;<1AIrynD1Lhgl0$*^gD#$S7OQYDH-o2OmM*kAofog*0V;Y3ybYIt(p zP&=+7p|a#Rp-7J4E`F1WFIaQ9%FTk73=9lnS^V(m<>oKPTi6GB44{MapOo&LRMkEx$=Lr_H1zd&% z{_0EoEaVg8YJCUKo5Pc z5w4o^&s#j_U*>RWzqpBqNGL~YYxySDM1+7#^w{v6zsmAmJx0Dq<^Q(Yi~jg&hlk*GPM?B@{@BNRR`oBL*EO%(ACij4 zBXeA}Hy0SDo+;>Y#k;nbC)jC5doStLq~ZOK>2&5(69*a3{JdP7;lE{*}%U&Q2js$d zf&Ute;jfO_U(GQsoI4);)p2{gVSm4lgqDUk`Q;`1tIXr)*RgWE#$UyJFvt92iyZ!H z#bbX}!JL|c{r#Ydgo1}isGQXPYQoRgvx?XFtC+7QBLjaG(;54#Xs`2Ehn08am=_36 zSCLTTHu*jFSK+%LEE;*>35Ad<^eL~rcVl%S$ar&o!v3o7L9OBy4)G@G?WMB>)m3lt zkLZ28aL>=(%wvD`_Hrn%xE)t}$K6V|$OntPwH)rR-ipJFzUTVQ^1)x-Q@?%cXYDWV zKi;SA_uuu0QGDPaBa)fP1Uv?UZ0P9Q@Z{DpUc0vbV;^Ij+ejIae>!{upR%J zuz?mADc|eUWA`@H@CBAOvBLXE-NHQk_1Zb1L#!WGV(-pa6)&wBl>B12v~N5uSA!C7 z2{UhiUr^Zp120(n+fzC7%8LkZ=;M8j6$QbB7KY!L!aAI%ge^$;+@2bTLh}8@EQXkPLcqNc8lK!#%|9f8}^dfWWthB-H>ROn-Z-biBmz&U8qK z>5S&wh2WiO@G1vEkuknY;70#KPE+ylhMcLy+Wfud*>4+M>tq!i+ zO9(WBr(a3D#a`(T;=~9p*1S!7(a%kL_xEqrAIuAx2kUY?w9m&AprO~?ULUdTs=eSf z{jt~nOvU?z?Zwk?ggSA&%p8-CFuZPfJTtTYBGQe$JY$<|m%6>Nn8V)Kp7n*$dBrR6 zniP17Fq!hpM=5S(-o~Co{8JEZ7>>nkT0lOGH(lVxe5BS~@S9N1o=c3k6bi(LeARp~ zu$U}%=d0=3@i1R$H(yc8Z$gm(Z-DtK{uVqm6dOB8+NpSTKENAHW_X`#OFn)61Y7yh zIh^|7b3ELFH&9OqPmLJ{lP?$Gp(BhmxqRk$M{r3wAK(j2w!YZmIsUd1wNcgak~ z|3gh*_Gv*^9@{Xg;Y_V3om2#+0_Y$|ChC7_MHL$aydP6_Dn4o^0f+bIDZ z-r>onJ1GGjo<=Jnlz<#tNI9^N6zrmz>?NeS4;J0RIqZl?rvcvW)fA<3rPznzqTJ-kDbP33kro325_nk3CJs zo$W3y+ZF4jaQpPmcNo{h^5C2q_ba!{m+nZi|HOAqHr= zhFJIU5bHkLdw%Pr-um8u?KRMApw~dJfnEc>26_$j8t669YoOOauYq0zy#{&>^cv_j v&}*RAK(B#b1HA@%4fGo5HPCCI*FdjdpJ;rqDYGpEUknpJ&svqhRR9{pdl!T%K3~PpJhw6?BLp_dq4mAb!Iw}G6 z7HSP@6Y49}H>i`ST+|g*8FCG%hNzaPhfv7X4MaVO8i#rjH5-+TdIyz``V6%Tbr|&{ zssMEZRl%6vh-!=qLUl%=cl{95zfdu#8K_v)BGkL6^{8#Ay{PX{XHgeXB`Ae4FF#aM zR9jRxl;l>!@wIzBi+@i>%|ykcmZDapHln^n?MIzJ{f4@Xy3Lru3uQvJKy^U%L`9%R zpvI!6p=P0yP|Hy3P@kgyjXH!njmk$|N0l??T?Z9_3Pg26;d1kiL_LifkD88}hgyhQ ziTV(gh1!EUiuwt40d*7Q@GPJE81pfs+El5o_;)C37-}?X66!USj9P+vAN4Wn3)DW; z_o!b{mr%DDtD{FXKs86TM?HcXfQmwmLA`)_6P1X18}$L|6Vy)BLDUbZJk&MRUB-+) zs75FY>S0uG)L_&|R5a>k)Lc{wY6U72wH5U>>RZ%*Q0Gy_C_7`mby4@DTBEw4`lB9G zYZN|fH4$IeRIlQADjO;l^`2TA@cDC84(d4S7gQnYzl_z@q3WX^Kn0_Epu$m4pq@uf zMZJNVk4i(WMQujyKpjAxLj8`qin_ztecmX4)Ptyws7FzQP*0((sFzT4PzzAYQ5mQ$ zsNJX|s57W@s3KG)V}8DwngGyKHGko5KI;_@@_*{UxfvRAvff3ah6@==H>WdnJ`WGq& zH3JokT7-HRwH~z%wHNgr>MZIassyDlX7WQdMYTnBLrJLNsAo}=Q8Q8TsHLdYsEw#E zQTtIRP`{xrqi!?S&uc`7; z*HPt+`PV@OpaM~yP<>F5sHaimQPWZLPzzBjQ6HkRPd`)0jfEwJ?atE08|ue4C)2c zo2W$8+o%svpP+W44x)ZQ<)N;j?lRWc2h|8=K|Rc*3_a8POTkk+_@_lOy%gqWFtPwi z4m5bP2&T_8%nu3j2`)8S5}O(8zAXh>B;0HMnMvphQwC$!jPWm+OwDuB=@o$)PGR$RIL=D^HnG=#esJby?-KMy&o4bivR?X-l;`K(KCigTJkuyr$o*Iq& z<%67mZjwB5bUszK$0H{}&2brq?a?KhYQOG8xEbxbL&oPG#sI@FALO`9U8Z+4BmBI@ zb<|tEjyjF=`o^TU>yzm)TU@Wh$%OY9nckj^rO_X1PKqh9dENSNTQe;Al5FM1s+k`~ zJnX$NyvNvWF!RS`KKF-TW*1AH;L=466PtIa{|-#Su_~q+%)o`={kT~?Wt~Kd#_P#c|Jkm( zZtiBjW}3WR!|lOlEr))7w96-w8@e7ed3#Fr+Yn)Tl?Jo*X5YHo_-%-Vn-Hgo2=nuC zzugh0cQs@1B6Fq5+m~d$Fio7dRhtcqaNSYOP*;v>{Qumjw{PeTCMS66W*Fwmi59ns zuHF*OzC+)Z+R$xcXnFC|dn{$F+>d+|7 zxz^kyiQ8-XS?EBwZ%vI_Z_+_8i+)s2$Cn=ZJZlQ`vc@zI@H4Hn$}xtI*#s7p>638B z(&Q>kD$J?yheL4JA{}gH(VsopSC|3SOmf1VE=?|%`WaK-GuAy5{&1HlT=1&7&U0d{ zF)f!He`rGo6GPC5%b!P3n7@ zax}Btnivy6COI<^y>XNJ9*p!*mnf4Y?TfMKS4q7tcW9I~#h`AFY6fvrZ1I^|>c>*e zqne$P9txC9QdE36nP8YS8123>F*+d0LWch=Or^Dkc{{3=MHr@;NJF@r@rFqdW{QVl zPsWEceHyOU$niAwED@e2vaZNs;qiJs-6l?>+c#H^92ig!*8??2+-GUV)P5ZT+zh_tw^;V<7V+!r|I?+tM@y8VC&1oX5_2yx4yW?r5A}6x??T&kn z(@60NJZEaBX776#zbf}7ZntkRGOhL04$XciGeo=X^&9B=nd`_eQ{?0XTEg-36TeKZ z%gwXG3^N$L1EicT!hF~|p}W%c@a>vr9hmw2w-S^&+XzS3b#wYSymkHqJQ1=NQCbFv0OWzJNOrhSk zQE!x^_GUm1dc);IMq2~1J>P0;U~sMhZI6*9)BOV0K%zBBVE$s=J{an8`8bV*8@II% zTc}r!f7(!%p#A)~9*>IaPNe(&4~*n*ir7ex#fBPTQnkmhja`>daA~b3k{dtu{s+dS z&kWFV4v71?U~CU^qOd(1r}oD6&E5FGm^D)o$utw8;|ABAnoZLhje&AtHmNybVX10w zlvCYw368Lkku)>Rm=HEln3O=D#u6FvGsk;N!TolZAeU)RIl~%~;p--h&dns6g^?^w z5;?-Wl7QZ7GS0Q`5<~_++u{6rfOUICUz?%ZQR5=&t*Qxu5!bgA?3YMv5DgxN=M;#w6|Ne{M`^YEE4>N6*jmo;ffk>doS5BDHQBU|)3^ko>uPeZ)A34$5P&1NIz z+`|~OoFM;VZv0J~gN3Q9Gs-LgIoJ-KBdNW)nG#{lFd>w4pD&px7!&ix#g`WDZUSZ2 zI>?_JAAd4gs+rFMo>Wa3jEUAjmhq$pYaJ*Xoo3mnjoc&z-{%`VpLK_cio)aCFq0ha zAK_-0(SRKNOu4;}Fo_JNUa$G=izoGR)Chx@o|{3M@wNoAU_(&XbNZRAz2O(0Bg6QT z$%2u{j4-8yHyf0r7rnJ%WGt_8)B4V+oZzm-WQ^XXZ(#h%=mB(t5H){G2$A0{wIZXz3mF-M1oPvS-j?)qO@rw@({FbyEX*ofA~ zp`*!!lS$*og6$CIQRGO$rXQWAPB2rjg*ld7};#uS4r2yO@718<^qPC z9#-q(sD)_*hcu0`z&yc9V7x~hfeAj!9+19F5032p4w)v+$CyW5XJH+tHJ&j+n9(s4 zQVlten|}z4p4eu@QS;EKjo+|7(w=mqF#DVI&s`P8%?fT-4IJ_;OhnFD^J$oNQAf>s zX=4IwFMpk59MR#OuMS>MXcC&cDomKD35!h&Rt+BVOUwjAL{8j$r^CV~wwtlv%*Z^P zwkF5eu+uwp_L2FG%)SmUtQcX{%!Sqya^ikIy>H-zc3JbYbTIJ`r!~tlHtKYG&c4co zCXfDR4*QJ7!;H4xXZj|MaxS#Ku#Zf{gmxR}XJtqm^+|2g>gFVNYjZT<*zz5fN=Zou9SMy3yyOHfj1(%e%GNHzj9Hg53T$e;v%`4rfMN z8<_Sr3>)!FG?{q^Pwq2>x9&(L9cGpOz2)7!VOl20flKwej!fPh{PbvReKJwMjL7f4 zUdK&c1DX7E0~zy5GAElP$hxKa_+S|0(@iaYWK6#|e64%Bu5V7-!9-!sal_0_x_K8& zJ=p|z2g&+)qfY+B@Qg1^SVNuZx2CTJz~q~f5}O%1#;0#P$69~ny^5KAy6FnqM>SzN zs@W7U0ER`ujBNUp$>QD1DvX@J?VQxQY-LaYoq ziA>JV$dp@-sX zdvg=6nuURWI^@7GGJ4!(n^N#7nUTSb8tz>r)i7=X1H9se(Zeu3?vLrNQJ6t4gX7Q} zZY@|nv(7}u8##Ilj^_qnXom3|CRy>lrje3nbaD*_SrUFX1x>xxY)o1XqKr6YRfD5g zQ;Fwzn{j_8>-@ZHH*{(y(MK|2SP*U)jKNn=-=q(oITkva8{b+Coq$>RM|_~|5&xi% zIuB!T=HOe}$?%1zVO0z>=%_7muxMWxoL})T{*rNZ$BVF#&G!qBpETU*20oGQpIyzdg2RpvenY zQZHkU#Aq*qMLw>RysrXtc;=2Dsq6w@Dw+(csGx za6c?zgIuPAobf=tj`Dp*{#Gi5o){!Gm;)!R~1ewTKVY*r) zV3KvkP01MjmX*MK>mSt}#*2+QX&{qjHEoHWWJ()mUS_S|ihXXKpo_O8!02_dflRX` z_N~9vw*tSJrScE3JO*809R0InvOe<;d(JOn;Lw9Yv0t z(Q77B2gA_YcKp1q_cVd%?F(m4E0>8k=wu6Po{+_0m^`ngDM^RkR&-3Sn*p<&8~k1- z6~;2zi+VG{;K#LIVu)($n-YcDST{YZ0}FsL(eH9ngWL^%w3%f$ z`(5H@q&3{^G;H$x!y<>91HxnstLJ7yIFD_4EAow=-V5+1pkTgwonc~A8x{Z>vF>^$Z1|Zrz0NKdYHgEOf~37 z7>2td_ui@-FK#Tf#TKL^M>Y6D9I6<-DR6*hj3x_CV+IyEs`y{{MCbQ+4=~c-GkWU# zf5B)!WwoW(l!FIV_dIwQi#X|@8azQQwg=!(jc7n$t1*gDkB>DO^*H{is*$1b4Ff)U zbgV8J*%*P|u-}Fwv>Y;%SRj76A*sg7tUgAUi5w_Qj?1LGj9K(%HK=A0&xw3G z&_}1!r_ieGQu8V2f9u-jt_j%21YX*x15 zi{7|Nvs(QoC7b8VOy_QbQVznjAH_`u8C`~K3X#Z2Y3HndIWm*07^alc3ui#CEcrsi!8wbVxaDVj-KFtL8~lXJKXySi`;-rdNEtF#b8S zPfm83mSi$M)IXWkIdB{0xS9U(@%!1Xc5Ek^!j%oDzMOJ6iuKskQpe7urSH(c%1!qf z+?-g&x5sI^lKG(FWtb;fk4r6e@;!}2Io)z*pZ#NJ!^kZ#`@^d8{8~- zO4p^brB}|y8R<5dtz@2zu?|WjGqYpNO3Inin{sCKgIS9l&3rUVGa22VY(?fc%!@D? z@!@3dsOIiGqlcN2wRPgQ9-1+qpE)n){{98B6g;Ppm6y(>oUiTk>cVuZY9;g%K6uYIYH)2GP$uG-mQ4(^Em$_^)q8(aw@wx?&D_f zJN|yp?|5DLVH_RoNyrs8Gr)#FCGJSPi+Of_Z$Q5`gEBCd|$QhNz%`ypfa}6?AF(04WPk z1$WNZF_=-qhhLQ%vtCv|_GTMnvB~@F44?MPXRUa^@JGEB@~)ecl5(Y>BM*hUo2cQ# zV^lLakQ=K{`$TS-H#fhrpdzhwe?x;dL>)k2qi_ZDk`8|IRfJ=yj_YgFBj$76h*1%wC#(| zSCmUIj_wX&vTrKa-YlxH|C*~zUDj`}lD9Kk;l15f#wdzi+3mp3=@t7Wn*w8-o*SCG zF7ZZ3VC5G*Z>|U(3v&$S)fRrUzib|_nq~H{$-MINoYE}zsH0hB{W6%P zWQyN@@Lyq?sXzM*GdE8qHd}V9@=7VpBr;tb%^b^p7}B8!C?U>=jo_8G?q z)VIROs>z=<^!!im=FCpq2aggaD#kp})4U(U%#0g`-u9!nEiJ#xaT)o+sPXhT(UmjE z=w_^wjAS;tnZPXhev>e4e1ISCO*2!_8}l@w3A2j(!8CChqcsuRv)@XB4P7|`R{$uhPmtmr}Z{!-xN;L<@%V`*%N)wO$Veq^R)*ya=nA+Q@m?l+B zSFAy&n$04*9^_1ks&4ePn;>pRRx=^h4EBFSZ+IM!9P7wv&)(8lh`x7~-u~3+oF;_t zGhq$xX>{_BX)s-jUu1X&*u$7;-L&2U-FoB3Qj1sC2nN!uUswRbr z?MZYuQ92(y(@FcKxE|<)38L*`e0y9*uWgTP^)x1)Lk0(0gK7pl?ll)a`yJuO^AoF;ChhAYRh4!s5HKR>Sp%K!uYs)!>ebUYwcIF!4e{; z27{&DSk0t_8QFdrM*2Gjzggr)WuBzrB zR?lRuw=lXMh~C<(z5N}7S5(ou$$!P@B$4yy#wacy^hQQP$8LuH>Auzm+8#XXL?2}9 zKQ%ZH31{$^b9CsfcB50TZ=Sum89e7_Vq)DWmbM2bN5+>hbl}fe%90LOay5q=nbw-O z`adu-oyR?Lj3#e6-jEPgoMp&jEF8Z(#?Ln54fK0-oh~bw z`8=NVcQca9SQ5>;T1+HsEXVt3IWFVJO^(%-HH>XKu=Nm}un#gfC(Qz_2V;^IUi{sm= zMlV^}Y|SKL4faRh!Czl*&#}%e3iXbTW5P^lJ;;ni&NjR@7Ur1933fLVVCqE21(Vq_ zwkWYjamM6D5yf8{GIXA1l}>j~&4I}#({=@GxOHq%D$JY0#5;|ZUF(%}c0hc2bUF2Q zU?w*<7`-pd(jLVHFeP6%|8RaEX}lkL`>=s9hZmBOS&1;O+vYs7mW=ZKm&FCPMaLa| zn=e@0Cv1H4*~jX;nb#|vrY-iF9$6c7pE4Hvcg0t2i(wu-vAWNYn3i{s)o;~cg5g@< zq_fC*nw#8PbBh!*JvCEVWZMc8dSdn5Aw}!%R@QGf?!|i7_I`G@@DoQ~MOm_;B)6C!6xp-w*nxHADS6681$)2vTCv$e@tlL=C@aFZoaVZs?6zlz+7#75)y_qkgx;p?+6)+jFn} zT>f0exYRng3*SV}e`4dNXYXm^tNgy>`@EeOx#@9iO`jKI*6sPeeyhX%jaMH1^0yBc zmOobUjOG;AEdMPdzFajkUgKuy0&W@|z;oWJkztJe$OM_LSeO46AAb^tAx9FXu4YaN(+@93 zYdXQg`lV{-d0l)?Sjj-9?|MOsDGEhz`UT6WH?ISy(&8t{!NoAlEKH!t!R-MzEC^<6 zPoEJlv*hJW2P1J~;l>m}FSTG~j#^DdGOrHAYaF=IhrP@a(#YtflaymAeiS)$!|n}} zP|XbZq^D*+6Q-?Zz{to!rbxFOeZDl{!=8+COicHYFdN9IcOJqpqiUW+Z`|Bp#Uy(g zogCye{&;N0CaFzbLw=Nz!tHL~qyy(#edu8$4cM=@MT9AKI^H z-hLP{CCf6?7YEb!{8Iz^w{zT$EaA1FIFIVRF^}H4sWWu(eZV| zH{I6F*_+ESNz&t0x+13rgHx*OMqkkDVR5L<(D!ip&_S#|rR`nb@v1TlEeZ%2@ zZVddo7I*55R&2E&Rw(D~JxQ@f#;G15V{VE>eVp`MHMj|^dTd+?C^ zq)g{n`~p6Bdid|dlL6BIHgr=PfaF^vDS=P z*f~s3sE3td<^gy`l8(t{-7q@6WMV$_dYmBBdE7=>QfNnoj6|j(3|*DT4AvQi!5NV; zG`HN#7EFQL`&d!+Yy|@{E%-oF3&N{g;yKbI; zoSP$UQ+bX;=C)=oa&ve1v8GNFO6G|($O$;ocFh+JbfE>@yuppv&KPb=?~Xav^nrf6 z#ynU)*j`VVTy8pj;BLk(9=4WDZ}cY2o?Tx;JPea{S*IHf|9& z1-&P?foXvprzt^i)7^~1d-H!}*tpFe=6KTw_v{_>ZpmP!K{m|Q>AA0N{_Lxxojz#Y zpbgC8vKufqm{woM6!b1^v##WBi-Qlq#Qp^HM0QEJFxPJn?eu}6b34`aKSm~on~&C( z%x!V7(fxb2%>JolkP?$!^84MZ(|>z)Gt8FX4G)D@@EjA&6*A}ZI}{3Y{{qb@Km2~T zsOYy>6YONXKMegfoqFRY66SJ#r^1g~P>wK}$WcxUlMJ(!%xN-3A;>uwxi)$yOxZ`V zWa{u7ZccqqIdiyicz>8p=C%^j|2NzC$R*LeoaPLfUWYRWRm3QoxqG|r);RA><#gus zw@Q?d`nk68QoZQjcX&?m+!kjBYvwhl`T2I;E$-&$IAPK|?YI~#%=Hbu4u3qT^i|dL zKJ@c#y)gZ!BBxgam@wbyK6l8>oH2LpnZpanEP%0}`u@?a%5S|6{CwMgOI#iF)_-aP zGQY-I2f$Qjk+H%QF1W0k>)cfKI{5SLt}y8^I+$6)6!fV~gPGxOK7Wgtr(g^>#S{04JjhGp)GpDr%gm)z^HcLbToo#s6<$BY94HZbsbh`sr9NVea&s;xe6K*xWDPrJUeG@9L%)Ijqi+ zewXU~`sxfXso3i`6F0A^<`2WjF0?%li<~zor#SB$GA%CEJKa?^4OSqBn{SeX|1d;x z(-FpeaKt0R>{rb@U1zl7IStZaV!n8%U;#I#E>B+GH>_j5yOpan^HYLmg8szJBGc{b zmR`lx%?Xk7Ipu8p!ys}x)w?Ooo%&ug$mowWy;kpYEr*OT<_pc3(c8BX^m^WIN*KSJ9a0XCXaZwIj++_pGKt*^DCeA^nu#>g<2QYzngg>2z1_y&ic~jv zo!y84_NH-n4^pE7tD z9oEe~Cz;kDft!=jwV7^z#ju_yX?qxBPh#D4)y;?ds^$EtIscE0yg3tV;I=((ITH0| zsk#Q|b2PIA>voFPt(xf`qFz2FxE>^QD%m_za-ghp=E(IWm+4{&F^$LTNAcRpDmk)6Ghx2noQ7e0YBmh(<}_jd z!05t!Tgmkq_JXhmloOcl-rM8^T7$59>xWRiP%Vx_zYbL&^#Cdu)dLlddII%4YAWgt)O=JLYAtFrY6t27 z>J;jC)K%0S%;ok*`J*00bwoXi8iaZZWktP&nuA(^T8_#^)zZcYPws^!`BN@D^VY!vQT?aM^QhaE}(9r9GJ;{AIgks zgTigb|KZu*tZ{G5_#TWJiHb(OjGBu|L9IY#qPC*GMtzI=59&Osn9ukQ_otbcnAvBl zGP94*1*aL`rI9ojyn{BgF9UP_su*K{B<2Z{;q$u7+zp@c{dY~opX5X!XHc+bj%ud3 zZ;yL#!qE0St8EYF1?i20w3*oI8hDsxl;dG^+-Nht|F=z&vp34|9aJTUxvh1?vaUhA zx2mSf_Ha`NGa_;M)NJbN4!M~T+Pp{2VAgw;9L!U%tHZj9S=CsB5vhD$cS@iz=djk* z&9?f?%?uzD-b32ho($%7tLA7((%8D4-j=onS*`K}Zq#|*CjIFVOT1GsM|%^@{(+fK zaf3P9N4q4A?a=8R>$af$YutqU3p2&k?DUAwFyp)5*nh)(C+2l`3*siRB{!J`*W@Y4 z8Q@{~jPHA!&&fpdHJBoL!z^*$+c(28OWc4paL+++Y>Lxhj0-QcASAd^9t~0@Gm|Hx^;`;r4*%3=B)fyl$7-tmbg@g45`clM_7kg}-4w)*HB? z8Q*DoR}RfFWqQo`#v1f4)pC09?IF_}Ials~`;w^!L+i%P1HLc3EeTVoZ%qbsw6{$$ z4RFg@A#%vfMNXoJu?*P7=V+6e7_+7c%|u9v@!26wKyN+?+k<>6RC7w|*EI@jUD(Pp z;Flblqpcb~ds{V`Fy*aaUbLjH9`eGe@P6G?6aEXIy-g-MW{ql^?2s@MdvkI^dABAN zrNe?#SC5`^COnjkgd8%MqYV=i9rM9*>TPLElO0ygw7d$lX7wmCJ)=VOZYImaJi-m; zb(i<)?=%C2xfrA6WKC(}%t?)LGnl=t<~&V?N!+M2zTJ)JjdHqaruUfSCnXvtqKfI@Zg3x5gTeLMGpD-oPn&9T z%Nf+dWyF1bBn-a|3C~Di<9SZA4~BS{F)q_lY>%6fH4_OlM7u9}ye4x}%i#v|x_{^9 z80P=rey&S>GWrj(ZsRdSI?|Hd486gGTUdC;GWi919MTE1swoEs^R16`ssA)LtrH$0 zGenrPFhRmJQfF_&e0$6=B?;zBGJN*-Bjb}{+9vn*>+ts3SAvEH;W5%Od8pG28uINi zKCk=8mrI`>d4S9zZbttB^U!-|Um0J`_|#-3AxAYwxCz_jo}(x8Lej`E%8__ZvdEdl zdpq(hYI7qZn%ze zLu-x4NICrS5hh|I%|;(;PQYc#XI0}dA75*6y%yI4cOyON57WEZ2V~Uw*23^P+W1-M zSP^MCARb$GzSoH%vsm71R zGX>5A_IU4gFfjRVo7UXmjQ!~a6zTzcl(ii!_=cNFbe_&-WE>}H$J3cw@G2B;^mygF zKZC(Bboif{G@OG(XG4b?a@K1(CRa`kMw{_%ROfYD^mKmi#w2oNI^JNRbM;Sa%V%$s zu~5!EO>1G)dEMqkMn2=)qNfYXglDSMLt3>N-wgA*XULfGts2=vPezfE`SZn6wHe<= ziymesOi(Z61g^(xk6`qCUN?*mD^@kf-C*{%YH(4}jPI%jPd>2L7TIW_dEJ!LwHM5` z^(?_s*V~xDmOl#-PO!|ZcN;m(yE)TJjYPto`ZSa$RVT0TyWfk zN{sQj;BCZQaLvp@Z#U5!W_%k}Gs57_O|yuc>L$ItPsu1Z^9^$JvdL)%xtorygI+CZ z`$lH_wk4ykICIRxV3xSIeuQZi%?0NA+1tnDXGRx7K4WIA)fA!){kGKKQGQCYTG3oEKa7u4Wz+#veKBA3)A8l!F=H z^2&5Wn@NH4OTujK*P3#+^PI<8@El<ryh_akVCV?VfLwJ;-oH^3*NgFYj73jIWmb&&j%lEJn951d8$u*#E1(`A0KT# zs(*h_!G^#iY43iOcySPnU$4@0WG0k9ceefZTL+q+pK_#e6gL+Kj<|sN_fqpwMd5>9 zp4RX6OTR|Jd@GDiH8)^(9%b2wZfywscGpiZ`yXjxZwWJin>=owi+S-_fMhe3oSueR z;;IS#!M>sW&$~`@)6d>=^mQ_OUcZzVb@`oJ-~I|SGskwe6sFxPH+DWjIWX(nKey}j zUdrib|6ufWX}}B9_QYO;S$&2~D$LKNr>DbQ-8udFjVH+Dw|{Q$X~#kP{zpP{--r1I zX7+`=sG`+Bar57OWL}!sZuWOOr_b706d!srJ3n}Ce{NQ)=E^iXnG>sj`uW$GYhPa= z^UcJG?e2cJ^XiO^Hsw?-|N7bm%}j1*|7K^!jE#SM6nb{=$u+Na z7N%eB`y;OFzPU2(%xsvvcL$IDg`2os%sJ;~w{_!&k3#Rf|1+72GfKjL`sFp-2XplW zZhl?CO<&A8C-ZW<%K1A#&xh%^^ZlP!k~yU;`p?6^llk_OxSwcd`Ohl`j{g0%o5CDC z5Z9)1ela%>?_Bir%5LpS=aOkp#@A(rkN$mbX zW@72olgi>B+yCw#{~1hwk#lDFXvf@Anx|gy!@46`X|mv@hkH+wj>$n17Ke@4q-l_kFl9&y0?4U^?~m zkS86s*8(;Sj5~MsU}CdWZVJh;RfD*BWHmRzlmk=0A(_f?Fy1g1VLDNcFy=u$!!A!P zZ}Pwq-O}(RGJT&}812_^#M49k=RHVfo|_rGp3g=PhiUIJ#!tGtOpDh{ZR_+Mzlw5# z(uy??v4lH~gd8%A&tP{qUalOANw3E{oE0z=a&jyZW}`bz6wNso zIhdzj&7?C%hOt4d88=MIxA58Mh5`6pt~tp#$(2LqX*WY_PqWc!dsOp$6@%I6WPET+FQaTV(%&h#jFr}a z&uzyTJawMB#8guUJ42X_t{FZX9dp}fXmi_T6XWyLG1fea=Bex0)Bc0GIZ(TaQq4it z7@zhZAj}NW+bFl3eSAK=teRkM9-{g1syPTl^VCN@-Vkem-o}5#$ne?dV%GaXhFR}b z4d$Ga!Pcip#_>t`qZw1*>IUn^XT5tF558*D!32zYybFvm97d9)kmlG^#A}zjFvEc0 zi&$3VV5U5b7n|>HCb*e3qYPwv!1VeES7b7Lp1K!%$9V8bV?1)YBWHrQ&Y44I1k38d zd>%7@#EikhH1hKEVrgXLp_swWjjwS(ddrcdE*?g%7krfGJmuvVB&9`Sp1M3VjZBXh z4Z`e~q*qe4oXiJSee_SC-kwvG@=MkCzhLTMikA%KN1V&9pXT_Te+wk2y^adRtZ(z16wjKfL=(8`@ivxfvFd zYG$+s>kTv;9dp~s4CFcaC&`@TW_7JH*7rEya~#_+jya@c~P zWN-8q57Yczm@s?Slk*%=!aTg5OjD;>8sp<`(lvA4-K5w3yt=^*_GH7IKFK;^nk9u@ z_jl%;d3d8{OuVjo7FJh#umw#DGwv_-u&kLAj}Kqz0u>%Y2RZFmbT;OFy*}G zqf2jXY;g!P<+)L?J$|+yR`8q-CP#FPFsYis9Qx|!;{AT1`&T@C3B9T27-q_+w(|Md zD(0!*d2f(=PWrs;sKQ2mp_uh9%x%n5PdmQBf z+mtf=#O<_XP!Y;yu3iD;hON%~4Z?}tKmZ&)}g_eXSk@4KrJCrqJnRHQ~G4Das zj)v%sOh(pb(|=(wgMFoSO)LG!`I8ryX(qF2y+a|^?QFf%EF$w!)@JsjyGe%$-Y;@8 z8#+y81JhSs{6ZIf>38YqdYF;Ie8qEwX^C0y_vMhWh@5Mf+fHViF!IO}-rHB!J(ww< zz;n2fFjJlkp2$r1Fd>_{30Pv%5y?8<&n3& z%%0|GHIrH|fO=cHZP=Zo>s8YPIU~{A0?P5@rmi!mwTwCR&B>%@L~@fb@(rCC=F^dT zI_Q&N6u%=M7E!*}ja z)XDBf2lJVmX@5ZHGQozoQeCE@mcxz1l@knu9T4QO=G%k4$@B^rIb`$?qqi!Cc^b@z zw}fMMy^f(bGT{$D|0NmU&zw1!O&mcv{D2&`V54f}08g_i4!tp20}s=&D;Xwo$h1dJ z`?1?7N2eJx8G|#YV^_?S-^lcscduiz!Oa+8-VixtES{!Lx`&a&+dqpOVPxvfjLm?t z;1eA>`WqRU5T3)Dm;wjG)P=$9`b{(GZ%ds<2pG(}C&TC6W3P}KF^7I(!%f{`bk*kF zYZjmNm^;lRI>N&Lr?2#S`dItnUdB?5i8HI+7`1&0H_45zilN8PYHvo#;&y60-%a14 zWB!hzXD_N7tG;Ru-$B4m3Qms)-ee3~jtp;M`1#^S?27T;VCZ}IGI;3^P6C6E?k3oo zV?s^>zkCvLUKR_@`1UaL`xPTN6GldFRg9I44kxJ_MABEXc$gfMr%AE++?v9r$}jhISzBqbuJU`ZjMzkpSuj^k6#+gOt-R-<7vb^b>!&TF64Zk z6RYLGT;?WYM}jcjqrzz2zK7|E*LEzB6Lfsec~0z4YH#l5$*k#ZGCu4+!)aceoZP<< z^Wj~l1#J(^UCOzG95UO;(BJobzv}#ic`*xM_H$FpbJkvWnlzZQgjchsPu%vgYEE$T zE*al)%tnXFD^grWM@A852bsb2cSPqWRK~p9|Dt9NklDj?e0$z)Sj&3Y3I7#hoX9o=rI}6IT#_Xi9mZk<2fuInB+Fx9;YX z**bBzz5AL!LBI6>?*97_POJO@4hdy zU$HGElWm{rG_NaH%g9`+P?kE%Y^o_QtC+SM=0ssE<{Aq#@0pk>N_HdUyslu-R>Ay3 z*A(Rgm~704#|2ceLz%wYvG=>8Ld;;_DvV>?=(t=t@R z>~L(2RmLrW$)0CZc+Opihf(ZK^9kh~bo6u-#VVL7k4q2+=V!wh?0(2m9LjX+&EYbg zHw)9vW%}ZTN`^NRcYzb-Rf>#Z-oXRnCW za>_8PfXq78U@o}YTNliSSGNcA?rShKBOf`x(u{mz6k*oV8n_vncdzDDHTHwtRE&Y) zbM=ukhI%7YD0=fWG*{ng%1hRX9Ct(W)X9WWZ&}pa70lHaX7t+0+c#Bx6Kf0}Am+VY&{t=zfpIW80JH1#OQ)68jyS^Tpw z>%EN6hsXRq41XWJ)}~a_e0W?wxys#@w!Ia36$&QvT~U;mF>|lXUbY)QKb;kryo17w z;il5&GK$mOfKhNAb?kC%<%Z_`A!p2SI@)QzQZR>J%YmUog=4n@Q;wf)&Ct~rq*cQ{azS5H(%Ru{@hHz zo~OiKudv$-iXHtGWzAlZa~tt|-o0utA8}8pk{dei_&TMi^Z7iQU2m@}xa4@WLczRy z-rMb1o0xa6ncPZY_N}uQH8_957F$H~?#b9-6xyCYVr_FV?>-yZC-}U3n4V>rk-u+^ z{YHcHZ@J8`Z^GDN%91gM5vHGJ-m=4#!i24{U#oxqt+?15cfaI0zrKmN`gX^aiZVS+ z>DS$>7|h5Yum&^o&%ZUNADMlZ9gqI{W@5|j!tlBJj2k{9pK^*}VuX3MMSaX5j2EWa zc6%k|yh5}1x%pl-cFffe{g=CWK7Jw3X}0XTFnkt&KSxXD`&)PK+LqcYVK7(U&3x6I zo7XUl|Dw~Ru^upoZr#1?G$Y;2N;AytUtg1(C0v|WUW(o(^*)WcpWNILhGy|=W<2Ki z!^AtLlF`FpuKp`x7XOWw3g+r>vMoYyZHku(<8%Hk&HOQhX7TGU%$wsji=Uee+oISz z3Yn;wHL|&SElku}sZ?%~-J7MN(W);m#^)QUyC*WE1X4U5R z|9Sw19*f!9WKnNqzLDK?VDdf9cR#8ou1!{g%V2)L)*DRDtY`7w0_yyJyib7B1j5W1 zziKqkahmsEt7`B*0Vk2O1HHwm2JbC^-eB7l zICFZ7oT|n-zu(iOo!J@xRRWoiga<>yJ#%U{k2wwAC*b}N{ytpP8_m0)?|PpAj~oxv zq*n91a}BUPH5+TyoagC13?|XMIv6i!Zh@^wEif{s zCNqzP$p6GJXAYi2Nj5KVzK4NzWX%1}_b{OC$%@~O%ZGCK?0UK$SYZ-{X+_sJtNVKx zVE=~<=J)?~^SCbU$9tMZ)pLZ&;yHLO#r?enL~qs2CYY01@mcyH^j5PG@0H-TJuoKK zyr>!H{C-*kn#Ir8!1W#mPDAti)tnm5h-xNX%={PI<1%%$9N7w^v$o~eH=dIguNubJ z;D2V+x%&9MqcyB*j;`h$ylE+)k^fH&&Aa!=Vf0=JRdV*z^=*R3{QiGrbZ+bBnSuy|+PR zL?VM>i|}+Ft+kuMTz$UQZoR4V;s2?TYs-nE%e3a4{~d#?KD~#5XHH6(+xAq?`E$b( zs;=9g7z3UKuGu^ma3(8*X0U&4$YKwKi+SpAVP?5i%u~mlbF-Mij@jrb$#^Wbb?mf6 zn8}Mqa?{7dV4nJZnvL#mR*)%5JVP_sxskPb>Xb7j>8#TnP-n1jOkTnI!5p}ynQbmJ zo?V-jbkC5axTBH+fDqGoKq-Gq1#L-msQ(@OYHwwvQ?Py74xe^=_n@ z@^6WpZ&XuqfzNt>CGN_GwR+9i7LB>{b>lB-);s35yNsCiZdT3fwku!36d|XKX1(V+ zzHYo@F&WHlcjhcCe+Bd5H4|$q-%-W1C|leoGDet5Sc4U33m2Bte0X!o#n&OND2IWfs_ZQhq;VqyGBu9sn!ILw?KYq>d5cBjbp7B}nqOr5rl_7_gP z?03z^jjiI%M8myI-rT9v*I~b;@WjjYt|@!Y7H%rXQ&6df5(-^lR8HDy(q8?7dLR=ffA-ZE@N5y~1F>lba%Ll*$K-6vy=II4jJd z#~$I{LNQYwC!^;!R$~XU2uIVt=GXYD0y&C(9F8fn!(d+&U*dTRo}-%YG=u#`ZZL;l zvFG7~Fp5H7lffq(Y^a)zl1+Q0lvA_e-&JgR*RdZ=z2Ri`>}(H%+4Y_|^j*xmKXKhT z?_N>XW#_r)X!GuIb~c1#-n}qy39~yEJvknP!O`lxdu5GtM!pSZ?_#XM3CbapeLSyE zY*C)wQGW;agNrIK@BVK2IK{SR!EJS}KFppb!rZ}({OgW>J;~sDaHD>!6Sn3zD)#(_ z8TsQXY^gpa-25ljW-H?}^2<)--NB6f>y>(mnQvg<`TRo6)qe&CGx8Na zBfo4d=IW0v<8$>FbMrx;c`5-TU(}K+5IMWRn zZLWUFMVhNW2($RhFz=q5tO7-tM40A#U=~c$OjtS1?-z6RF^iu}5#^A1sj^?=#KZ^Y zsk8V!jN2@JGFycyTJ%zJzs9m^Fe9JL3hIq=VCZ&-=JywJ^DfQrFI+U0jQqent67`H zKVt=E@oQ#=FhhlT=MZkMYcw>!pBwh-1=Z9&%yTgFUo%X8V5XI6Io9Q8BTBJ;xb7_BllA?Qty9PwTWn&IzZ;9<0x|8zYtxr`)o@cs(oeGh6glGEJJJWZmk zckj(DhZ{z{c^WzXUIvfnV8Y1o{cXm3tJN@Fj`scv?glT)I0?f!$Tqi{bBk z5Rc{L+ry2Fml@Dn)6BV-iF7kbH5jau zdv7}YJLex8=0n#v?R^|*dz`(w=g1OXvyEJyQ{B9vZO{K3hK9T2JtlD0#U42p_428y zw?8qgc7vFjbKFek{dhePUpJBSSB&fZBJOE$RY`>4565fes%c4ko4mLbu;T@o_c@GZRcisd#8zY zH)jTN^U}c?nVJcOIeev2+hN@FzWO1}#4bQi*y26!k3r6@f!XIUXWT%OYsCcB{UlQKuQH-_6b9m9J*S6n4?vm&>ODY~9(+jTu)(H5Gg?lPai z>{iGqWNxlTPTs0PxA^Y?i}3!yZl;WKZjZ5Dt!DPd=ItD0e?mF$z+CWL+g^JUp5vWe zpm39`nj$Ud?rJ-kf2l?x^T^RH>%M4k53`QUE;6OaahcAqW1c$Ylp$vrZI3HQm}eBH zaoE42oX)pYgR`j^w&cy-iV})7D8(Awt-;*prmQxTCrr6B2fqNa-!9*h_XU1$CVJyJ zYmsADb5!GaMzLXg?0EDn%~@-4TF$-9 zdE_X&ZCZ}gDEG+Wrb0E@%HK4@bKlHXZi?R4=2gpyb8XLgWowzjbB5=>24mxwPZdLZ z9b_uR8kBJpitTxV8>Q?bF4G#!9Bv#tux?5%ri|90%rO=xz{@a@LuT$)mnjcbY~{`z zJLL$2{n0W{(^JcF2vf0JxryHF1+a`}lXHl;&aG9MoH%y^cEA0e6~`=_l@TQx)&d0+-pzbA*X*K&HYrBSy=acMUlg7bqL&rk|LSJOi%w%ubMQorhp8jhz?Gw#Z;(^P^sH2K>fE7q zi<=oU{tn(N0lf+Hu`oBWJx_%4oKtVWJjQz)e96h4pO?yr}7@AFa{>46IO zKKM-rdYq7Hf4+Jg`>gvxrhWF^Z|D0wfX8WbZ|0#Q?|r7wK8t%}zc|l6 z(cb$!U4Ne$@J9PM;=bUIa}AGEZ=aLA54g8`nzpU+-Dglmg7-d)=(h!CZaOaZ!S4iI zL@oZdHTbOr{c-3w4ER2q+>gV5kAUy98-3m2KBx~*kAr>UdB2I@FsQfB^$8KX9%#ec zb0CNI;lIf+nQF=((SP>CjsbIbKksH=aA_^kx~I6nI< z;eCL|*(?_Z5Emam4Q{+`@lf z;g+rq?9&_lR>vFm!EaeKxKGz;>g$H~IgsJrr-|or@Y@1@`abkIRj+=d-+tixh~IwD z_rdkYApUy<;&J}LeKN_L&ptcWmH+bNlz8v+D4jdo>+KVW_DE)ChPiunO+uvUW+)cA zjoH^8X7<-Bn0<=_c>=9$U?N6iqRr`B#iT$l8Ov}T%9`n-YF?;WjlT^G52%aH3pB(a z-U@YsZinKau26TV2ZY+zED1`1a5siIbQ$fUc;9anHv7eCAl4chj2w3k*+X7>7&$Gk zD<_tXuA_07e@xdzL=tLLGdd2>=$e9v`ps+xgxe`B51Iv`W;dG;6+);9&R&L=La#!o zmCZ_^*P%7gdgyKF9cVMO1^N&|y=ztm?SOVepF(?~&!I0Nqn+OIYb-wueFq(djzg!Q z)6h9x7ZEQ(m!T`rb*LJ0(N|s|KM3DJk%A$78=n6@LO3Gs07$KL-GZntf764bw+HAWvu_T zXgtQRZTD;I|5YEq#y8gglaC{{W=XvrEGb!KN%kcyX;3D!A1Y?{qr;f}%PGu$xRBYu za5DS;@mOYI_LFOv{jDX;ehNC4X>?MZNZ+T^yq0OIXNH!`Vph^~2`8_S`{6s9QnA>^ zPd2l_VtgP1+ap(kHzLO*a#X)if%5ZLg5P@$0TqmC^-LT~b@}IW3PCQxq;}*Ogn=Z}R_U;o&dvfvqVmdG*Hf%zZ zJrX!3b%w1)KNe&mW=eNk3%r+#+}{>%9# z(%%xDtw!7Uu0t)w?)-|SSTmMiVa1iH(KdxPx*`=jWmBv|dsN;;c?{1Lkw@cw3%?wJ zuMzq)%4MF*MJD9_?54QMvRp)CF635sF5MJ9S!P8Rytm0x7|)yFcrmF(IEELR$on?O zA4){d`ZlNWz<{arSb02un9c{5H-s&~+cwW-pWL*7sbymMV9NQq#9{`UveCJ84p3g~ z&PSL^ut+Qq3G(C?PcB>V?M;0fs`nC%(MkFt-fL{hOAz z?_I6u{^fV|u5OY`HvRp}lXe_2^4&>0_BP3-zu5TuQ+|6cD#tL{e`eArd+*3?{srYt zvp%Wt%FFJ^mT7s5rdhknI8{ej~SCRwND#3@CJh3I+$=NQ+WhzZc$5M4_o>qHva zvtPq_->(pNh_FvUCQFiwp-arZ2ipf>oPQt_-PoEs^Z)V50{@!c2A->8#yiInNz<1b zxlEgxm{g2_oGPNba~Z|`FhHQ?bREL;RLTRC;7eHU&T)Mra$E4FrY3s-^Yd$I3;yY~ zXpy(I-TM=+RcLvB+jYp-t9T9d+ttSP+12G6RJ@1DySCv==%pLR;kw2DugG2cb<2Nl zbKP*2R`F|?|E$Fy1@-*0$Y;^@%;g!SSF1|T&Bc40>2EV)TmIN~PaycF@)@?46PoQ2 z*D!WliwPmE-1&GGCh`_^oulXewlKP$!M@DoZ!>vvg}HMK8r|1CUO9GgFBduet50+g zjH$p#5SMu6VjGEq>fp;ou6lBZ$ay_Yv58pc&Mnbi*EzVJabM>kHyhVHlt=OFAG*$Q zwCl+8zLx0Y{gJN#@>xsC@&3^r`TfHyH&bp?Y*>Ef=3<{bHCkNrT)BDUamw-DiETDk zrkdW2?uGpCH*I#N`md&3u57t^OsfB%qo>o*tYx>!ss8VFqFh2Q`{YF-V)>nu^W>r? zM!t|0(KU}OwY+mO@&U-1G_*PL9DdCs4IMIWA=4hF>mTXhkZ}u`_C3EAdU;670+i9@ zx&IKl&iTg3-(l)up2rL^A+MmZ6pMM-vo_4MxjFuJCP)YcMRwM32jt>p8P( z$i#AHGBdnnv7Wln$9!ssf7?Uxcz;z`QY#BfieN0MwUe*){fG7^;atNL*;f|K8wx@m z0^uG%Kfl8e!=V-s;H(wYS{Ge+DNr;NtTkDd-32jF-+KzX1IRU4m$mbn%)0 z#((H>|2_TdJ@8NVBd!bVrhij9ZOUyoZBjjZE*|;Wo8QZ;>$?OZT%`z*qzl7~y!?r^Up$KMw zwUXI~;c=EHF#E$;KDdC{acjZue4p7zTx0gJSU;SWVgE5xn0@47MC5<4AY!{Q71$2> z_ylZIjCpA6cxHdR0K&EtBA7iEj7iAjE+KA&Z)}sf206Crt}=T()E$1}v0V=iR z>^(0rd)Fn%hd~;%+c!d3m!M$&8nY**<1iWh>>cao4b9WCe^zvM82ftX#~lhufNLPa3yc<^XO zM8$Z`%nI~4&xkETnLn1#^A9zN*xck_U}AgyjhN5p_xU5v@z(}WoGYR{HzY6FKi|yU zvA;e)ai>^5{VR&f`sv?`Sp9u+hgp)czWVzNJwDZ8rWDn#`uj;8{H3VT)!%oS=r3iA zzJ@qX%6Js%WM0S1hnW-YK=_z7CCyL(;)_CV= zEQfcFCu(|UX<>W=aU+^WPF-joG1`c9_G0*?NG8Mxibx{xv*-Qv=rCe5AC-mTPgwZ% zH2h(aVw9|kn3u|9d|s+m!QnBYBpO-C=PI}D;6=Rc!8u~KB#z+Suyn&k;#a=U>tuU5|LRnKF=(Yh)mjr z2Fvie&mGY$%;)3mqKGdcsfZZAAflT*F;t%qRp~_37-F4quZI0-QlQ%)-1q&>6KvDt zbNM6UXmJH0hCodrI=pmfN#Rfn2nVgfF|UHmwF*@Om?gzsk%}SNvTj@$l;}a$VvU7`=InX8ie`5x`rZ>hUt0;ku(At z2|0C*LL38)gT_M>pozMkKqO^Clc34av(OZ1Dl{FM0clW99pzzurmh0SInX>^e?%;V zigYbRd|B5LMA9VsW2u~e)`ux8xx@Ua%6qeL;BTGuhJ>VW^@YxUC2fpVTOG+zXNr@Uuy7zs| zBlgF2@1Qc4)WM4T%DDc$4f}M&^4MaQWUE9>XGw7wH@R#qDIV+N)0zEm2XMa`>+irg z$aRU?tK)Fr8S5nt_m>Nqz1GR>Ut!rH8$M%_&g|buAiu=yN1$)9&+AzJ{di_SUcu~d zU>rrCH(6KA>}$&KO*8C!0^4uEHfuLR1<0}A);0J{CiZGs=W86CoV^wTR~~V?%oZv-eJ5 z_B0!_CxdklwoR*Gc0c46FmeCY9)k7F&oFx!@>|9;dn@Eo#Vqk%4LZOQ-#)_&rVgb{-&^$%-C`MC8WSBS6=&B2ido2+x+8u+$ygm+AM3fMtG1QVU zKIT!TXo{5NArJAGH)I&aC=mnoC`IM<8CJqf`SE#u!VzCLV*9)S;qPKx?3dSHj}yZ; zG8OX_Q;qlr&2N^*toG9`h*Ma zb~0k|*Jd$prq~4IWr}`|pcP-^WaH5!B8`{*EI}*un7xmN|IA`U3G*uEF^=ZrWp~^~ z^Z5Ls9$7xd$zmLhXqM@7G58!97auFo5mKSMy&_)AGFM@qjv7tahmT8>5C=n#K{KG2 zpf%9P(3j9@2xAWZ+3yaBU`pxGSZFHrA_T7V0fh07bP~c?#nc4CaW&lq^@lQ`XQ8=J zG4vjUag*s7gt3k}5W;iW+!b;_4?~lnSZpXCgO6zEQwq%p$O=9CpF)>a^Za>9O?+!p$DMx&9w{{Je&o9E7>rJMp5=x0t%ZNVbu=bBQ-v%PT^7B_oPh$@#wm*e%- zA_Yo#ZDD!g=_W^ENR%ZkCrWXs&YTj~4!?7(X-o|qpelHJEA$-44Iy7S^MB9Cs zw{f2qr)YbJqM(ND{k!jgDMjZ#Fa^_9^xTEcBtby6YR3ql=%ziDr&ZR(0 z6wW2phbcwjTuOn@0g;YuRJsWyrfhN6Wp@7+rkuC%fN4)C+HS%faxn3s|IvLs%<%fm zaXRy&vag%rb0^Mu#Ugy>%X#4jGY3qE(+p;^huJH};_Mnq=lp63ZBJV;H+5Y1RNnTB z%wRfF-Ar@5+90zXm@Af8c-s@an7cW%kIo$9%iPVPEtVCLoVhCeKy*duycL#cx6gLT zO9eA6RY`~`aQoc7mZfFS)tM%5=HRbJ?Y5TKf;clzelt8hx}qg#nyKxRR z_`nalLvzsfn{zj@ujaw0$>dXi)pKI7`^k?7hV?^88gp< z=tj&zt-9gFe4qs%R<$_;SDg&U@8%B!6z|W37_;H z$C(`sX5V3929sVxI8!Si=D;{l+b5ZeE-1l)=G47+3FgE`m@4_K$Lvk-abSJ}rZp#O z0Y8@}1sCmAf+aAw5Hn}YL=`?|>d!Ln6Tglub(HW{O7F#-X-UaJ+tp0G*4@ULcrO-A zr9QLuTiNB4Cng_>i{S6?Zl<4`X&M$5yD~5(N5cF2WH9eY(cj;{8Hm^UGGzgOohP5p zU@0JV?_y;Hm_w$!nW{c!u=#cLGsXoLo~yv~_1OGVptpZx{%%OO)ZGj8_K*i`u3LE|O(ypx$&9+>^Xj10(;Dl5`o;687u zC?n>LbSHdzF;(KXo%5ApvWNDdn}!G))vtzsgU+yuy-9w;Tqux0_i= zuS=8QQw@&d%vVbVGj0jHbpAp3Jl^#$_%lsNz+3H!ncrqokYMKeGPT~bZ=_d-kx#+Q zCZ_QD%S^M*%!mxA$Ar&1Ojd`vk$gT9tNK@Z`*bsxHp)~yJ|!^s?p$tqZMkT>huH;f zuLud4Pi;4tQA+~mQrjno)k>m2wH;|U(u>0|rmf3dY-%%~m_v?ST7qLV66fcME_B?% zlyJ>%#&Jh~#`-cNag4O>Y3I|6aT+O05Ab8OplC0A-m`P3!5n-fbBWevHZjY^*xMXP zOcR|;4i7Uk>r0YyP-%ZJBX~O3M z-gYn#OM$)H=}iB=#OxJ_*G~^K8BFvRwH?g%1#@!zRte_a8oh>in2Q86v}lPGXl|F< z=b+YR{tEy5CgkYM``kVSQ^{-YW=b(|tig!EOUhj`U_xHZZ$S?NjMrl0(cTuPd2N zIJ0?A@!ru>TE9$=G?>{wKEXUu5-FGg%=ybqTPW^$)nWFbwtF+@rOzW~VC%?%@EKss zbTfnM_+;eMW;GeiE{4xOLzn2xy`n$osq?`+k=8mgG%)h{09*ROZg{?Wx-4V6EAr5Hw(S(zrvJlR?f6G!c4KH_YUo}0Qcj3m<0(KE8@8CjSHdUZupFV z&(P;%yqP(HU8ZoKD~pFu6h4V*?d0uK>e{!@(1J)WCSAYhmqnjQRV|j#K?4frptdm==1i3sE_f zSsW64YMg0uSQ}tsOq)jcm%yagPo^{Jz72floBNK(xnz-u=_DqN!2;7HFvUG9!5k>s zuKVQ77%=rQZFKwGf*f(*MrX3Uae*wpQD#$WJ7-3h)L}B^3+}TWn3~Rv;68tWIT&sC zW-`&AhEGKqs55g6ChpheoAvv3^?ah6^1_LUduwi=zloV`Fx|(<=uaPJj^6f0n9+W8 zUlG?b{Jx?uQ`V1>gk$8(3^mSAel8iu-DrCqrb%x*_gRmL*E-(z9E0h;SIN&MMG58o ziO2u%pNu=XxRCqZre9|oThx8X#`}Tq0h5jU{D+z9e<4%0Fx1P}FmkEe#0Hj#O&V-F z?XG6i1{QqK^Y>$xWrIb`2-V60HAa0=r)v%8CSlM0Z+)K{nu(puvt zW`^p{e#K`Xv*NZp`Ap$HBUIjY{7JEawl~J)_eK~~^oMjaEh*XfY%kN9Z5v=J^mlqb z)%DqeF%>Hmz7l^EHhkK2rbCWMwlXg!KARvWMOvU|Cf%4mo6^G62s2Fh1an{ua6`;upSIUw zwgR(7gUmu=TI)0YsqMw8LJx>3kcDbGb32X^e0FaY$(arbj#LZUt{FacW{}6HOpFwK z9ovg38%)g>w%uyA5_1`tVNGN&rp$XUCa{p66=5tW7%0)79;Vgp(_qS)&01DS%)Q-< zz#K$OeDs8TCTn0SOjQC85VI8v3QRT0gUBKM1zh);0H#JC48kW^SXU*RX~f)FXblhP zvA0W6WPFrL%oxE;6U^jd^MO38%$cewZzy0aBD^CW#UGy&P{AzDW2%;F3SotJwXGm(~fU;c`34MdoT}6-N0mFrZhaVshA}fw>yw$;mp}SOi3^c)sj3*inh~qi?;FZ zc2P=!oTFHGzH2bsaV9=~9mGBkP6CKJ=N(UC4QzZEQP&FHd44mqcYnbxR1 z^Lg7_ZSJ^?n5!gpUEz|Ir<$g0*>7rFw(+)Gm3YofEs@+zrysipW-v=Bd54(!U>2OL zT;xdIQN+|@)xPK;_2<;hw|%?0x&Opfp^lQmC7Vv&mvZ%}xoz2{+X8Iyb7YgXb?TbH zmgF-xqFsBZEPKwQZLVc>%Ks0} zblM-B)$ZKcBVY!Wp61N*hi-tGK0534tK8?T+M1-#-aLEZ$5))o!L0fC)OROpotMu) zXmLF2>;a}@UsdJUaAJN3<_FuHNe|{mox6BsVyj03tFFFzxV)gtj+-xA*>Nz>|B0Ac zJGx(d5X|Zyw-WO{o#{IJV1(n@QSpBXu@bWnn4LMZ$D7W?LvxAwc&jHnKItN6r{gz2 z%b5cN^O-%}FFtT`)!FH9Z_QZ##+@VI`*`PfMYT@X*^v=@2c^Ux0&~}o#B_Gv{uz8G zzB&GuL(7_-$qTM_*_RmB`cVSEAVZmnAQrI zek{Ie3L|EYH8(spCLwwBnPR{2G|prN{_+HMcTapOG&RjynBI3_cT1sW%V4a}`cP|h z**b|cO){7gXENrrC=!bTb0L@-%MQn%LcuH~W=xu?j1|aoCv|mCeD5PQ5LG_<4s5{+ z^N8sOrcySCGYd_4^8qsyKJos6+(JxEC8lPTi8{HV?OG-po*6CcbJU6SI)XQ9;C<1?DQ9sWC;-`~;JMDd*YX#&2bFxqu0Sg)ocH zjd?Sb5Ms(QZY|VdGV-Z2P27{4DV-!HpCm7)Si&rInE37!`IPe<*g!BDe0ni0@X0<2 z{dFdTA?_0mvgWct!9+*Gr-!Mc?K2rMce|PBGMaeXc}D;6wtJXZlFOuDW2V+)N)jI3 zm^@5XmV#($4lyyh$It{mF`?~@&Kq=r?lV;~+HUyd>uDRB>N5er(2Lf&nK%z=9!DCV z<-;^sk~NB$^tRVw`eR=hvce}nJsM;RK%-1F-D@I4g30PKonAhTJ#u&gpT+{=vvH<4 zjd9|LwmZo_CT|Z0la}+TAttTRA5@HmaU`&S>^mD}hI=07cVO}!7e8D@ZuCd>X^L3x z{#QRnx^btmhkHFg^q8}HmS~=|;jA9B-e$T_&vMO^Hk=tuvDVqZM*nq8uLsk-@&;31 zpK62K)LE$2ng6|J`5{vqr#bcWf^R2P;!}dOt%5UOV#dWg%fnhl<(tr2{znRxvIfo+ zEc0cS&F0@Ct;<|UOlKXYID8s`>rB6i`EujToH{(3^<}DVpQ?A;1vA~)U9?^JEH@T< zMJIm2kR~{BGN#T(9Ue>ybGmPZU>0xRXnVs<4eRPm4Kwio5+22v=!#fzs>Uq$DPGj@ zo94z+PxNK7Y!iRQPlxF`OlLhNrx8=};CN;*G2?`Mm)meIP#m>RA0VIJT< z6}}k%q(G{o8nb%a%V-`W&U^~#FusDOMaON8B6Uh8xTlVA2Cb zFE7X|^XKFY`?Nl%$RSUkHhz^uBRV8MKBABVDWza$k z@w0~P4q<3}iozo91v8MznpJkkxus~-fR!GcyEpoLy_`7uMA9*((Xm0-RXmLI!eOwKkd zQNpLeTq=BeF<&>aj8>J@c3)-(Fhj!&aNNhmr>qvtd?uK23s^MMz+~hT%z_B~ncUL+ zEORczZ_T!TJ%we231%;EpTx9=Tfp>pGyOO-x@+7|g2~uQtM%0-xLl2}U}|~Rsw`B& ze9Oa(Eo5gu`%vasaDUvtS%4S;twFzbsn1jPI!isr+5;L59>demF!tn)K$v?52 z_owt~fJygBOc_2cE3N9Q8kj61f~{+LJ}!EpS`=YO3vHI9rPt#+h9gCKoN?%sfT+ImgXR@xS9{!HjBC zQ@%O?r;_w4XIfLb#*GIvCPa1;Gh`KK4re9sDVVnpo|*wB{x~qsR<|6^1~Woyzc@GU zCT&zJ&Ws|aDPo+%%`C=CQ!|#35E>5VE46!8>&)ofy!jPiS~?OlCZoXI-BdrxmrX-e*5(S zU`}eajI{d1)89u>G0JEiFM(NBAZQ8)+ zM)Ijj%ZlNOm{DNPUb;JIBWyD17XJ?By!mB&x>_x_j~zcg%8OYRk>DqonXQ&(w4O3G zD%($6-=>XI1(Pk%ndni4Mad>*cBsLOG_5x0?JN7XtCijUxEIq}oC~HTm~mi^@Gz5! z8S2ZlD%qhX;gf}$R)MK46U+%wy+qslg1Nm_FV2*-xK?vUj~G8i(tReg;yo-BuNrgE zc1^|5!i;aVVx(d)nTI)XBA8Nzxqno^DxV}Pua$V&oVG++~;BXfr;@KXIhzxaUd}>4W>rS z3}P}rEsFbOI3<43~)6$7vcZn6h9>f;m$#oj7Y{rZY_{ z;XkV(3jw85eQh%#>Z6Wm0)Cd~#;X2APbQxC-`W%3jQk zGV9woGr*UrY4~hpA4O+sV8$3eWwzSwbK^qRcZV=&_{5Hw)QE67Loi#+^S3&2F44z# zz+}Y4@%U$$Ryo7WeTL052_|cZiDyrNB76HRT*&+z@#$uo45q13CjJ7mx*0#0&>%2n zoyo)`Ydqpl?1oQMp2@0G+v_qJ^(Xh~VJg4CJPGVAZC7?}F2j5RVV zM*4d*D}Osv!lnwur#e10eKCT}D)H}c^!Y1H<75O=Gnh!~PTpoL>{poc^u4{9{k6J8 z#&o8Y&fwpTsqn|KVmw-xS?4JNL%-&enu!TFoM_USQFz9Q1z2VR2BukIiib%%XqwJcsO{7~9HSgM?%XFf z!3GQ$>UEj^T{RZ0y{^e-g<6kYxKEvlyJ^^wm?*qT%xf4}`7kZ@nPFhw6r2NQu9N=W zl9Pwgrb2rIrdb5j>BW2-OjT0RpV&l08@J;q)2&b@S!~!5lg@lY^UtmHx3Q3%`36(Q zh)!o_&Qsk!-?kFdsS#7sz&y@!Rl)2Zs}PbE!DrXt+znd54#6xfb8??*tj?4%6rHci zyzMa|*WQj`oy#O=PJb<&Nd>sI1*TJHc2)@KW@ZPRtn`m%A)l3&RdVKFZ{~}t98LY{ z#T=0zE(aE{Sj$Pa(%Hns)Pm57or?)c%)+j>&DvlvZ7R< z6LS5>NY;4=e2#cMTuxidVhcDk49tPkJ8vW;D{9g80GOrOpPa1h8>`L(^Y0;{41E(i7BnjY|=^vv%2$U!5rH45-~qHbh7e^ zj~ddyA?|<&2ZriJlRn>mB>um5(uBWs9w!V6` zGt0v~T2l*Va?P>x&u_YXtk$K@z7FO#?$dR-0p{|}pJ#p4?bE7buG;gToW1!c*K_rl zwVXM+=2+FV%a^kZW-XY~CfAMHQuuWF_yp7SQTI<>ub@!l>5Gv+xvpkiSzo<@`}}Sx zwf*NAVAj-(b_u5InDB|ZS4`T%nN_|_*YeFSFu$qY3g%T}Udg(8zIsFDb7=c_OP~Ba z>x^K!o^~B|-MH~|md=2b9VI7U|wUDf;Z5IU8ZHQkSK=6}|JSq*0GhDscFm&?OM ztw}IlH>PH}?pNp5)Fyc|KQWlKuF^xU8rSlVzm_G~VpTNZ4#9S_zuKLVDFS=?wyFS;M zc%WIX|2%|tY;!ZKU2f(JF3xmSWf{z@&%u0z`*i)teZJtjA52$g*ABr1XPRJEZM)2U ze(YM#nXaEdK-)Rg1P9DOK0BW&0HREUT02!+;#s@FXjv|@1EuQ=@2S|Zu@CDn8y<7IlB0#6xX$Q z?vt3M+-D!b{Ol0<6wEX531%}d=HEFpS7#pQ%saA(`30DvH_-M{ojJ`_$(h${IkUDc z_qoQytcktK`}6P5lFwY%rDLv|i`&3-?Ksv{FnbH;be;JGn0_ze8F#&QF8TB@@18!u z)okS2+81IUt;@XdS9?ZVFb}zz?}uK_a)DV{dSrRc^l7fM2J>q$@sVQm=l(UdF3x;p z?GcYYYCXoP2WYrn=^NV*#kZU+D5sV)g4@}u6O5F zq3suT^%TsYEEoE7`wmwGF{g0m+>6tv-Ozm=UAO)H+K2xrn8meO31FHz^H$#WYJZ)1 z$aQ8{C(e9OXKr^zNDVP-z$~pM=6aWFcX4e}!t^vSGvL!;mb$v$oO}8bnE9QesO+va=13b)L)6=q~w$3Y$*JWOHoyqQ`GsYNj)@=j%R`Ts22avVEAb!so%*2ACReyPG*1 zJ~KIU)TVh7XXa}z!CYQ5psD%l+&eC(pzUCeWNn>Z%#X?ESoo~eqNzW1W^VTE83yx- zP4ifj>C>LSmw{bR#iM=BsnK zPcR)LS!rgA5K90*M$`R$nWehVpFKVo|K0HE%5xI)wK~j@>1qQ^&Fzzz7968%2J^uL zFkeF3Cr^8xn9Yc(dHW0ivkiRuyM3C-r<;khKrpA|W*6}OY?<&9m?11gFi+QKnnQYl z`IZ-xHNZ@AxpZb+vzroLI`za&4KYo;Kb1F$*{V?{Q-n`26NFEliJF)7eS+ESa*8s~ z9MX-L>7KU3XM8g){M_CdvuaHmXzx9IC%!sM!Of&k^ z%cn2XAIt(Nqzn?TXfT~L2019}#k3l&4}L3^NM(BE-g8wlZ( zGXHILeuv;A+7qBGXd$!~!r1%&%i6vzarZA4N`{6)e}FV-5ws2}g}#E$Lbz7=|GKX4 z%c$AA9x8*rhR#7Q+zASYIzo2n0cbq*0#pRO0eu900sR2|UtGgCyfgm%)f*ZHJr3cv zPWWQzO=vrG5YOj-Pygh;>eBkPqnatGb=&TTiQ3-yegQr;K8$)C zyw>fDRO|LBs>e}Y!cL}o*Uz;%MST;UxiU4idp2iYC-@@Vbf13~ZC{UCw+0jS zUPar-&@ob|?V{E#F<+wo^kJ@8;^4J|BVxeB7z;lxr87~N){EJapG#wQMx)kkN6QMq zTxBr3%zT>a(sG|mQR`MPdwDTup?>b7^6rW#m?dBi&!w8YoVgf24dzN5 zcQBVxJ>otJm>Gu8Pu5yL^>%s>FAPk8FgvFlur(Mg_!+0vzyzeAAFw8U+-ZS za3*T1u38pZpLtd=?>dG4 z1g)guP;VfbwA@wxb-bv=CUuE#8!3})m^Tc;J%h#3N( zCdXo8me4Vxw!4|%4~dU!fVt&V#9QRE;)3wmy1X&wB*A=TWvYicFy0F0PZ-PL$I<-A z=lu0U_HQ}W`mG%&pY>tBEqp!(pMx#B&z1c-^Px##RvB&o-iI0B%Y5!)MK$@HJ&Ak@ zW;QWVkNB(flwpfEc$jbKOw?~mspRSLdmoy-49t68ahzHfI)F1HjJE&n#g{m<4{!TRCk2z<;{?+((`K9X z*eqU;cp;dj8&)>anMH4?Rrf3S9vol z9-8c7CI?d6BhHN#$A~k7=Wj^ezhzbHk9D6uOp7hxv3wuqAhf+=*fT||;WN@vxh#~J zafM)B=f`~#`3&~EgP0>ee(CaaHM^?g1hb6$MBQYanfEG=(blS!O&%u7OIS^;{ih|GheM z`mX9O-*VK4fle?_U0+D&yx#U(>oQS4 z*I>p5oCOp0bHSX>nYqM#K`^O~D)|KS%&Bh*4dzq9eqff0wol)61U|1(OcEM#ZKxS4x7b7!e`q!htRmZT6Nr;*;n{%i0KFB zF~OX%>&T_UU_OGjH^BS=%$9TAZTDf07j2htjE)RMe-iUG@=45VI@750>*cdS=8iNM z)ed$umlHF(wmy%&dO#Z)Z{G7fc*?)FY;I z3AKZ9Er8Mg!>#Um{gA# zb$(q`lQ(u$qs*h+C+hs_%z}H;0^`G2bl#s(KerL)F`b!C$7t%JO*lsI`89m*BcJ8O z#B1_>D!tZGt=ryUqJHkoMPOdv@OlCHJOiJr(Vuu-(oDzGJ|Bh}vbRtA-kW(VG3{<< zGcfNbCa<6CVde;C?lV7}8=T3RJ;034(3w*W=GG&jZ3FHCv&g=0<)gx9v$&;0?@tEv zTy%un=fOp&^UHnaxS2mZf2M+%_mj`Wsrda(w7ut|tw(QZ8_>0{n_0F$b2^v@_YI8U zwQeJp!eipJa&gaY%!#&K+!lw;Bb>?}_>_9#RGmQ7A-_~W# z1#^k;iF(A;pQuOt7JTZ==UbvC@8*Ff$2S{Rbn;@JdVb=!uM;!9Q)oGS>df^zlWOv= zZ1=r*3atxnM;M12G{)^0r;h|~6pI?RA!NmLm$H>DxskhzDr2b@G z;PW40F4(ZPQ>&cksUGpnlAM#L^m@d?=LCL?SeN$em$iUTsz=-d_gHY;-EDsz^@ztg zs3vdPQ{;0Ye1aL&s^}@IN1TKEA*WD}I2-kd2c)1LaY6&kNG~SU&jqub*A7lgCg#hW zx!jAXoIDljK#gN(;DAv+%#};vvz@4$JWKfOkJmcX&t0W6=~p?xoY??#C$Di#%ouN< z#Kiq2UN^Zulj;!@v)GqOHI8#il#_yqYweU6Yl^4sE0+`rCTjBP%mK~ZwQk)^)Hq&< zYpw;N?OsexLp@?*ni^w{qhsXDw2)5?^@tauelD0693x_u2%l~yGnidbH`&`~T_)a( z!K8Y`I`c)r#Jx2)6SZy!s4-TSFiPB8`z_2s&cwYS^6AZF)SnveS=DD2di5vw+27Nj z5o8I;=sGH31Rn}*! zjWWf(N_rmo_%!YT(sTY7CyCZt`2US;r!}&?@x=`=@d(~iw)c!zZuQE2QxB7GgL{GG z+I#vHrsBOV&sDw$K3_?-ZZZ9?%!uEW34icOObMs=zu0!{PT#%%eVI5If*B!;I=^|p z17gh?=~<%plBqF;npOdKHI7p{+2veF|nsWd9hd z$@|DIy)G@7$=HktZl9hyzrtrq8+-s-F_xn1h+d$4jUl=w=q{ZHLdo9)?fU`L&YI zWy3jhm{gaE-^WeYnWnt@OqKYWYHA%=c=t@b?O=x2VFr;;y?!oxGkAdvCe`^JY%s%# z({~27eIs?LU~c5h5Y0Ad`$ArmSNPl~50he85Y_qBnd+POegURQXX0}MRFl_MXcf#j zF!MOGP2k46XJzBd17PmbnXgdW2cRbJ7-AMhrVymYw^vtyxE;yx3=+!>;x zCNDAH$i`PIb8KO|Hiw3XBwpzL%J8o3XAl$hbKT4pV4nYC)i7R@H+XPRKL7i6#6rQ0 zMor$0WyL!~QVI{6+I|Y=3RVDSD=?eir!$`&AYTJBSnXF-l6F95X4KCG^YvkJ4(j}_ zAKb~!TtYrOaONg34<6N-3YZCPc6}7uk~2|{n3!hHeD`G9rEoA6V!o#{Q+o;~)#NSo z>-*$t$&s+}Ffp$X^U~d0RzwNr`#Q5n*Y=Z|2xdyZcP|q&0L(S#FM+wE73;UYkKN7u zS!ce#`LL+-Yg$gswefR8iV|Ay`f#}LdG{vH4451i+&hJsE#z5ii__je31imLPh0l61R*TPf zmmV{iN&8){E3S2}N*9=iXN>A`C?x#s=dRPhE~~}EbRBcKvOXi`jpNJTPC81=pE+~tUSh^}d#uWJeC(p!lbx#sbG%?4d7+JK zt;_Y6hnWeV%gHBao(Hq`#_?kt5|6q{uefRjbN`GJV8#*i(S^CkiP`-n*R}~+$6mSX zT+NXeX1F?ndHBmU)6PEP3hj72bF_;yCD*Z}nym91E>{0|BjeZ`iAQTbzH*}$HIC0e zGTp%-|CBe>o3zlds%K z%qrdI{_bZV7@d#$&Ix0Wz46q@k3QacqfISpF+Vbb>LwqG?U{DE>e8d(#M}X9R^GWG zI`i7s&p&zDE|}eitbZqc^k=+gwDkA=sP7z=fA)n=HZU_sbjuga$3_zK&ac6I^v8|& zgr|+^{Aq4ww`DWHy!v7D3#W!Yetp4kFe|>C*J3JGAN`L1yqw~)W2?Fz0 zV(y*R=IFE4kB=NYGwh{qL%f;h%J702^0~oRKU}o$)X*n;5VL#5L1K0pvOa$BD4qG$ zo|wu9@EODALALgPLM`TQ`HpFA9vbRq-VUGVgwHf{rA*A!t3C7fr4D^Ea=|b#_k1M-Z4nYOW3&RnN63r#V_sL9Kj3mKRa zn1hCHe4J`A3nuD2doxd{tGj^N_uf#{Vt&e>GZ`_%*XVVq!Bh;Um1U|i%mQYMjS~%K zt7M(I8nvzEg4Xg0)*+gh{ZKWf??C^ch485}%W`;s`ZAp=O2wkKwVNqN2_|YruS#Q; z49pUpnL^BT|De2?wmytCcZG(pQFA0ZMqncFdc;Va$We#d)S@spB$e6@riAhgvjnq@ zYFif*GoRO?W~f80Go`C2ki`rpFoLQ0FlAXo2?eSd-Oh)}idi%>m{cWl6KWh6P{dTo{|#N?%?x4wdfQR!gs!Am0BRi9VWQu#LPaCo zOpP|hB$!g;OzhMMlU8shCG~4YOHq8e{!a*#@gE%{_~cA8`LtlQlll{rAN`|GGkL6E z-`R&rI~z>qX0qR#iP|uYGIO}kaOz$$>HXZ~WLNmC%kOd0>oi22 z39#;yiT?Be#MiKycg^U3l4)3?_>cIc?Y(`{yi@l{xiJYQKV8Kl;nQn@v89N0nZ9eq zTsB6=rc^^8V^4IIH~F$CM)@rDrcJ#XwJ#&{PGXf(Ja*tc2=fSN|VmiDF1b4 zS>w!6jWZR3driJR4Q8dU&x7zes&b&QTOFUOVyw_(!^}&Zd6Thi!^|>TjkY())Ub}6 zFv;-Q0CSSzQ(|GXL%OL9|NaK1s`uw53)N!2jdr4md>Tv@b$&H0tH+cTEaelmT|_Bj1%#BXznin&SGn?YhBCpA7!h^e+d2XhuE)(^L;Z=HI1@BK{w&6bg z%EGcG!Ni?QqsB2_iVdH>O!X__bG(MOH_RN<5;b|Vr6TT=n5{(JWWmg)w&y7h)3jlX z9iIuGk?KKW#&sI6X`G1``T7iy#@q$w0;#C?Yl^{Up04ntGuaq2!z`V@usD#}33Gmf__4dCX3D&k*U4T^ zd6DWwW<*#5{Y1R6T$U+wqs_zyq{%n)%zR{qeSAZxjm3z}2*;@oGRIIR=KQ9-ID#@U z=XWDAE$qfmWP2~RU)uNvGXLPxGlMC!3(v%y-!CGSnV4JZtJ6hw21OY%_s8_2*}=%% zfXwrHW-^=IhiA^+nx7HeQ_DOQJ*3a1FT#~1WOkqW_W^Y5g(7pnn8Z!4(V|X&W+=_N z&9uMB_FG@ZtXp5EG7)orKfa-}+ZJ;uI^kx@?94OY7Mb}{bGRd@|A?`h$|y7F#SwaD zq{Wx{70u6$ncc@t$3AzjF>^>lKapvkJ8Y^iv)-2(;nXueKH)cKQR5YHSybs}?pN12#QYvJ>Vd*Rq6mzK$A z90$*hoBAd{_TqfjCUbBNWoA-lpET4NV=t1csLtUtH=oinp9${O?c*?MALi$x&fDLm zI;FYerijd!*CBIFUjGqsSG76^*I4g6@sibo*})=nCNd);|8ZYD&orS<*E2Wv>b9?a z<{x;b^zu4Wg`RmU9s6_0oPx~l>x;F_*;?ikKa*K3F=^?Z~y~g&-Ap4gqAag{~OsaF$6En5Up?W6G zO&)|g6K>?0sxPy@FY_VGz=ROYIF5J}nNA#gR=Z5h&+R*R5i)(Vn6=Cx)T#Pr9QWTZ z>RgUlw{sFgW+HQ@mU(}{j()t(&BbB7&Nq}J?K8(xok46qWp=|;2xc)4!kpjdPGi3F zZ;?6VR%AXfv$Pg<7WBDa*-=`DW1o%8J#_4ED0Ms&Gmf97S~|?2_6gr4XJE#$Ejf4+=1>m{?`6o`*6HoMHJ_EE&a~hDY2l*ZCh$6G4)ybx9sI&A zs>nPp>eMrL9S`4>Br>H*LI0-A!RNPmwL1NomUfw&Df7=Gsm|lGaqO?IX_L8iyDiy~ z!FI(^<|mXHLXXdWzB(y$!JiUlmgD{ubzUcP&RvdO#|x=W%2X(Ggt^Dy_1jMRGHY?{ zuH!pnEaP})6lNUr%&*EeCu|aRX7}ZFz9H(=Gt)$7Dl!*S=FK0czem4^pw5mni=1EW z-<;6>!MdB??WxowbNb3B-f-|^pL3UlI=kP6%(raGg{iVLCS{P7GAEWecxGR3-kKwd zUgv`eQRN|2XOXjE|CWHvW|29)A2N$6(;_|dmfVRlSyF%NU`Nk8CzdG2%-u0;+@(be zkohUkEOh>~e@kTd2OqiV-8@mJF;h8SL78jclFgW#>||xg%D_fo2_3u0yd8BWVP>?*{Lz@nOj_pDKCO9gRJX}I zh0IrfO4$<0GuJ33cbr6K4&9%(o$P0)Indtnla8HDjc8r>MzxdI zc~P(P)d6?@OZq8gcbN5Y)Jd5cv!|b5{*PC0q|9u}balTg;`*5@LyVcRIQC#?RNKtd zwwdnkcUgn;e=HMqYO|QNI;TYVGe4dCm>ronNzSO`*T(;p(n59K`0g4@$sMUPrw=yP zS?(UznP=i70C)*0i}}u7#j#W7_o_H{%EX5(kZI$Y7er>h#fi-O1}}d)%6IGq?ieX1 zIDbKzmPxO}nW?Lj?oOs>eu%HT$TZ(1DU?~CQe&~+JO9S#MP`q|k3Ma?F@G@KpG;Bg zZ89m#pP3Nypz3^ivzCe1t~Y9#L6kY~M$Ap7I(cSr{$R{arWFD^lVV=TpSz#eX_6_@ znAz#UQjxh4$1XDQhjho+2h}h=b7-nv&-{Th@%o!dB`=ug9>fbnrkG@0(scs);GTHSr zF^joPomwV+PhnA-9&$Fis6c%Ai2fl?inx~kfw`Y1dhY_K428~dQusMP(~KfhL1vJh zu2-P;nfZip(r1a{;xA`L`7)J`GB;8nPQNyDI2KeCtRn=u$+cSHnK<{I6h5Dn|MR+; zxJPiZ*!afEMqs>&a*I+Cm}xlv4l;391suD0SH#j$CU&FMSs0LMHyryj^fmgGnMYA) zn@oit2~(JsX`}n|*JPSTDBW@FuA_M(Ganz>vN2v~$C)O*PP4vC{5r$$Pv5cgd9TR) zWeVLttc;?l&|3G!x=-7kT{}tam zr&v(7s54_oK<0l_oojLIm?6u;hId0IezPiIu@m_ZUVMy(GVNw|_3h)7$!GHN%z{%= z7m;a2ri1EyuhY&esMDBf3R{XyGi6%zOj*zLP^L?=7i8q~I%Q`u41Q?GPJfUI|^q6y{)2r|Y{^-~GfOi?=D>}f9mw1-xj%g1jb6c@c;s3=Q!e=3N-gtBFP>RSnI8|a zQ)W}xs%`J&YMGDdnR6dnKERF4(tykqE%OhOT=4sqL*M6LBayjIWDcFVQ7zrq6qd^~ z&DE&$k(7}*b~Q5?nSXOXxDoSSi+P>X|IsV>uxD{?L0$L1W#5C$_5RHJs7}gka({fs z%$NO{dqz@bZpz4)komw3b05(&UmAM2r~BIUOMOEQdm4~=GcvuHNBsNNp-oh$bmhL? zn0?7J&(!Umxs_&~Ql09zlv%JmciPGix+C+q-ba@2K%FY)s17~c4>LL`Q>DzYT4s|m za|~vlUjDWTnUC_!NOfBy&)ik80&_q2bjQro+r5vhDD`D-L#9V$*4B;PjXE*oxCt|4 zMV**ut7ZOBw^!wv2{cEQ>fH4b=BTdp^yZm$$P}|~Z$DBhGOJN%ZQY99OFPKKyjRTR zy@E`97H&U=ej>98nXQ!hB+bt~k?7@fY5B41ncaA%ip&|_N%DHtdxuKDJ4#L77B#Wu zAZ0co)2o(#<;zrCw;)r@{YMGdyi($%` z8Qm_E=I7=HWU4rJ)!R)~8{jZz9^rN33t8GyhdPm|;uiQ|pg4BVXw@s~M5YIs_>`nx zC(l&%Offr{*BOwhw#~$hW7S*Dk9}J`KGVoEbLrTxJ|gD)QYIZ+ZcA>idL$E>D9B$Y zZrM>LeV7raZ9fHO^4Y<;s>lRyfvTtQOjXb1SGLc@6kKtlOn!3bZo~GfN_FA_uM=C! zRHyuv>ioZ$naeXN`WNcNoNsaZiaNI7N-s}acwmok=6JptOp+i-OX?JN=L_@ z%a5HuQd-o&Om4zs311`aM45Ov1!mG+g~!seZ8)d)=PV56GF^AgIVde~E=3YM&ZeN|b*aLkZQRHe;uk0r zk7vWYY@+8M+Jp6@ar@~^WL^_>dc?8k2Glu2tq#mY@!DK(J@Yap88fSK?7t$D=6b6%gL}p$ z-wbY^iRWZvW+~OlGsO(TyJEazyl>$y`+DQs#TSa9fwP^ z!rRrk3Qt(MqE2LPqx&-vH#25l?l^OsFOz148;_kb6ZK5Y3PKdA9uGQI5=2>2+o{9O+7th{--psnv-s7_#7&7^s^wR7r z3x|lA=7E`ft~bqIS5+u&)qF%#EY5c>bg8S_Q&)(rR!wUjCh)9E@7tmLgS2hEmJYf zUZ+ei=A=&&$G-9ZG}FTGPm#&zxi2ae^V~Z)cFc1hqu1HNjCi+RCuO#*%PXKdMdt2} z63ug0^XdNdiu-ddX4&tc8S%b(?s}%V6JMIViswB@nuRT$5+PaJ4#74~yZj-xbfV=a zTIQsgE;c9{ih<~pp)!P}8S21f_*fQ~9ouhAsY^bXk9~xv*1Z0Dvp%^F@lA$yx6Uu_@&^#y`%7GN904juvAt$sQDuH%E zE~p%;fGQz3bPlSAnz6#qAPZ!LA|M+S4aGpQkPM|knNSvFhvq@qP!6O(1yCVW3^}3g zPzkgPazW)#1yl*Sp>t3@)QmM?23a606am?wXeb7Xg=8oV%7n5YJ2Vf1 zV#ogOA#|K-mwL%e)4T^?hpjb$T(x6Nz3$jD= zplm1yQlJ8;5Gsb8&~~T<+6B3wa;O5Tgxs3WVO#$TY6jQTiD7|Gmj#!tPy}RyqM;Zl z7LuVfC=<$p?9emXH*^lFhnmr|z#t1`g(4su z6b;2dv5*X<{YquxcBCv#c5LTC*-(xq1=|9s5Gsb8&~~T<+6B3wa;O5Tgxt_Ms2*x& z%)%g?S*%b5WP_ri7$_E!p)@EH%7X0BJSZE=ffT3!Dujw5C$t?Zfp$SIs2r+*Dj_#? zPE$R$q-MN1!Zca1C0U^e$Oc72F;FZdLupVZlm*$Lc~CZ#11V4eR0tJAPG~z+0_}oa zP&rfqRYGp)98?cAV`e;qERYq7fNW4S6a&RVGL!~oLRpXu3w$0SGS=!dFZRcs*Y;BvPZ53@>plu7a zZLzj>YTNDFwnW?R(zY&bTdr*@v~8ufb!*#m+O}TXHj8Z%HdKF-McZ1nZG^V9Y1?RR z8>4MwwXGa2(il1V6KDf>AACv6o zS^rzy;ma3(JP!rr`}qSr|4zY~IKDB-oXb=oxXh%V;q=@q$?g3?ERFjGFXa0-<_9Tl zc&5!x+{75)e~|J3lbP)SGxCFE_%oy0@Rd3@flEf7j{1WXKM#?Mz;CmgwS3||oksqS z#@&4X#HSKh`2I~Hu6KflWZsr-md>=}!ND^49*xiO)j!R-Qc6{v4;c8PW(IC$y;3$Y zHf=lc*D`{GlR`EBqxo&Q>$NcObk6U+jkt;Yvo(K4MIhfO&oPBsrRj^oS0MkMFWwY> z_nx4HCAT$fl$V%7yMupiXDj~h0slc+6M}rsZ*SO`N<3)d;;-G{m!v;7{GhxrA?a`@ zw+DXkJC~S#(>*AC@zH9OT1QjVHxKk;~I&N z^l{b4Cm(*st%8sF^UJG7h7Ro=lD_!6Qzo@e^Eb8SlhPZ$I~Al}x+Fb`{8x0|I%-=n z_vZ@!{bzzVOg+4n@>@r30q?V@w~>35SA0Ci&ppWJe(=SM4++0@GyHwI-*YOj?(%0+ zZi9jU+q!wzs*!tjUQ4{mtL$0a@HzQQcUd=ourhq_+}?@l4VO+iYU@9f)MYC1k9~Y0 z@euHuvL7`5?&6mGzQX_92N~hsx&1hI{gC~cR1N>TDJ}U!i5FW7<1!+>gZgz!-+Ae@ zYc6=zYgfPSkQ%ma|5|+ z=RZtGXJwC*U(`SMW^1qq@Ma@h)=Y#n)~#x$=Dcm?F5B_HPBh z^e^zBS9O1-#;<5R?9#smuNGXMARW4g^O1AYz9EhuTj28z{06~i>0ALn@8|n&s8DQO zE{qK1iYq7&`$xWyXMJPZLHUa1(sSnpx62`dht`dpmGuqVQJjhA9LuFb&O?t1?ow== zS7-8-z z${(!dpP_vC^LTzlrp~3pb2vUn-vDlk$gCv4q;&Cd8Gc3cGw|1pTUBmdbL79lsk_7v6E#t&{HZuRr-V*eNMi!z4c$j`rS-e#K1 zrUsjxEJn(bUj52NzimFCNGynT%P8LRx_J%wk|BFlk-QC z|A6304zrbbsF82-^AwGz-}6b;PTdba;)fn3x!8Y-!({Erjxo-Yw0!X40sK3b_NT*D z|K$AswIcAUl2SoTKOFQK%RPa_3&5f;g8Q69i9LAew`;C7XAY|zXN%THrCcfyZ#$N+}&d&!dY{UNrzB%ECJJLqS zul@XR-hhRw?BV|2lX*Vpkvew_Sfnz}n~A@0z~kqMZ=S4a%UwR6K0I+R@khMEU*CHw z{BE5Ooj5$vtMN;;|N7ojz)yLG58u|fIr0a=&pHM?V1S$bZ5MKAt%CUorWyq}fF}e-ry3mrgv1^WAOuYv6IFD?h`#4vz~WE(iEs zeE(a+UmFT8alVxHKXy^!ihAkTju_DoGr=fv+w>cipDd!KgsXsd>UT$bAME(g!=LY{~hJCd$Tt0Yr~5K zAKwAD4ujvo&FtQYE&HaK^n7|1Z3_4Mb?y@Tmx!})iPuN@Z8=}>`8iJhk;0$K(A0 z(zlCi!NmoMbD8@);1P@K~xJyhmF;nlbR@ zoF{j{2Qf3}+3>gJW!S&qPsYZ8PqFZPNxqAPVgCypXHv{-x!)6OHJkl>zToT6q%h7u z6DzxZa+e1bEf-kFGpOUqI z!)H3LZ3OQIe%4*HnDYYSN80m}nwBNxF9^!of9Ob3^-WKDhZRNEHYQxT+V#SHC(eRD zuJNyYJVEdlozGi#5q@xyU&Q(Ht}WO<_k$M)WmRdsdr`v0#>l28!&;_XJp0aR>uN#lH= zf#Vs3^W^j3FQyd*S-)@%bRFyof2U!e;BvQ2n)mhiN~%XZZ7Js^Zvm{!6P+p>ws67Li02(Gr_~dh2K^n_YbZxS@I&W zj%-qn>)c8l^?}=hmDL6w&G*kav&l^7y)glVkzerc@H66y<`=vY{AsG+$R*0}&i%wU zYFy!dCfmT7!^dp~&SX5@6PFx#&cy!VZ^xO0VQ3B(O1$EFTngY=R}b%w{nJT?6saw@ zhFcW?oMgz)QF8%vn(!xL13BJ&;QRM+`eDXTGR_q+&NGRd75Mpo^s`QI8Qn#=T>$5H zTuZmmPk59DoKX1BbFBZ%21h%8u!Y%wf!ltW`)<<7a9f6*NqFxPumg_Aqu`Q(%fgR? zV&JGz455`R+!sTE&Svpe;9%Glwz-{dD#|%6*I0^g%16Lkj z%=vtl%woe+Ly`_jXFQHKvtsN?^2Uiyi$} zH#j{~`V{anWrxGgsClK$!`z>I4<45{W5t>Us~fg^q#2KW*h+k(8@x5{bkz?Sz6{>5 z=i${YbxqQYReL?(EjwR__1kse*Q&g8Lc5MFozdWVIIphmTPZhJz53m=nEP{11x~)$|6Bda$-h@DG-$mpT7;BKT52 zzkE=_r#X*(&{Wr0>sG5v|9!h!x@z?FMY-JnU2P-rv&UXl>)x5$tXA*ddi%056TzQc z8`^MQ{qD!cHBzg3_VDX1btSW!tEcVWeCzK+%y)d+|I*sfo%>IIcY4VhsiOL$ef_Jj zKABU!{BK)kjt^m>;Hym|@_Mz}mjp|$JlE>}>2)@HPSx_iZb<^~auHk^ZUVp8Ouuih z?jKsmB1oS7_Zi1&8lwE=iR zhde^*tZ_?s;kWU8DuVnG^qs-|3@tg7Z{YdFnG?6A??}nch-)VqensP8bS*e-#7XvX z1y^uxGjd#sZ&AuOa9m+HwdDrHFKWE))dBz3pN0dj{8j(J6EX%r;yaao%xOp9H{$h? z#KPIY{{dJ0ZaQ}OBk{%d=UgeyFk=tl+Ne z^QUzlLw-CO_{s10b3Fbym-%A}H?tt${Iqqh$d4h98EV60=CV6!|JmS*8DCGqaUqTS zjog{4b6MlTPy2Y3!gQ`k^qxQbQNinV{%Dj-=LdA|hzfo+X0DuChW&$EHNW7UG8D(F z!Ka9SqH`IKcbOeff?R1>KjwV+TzUL@^Q-WmwwBQMgOA@+NPfZp$sUjTQRB*i{2_6tnC+a3Fw}j|X=yd-q3cl&Jvg)6N4&Q(RbozvuY+Q{zt! zUs^uCYp31dwWiMC6Tur(T$7}(_*n2772OeT75tG;JlB%ITVscBeINco6UVMNqSknB^YP11 zBvtEtT5bPMH!nY^Hh6Bk)Z$jzv8IR1F8?9vNKwVn2=Y(6U^3sle4Y9g_yxCzRldKV z?CU=yeID)tKRL!d8N5%9qZItWZ1Cdu7aS1$fZ#RE92((xf2wCN_<2_F=7IyiODBIS zc%pkUxXI#pcXF%cKnl2J;DY?|=}Dgw?{R#bUE@(nIycYDA0Mjo1aKMOla464l^qB= z1HW0$Pa-a-T2_Avep=%vh|klwlwk%}Dq`(PoUg&mY3i!Z^oIE?kWht*_ykiV20I6u#)bIR{5`|>$pul@p$>WB*~KOex) z6*~9l^XtSJ`r7rA#%2BAd>g>cRNr;E1$PL3TMpzm^ArVNoRQ3yEL&`{jbDQ^ZT%6y zkyvz&{%}8^$#01DLvTdHKervX;I$sOi!y^KCbaZONw_O~e1gsg%6ffwJ?}r}af}Ie-^L z57syq;ZyLR;A#69_ditd7Py(=aY^fk;VE(;KU?tD{DGO^t2w_nMULb7Y5SB3<~Ny@ z_zrO;%|d*cx!$F=X`yT^JuKkrSEeP?*V=u>x0PW{6va8);>@0N#Ik? zZg{N?A0fEQK|J%|hS#og|8u&3@f`TOEO0sQ2A9E`iQ8Tzo{W5rmxI5>Qgt46B_g6{ zSn`Seg5SNMV9nhO>wgC~yxt1G%y~v2mx*sC9-ey3(um#z1Yfd{rQf}v_>H?c-=gtV zvAqY}L;l&|X@Vazryc~43x|JIeD491l3UyIBU^U1#ubI%dCRKdaT;%2$Se;pcyLgOcHutq|1_TJ+cYx^e(=YI-!gg}=K~G=Fy)WZc|zC)>|gL@i#8=?ebEsg#QmKA z)RwovKU(93K{-Di?j~1beUH~~8J%Djyax3xs|CM$IE3?_MI$ec1kVRQJNh)|KeplF zmXWcX_ww<1R}bGQe~bK{;Uhl{XCwcWUzOZ?w%4rFNyvxa&#lCl7 zE8DtIUEzj6q?ne$s3iQmiQe;uIwwZx;Z94Y}Xzq9=uVJShEV}U%XAmPkf%71p z4>=?FQ3H3Gf`TG6zky5evw{izgB6`CRWflm@{g`}61Nphumvkj^DD=Gkq>O->xaVs zq043=ztSbhiv26_8@cip=Vs0=8eby(MVp)waW=t5JkJVF>xTn19!`7%_?-;Av%}B% z`eStXCI#z`vY&^tGMW2zp5K;wBnhGu{qAIi@WP(iplB!tqT9+)8k7lTL3U^!lnv!T3RD0U zLdB31+76XKyC4@-4pl&vkQ+J&)kDpq1j&M}6^ejtP&5<+#X>Ta24zB7kR6%_WkWfT z0u?}oP%-3$wnHV*F31IyLlsaZKY21P?LP%I=vX;3DV1=*o_ zP&SkUDNq4a2o*z4XggE_?SfoTIaC2vLT>0BR1Y;X7Q`S6WQ8Ii8x#%2K(UYvr9qic z7G#I!LD^6aq(B8wAyf=Gq3uu!v9XX*-LLu4M6pGgviOnn zAUBI2Q^4ZmaQ)bGEKV(iaM@&K@p!(7$MZvcCl`yiI9WV;dE&9p_#1Lqd^d%~caLT9 zJquayamc)aTj7)TrDow;_^?p zzCmGejW!l{C6mQ9Ky9$a6B>&lR`znR5-jO)sAT{$ivW-RX0T`cbNY!+96 zj4$CkTEODIiGXknmB^?n%#nkWSkQz*N$;02F}CbZ2V*Hp29ud>rQk&!aY<%&&O;^? zN%O3Dze#d3gCE?z67MH5;tT2hsOxg-_we%xV*k3|6f!~Qf8qHl#GM($PmB*v(s)$1 zEM12mq<)dMe5E8y@Q1+P0blHVQj#4S|JV~Wq+)=`x3Fcj|MvX0>{Qx+Zv!toE%Lel zw(JZ8UxEA#ogbk5+p@!`-$nDc>5t$y`gy~~@#H6N z7yRoVPQ0M=*0G$UA0>AM^|O$_34FqSk#FF<-{m6lTH=R7+-lIc)G_rwc+-b8-l z4d5#G3;wLgZ+ayc?cy*KaQ}P@5(4oD%30~)s)F$u7S8`i)uEPKJ(B6|y zHhAFI_#V~3S1qA_7Qt2cRqsadBiqFO)mK)n^mET8o!1$-*XI|!0Dc2c@Ggo|MZSTT zYWaIs`S?MVbCvrAAJY=RFVp_fPm|kHXXLBK^8ACUpVtaLiu+gPw&z~H|6wg3VEpe%0kC=b$zL=o;c)j57=C*hj z8Tfm`f3yy~8pro8*55gA&VFvorwQtND)KMY9DPLRkEri0YNmdp>+nedyiWM99Gy-6 zCy@`1_lvug7H1be^=Sh5TI8QJ@Ve39T}ofdF6{DY!ln0Gz;{f)4!?u=KAl(nu|*lx zrEcWhO7-T_E#&v{M%sTne&CKv|GG;4zKyf8C!ycyTHY|9zrL^w=d1TE{#!@9!rH|} ze(=}9?ZO`={L@Oe5bw)*ijN=9<2*~P61*Sv3+)q}_Zzk0-TYkiBX!L4u>Sz=)$%pJ zk5_2^D=mEgir_)*_%{Yl{YWAo{YrUKm%5m_S)Ak7jBe-ge!GFYIKMONXv>Z*6~yQB zekOKM$4B}2=%PUGfxo^7cnSIUJKk&0x77C-BlzdU z7xVh7s&$57m*L1*sSqZ>Q|YVTUWI+3H6nG-t+U-7Ab~GOY{=Y75zFs{^96tm&reUjLvz#4d=5jpYa6nn!Evzh<+Z=<9MsfQC_@md_ z@;u_>b^qj!d8*1jV_Bt(PH>43P2_XG&X4-Jrw#vz{70$ZrilAJ+rodKexW?? ze#Zbg7yUkg{Hn$!?^l9LS&2sf)qdfp7j+#TD>(X%+VP^(i#m10!)gE3SNm}uEv@>> z(O-T{_a8z(4(Bc4bE)4b_p35L|DDD^tR_B7aQJ71^Zgfb?(#hG>$v@T{+!)B-_QH& z`RS<7%bvWTboco&_2=|DeI z2fRY~$9(zE>vA*l$8i5U#{4IPwwRch&q$1Yg~jhZ6Tj%1a`4e+hq{i~Ez;AV0B8ijBtm#ng{t zlhd7p{rxI6c}$l1`_-X>$H<}1`8qeT8l@fI;9TzG@&yCu{Vp-C4R2l5`&~@nHhHp^ zUqJmXZTQ1RPW>n`=oj(${cZSYA2&-3? zr}?wZW(gm0Hf3;*zfdVhr0+vN%6p9ZT|6tngEfAOyc|4P=RMpTb_+fQ{Vwopyw3va zr+JS}X~$j5?HN)s=fQi~eW!hXyq}zrqTJ$I0iNDx!6z3d5U(-viS7B>&3yl%$){vST` zN6ybq8ei+^I|K0fWE&r?%X*F<- zH9z?O;kB=`XfuvtzG{R_-CBc`_=F4 z^>J13+0(xI{D{WU&!Y3jn(Ol0Dc{fkK>aXkzdb6wUrhenTc);xw>-%fAwARpgJJc9GT>)i7W@gdy5voE-Hz*6}CuC62h$|lYa>3q+pk=Fi?34Z!2 z_?6}4KZJfB?0;w9Jtx3>;QjKyd)Cc5{}FqDc>Pa4{;uHY7t;Ct2L62C_fJH2H}yb2 z&R@V!!T$~CF6tK==OrH2-NgHKJkQQ@gTF|8rp`l&GaoN&!}CJd`S_Dh%kDDv#~1Ja zi&PTD{iD2jUGiS(ino8MABea^a2xUc_&h-eychb3Y+ApPH?OnsE7Xs~{r|-|^&6p| z$-BC9-hWZPpZ6w?A6^3sTmx=tHE>1vdxr~dE^zhtar6TvE5GIWKCTGvh(tc;gEvzB z-NSq5^FLFe$lUJ;(|I&_URy5tIraN+t{_mx|K$G_ocG%tPe#9xnftr@{K56BB`GYB zqu)tkBHu(jDS%@(2e=g+{W_-L3UJZSQz`N}haGPNqewfyB+kr+%XCq~`;&IVb@Y~@ z@gxNoNOr>&*Wot(Chhom?(dkR80mkD}?q zt)EBtx96j=XS}fn(f+|7ERe{L#}YHyl*~Y$1YRti@$-8}E2!DV{Z@wePdLZ>Ba(yr zIS3(rgC{xg{)8Wa;~3(hrqB z{y1^;^S~cllp0dDPx{{Ds5NqjNm(rVeTv41lpP{or}H@F@nxLqMUd`KqexSR)C!UwP%&}l!gWH3_S=Z1H zbe{NA)Gt(ZR`8}59{#xD4Elvucs(C2yL91c;;nHtRcFsw&@YtRwCCZETh6M|j9jnh zyWG0EZI^gIP(yWHWBP9rpUqW0j~v7&s;M7{xVp{5d5fwpNgtZ4&S*kE5Z0bK_uxjQ zt@!<+bu6x0$~pB55r;ehoce{}2gd|#KUc|*zwn@Xm!8ykwc5njpE11~CyYEV#lpA7 ztKqM%Lq8HoRVmKEF>5wg#cjxc99-4;ZoH9D>*wA@DmW~4=vTs~>U9(pr=4W%QE9{3|9~R~PmwHwF()>> zKAX{xE47moTb=9I8Mlh@AHV+cYKh&1ej$9Gj`#EMJ z6&eL(%|W#sO5JTEHPh4fBGo(e?s)aT@ux1RpQ;Y;ulo3hqruS+1zxHi23P&O1^rs_ey*zcTL9>%;{K~@ z?kV-k72-Qvgr7K0=$ER-&u2AvrrLyltEKAu)Xz1Mev0GoeE7Ngv-~?1{0jQ9Mtl8! zL%xy!r@9XLx%5|x&rqL%i+(Qre6K^l7WIRD*0f?~bz=qX zpZxv1j^+JYE!59N{brwCo(Asn#0qZeD*CxDTxvO^N|j9uG=3s>%*oRK++UZBelF_w zx^NMkfq!)Q4>M05MZech;unbL5kE8A$CFOpYvfrD@blo8STXnOJh6T<{H6#;X$tj& zfk!;FV9n+9q?6&U1IP#eQKo*d_ftI4;OAL1_}Ac(=m*<F2b$*MFo6~u}7`WvrA6IxkS<3g+PnKrn=qFR? zIalLX$Kejeas8cLkji z{c4gW$-@0E>Ng|K$nOfOAx`~bQhSbmwKkk}cH#Af?nl2_P=n9Ukl&6=4S4<*`F`%w z_K$uuslnpsO!G61n+0dQA5CJsUyJrn96vf`a0Wk4RKJhY6?BsEBD)=z{Vy>CdHa5` zU&VLx{ku9(Fz;oyv+2?2=46|(Z-y7dHQ%PN!ETFm$dab>(CBkZG;U(Ej}niS4xyh& z^9S-1elAgd%-PWG=M8@Fv6^4w8w@;bHs?7!pYv?=D_!;ZiOXZ%*X0?r(eJ}~dip+T z67~=O--I6@j>q~n_zL*b+w;}LAJ+NZ3-+Pk$;&dykM(WQkCibS{X*Q|j@!^rh5SHX z3;*W7@%3-p>IiF(dr|^;s*j@|X>~+|#?PR>yBB=vn56TBZ1A<OXOXY-ui+OQ>(kyl;tV`@!LjdWfTyb5 zFZh@vSl^w~()cH=FE8S}Q)+cP-eBP9r^4&gIO>-|eqtT@JK_nP-~Mxa)g5sG{3WW7 z=X>L*-wJ$(YRG@-`)Oa)qF-y<`#K*dc#WtJ{HM>2d^GqJ)JHrG>(38P`-%F!;OG5N z(|!E1j~_PhiKE9oOMa}+>-_5BXUF^e8N}~vJ^MpVli&}n5WETN$EaWDZ|ZzDalU`f z!@&A;={V=k_=JpSQ< zD)`qK_#yOzAs_sN;K|^#xWA<&XkLrXZ)(Sz1fNU()}1}aoOpme&il>CkAAT>{Cz)v z+Q-vc4w3(s*5_}2|HO3mJK;C*YkvOnbe8lV>Q`&>bJ@rzvN8i7a;2&y$aM|t^XPY@ z{7B-8&kz2N#Q8za|4jVLw){g{-v{4xvyu15`nvFgr-3&UPxtxzpY!o@ov-T+ULo>1 z|EB6MlIugk>kDtr>y3UejoZ-AHRPLPoZA@k!5zIf2)?puBlT->E`L}K-UGZh=dyAv zS?Bk{&-nz?EpE=GGH`ie6Ym!ra__NxKS#e<_YIPZ_>jwf&Q_N*Ho+9k`^j|P=;PhN Xt!x4#UM6XLU~9hlTpk;D?@Rv+G!sdy literal 0 HcmV?d00001 diff --git a/tessdata/fnetwts b/tessdata/fnetwts new file mode 100755 index 0000000000000000000000000000000000000000..dc9f06295f5864920b3c98c2b13786b41c3e917b GIT binary patch literal 751 zcmZQzU`SzLU|<7coFMm`M+Z*H;U&Syz`%GrFE_CyIfH>=htLb4@0dI?Q&Lj%;(Zb; zQ;R^pWpWfdrh3Rf_k@3GNkM4|LW0F1a>d~TH5xAtUUq8fHYw8%idE%kC zX-gfIHtRb)uD*QGc}KOw?E7<^{GM_;HdI?UurO|RoL73rVcy&512a3nIJ|0IchK%! z>gjkgVVzU+4n`;b z&!-&!UdwaRIPu7FuGc=tX$4mg8h=!B{1IMoFk{mGgNkV{9iQ!BJ~*K|#xb^})p2_4 zMTd1dwJ2eZ%rOK7C&z)u*Silq>z%RB|B-|vquLCI7c4st+}--@fbz`O2h>#S0frgb Ar~m)} literal 0 HcmV?d00001 diff --git a/tessdata/freq-dawg b/tessdata/freq-dawg new file mode 100755 index 0000000000000000000000000000000000000000..433e468f70eacc32a2ed2f2433aa930cb4eb6e15 GIT binary patch literal 720 zcmXYvOK;Oq5QWd502Z)8fP{GL5bA=*fIvb>P!O~NHr%Fh6E}$+#ZIjN3&e`o0u|U$ zp5n1cQm2JK;Gg23!Z%SCqq&}Y=A1KgE$QFL^r0|)+%kO{ni{$3S!PQ6rtQ@9A~7xZ zOx>>OXvg$)({wnlbf|4w+Av*@OqZIbAM2)*4bzFWT4&RsP89SvuQbalDXH{&qn3t2 z4@P_sOeHn-h&X71&Uf-^9`qQ@L&xA{9Mw9=-SM@cyIIf}%rvhhc;&nco+;k;@$$0~ zbOrx=-JpGHzv&nKw_?+csL}+Ute}QBKks;M1?}PY6?)eOL2u}t!uyTB33`R=^yqO< za9_+gI|tZSTI-#2_$>6@BihtU$AdlA;hY6MaZc|~3B2sEvw;4XdWlw)nNq z7~ETQ&$wrLU;57RJsec?H#_fd=JTOD&VzQFK{GfHFvE;}9zioIz&ba$W{Zr^&+P6h zm~(i01nxe#o7~@CXV=486Q9dx2fKo&_xSl0;}ea`1^u Mh?vhx?h*;{AH2}y&Hw-a literal 0 HcmV?d00001 diff --git a/tessdata/inttemp b/tessdata/inttemp new file mode 100755 index 0000000000000000000000000000000000000000..37835e304dce0d2b5700bb7eccf90b4d050babd2 GIT binary patch literal 676716 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%0D~rdQZT%g!8=_&3jRl>NLP=5{~?9!e~|e6 z>QqLt-@*Tg>n6beXr$f3|HLl8GDOeDRHjv2fxk+1RS#gl>Pc-MXdUdNmQ?hj8+2oS zuv26TeCd@ADwx^(YJ25=k^;}Z+z$T}I^*KYgG0S^%Z#gYxNZr#pw~4>6Cb!bzPAe= z2>hQ|UGP@mH{>J1k9uUqbnvUd@53qhz0y@9css!Vc;VB4%09H4akN7!+5c$N=Jo7< zH0G=gs9!S2+Oo3gKV!_PWm)x9Qj}Fy)(8Ky5B>_{cC_qkceDTL!Nw7pg7%}#AJ$Nr z5B>|Y+fxd`e_{3&S36Cd(L1{r`x$cF*|@jhQLawRK>z;n;OAui4AXjbU(p1Oa>7xXs+geh2|Y^B=|gS4@ITMJ5}FbhQAhiC zIEHNOuX6L@uQ~*Oi{Ni}i%dcLanD{V3D_QHM!!V6|63!?aTw>>|H8;m;=nm$aQs=$ z2L{Jw!1s)wx<9~#>x7LD%@;W`;++5!^GCiDu#WkkG9tkr!U=A@Le3zCCdjk?m4aV` za}NK-{A%SWkN=s&{wHynZy5IEyQ-C5xL3+>mypTxpHXe=YRZq)8?8IfVmp&$7jl#~6>gb*5Mw0i0B{@#+L;Z&H7 z8a;bjiw>Nlh~{&S_=*gc`8D6ePfZB);T<^fy9xgA4wK;5{Mh9C1oJ-v5jjSAF|`&LN|I zy6W~NBgn9x`E{h-5UL3Qzjo8CfOTkN$r*&@(1Tk~+Nb1Xe!YbKK60K?FNMJWOh%h$ zKB*s`Y|z7_Xnyv1hGoY|(j-29HZB+AZS6}tmxgxW%Ar)@-_qO_1H$`vx&?oahac(O z%XKGnoGF$nd|rZMAG1^+r~P`{v|CHzfArJUx0lYLCHm=7RKgsbk3#(qu9w8U>doL` z55!#SbMyh}UO}`rM<-Ip^VmLG&I#Z~t_{O}lM~|4skHd|zXETiOqy&8u805m+2iXX znl{lG@W(f8rzu5gHEE68W&hT>o6_8$EtK6(8koqOR=b}iqVmc#xe zHaRV~`UG{O40A{6N9cD4|5N$|`=6|R2~WWPBxJ?ML{O$v_C7VW+){(TYIK;=(Fi}> zxK*UCVZEB?!SA0w3da>l-JBq_<3o_6;T?SZgI}Kuejn4EQ*&vI{r>bjkYnw?IDHSi z&y@Vsnl&H8|CGQVo`S!sC~NCEbr5y9uFio*{O&S;BYua$zYqPJi{KxI*q@a5yWLf% zXuvrH|MM}mI#UaoyS6BKs7iPgcdIiy8}&asu;$rcq1%a`{gp#&@KVD+Y%v(TQ1{%J zt~M~gIY_N#exxoK{IIy3N=c&*ejRCLes~kB&$@}i|4cu9*D3Ztwwgrdx7%tHKL&p? z_&)}Jm8-L7C;0acb+!n8x3jISm5S0j-TiInv3vvW{?4m>Y*W-8+9n{ zqW>EE6ZBt$!DatrH9{Z?e_|B;@IQk85cnYzLbPfvl0v|(M+$x%=1J^-I5qG;@D3(T z(D=wBR z`st<_RB9bO`~2orxX#C5&9moMQ)f~;_@lvpxq|)AzO*)E1MOSW+17&fUE$>A65Iv< z__&w$3jed4oVG%TQ6Elql4NAhcH9hpoQ-&YG`R7rr1PW}-$^|GZT%109a?Yjk0$m% za*ki`hxxa)D;xGG*ZmK;@!1vp!vEmanU*K5`1{R&lz0d?<2b+G|M+?C7pXR4JS2Rw zg#UrIlNP}1m~d8o0s8Opm&r4mEDaat6$^H2F6^uxQ;4$39DY&toupeL=PO)EzTjbltKmQm0@b=?r znCt6Oe$>DGSL=C&@0$Pe8}kc%aXWwam&Eh6;^X`s6Yr$;yxYwwZoZl8+s=Dl^V@`i z>wi$vrQ?2780Ty4H{`X)@pahw@j-YOTpw^aweq}N&-5*L@k1o9e|`o(9wDZ0nK$qo z1kd)xi)pkf<1l-lvBRm(Si2kkXN)>S7mec*{pA!nv#-uIR02N)|1;DDKh*W+1zd-CssG*-p4l70{%YaO!G-Lv z7JB?u6mrO21%K67ha*iYn(5lsf%f^XnXZ?em-kQ%`;%VbfBxHbbv2d){s%dB2K>)O z)B(+Schubf z7o(rCU-X2s=o->YMeatw?||L0PWYd&fP*X9U+s9xM`IW)rm z(-CJ44jQ5URIv{IxZ&aO36cF9Zw3CKcoO{H{F4WMBNF}x%Yn;2Xt*B88~((E^?~}| z*kvz7(6iQ@v^|5%={HFYY3+kW)RNR*)G@e^T2co0b-@4JkUN~c824yq@0|97>`%_l zZa*me&+NfL;eTci_6`dF!~7FxTpd5S>I&*=;I9_LpZLT7sNf&`+5ezD?{C1@l;B6Q zf5VkKM2aIg-i=Vds6)d4DAKVw8jad}E%_-l8k2D?E!Io1;nJ^Yaj=y7mb8t28&Ol0 z)3=SfBbv*dF7U?>R$r3&hqK7pi*dUHdc)2gw36h1VfN)0cf;#lnElxxINASzU-+M8 z!vBmv8@&kr=iIL_QVHH`iLWb}UnKM6Kw9uC73_b;sg>SG z!9Pwt$#|4j=@%^Xkuqsj@PZVxxsHCjFtf^>R0mH|P*s}({*^=3wHWWICC%l@D@B>h zn16ey1O1?^nnfcVE3(Pz_twa>D;nrWGY)!kaD*wG{m*-&OeQmp(_~B#{bqf(Okl60 z7_$X_0qc7V_;EtvT6%$>=aq6^<5$4{Fu#KR55_O@Lj6xL#;?}jy^Q^p{gs#$3;V0r zmsXjR!T-j#s`IJ4sO621s~gi=s3rF#@~?C$>ZCI}`LFOluSrr8`;$c@q$KzszeV89 zp`XJ4aKZz52PxryRMCncCH#*X4JM>wk^u?+(OM+d?Py%`3jSvJpAPUh=kjv=m71eY z3I2CAJ0!C`jGog(dYkP}&~qw__guB`Ki)xV+5e25;Duv!4UP7~Xm&vw^;|D-&-E6( zin$7z`=6$VU+3Z1!@n`V?0Q+QuiWkq z&RjS6kx_2v&g3?F=a!-*3-gbPc-R6@@|?mpZS;eq=mDCp9(}hL;mo)CwsH8?(g)CO zSj`4K^W(ck&-|Rs&&lf_86q#wa8ihuT94%=X$RXIO?zl1 z{8c~vkKl*@;gbN^lQ^9_m$%Vx(uyiAYwGDQQOPNmWYoDXB4=Df>b(AlTxJ%R$~48sDfWd z+I z5#6ng@F)AKi>lATpVU{cNjvBKnCgqt))XduOxeyftIVBzsjA4r{lEU=%$~&lXRBG7 zMDSNK|9P%|z(HWPRvg^%a`0CrZx2r9SMd4=ze4b9RHK;RsxjQc{P~*niOioLaESfS zlzd!4&pTJLwU_1mlg8h$4 zqrFA;Kj{&d$0zYzGYM*m|j{kohZ^UnKjJ z0e35V6!@QEZsd#Xf53k)_?>cY^QYMVwAVayO8B2ke?)y* zZrYB5y82HjvU*2MUDE}M9LT-Y(sTv&mmS00zyAHlFJSzsvS0st^E8YvQ3!7`Nqmlt z*#C$931Gi=QtX{$~m5V~$}QyI-PS=A#TUCm=VAEcnglJRYaIz{71^AlK0#{lSg8C9HSZ z|6p(sw2A&tCb1m&!|9DbXeS}LopfU|KhJ|7$>U2y+Znn3M^Ad=EchSJ1@M5d;l>5? zh5s=wkp0gvH{AbXwAV~Xw0A_J9V3$8pZ(8wpO}ZI?~?p=z>im8-@Gxs;eWi8TJQ<~ za|5r;jbllED}0>{`<~$xH~-)N<5_4NTZ!o!-!SJ*`F|hq|GneCa}B8Z#YFoH`yIEN z;R9|+oo8Moe9z6P<%i=J{aWIek@tTszpQ5$;(vbqr1d;*IKTdpUwO~X{NV;a@NZFs z8~izSoC;2(KTaWRLA?&fzhTey)u|0aU5uAXr5&xqPdMuUsLr#Rt%t|dOwd^;K@ z^8K8=yqw=C5tMoX0pVfxC~p z&_5?K@ZxcBEnH%KpcY{^+N|`)K`7 zqaT|}rCav48ruQh$Dqdg=j5#b@1wdgwek+>`FrEye3>81;g>hjbrtn)Gp@;bvI5x~#{8Vjzreru z84v&L_V?f)*q;c0b$S-_kMa8y84d2wXI;4pZq?@_E-=69-4TLcwPl3h$LBBzVpn)8 z=7+z!dHUl1&;AD=1C>!@!UgzL-pIu|GH8spWvgd##_hR^{vMi|c-O@+`c**la#!yT z`qj+dsCG{_9Kd`)@4~DD=>HaQU`~VZCmHZRIB(>48vY001Ad>+9=wY4FW|oNo{Gk+ zJ|B5u6|9Hy^vJic{uIj7;O9Ife*P5vVaGI_*wN%^II(5q35oykaezMozXCZ`M~l65 z%O+W8A&n`EtmyZlsY$UkhtaP!&9Pa1JCJ>8HO$Y+{F>h7%>OIRfzYfyJT6kkh{6>( z#!;Of`Po5mg7-6-d&Jr3E#SYZIJ?*Y{tQLNVl6n~+XTOsVX#AZ@V+huKi}yM{}6 z4aHDuirHRAf1ekYnw7kb{xWY~Y7TtR zALoT2|Ad?yo0a?~z2h}k(%YY*=e!mv^w|HNqebA=(NE~0$RBaS_Xz)kqgVJJtUY{g z@ny=0-3;FSXO@K-@0G*kazw6AoYW^PCXKkAq^F0C|UJ=-^j zRWX15=7m+vU-Q%xg8%eWPYC|Ey%vI>*M9`~;ZIa%`0!x*iE0^AkL|Dw-;(e@Xk($( zieqtHc?QWleURY4{zI3#$r~>nT6CsH!-*i5$5VSPtaV6|p(VJK%qo+vf}a6Z*h>=2uw# zMx*}j0rFQV;Wej{zxp=R>#FoKh5u3MH8hub^xlG>f)rooy_uieU(Ap8no@)PAN=a) zCTP8aZ-f82TkFf&^N7JW^iKLgQ<@ZKwopXl%1gF*WK&UpVd{CxZrV3goZ3X0P1S-Q z8JmOaX}&XOc?$C{2~K=$zI_fnaN@MlQ%A7(5!^`AZ4%dAJz%&E?L4F8ZJ3MHA%TvR z`L);>T43IsypY;qe)bQ{zY%pb%zt7ud6nFq?#tQp$Z6l#C$Yb(iOV)oR7dKiq}*_d z>MZInb6=qBPVl=MXqy}Ss4qbZ{x{vrQ$k|0$%yf+LSi4Y~;7>XdYf|A)qJ|QL|A}%}^p~8v0{-fuLU@xM7?3Xq*IP%n<}_s?msgtM zAK-sdq-`gut}Si1PuNMyY=zH5zfGy==gs%$>S?3xI@F)8Ub|Dvbtn5lW6+%(&<$rq+^-l2l zo;<(DB6lIps$dLk4mNWtlFVeHOXH z-T?mt?~{~M^DESw5c=JE(>+Z(TO<}XDqbVh)GF80HP?>zr;5Z!Kby!}h8 z|66TF1N#~;N#{x4|G#1K^4yOU{KJXY;dz{X63gKy{14Fsyd1dwfe-w|?LUG48IA4s z4CSUqaKhs~?8E7qy&;Ala&zABkbnBmoB5}`%@0>99{rR*fF{L@ zpYkJ2!gDB9CS5c<2m0mdmTi%9_vstcpxeHMy^jIjOC&AAcH?&Uo#}WPOq0?@ka{?A zKfjY_`-r$6}j$Dr;wPq*EU zc!+$O150vNcjf?Rme!LDQ z^|Zy}7fHPgsUxjs*(!{;qcTVb-ov;a{u!YM@?uCAm=QX7Rn9A%;Q!=3q#Da1xbtRT zk@;s|d5`(G!22+F`tWinritZPMe4=49ot7O-X9x5tbgtGWU6n$&-)>ssz%p6Il*sG zYBiHqp?`Z}_XBwmUI-a__PsFX58l2yl=SM1;O)^E*9`t- zH|oOl@KQ4Wy9#+btWr$N!@H@#kIxJDZ0MFR{r-Nx|93~ptDk3%eCEhx=9yDQD?$XH50^P233mpd{wiW>b`Th3M$VZCmc?e zHB|Ks*z0fZ1e||iB)h=!&qnn>KmI3{=2j0t$2(`3u_1=-Q4Kd~Tx zY`0krw+$vExY;3e7f?7Y3pV%kBwhHVVH`Mc5Z5Z{5Z5-yT6}`+j~*>6se+G zkv%t2H;_O3aImhqY$I!yKUrD+5vni(*QFkb)FLj2pR}uqK>b69AI5(?d2v2n^TW7x zQx{L5YmSXyH}%2gn2#G@nsNUMIOpWmPvCbFkIgxH^`G%OVaKk2Iq#|hn)n1hFXPdg zV9%`SvEwsIdZ$0~hWGc|m2y=Mjr<_llX3}Bo{@hx%Rdw88u_b6-p%Fn(#uky!qr%S4sepRC9&xEA#%SAU-S@tq&w_XPLfd+1K&KXL#4CucB^-G6c-^1u4n z%0mh zYqFmIvM~_(FI$S|2VH$^+_FdR*XvInLA^=N*((dC!I9rw`SBg7KN+?EDn0-Ao98d^ zOb}RpVaqI|5LEc!^E$OdnPZ2 z>sKxrvl#7WVCHD#zkb{j_PnQw>(L%*G_OAyGZpnGBdf-Jcn6mIs4vEA3GZi9&`7hE zz?T0=L1piU-@Q@!^DK<*2HCXTHP1gVABNb?@%d5&qu$4CD4{(+4yY1FDXjiS9m9+k z;1tx?nbl0RmtnH{lOX0OR&QcfCnNu$iDqpw%X^|(<@F@UH{fs1ztNoW<&XH6W#2Na zGgNi4#P~S}e-083Ujnjv6KRZ*UqK7eW>tT`fVgbRr_avE0(tPa`=5F--ZW4T#M(cx zE|6by^}hdo{qGX^?-KYUmq6eQsC8BBeOPy4^_*0l)?Nd>n%mm2zoE=-ByQLL$kgkt z0{7|RzO4$jzx)MpKFjK__-&||PyU_A-d{D6xRAzRzZ1d8AD6=UjMV2pMb9g9N`+oe z)m%1Tjq9)SocJz4{T2HzXJ;x**459Chlefa%P9W6*x45^x_l>76{a|GZj5>;-F#t6 z1+NzprqmdEb@Sc26I@)%bKzLZo=N#w;ms5^y;`nh~o&YaRj%s0;;>x)el z>(emGjTj3(&@?G9AFTBEc1Rlkv#nLvS+6~caAGkAd9wrD*2EJTaQce-h#;H4{CqC2 zA4E-yfaLJJqNXVGLlAVO{A7$fGg z{MiizhEC*<=X?uJR7_#bv5x}l0`lMwdqLXlaLbOpk1nhKK|Ken2V(UFToFb`W!qW( zh_2IYM$GExn5Zo^;|Cj?N-8!CwT(ovuy_}+jlnKljK$9n7c<%YVCZoF63d^<_D4A) z=0k)Qp1;~ecM#mVYVMBTrNPIg{oUWsbVf*dkGirW;??j>g;KAy~inEVN_U>uCeAt&Z< z0`i7Yc}%oR%EknoSS~DYo~I#OzxXu}o?^h?oj;oj%b#tkEdPhG*q9&Vsus(C-oOZgY)T9oHymcH}U)vv`>)V!;L{`uOPoauAm?n=dWLk z*L^e~<|{XcLO*XX8}0vsR&+yqDy+AHeoj5#R3q;M?BXz8PXy0U4$Ozj!-iuFJ-py6 z&mTjvV-udgrl0qP6eEAuZeu~tm@#ONG3u?{B?ZjSM=UA8^Oi?`K4MwHrSQ?*f&$b# zj6(heIDcp6T#EXJ71Q#i5m-OV67vc5N_oo?qgdW~TUq{SU-U=k&*l{Kz}nBS?T6Ph zu=*2RN1eRTNuzSIHC7Kdsvt+3jC!7t%W}0bdj3vUUoz^^an4NCJ8&;y`4>1#0%N@_R3e~V*LMLmS`sH4KjHef0@U?#V8$ra?L%g{b02zf6--uOR6)!(rB zvFX2?f7AJ4d1Jf6-nxC-q6@z@EHi?+gyF_ zF8tawUp{Gly2hw&^=~re)&^2B1j>0{t@&G~-{j5y13_!~v$tV+x8{E!h5t_Szf0i1 zOW^Ts;rrK^jli*O{dpeSccZ&dTt5MQ@c*&b`n-*IL(J z^^F80f4zMXj=GHbxR5w*)~DCj*FvlB_J4j4+07OBc{gRV_E~uUX5wc6dAVI} zAtuDmeDwL?i-O0-^#4-?c5%CD@JjIjy@O!XAb5S!yJo7Jhq(oAW=If3{x#!0NGNRLQdZ~` zJ}xW<;q8%_Dkk;^=FM{Z-5q9y&-ZUN@P>TkK=6NofqZ_g{}-hEzd41=L`4<$rKrN8 z&3y*+OHEgl-m37uhf=9OrBMbYQ+FDMe<}^6LHO5?|I4PqbTJL3AsB85^+nW&dSJMr zGyvu4lxDL8TO^aoBFiSxCYns9AXBgPz}m?sMj<`{8~MafGM9OI^1lpQIVhGQJ#4s}!B&0zKk z=+NU^s^4{taol<6mLK1B+-CR}$L%NQA^wY_5Uy(EZAN_C$E>`+BX!@rA78T@68EFm zxU;zlk1@MAgvVx&<#uEyJA{s>14h0`-5f#jXCl*dWBO4qha7(mFw&my66rPjjt}J%yMRXcE3xy@=#GBDldUW;H$90!* z&DBdDH>2DHg@9%rFUEroS@}Y)y_pd#w*1|sd&(3M!QxZ%Tl$X4`bgcvhzPj!*ZPAo z{4Ivl;Uy-^(iE5{TI?z10v`A$+GVVQRwSdGf1uI7{)7DsYuGipq{JSyiVA1I?yH2` zW|X{T?;h3Qbi_RoPhay^Y)^X&kB@d|qdekx7)+!q0&WOXc-+K(bOnnu`qB6$FeTtj zn#E&SY(e>0x>K_#)IujKS4rVIW0|EfOyy2Il8HGDw+=Fs+VDwUt}0wo6*uAxb2fL7 zSyD8qB7cy%wBB+fHgv&B=zksTILOKkO<|l zTWur6+z+U!50WjxRI-g0k!`YA;=%e0cnQ`gi?bR@M$Ej{Sl@y5K4XdRX(6Gj`Ha7N zor>Rrxv8vOcbt9-wf@~jPN%b=)_=T+9JiTW>%UzSg@z+Hmqv6~Q}U*s@gJ(iRW@@G z9eUkuqpDheH~&IgRbtY~Z|}EZpENJwi&rW&(dPH>DXMadRrjPv4P2vBx=IT@l!eVTd&=VxNl+sH2~DvslI!rpRkWs3CVw*a zE>wPV>38eV-+Al2c0HKOACY&9L~fh^i}_|Ut0n!;?7*$A>Wl~5CF7H`z=gywZQ@(Lb|Ks%3}Ixfp1{r$uA!?-(+ z%>Hi#d_R-m-=|S{CEe3+*CrBEXyd!dhuAl+r_hQdiwZU=A7Ja zX?T0e+u1X=91C{U>W{BtyN7%8JG@6JSb!7L-^b+tjSfqjccL+s3+gb2&uCqXQ64r( z?;jFyjuE%zjYj`~w;FN44~)2(p++3~zTUsNW~02`Mfz~&^4}Z4})JMa% zsdv9tWAOiDG&u=H7GXvvVVeF&3q3~p1+*CbEnebXy_1dDP%^|_n0j|1yBNc@Wj~{T zz(^x*L!p!2|2G?Nj2F;YkV0FwwcQxyMwS=EYE0i6st@{G6~p!MM5|%@X7Y0kd?hp! z=SOrdRqn>V2Q)gvzHaD>t#KXs_kuqO-=;(*0}kcc@JoTi_L_RAPBh@Xe1 z4(i`u|6>3f8mFiO`=#lB49ZB?M@vobhk@Dn103mr^H_&bG$^f~KK{V|7#iaba5(f5 zha(k}?w#7lfleQPP+EVCk163?87IqZlIa8V(Ng=T>u1Jk{T+I*w0^1jdGsJhx-sw| z2Ro1EqxCapDV?2c<84#ZSP6UoKq6^r>FoU-eKH26>B*+{C>?)b;6WK_#xhD9$d(^_|5S%@9^XIk{uycd$$o08vHUP+D8ax32N>h` zPfd5|%MWW}0J}pS^%_4^s@7*U4CWXxupi=|Gw2e!6z#S%X%so}-{o`}8srVfe@>K- zqS2_yaMCz(@c44{8^UWj($VJ<^v@Gm6@|$ph@vEktTSSxz$BrV)nACBnZ2;yg2?JK z(1|92_c4i({TCV{hJ`{4Dmr+7(PS*l{{8ytGt)n?=&|LXAKL_*bb=8vRs7$=a-)0) zQ&WX^n&>hic9G^XebYz*@ef4ahYET_C~a z`3~;W^9N-Mh@aj*XbM!CO3G82-BC`;lbPLJf&z0OS=~>L>U)FXH%w_%pDOO8Fs4_U z8}^D|H;fI}<3`e4%PT@`xQ9v1A(lsf(F|IS{wj7Uc8ZlRtip%G>OsdgtEQdH;)_ia z;>rydx-37jcyT~)*m=7{h)b`gt#rMp9mpuLfomr+;nX{=wlx${y!d3o&5$NYEaef{D|bAf+e*Sc56k@=Z-j&%OP?*E(r!yn8(G=1=Q zId$%S{H%!IUi;kwGlrWpY|l$}D!?zen&;K{e*XEc(dLbI--(}Zysv%A$7bi5yRJCb zk9_M*Khx}6K z*F~BHv(ta~WpVv~GJou!mn01S3HiMD?de;^bFcerzCTb%KUNK!*DEQ*6q1SlJqFv) zzWTQlbvx(camx7l=uhXJteR@N^`5Vv5o|eYr!7DK)JdtpukH)M;a8COvp=QQzNV`< zdqP}lk8jqAv&Z{4pjsq<#)Fi6JLR9hrAY1}k|>?>_o{W}J9^zUR>1MKGry=)+{MD# z9+K8>=WxPPQLcF|JOSlDnfzz5P8`ySx{v4LKE?AlEdI65e&5!c<_`V(CGu5K;;FCiKYO-{irRhh8vWRV ze)fNSPsG{hsmNA4R7C&db-U--oC~S@@gE+bnl*ICb#;Fef2=~i%Ar?m81L<`ZQs+E z7{0yfH3h?;Lw%Kk;XD7_!G>RJqoNMoFQy-#$MCU($<8+@yjrBZ{U;Y`lnDr)4vvZ|tUfT{_o1IbCJtFt4%Fvx0{^SSZ6yIkK1z0MXo3|{FsZcHJtgd9^c8$y!g&i7B^Suu9riN z^=~d2<&-jx_oo!6p}ln#qnrX_qOEj!8Ig-vG30s4-J9FbDDU682^;ltVXefbBNwk_ z;88bCB_q~a%J&1>g4IneHR96MUD))r(qhAMZbfNxWux3sGNjgxG|KB$Q5lUcGD|nc zFumS9)|=9^0;)XjmZh7VmCgV74f4-**Q{**IiEV#Fw)SjJJ~R^UWpH%8Ecpk^%+Yf zGb3s*Q_ZliYHXenx8*ah@4MC-noTv|e^*F_8Dy_*XQskRs(dy4fIrkpkF8DK`yh5f zm9PFdxfb^eX&pMe!8R#@f3ImY_u>xeg=0{^zA;$24>eobMeW`oGtrNApUznH^~`$4hi#A#)XY_bBT| z7H{5}a@YlzE#5i71OL}|6(&WszF4kW|1m+ezLml%K~(Ge4%BF5qXy$r!qe61I=UWp z72|0vB0N0{zb8Kt?K0BQnqvU^%|J8*tuzLbiWVLt=_)j(8fP|17PPq#S(^-#h1FgN z5}IDHW*U-+nAKmP%EDqY%OWZ(P{0Pot0bc8gV%K64@|*i#c*s0jLiN-MTwOSh<_GQ z5X>Q%jzyC6S`mFryoqRKy-^s1`VuoT`M1|I|F*?cnkgD_y|HX7#3rvRinyAFkwo#L zfU!nwcrjG$78^7%ht(@1w%UdbhK>gN>T!p@ccZ+}+M(}EdA?F;Uf%awd43X1X5ORQ z?H*x8@-%rqd@OldIdflEm_AC|H>JU~JnmY)I%bsiv1?tq8y2|Mt#?1lea5>-*SF_w zt-!l(Ev%U0UDxHb;p{#4@_6>1vI>{CxC>KZ?k-!dcm-XLkj!J|XJwlek8u5va;OQj z;>*iU!#m&+uH3|e5V>qR$^+(y$QBkuaXLY)-04`TOjjdS#0uiJM6b3xwy$AbgV<^D zxD8d2G}iwT3&fS-_ZunOHN=X>4O2XFgjW21n8Op4+R_ig94ae+Y%y0`vCyH$r50-| z-hmtDYb$mXsuPdv-qpyLj})pb-T-fdsouh@+YStGq?&cxAr@ycDZ!U17QSJat?SCp zqJ+XsFtFN!X+wXhEfi=!>JAvZxuH>?-g(&my7mO?fO2V<>CMmbI5{Y9oF1z#`$^YKws z5Mx{5EAB$Fb z@>o8P;Yy9wM2|EUacr!RVpw^x!ebbz#x56_dE9ETRg;Z!xPuMHEoVwQV#@`uRoBCm%LR|s zXI?IslSlG}E|)90n`Jk%_hxEPm1=mbtKd^w)4LjsIO3yYA;%-# z-k;CReUC;v)q<=&N=$^Agi-H){3yAV1HF&!UyQTA%%b2=w^RYFy-Z{Lm zqD$T0Q)g~Ra}nFzq2IrRRvl~le*f8nio2@nwEx3<6gQnMis<&Is;Xz|&iH%Rx^1?q z-p9Iyks{X3>l!u~pNlmw{44p88=sBq*0fK;gO>7p9RD_y+}gosw;lRQt>_vO74&CJ4};z`nn&NS5xJllk!h)3YVkLUsOhl@ATJach4fFZ{^;TU;Tu=wy!A4GDLKH zee)8I3?@bK#hv|HhR(S|q;$-$@6g$hpQ+I2%l>TlBMWWhEDAsU4|2PGC8wFgp5P89cxRvy?;?^g@-_^SuueH%*POHljj!N`~X zbYFFY;gK)$m|4DA>k=OMEOdA2yA+nY6Nf>P;YZ9AaoA4XAv>S#NO>uup)Ku07WabB zhbqk7FuS+SvHSTPCAYV&aCe_baD$| z7UBYnGXz>%Q_WQ^V=PXIvQ)`wQBgn2#3~R~La8nw5iyr^@i&iug>1AI9o8Z5T2;g` z9P0}mKq+aiW|X(k1Z<1?9(FkaOirxJ^4oQt+nkR-*pa3CLq z9|3Z)$1DpW5;FuULXHCQ@!`sfFiHs%Ldx9{p-J2U;YpT=(dF&|oeeuB#UQR8&?&7= zBS)8S{uBDc<$vlFAJu_7$=>0T^5rQVCO}O$s}&>BKZ};9h!gB`7Osi}B*eJVhKq1a zTsC6|;rh!j$c9-SHT*%+4auAom@%wY>}C! zOKJ8u#2I9!A@(;Es0^`tl$&)EbhkFvHN=isG`r$s8|=+ud22#%QD|bXEWyjlxk1IN zdu5rt@j=B0q%6@JI}%!=)6`&Co67#t(n8!KL=i@qE48=MIYb zJ2nH)m^a(S<8WR*=Ra9TDjuu*#zm{pvR_;O58Gt)*RntRsy)@nT2c5H{}`WBKpBS; zA}_;IPx$$m%}G}J$)9u{wZ`;aZRA}F+C8?bjY9o$7}|e)T(m&8(IZQ}69~6Di2ejj z&PH>xk_If8vX_ctI}Ir)$fw1#y6(hr1nT{NIOoQeyXJZ23v$bp^?7p48ry8@bb9Z7 ztX<^je}2A_-TOa#{MilozN91Pews~=YAT8keU-%<-%XrKs*_%-3I96u&-m%~(@8-R z%GbTPcRyi4Yu3{mef^lo!rE_bg!nUH?~qH!LQ4^|MpcL&kb={h>3C12pP*M!ru|HQIZsUhInRfK#5~M*lJn%&d-kyq;xEyAXzn4jd zJ&$(}W$}U2L8q9Xo;W{K@87Y6`8ztv7^j(dt`YA7?bC`w^!ShRKE6*QYGI!KKKC^< z$cVd-)cZHL=RfuGp1XDXerSj*u&nEAc&p*{ZMjh&{(8e{hBKSnUmvcpx$X3Fr%4x$ z5qS5T)knjJ_&?hj>9LKKcQ#|av@po%e*s0Kzlcu-?+mpE^swId^tiaWZyMzR-_YaL zo@S~>tV64txz32!84ECAS0f%^nAY4#eZ;h8Mj3IGvB07d_2Kdjoo%V;!#OXYYV`lj z>XWa3^%~Rhy7i)Z59+b!0%}HoGM4-ARWinxvi0|sF4JwRo%HW6H?}u(8=8#rdIdcX zoALRy8K2i*YX@Wa78=_}hc?VG%A4yjw!cC>E%GV)9~ZKIJsZR8@rYdqmgissbFvP< z#6Hn8Z;$QO1HS*==eJhDd5Z592@3w6e9vZ^tJY6xuhTaRtLH;$Bu<&%kvMwCO|- z)<_hWNK97r7DF)^cCiDORG@?l9+(EZGJ;DexSFD0H!C4+q4L zMdy2NQs0(zM{s+oBczx`P7kCQ4SGhKXs<9>$AG+iZGTk*NZ{znD>c7Koer&K9RUugS>I5#;$8%tu&Ia zCB&`DiuJX~|7!Z87-0evL32}$@(o;xHefRom2AT|XoCrt$6QQofrgewo=hyUDV>{WnZ; z`*@}(ZXX-4DrDvE62w*U+$*`X{q7#|D?gi0+s`J(UQ}~`y&o7c+h*KM59pEL& zNlqrJLw~(|z0(Bi4U43dH0(ZZ(au+q&nIYd?be^s%A?4h@Q*b_f4lOP<^F-ZGbF4(+Me*Ofy2(wZqR^UuC|G;W zx87OV&6)MgIsdMCn|xkBZu8BIAhGjPo4$4VeLjCth~rPF2MB)hg~NY7CYr*v_)|D8 zZkIE!d&HnIUeBu&H}&uAuQ}mg|7!GspB)ar*`FdjA!5_0vztWRultNA>PrFdGxtw> z(y@8tI?KUxe|60Ck9+OMPPsMS@F2fmyv^w|v+*)O$;6 zIgEg&DO^v3*P`6+F&%VK#16Y>e!%aI_(Ee@u5REI`c6zr?Ptfj3=ccdhx> z6?Mx+cGr*3Og2nNyQQ7oHS-x}wNmd|!EsA}#A}5N=qQAJ`gvl7=T;3_fQCgx|F7Mo6)HXei z54fGA%R9@CSQvg0aYGwB9KX!g4&^WiU#j&Ai1DzrSY2Yu_{XG1+S!^#%*CGpS#jGP zKe$70E=J)-8g{_Duu(gZ#MHPdc{(9BF*TUos`2x4czLJ!1)685CUu5ehwgyU+SXw$ zZEuuYnSFPJaM!`FB0R;0!s;Ee_1g%Ku)18f9(%|mxXW#pV=JL-v)sCJE3+HZE8CK8 zd33wrC7V)j)o?)-Iw&_)T_Nj^hZiB{x^&waKIBSr&yv-yP#I{ecfdp5Buek04p`!W zgP97Y<0&`Hp|IM}S1;PI4yA!!kDYoAsW;ulVmyh3i5+rcy&4|(&0=O$y&{h#f>F` z734iYk(yc|w5}$&d4EmN0)9mzYLEr(I!c5Q@LEq}hm&gui*=W1!&TP~(sUJC#^TQu zdW^DE*J-AxPQJUrReFq^c{$THd7dIZF6(Zomw5S>AaZ4*|MS84vE89f^a|+?R_LZFM?KGGPh80p+Cx*6(qoR@vxJiwu@f^6Y7nOUxB#0YOnV( zBkqW{GpsyR6T-WbJtR~k>&+Au626G;y-9ceO(e0n-no>`W31l>SJ8aLi?H737s+Rb zPV@P^tDoI}jUBqH&g@g{pA@i}-CTNRJrQZab!IegBkl4X)|I3bYFb?48)lRb{X&c)oW1n>)`Fc% zC>5DPhG92GrP+JXp4&=;51xF8^*`hP{vM{uzpGgB&Y>4S@J=HOrk)M|^gX{L16e_0ozA+u6@Nitek+Aa;aJ?k{5WTaV zto~l2>ODvE5>f_}EtGt9k>}V4X6=Ta`^la}S~PaY6hiY-_Pk8t63vmJ7rgezZ(oJ?8@ z>YesYBa2KY>h_;gJu*GDsk4`SJ22l^NQz9mPlTReJL}hWcR5OujQS}X?nCj>@ZHgm zki$WT&YEUXVIkdl&B1E5(1&^WwH|>uun06)7X}t=EFZ< zY&ic2i{xP?_ld8Nqv2fOi*5p)eE_F{3kyO~-)y~HpTWp}WJ-(_BUG?@_k0@xnYxabrV_IK)G|yoDbck0c0aqF^H!S67b62qbVJYhkSGTGq zWpx}I4hAvBlwzT=kwzSuq9w5MNUBf_U9DNaovOxm+T2W|e5>L1X6`iNW|9%pr*@US zFMUb@d5E})f)NXU)r(ummpVSod~1OCwq#wsIHu!qz541FBnvMQs7?8R0_7UpN7L=6 zsQqU{aQ;c@hdbgfee9b3*nQaxaUWv4zGQdMEhT+>P@;c5>{&Ei|ME>%=Pq{&}wWfnzt7!ryZo;B%IwGE>E-;k%^urIz@hQVq2va?U+ z!w2uS>|!6-vQf2{u@Cf}*Z$a-?Wv}&lDewUS66d4^S@miOP~nP!LT01HIW}D1cp$p zXI;2IWHjpP$K(ExD{()_HF(lRK1#;YIFyaYRg%GYn&)8rcLT1TTt~OyipWTegBGP2 zW;~7Jmp^bxL&PNxT#E=?tw7AKc(7|3f#+Y~`UmUFir8fh6k`PT^bCxMas(zD`vZOP zKr@WYE}U2_!MJE*7I9k&21HNvm2iIvu8f#NFoD3VG?HxsoNs8jc9<0rszhLi5q zE}vks+&t-Cx_pw!at{n{FzlYI=d(EMo{sR(^Wpu>PWN;SmSKlk9WRpQfcx)_of#S@ zm;0ZN?H2^kJsq1EG)OK#!OW1gwNFJP2F->)EU8zR9mZoSgx@U@nX9>0ZS_q{G%B=e znl5RxH?SD4Haws^0lKWXBi7qe73EHshHn5vEHyie8%c^Dx0DY?q!|7xBx9Rk;vd@^ zMd6>Yh$!B$BTPVSsCJZ839+l*QX?*{k-D(*R_wosysVtrxwOXIe-+mXD-m1!#|o7u z7~>QwMS_LG=4QSoQl>S(P#DsnFdT6_F8pQX7nK;ykixwZi=_ugg7$AS8nQH^2RbBHF-0d>dCPAG?aTX!ZvHB z&7x-z^E`8TgWn;x#A+tk9SR3U4U(=*!W7gT;*_RAEPNj8VrRo+E>=Xn*(hgmKov1U z{zd%hcPdR$JnhE8Y0QHvW!0-0vDWSylwYD45}e}1gM769ow93~{W_pgBfftO#TE|c z{l$KUiGxjScrh`gX@yzbuQ^$@WL;*v;r#Kde-YmpLahC*~Xv7nvux*FlyhL~IlB?1dkv(+q zk}Hy$TW*v$w%?MSNr=OsvV*%_*->n_L@wE;c#2~!kuPyY=t3+;zG#>fa1F|ri;+od zT#41Z+kNbufA>7n zqG~Vx()$o;Vt7$d+?H8Bzkl36!X9@z)!JHrU6IS_^>2Ll$Ei4Kv1ac5U>RwJ;VT~t z+cVatgil+3V4YQQgctn7|JWTk3@Q?O40|Rx+`s;{4<;A*Hu)!P?$CL&JKV2{ht;bW ztn+J~YO`glE$FeeKTrA+*C%BEH~7%*8}PTcQw~;DX`MBpy~lJFg`e|>*J$O2oGR{*sd8 z1)8XZox%MXovRbyd3y7HZ-npkmh-sJ1s{bTS3iF`L5mCzi>%$h0B0@Y#7!NZP$?`V z>+^^h)mITxWIy*O&FKsI>qE2~!=Ij`zW0*m?jgVMub;6TNS;6R#7Myz?Hh97;KO9! z<$K}XBU7oOo#V8B?>$t3>kv^NyoTX?ukG%6lqvanHMDQyCiw}p3oR@PI=clsa^F*% z!dz6SV2PhYeF*t3`QQ*c{`c)Y<@*UATi@*bPhP-H6~3hl#7|IOeRIhVRjmJ%Cu)rVcSs6D@Gf&RZ$fwK5m zjdZet1j=e%SugL>!fj5ROVM?*r-oITLtk^143x)us$D~bSi_9#Vl@_L3^&Zm&|WA+ z3=_GtwZz-{tjpM@yIJ=+Sc-CZcWGiopXaK#&6K(@OYSto?_VsNWjLPs-iu`;%w6Kb z-h0?^SJ{Z@o^j#t=9i6#(0x0iXHY2Ow1>4i9MlVApY=5*biO;$M%4w zpC>qbO#U#d^osz z8PBmkdz`c|)Vo_(kYgt5%X|Dx`0lC}DRfXr_#zo76f;*OqE;{~drTYk;^VDZ({!c#KFVgLX z4V5^O-zH}TjrvzihY?gw^Lj)LV)u7UUFGY_>Sc=Je!Q67=dsOuHCj~IZY+_8=U?}J zpFA3_2h==5g&JLp`$VyL=$^EYP37=3?khFt5T2La{=HBfP2bc1^G&BwK5cpJx`oi} zKU@4b4v0_sdz@zn#zFq0qALvbGx3LC&Bq=&ye?Xju&K{WlwZN^CY1Z}=j}1Ov~XI4 z)1eRz-EYH>mZOD7ZEYFSp@Cl?jda4-_XXX+;#rewarKDR3;7cMOgPqmJUGb#E&iTj zA!PI8g`0PMd6?;|J9^FypQsGq$E@!X1k`Js%d3y^hbo(#ltf+eUlR4guafk{zZCq( z%DPi;yry7Pv%h+ye^2TGlku-R{!77M-6$EqLJ}g|f^2wZ0e++ech%v?N8z&v$KVPuDQ|drWK)`0EyCv2x+-nOXVFFYl~(k`d2s;;bw^ z9-qtoroo%oaEP0kt;g+~+0KaDW%pv^RW-9bLP}!g<#tKo;@gIGcyfIcdGH^25fpsE zp@WgFnq-t4CZwy_Igyb1cl9FOyjL4L;O}}o;O`@O`L8wYk&!R6>7*?kxxT5RjJUZs z8|BTN%FC0M8defsOndx@{x%>Aw{)?;`#_Z?zPAVH`5#t!!ex zypn&uqvRF3<-gYZ^x?JjO|%=aU32sD=0>>P8p|WXtt@S<;8yhQG9ZqHS%0CqNqTug zH^cORDjy!(ZDWPNcHCSgCVMcSd`xyqV^fUyf=24&$#z2_pv0#q3r)oDj_}WYy$ds? zDR^IcCsW@pn!C*?Z>h088S8saX50X$Ia)a7-#s@kHvi-)e^PCjGv7RG(%01v_c}BC zezTi{ZADREy{6$g5a;{}brnL-v0C_7Sz3oGTj8AOo;Fp*gD4UND$y#7q`j}B-raZW zJh=zb^IiVWt`})linH>S6O&my^|7zkk%g*@I(EGnvAcRl_k;2 zRXL~Yix6ay&4}>KEZHK+K^FWPi_K<{Eml*oSqd@*$wAf-$%da_ zlO%I+h$WEBe@<)`Dw{f!Z5s6|M%-9URz-!frD9nPONh!2(apcCu`}ZJn0X(D`;~^w zh>sezURD>ukuTq;F1o8zJj{MzqV9bYJFjB#eOLc^Rp!eJr2DSk&s_n>Al|*t50{ng z-tS+21w8(#f5l&PSN^3MKV0+axs`u$Fu!F!UhhsL4*1cxD^ECHdi=JJo_UqK?U{`m z?JsUAthb8AuRFF?ZCo4K$danHJT6(Yc5O`B>kf^X{<@uM33#U-=jyJ>z4IB?AFkD% z#$4^rowk;ASCi&e+}YC%bBhs^I~x)+3v0}cHOJIiiqf_^LDu^)vCi2ukqz;L6`aKj zA<)ubLP!>ilM;H3r4E*gq=Y|>HN&*YhEti=iZpKf<-cTTr*^zQGb%fEY`yXAmfN#8 zM20eRH`qr-MJ%_!wb4FGien~l%TDN)e3>v(ii@+uJSjE;c7@pc!YQ&q3%9`(?u4nj zPAH??Ik{4Jr-^Ri9hsH86B_xPeX_gjJF@55fE6r`^*mc~z&(}u!U6A;e>KrFO^>0S z_2*VH@3(un!A!4b8rSWaX0mIQYczG5V5f4JWQa->YycWD3G$0!0TWq_E>Y(fUY900#B8?b|GQBA;XjZ~X!kyAIsP-?E4jg&+euEycO+@g?* z26W8q5Nu?Y1p6;3SrQ^yf63B4GCK@fgCZpxG;>p$TWXYZgA-E9%%PD|&xG!a&7u8t zAAnsaFBV`IUFwpAcu``jCg|n&uU@6dU3yJ^X7wWpasqU7uQMFp+@7p|YOmp3$7RC@ zd6!O>?U!YxdY5Lxk5avnnL+jmOI!C!qg=PU9eN@&dqRgQW_GC<`v@Kmp{~{Sz=sj< zXzs>}j~;eTpdA}MO!eK39?}1)W-cf&Yr<~TT|CzR^vio(SU2sRZ^vGQ=UCifuT3!H z!@qW3uWvV_Tv)r|#C`!kSXLy6ucOsycv1N0|3JBE#4hV{a<2*U)$N_5prWN+t-myy_ii4~jNTPB<005dD4Am$K(~k#p0|V~3e9fBe;S==PpIdXhPH<=g9+TKnVM znOpY$^bPaU_k-DW3+2fd{#F`(!AGgqE(~5+oVP>b9p7DHo ztr;`=P}#{(Bs8YD?A^LY@w|1^_e6Dw#^p-0zl$&iZrb_tVRHAN^SJ*c1M0q z_HoL?e`X2%yX%dQf7YK|r~PO0`|ni$UswV|1=MRq&u*NmDtFXS zOX1-48tJIT7=R~?_reo3dhz;<0jS@|Kpn?e;|Um6{4d!;@xToXhv$#m*#QkZ zh+#F1c5J2raU`W8w#Sf)8`ika3Ox4(aZN=}ja#kzp>Fn0u9Cg;#lGktFfn`2i??dA zF>7EEGdJM=CyOdr9Po)J=kb{P$eP@MuRl4jIu`NkPu^Lb!5s$|jvn?bbMd^s&yL$% z@8ZQnlUe-y;-Os{Je{V;FpQPM@S$D4<8FaR5O3*n6SMP{E|*0eN&BJW>k3T2g=s&M zenZDi%4j;0J{?|4@AjJ>{gN^q=0(4RU%q><8YbvXD@$1CmGXVDk>^418PQ$Lu*zZ!nId~Y80uc z5zrpvRwJsznO>%&vBx{>aTBY(5eM3%yxN<3pnYkB72edNk%$f3SGZGWL`se(E|nb1 z9(E_pfD5?UOl9O!xJyy;0+!@D7UVk=xv7LWbwNIEBK2@ZnS5jiFTwKvtY}U(gMsFb-s)brA5`KUhm*N_f5w_l?_K6IN$-m!vTc1cdChC6c zUA>qKEe*Ca;(B9w|CIg{Vpc7Tyep-@Giudh`&}s)!_sQK@}KEMho4wkI<- zFSffg_E~6$m8G$l*k=^Vl~SyYn-nWHv&=pn{gYxF>C)rN6j@2iE305CFxxFBHc~U< zfF2$nDchRuPDyll;ggEkVChz=l*PLC7837|1_m&}ne|!{1Rl4pps{kS z6b+wq6=&?4NyyB^Qvxoah4(j;MGH1%W?Gq|8Vu2R5!xH;MBEMKFru5Co7ych$by)y zcT0Y-b=^zJUbI?>)}*c;Gvyb!Di5teQo>LEl=GzKnD6NmlY!X$QjL8YEGQCx9)?|u zKkn>-a>-pS+%-C(KO273zo>)RI$n_ddrGSC-0k8XyK+&!M%{NZ8YULSB%@U}i7$O% zo`$cTMEnq;CvnX)Rb$`SzX}zpwhjO6d=S+KRld5!b5tb`p0N3RXHrQjzjFI%>~Zq@ ze{+I;Q?;?dA6&Cvy{ zeqk8qi!Octn-vjeGwxIUTysih{I2c(6;){8vT0|h!&GI%{kX|kEHd`Wb*h+BiTirG zi{@xM<$I1iCOO&jLEl6DiH7On_t*;v&lMFCtMIWb5v9A0$h>vC4#Fm}5&p4C4T ziRXtgd-;CElU~t&V}111%x65xmsLo-NDLlU$uxUk$6&aA)T54>_ff}KJACZP^OM}r zRcTLD;g~mltC%gi>)7>{PARI~ZF8WXD);f=Xe0wiA^rHVM_(L!^vCs;{wNuO{|4h< zf8#hL7quukxch}2mGnXjkUaDqL=OCP347=kegg%E8tgYv@B-M~Dn@59#3Q=!7p}6f zS2hT)x0v;D`OxT(erA63f=7k139}-si+`_EF}lkCYo;AXJqr35I++P7)x@f*Gr?o8 ziS?+3T|9Ol^STzsqTGdY=r#0lf9eST;)(sKd_Lq+)uz1%GnUZIP7 zg?KEFrS;uHXDYIjp6h0YvBGoRtTXK)vBEkFGhSF1W=)5t<{q+{c=?;bCQB^(Lu>4! z5K~UD$*kKYG@i#@LOYf%GGzxdORd?#YeOF^EA6Ag2g^!_s=)^uyti~{R$B-0Mi7kWAfq{7;e}(VD(g9-kE7n6S&R>pQxVt zd<?ehvm#pY!$rCWpqf`Q=0J#8sE*`1Tq89WJ6xA*nFm7N z8RELUJo7-Eufv3Do%O;l^+_|Hscl4+W@ z=?0;#bh=UsNZ1rvWV!$a5z10QL`WK=`&E5&$%-MTLQ#$MIowQSqKpc#MB;W zkVtwMIEPP*NIGNO&4^KwrOEBX->k>x($G^RFM} zL%C(2w%!lN&5#}kk38oARa3+o6{P)rVTW`^lg~r>K>5#T{MKyh@=K&O-%3K9?T+`W z#`4?_#gk3UA8kEyw^a%-7vC;@2jzlQYuifb9m>D>+;6w$hlKE97ve=nHi?)&d*?{F zoD-4r`H5HZVIYldrxH&Uz&KWuCWc=t+}@|w1mj!;!1>Bk6I*TJ%{@Gxx>n~%tlgfs z_;?wd4|%3`-uV%+dn(FST47uckEb9o@kZkERP@>pLnFF8o@XBUdAI@2dxGkIDZ;qt zfuEj(=Bf50=i2^B$`A268EXPZBG)_qrSW0cpfpGQ60<8@q`P3@yJuUlXuUNR%dq= z=Z_5`W8T~0g1?VseSd5Ljx$o!H|!e+IQ_=xuT?m9pRR-UvHRA-dCTsp3iPHg$=^B* zE>n?Czy4JU@0Sd##PI^`WLn#x8^}^OQO!YXNQIkpIu;1mV^z4H-l=p_0o5nJ94&=C zHW9Q)x~q8Tq2LZy_<*i@^3FrA^TON~*vsHl5*pD^bz>;JFT(l!mc>4Y>iK(pUPH_8 z9Uj}zLKb_Sxp(*-m_KvZFw`(}>#!lHaqF=DAa5L&p3%_Y;d&fA)T=`CgNKr}SS~n? zj{k5motSSf7QshAJCK;!s0uW!-#;W(<`^+8#WK^e*m^;%ebWe$aJ zZ!kZMp22qU{`z+PSn6+03Y+WFawLtG^J-vmhN7qYDYID4NMD2V$r)c$T^YL@JD$b= z*zLq}OMLEQ@xCQ`y6$`T?@Pq^y$*i1e_l^-AL$j_=aNqCBdJ5ZCJK67M&+_~#d@*T z8i5$j<*71NDU(}coW9a0Toomz!7F92SWAepZfRqKS5CNH-VIVy@f~uI-q;crmnfx>Pb6k>2@LrK zlUm|l(8zCDykU)=UhfWCZi7=d-C0HB^m#SRqgUp0_vqzxNqRVq!CXxDNmxue({!2i zpxWbl#9=XiJH@4Pm<}RM>=p;N5r>H>bc;*DxPk3y{777LLMtaIKQd0B_9s5!M35LE zruOp+cZI?9e!3>F>H2av#r(B@HtsSOE;8)G{uM!W2Ix*j$VXdYd?|e*%$6xYDo~Z@dcj`=ND9wB@-t6I%AqxP9pVnJJosdX0|D8vebCbbSTXO<-L{Pk6*T zxZ>}6-3E@;fi)Y~L%j#qb71`krdp{oVpyO(Y_$!RIAwoYFxWOxb)%6i{ zdxH0O83mT_9kkncmp%rsH`JA>XZSB=_A~ zJgyM#yNvGClgxK>^|)j1WSZW_C$8Q^Ow4a=*L+;viO@B_`l8xdx=*`a&Tezj@^+ir zP@`3kuUaC}=P|PL5wARpn-^cb;iNBCPQ8ppEWad;&dinIX&!u@Z~aZ%>+=zx57Fl% z?ay4Lb*%gs^bH!xDz8q=fOtC9>NSSNF|+jam@gY^VX>q;O!^t~g<<3*^Td@1HDFpm>dQ8f~4CG@E|X|51Te#BqE7pOy((;LVKA)K7!1xzX1@h^YQ~`q^`y zx}b9(L42xi>ywyXAO`mlMU!^&!f#JtJS}zWC#%8Wx>H3M-}>P%uUa3fTrn;Fw@-hE z=Y8X&V~(YpJ*lHUq38A1C$fUN!jX$i9^rW(dOi>}=2Yg{v$fE_x5X(qZ6Z^89sLk} zpfD;G{bJ7#_u`5@V9WNGP&K4er2_Jr5l?&mJ6-7~a4i?3NZm&@EUrk3gCr8{XPW?f zt!~h=qdiH2YbmMl(+RGuU~k*5@OT|mh+vP~ROr>x9?D}sT{c0l7SSjMh=LGgF1%Q<1xJ@^BQnA#0|_1 z*a2~BEww{=ZEn^sLOQ%tn>&a)Id{z07*CEH@h#daZp2`)Bs9tEB#7HX?6V(>sa(>e zizt4B`U*cPYpzRy_xgKAC zS-bS>PzJr@S0(i8@OlH^VevZ&gF7sN^moR8+u=R%jq%TP=!9lG(;>9pZf|7F!MNMZ zj5!_ALO&~6>@$7NwOFpQUt`_<7QO@NHz*rku7m#G;AhSFPd$B|*Z1_eh}udidhhHo zLcmTtzyNSFO4Lr9)A6HLYNeX51fyHWli5NmzJ#x}mW=#(a1>b0BYk z^@|T5$kT39;iI972L5udVJzmo!mw(X57j@M1q`-9_bND=2e;CFrWK4*KuBT(6EQAn zB{UKCqL6M$q=s4&;R|4533DS?LgOZiESvFD(d^ZzO@PrT5G`{w4?4 zHU3c$0aeT)7EX&Na2t8y!AFn21xE&{=3G)O94@5n4aw02Zjf$-ejdc4Y*r%F(I2P3Zz|MkihDS6|37GAWwVpFKCGxamw=KQ30Lc^l?U&kVmD< z^e=UzuF^18KFp}k4=wfR^sgGqo+r}ljr#LOGR3R4hd#g9-Om&~rsF{1uPkm>z4*WU z=zTqby&qPO16AnkshgA5J3^TlR?fTKU#;H#gV!SVe3wz0p_h}`di8hpt&eH?^O-MW z6w8mo<0wpFY`eTt!&!NMBiVL+rHb+T>x*nXxmxw*u4xAfeunxCyiNjW1%~)OD5u2` zC2G^i@vb(BGkLMsc`c@H_2JITZu{p>mkRe!S@zkkTv9ntBv;|7k^*8$8$N!C=Km?wb4EVU=TT`T=ZW2fT$xvg`0nRky;hK#; z+6VTFX-`wJUrk4vdJSn!>rG{N z=GcDI(46u5b`uysftGLTM3&C%JVlQ?PiyRTEcVlU-B?;qYH|k1uA=+w^bn8pz_nT0Om4YnIdb3^n?BqR%Q8XECM5npk{U&H8+%KtDt7 zp|5KSd6FtN@ivyuG&NQ*ji&ich3|bh!4)Iju!~Dry6<|8UhJwLjqG)D_37%r5)_N zs7l{%$z>E+zE6FeHX^_+9&x&?L9Col{X9kx_4VLNC9FIV>2fc)ruwsKV@fq>7kc4~ z)w>?kw0#9n7j4z1CAID`St4@{n#^%ZlB7oL%ixG9f8=M0t*3XcvyX7^c}4AUue@YM z;!oxqua`%!jFXATDf??<&nQrRP(b#scE0ux@~5_MD*t2$7`Jox?NXnAkReqEUdFHg zqd(aDD%|FS6a>k=XZ~3O{xDSN7Bgj=Z6>4xyX=i_X?CPwSe_js#|JJqJAPw{gii~mzTz@EQZ7wGTn zF0ZRteBRGvEcUY>i?3F~<|B&!yv@q8uNZwlNJ6hvwm#$p>aXw!b1GY&S1eoZQ<>i7 z{GVE`|EcHottV{xKkzqAJ*|J9Uim|M%$Hr6gq71vH+@W9si*y2Q}pwElETiD{;rqX z-+1#mwm%AG>zmIjT~GbW4Gqx${+z3H1RO_k%FO!rC@A{#G`s!!<@42g`72!yE?(|0 z?T4v%z6b3`BDQ8kI)zZ9mdr!@)(pHadrPP{8T(d19D2I0-T8Fm9jgjj?ph_lm6@|z z+qw^yy9oC0;0Mca{*iJ)f;TkY^?TVeaNJT$0(wr{PU}s^R2btFh{sg?wFLd|^uOd2 z=!xegx3OcJ{+<+RyiEGRdYA=2>Ch4`1A6RckelE!6YK~7GT>%T8IU&w(lEw8y_rxx z6G~;l?>7r@Cpn3SZn@B%*9g6O^$|qix(z-J6+oR&F)&l2tkjZ3&Z4%3;cR|}U%ggJsrEpDiT2t-gNPJvI- zC-lXnaZ_>W!9>T&qrd&F2fA2c&c@0t_9=~0aCMPU0UCGeCSFANE7B9Y+jgL3y==7( z67dj~^V$O9R<#bUHF};JbNew5*Sd3A%Y?+!%q5wr@1* zeye*!Ll=mnZ`jNCEn^nxuBVRXXP|9wqdo!7nz=v(3un!$_ujU*RbqT!huhZ9n*Ne_ z-`mmPE8?$jN3VHpdaUsvYS3NUz$I^ouYs6H;|(qOB~OR1kvXu$0GjwEMY=Apf`P+y zmq0)xuL`(X@=b-sMAMnT<)*Y0ayPH?q;Cot4;pmI^aei6t2BO?FKu93@;hJ3J=LF+ zf9J!vZ@~A+B^=4}Z(+7DV0q^^Eewtd@K>-D?W|kFoX|iRzs8BNN_aFETtA^R{~#g! zI)|Mf3V-T4`Ettqt+brX-$sbAPEH|crktWiqXH_3gCZoKFSEE_5%br*Nh;)b&i=IUJ|$!}7WUuElBvI>_GhY{Z*y@mzkt zACgD!RXexd69HSt(Dez?Foc$} z`1O=MsVU{^ov$P%sV?y4)=5fs4xCq2m*wQZ_#)(ZpKf9anK;Y3oEwx$UjJZ+qr7|> zQOS0NcP%6LKK)yYRkV_V7RRB#QZ|e?vgc|6F&vx`=D z;wbMtiZsaq z8V|z|2gA)~c+ngf5@-p)TiwFt978ZPh2{AGGn|eDz}-LL7ek;0&sLzPj)9Xb79%eO z2GR~Z*nsTsPdKv-Kf0Wx8z0$=We;U%Qci+4ie5v&AAzH(erq_FBA(nGY`Y=qLw{f~-Qbm|Lzht^&1h}YV ztvsS4oVuqXyfCI@ZTy~!E`>40Xs5!~KEF)R<0iJj^s(xfK6kKqM@3xWU`XF#k1HLh z{_N*raRE(FqGqW{q77oy>XWdTlqxixSQWU0Xq9#KRRI?%PBMLLUY30^idbS1ZZ|Ob zOosq=4Mpj2Zc}f5Q99gTf+P8hGOUk6i_iQ;_eFq`Zts}MH16FR?DZ%I?x8Mj=wn=O z8W-qJp^ji~#{qZQO zatYI*obu_Y<+`K&eH+u$J{|4#gEg}gA$|xkG>cbzbrFH@9D&t=5^1<}k zXVGrciyPUA#dd0J7i(ehj>@=Tw5l>rNC1mfS&*%T>cxfIeqlZ=78D@Gz6FobC8m=1 z@p-osXNls)=o%Uk^92dn$@`5`?!e)N|^rPi) zSmiV=0ouzFQ6(o8BD>5Sj0X>gU$|dQmv-jzW#y-}$SGt_YWX=Wu@1_Wt6VF$Ok8F- z&v-*2M7a0qfE1Kyd!c!YnEhz(u?ix>4I*O`*TEW_{oV019wG*0-~Hm2H{gUZ|M_P& zj3VNQ{K)gCjAU-e<_lUIeEsYxeazk9dXuNe!Lv|bZ*t&uE3&lQrE=kJ7{|6h2@zD}^8229Tn-`Kq z;CyE6TicgNu>bqv)Un_2yygKX+}{?Y?+;A+<76OwZe+*rr+!B_ACJx_aGG%NFUzAL zvI^~bk`KYK@TBbh@9)54@wBAa9%$bmH@}EF$T8H_oQB%1FHN4hw$u*TV8+u2OH^(dC3skxo}CEB;@a`2?=?bNv6h9DKc@)0M7Q ztyy`mu`KpV`?W%vzn9xiIQBeODl7hP=SpASzZ}$0(urL+C6_admG?J_rT<-n+5F!9 z#oyI`|8uo5dmI1j@Lz6?;%+tVLS=Z%YRKkv7iiFeo#^WyTyUTT#MDOy|2zHv^b^R% zdW}c26lv-X?_a1$Gpb?Ks5CMF#zh?o|As>4UmBeB+yrq3{ODIz2EsL$n;`Si8%HM4(E;$T#qpu zc+qI!0l3#4l;MmV%mE=EWPt~0Sz-(_!Qk-TH+xB;6g|5^WhmKL+SSw`!|4^I`^5Xh zV~qEIhi|urdq9JNS{f^1>@Yko#nkK8<8Yt39Oggk_PXD>6XMcprfZ4wCtHMEN)40e zgFo29!S|f&ZQ*48lK1AVxB65e-m>1>rF4GWd#C(-aD6DGCw=tbm)*LQu0)f%l$IrZ zH1*4*glFr$9^>cAMBT(j#?ML=``rI!oA_CI;D$EizRMFk-oL>_ok)!hoVel3aYOQA z1M@a~d0`&?2FgARZIap%Mz0mRE(v zAYFbieN%iE(hLc(7jjp*?SE=F#XwkIqE{6 zq21#+@xX$6LsQ~7%Yov7)Ix9~#D&G_jg=}x55*bA@RB?9(CdaNVNz-6p$`I4>!HsB zCr71#ub3y>yvi6yO^$&}4(k^!u5IWH7ITXh(R6MBsuIw{bWv@KToDSS+ve+9=c^I; z5rRTP35!V)!HH`|nt25ez0;m|9?l*}bjP9GVQak|(q5r)N3VTf;f|AWq>cD0nMlTi zeaJ+4JV_RNl0oqaO*ESHbf3l^Bn!$Jjj2S6k0a?`gIOG$DogPkN!OKiMKB)HMOo2x zDCYPS5-;Y#-SSdMf&eNh#Gq^Cif9bpiRo4+Xj4+c(2tc~;QiFuMN_>JOrKqJfmHa6 z345Bx=uc{e0;*p3K|B`XSHWArmsJWN3c*T} z=@TxE(O6fp>1Nw_n?lw)sVYjK2f1?z+=8yLSokD{Er0Jt)ZhdojAu$BLLYg2Xi8^gw7Po0mbNw_`!P*z@qI>lVqrU? z?NMVxw2(S2NwZeVeC7_ZMK+fslsr&1Ojt9yY+B068x)1aR8+;}DO<$zmb&QaZiSyR z@Vnn%l9!$peuMkmhdo0qL#m=)b`f6UwIo=}zb&5H`NZ|`Oa3jZ_|~u&w4S(7dzL66 z`5O+OJPzre4kwLx{_9!(`Keq|O*~V21$_wpd9zb~uPwp!X>GQDl3JeX(cpRt#M%9~ zZCL;ZIM3)0j=c)IRnPi^Kf%6^WP9pJbw|j)~_n%k#3Z(D$ z_ASd@#c&;`ZBLhL0*U!6x|pc7#H<}R5bI{>?|d+dRJh2A3un)g3K?GVa+9nVur;Rx zP6FV9S&W0W!?MOyGxs;P*Zu*t95dT%Z#_M`_x^h@e{=7Hx;N-<@%s2$n!fX|wYgZX zf%rK?Je@h~HH`n-IqOaI@f|ZS>SUXq?lY>0mYd9cf;la!C>fupUbtH&jlEiE{Aie^ zGp0AtPGhSD!q|zy$RoM1HZvIpj`b<*Xi|J8vo*K-}N;8-T<$M z6L3CrfY<#7J-)P3z!F-H&{}((18eVL65@=P)X4IUaxj*&Cc~YY(Cgq^)TMls<)0xN zX}RXzL-T(l^!uH)IXx`jLpEc1cr2y5{gqfcl_M&X;MI5BZgn}IQ8b++w0ej0cLq!M zdYHwBNoyIOuX$V0{GZ7!C^;GMBVCT(<(|F45G}b}q@d-6UUO1hP`@6U7f7&uXwZ`> zM1}SX+sZa`Dzpbk;D$iU@H1NQl&w&Mm$5Vag-~$H4cA-;mkG%8&(!Rodn4@#{p)5s zg1=MXudMj0(;>VMNI5J!J!?Dkzz4lw?L(2g2R;B46#(lRRZiySi?mteS zhS|x`4MMaPXV>-?xWHQN=!I$;CukV&uRVH321_1jxm5-K&raj8USlS-*O)uX`xz+G ziw-gv>M4f6wljnbho5XXTg@a`(~${v7lWYXXcp9PWI^gkG8%qz;IqeerTwAAF!(nV z>of#tMGEjRC>hpb7&rl%Z9rWG)J{MhM<~cXBZx;KtxD^IuE>y42)!k5i~?K zFvxT$KOir50dod1HpB=kFS#< zzI*)RKY~!V@F2KsLz{H45afMs)#H5Kut~e*!kTr-QIpO#@KAD8-lq^BN?z!*JH)3U z2CG(5msYJbVjN#(H!jQsv(S`IU34GplGVxhtRg?$Wl$&M3vT(bE|yTVpc6DZ_i9w- zJ0PAZ=oSeo!MKs3EQStGG~Ft2D0F~=9gIqFASe|Um{;6EVhnqYM&jm;>0aSi8aEVw zzq(0b#Zi1_1)j5sZbIBDq*F^}P;tA#YND)jm%41#R%QJf-7gz@!0MnebwP#gK(^z{ z{R=8y`fU_-=WnB`zdW^|0-bfrMm2$JAU2(nD%PVOOX>Revs7vrm~XMJw`Qqfc4|>) zD=1}wf>*L$notBMU}T2m07XyH4xtn z#-;>k42chF6P*$~zJYJDcviH8>Ef)w#?FM;(LY`Qd51{`38|L|Ghs7!m|);I6n>0w zXmG#}Dql=kGBW&JcFJhu&YLAxKL9#ran%nPz?o>WTlN`R{XqD&5Z5~`{6-aG&$Q4R zUGmJZifN&}mXLK}_U@s*;u;&nVy`xgt=+BQpkdbTrM9*tYnavQb%Cv|ig8nKP_rtn z)?zDa1ABupq*wvJwt&~DSv6>kDqYmpG`&P=U7Au8L`vNC%FBW{i@QZ>Lj`vWtGq0P zYhb7z8$;z3OgFZ&rp9w7X3d`9bR`g>tPr)|IJZJVGItzpp~eTsPU)R+9Z|SD3WVT3niit9 z`8^Q6IEI86v^`Gvny%%n-u6miIXOFMQ`;!35D|>^Bx=5kAG+bUmpy-sv3#-TntO>y zu}tk+3-^dE7Un*&cblRRVdb~gM__+B;?0jm2kg0O9dEXJ4cmdh&d96^&}ke z3|kBLF>ynEM?z8$DYeiuywVawB-s;K_m=_Ar>trV`&yVU`{r-XFNA6f&!qdz@4)$$ z=MSyb6cy?>emQS?9JWADL2ylPC|8Z=U9#$`{=M$tc||2IlXi%Qa*p%bpLOs)s#VMG zwDOc<*XtidR-e{J!5sznsY6W~nLH|?lS!Nk?k6-jqv_2{v+;C{mD7cF zSfBi5$^R}SBGadmNIWy4ky%8K{hh$loAX|l57G~?fywv+_ma3qjw2k6eF|`=PngCr zcYyReP=qPEBDPE3bb z_uNLWFF`v0;_i*}&{U=jg1Lz*q!UQjmFUMp1o%hO>EqQuy2!z^B-0J;05{%kg&6P8Vuo)b?|o%I1kDtoUI7kbaKJtE*WN#|9^!kC z^dQDXeaSa?UzviyAHTqI1GaQ%<&t6lrU{OEgP51$v*N)Ts#vHqF z}dYANpT8u2( z=d~w{wvPQ@(;zh+(tEHPh52=x>vLd5e>v4-S+oPLln!chl4DAHV?_^6k%q+?=F}=V62A z=5(ugUJXazM-6kjzW;+eJfMkR)AS$%xZNEcG@Tmjb1ue}u|abyyDt5aYA*yWb1UuB zb>-<>D`QLMRxWj>r^goQh7>*RUh3r2E$e2xmp%<{o9#aEG_O!M!KJa&0qz6TP}E^L zZJhy5Z11r?58j{noBe}dK$q@Rup7ivT@Qg9b!(Ve{;q@kadNk;Ow!vGFL#r4yGjb_ z25}-7>5}C{SYTEckp(6|W2#DIDn7sjyO6&*skl3Um7<|4i~Up~mQ)+>28+?nWKFSi z0myv?8r&Y;HO%$QO&;zj=GSXDx{SuAqsyJOhU2xSqxWzg(6}r&FhBHo73v7RU+Tvk z{IMQCunKH-zl7$#1g?BIWc<$F(gR%|3m(6xH<{S=v1>E-^diT)UVm->JzWLVw8t)- zPox@88^LXM-F5YPjMud{LA<%%M2xeI*CmRe)p(sO7J0tUG=f-r= z8j_ynEJ+tjsRSHHoF$yUL8ZYoJ*d>^)9Q@jf(nh}bcONg?S*J-?x#8RmO5LD7*oF% z##bz9E$;bRc(`K8jpCkfg-#W6XK}~31P$6T*sB@}v1P~L#%^YDLtS!w%;rIADAR_j zgH#)A?=d7(jqxhS%QlRuf*M1j7)MA!l%ibi)(lM9{4-Np33Ul zUrB|dgkPPn+<<=_UM)IvJ4d$1<@UQ!k*P-&@OO9Ham(kJYTC&FM)p#Sbgh9Ge>Av5=kz_dOQ~?JmZ}kEsV% z3>C9U;vdydWt;Nh{LYiEz_$PR%{BiV0Vj&izo*FNYtEz~{dt?n{y?bTqEj2y zPq!W?d#%=EoYfEB>DYzn|me^*KEx z&G|jc-`-ehpB~%1?q#u`F|b|W6cfzUE0D-FY<=) zy`HCmvR)v8Y4{iA*!*4zi<>j>V?w6@uiaV94Dz$d`0jSSJe;%8$!92wy|V2JI{)eA zn8FoGSUG=Xmfoy*nhX;?T}F*Gq?dzeI@^xqzp0(ghpiBB@|n)!=2Z0jFt2VFH)9k_ z_gSTvkE+o%Hz!-JW|4#qQJ^=^TYb=NM8t;$tzB5Jo)B!aW*-&3Gh`=QgXs>MQy~d^0>^?M6TN zxe1~{&{ktCwCETNbr}O76?&jR>R>Vg>Ly@p4PKB;B95&A|2QKw*MMKJb_1#^;K5`L z5TI!W1_9VGgU31!8uCUXhuu+(&~`)Qjb=DAg$#qy%t12kqT+q8mlD@UoT#w(saL@o zS!j$=ucXIT8|+Sycx zZk5up7~-410&~G{ly=nbly>d!Hp(~8B_J;knoD~6Y{Ozu!gOY0GQ_h;&qO0ZqZ>+0 zBs3BC(PF*y+7QG%UUD0Z-$dV8O$AI$a@%S}(m&1aUgvuvLYYNj{+; z1;hh6u!tJtD9YkSJJlG+x~zcP-cwuq+`!@uSp#VLhOAq6zgN`Su_5cW-KUDi<~DGr zr)=<@8>HL7yYDngyLWmH{d}hZ&7&^x&jluG?rxQhs>2=09j~f%0A&c6r{^5VSNZ7#c~-47Y}5)k$BMz)BS~mINO?G(OrCs zoV>(KF;)?#0xlwam6CGJhijGG+v8R;cto^^-0*`TW&WEF==&$uZD!gez4himjbN` zhE;A~6qUW=4j#UrSI3jsN5{$b?3|<_HRpnAhs4HySEr?DFv?3>fOd+nmaIXaZU4EZ z)J_5mLOs1bPmq=aw*F#MQfHY|>-Xu|9)914(Ec=g1kvV~knNp6;|Cr)fB3JEkk|Ng z+OKe(Md3LunG6;GXf0_6c{sls^YSZ)cEjFi-QcedN4eCL+5-NUK2QZ)#cA~Gs{M&U z30+`~w+>w&9F6TL3QWOYs6?um-s|XBkZ#ZVURppLq@wsX8(x36w?Aok2K1;c+O8#W zanSDKzNA29dY8M*E`0rRp16$AUc|att|svLK>2Dx- zR+p=sxR$=#dMxC-hpub2$VDV|`6~N<$t7vF~KSU(kzpncGuV(%9@+)!w5 z(b74Ri-mT$++D6mJ3@YvCO{LsDLwao7z(EqIbZB~C6j~p72416v=%aaO}lHPRDpcB z^_RP)5Qz+3eN6(iBzA`3OGx<_UyXx5i>z2(H!e0{Gf|_AnPzyS zhoBt|reA*ISrJ{8@G2%0dzDx$@ot*VEU_7AT+(W|ziY4@q*I@1&S;juTld@DLuvW3 zySM0m?@7bw8N1a3P20c&6^)#7paS~^k;(N&vG|gvR9DdadROYPql_v!%53;NP0TCn zPSgD)d1YNOo#egM*i1dNPpSS#3-DS@{iE*M=7mLeulgkQk z!+c}CchLCIQr*0A-Au2`ZpVC6<}$pzObpFSKR`n!eS3scWGoJ6Vlw55)k`;d49j=W zCk6*#X4qt1-cRFX2ktisxJFmj9mP{;N`KEHHebC46`#L|TwIkiU^a}OsJo2A`_&S% zjGISIX~qenozbhJ(Y5-U&(fRdTi$g3@vhdepUEcoJZpjb$y&*T=YM-+ z&=eQh_owv_IJ}X-x)#%=vDMISBl2MDer|H_+~hX!>60gw=x!-c5Aw+m-%oHY#pSV6 z!4~5;eY@u+4)$23Q@d^>VB~qjOzfef;rRf3@<-2mYB3JG0K5tbvsnShWF-HAJ{KoWWo+Vn-Albb}5Z3Nt}5xR)&tgKl6k+WEu; zEjM74QSZC^yUI8ttdaGqEWWsju$B`+74ksd(!6)E{EMg2auw?-#dLq_(9R`n6qaf;b#aY3YjYcMsUY6@BEMwf$WY z)C^aQZpET%OwV|K(ShhUCaz8Y4Sa`sg!yyyyBL@J9K8bE>b3Z>p3A4KRkjvC)^7P} zYRBcL4^D;{{B81ZwSXDhX1V8mh-2G)Qt|%eP&M}Mmnu4U*M)}rBsTA*3VU~sT;p>A zi*34@HkaKBccsp>s#YfMA|!|#i292qDL4oh-dmq$=mQ;@8XiCVlOUN5x8K z{A^u5K-U=X_R_v$;XLp))Dp1#U|)gydtYJZypn*~y4>vF1z~1rN$BizaL<9zMV+2P zhx)V!>M>em8nMW3J~vZ$;mjftW9kCCc@D&xpt+FBQ;Y4qgt1q+A`daAP*sjD8uenr zQlsfjmEK-tNF!ZW6qiw-LOGK7l-j@mJ?6Yh7_VH!70Zc%YS*{W5H+GbRJSBr+|UooGzgADq^1enzsGn&Y(A%i9u%I<29Byp)*j zl&t~0l$kwGwl&qv(#?JKeAH!|9j3dy)X(&hac(XNG+f^gp*Yw3Z_ zHDvJD=7q4s;0lBtt5>{Jt_@wkV-csMLjA=a=;iQG?6S{{xxbw+f0$^3O01rOAyt7d ztHeE|fQwkIxHoCHtcu3#r{b+29*yNKeG6)}!xvTub^cuywkacXa_)F%{g&}C-gp6C z|ABJG<*Q=$2b1d7xo=orgE%<%jo1Vjhuyq<)q(vLOE=UN2*>*@Rn2vxc8F67H(B1Q z3>iY|HE;p@%tHO5!&)M6_>-QKZ=)R26YOLDth#MAba z@o6}ud8S_nw)HU+6 z?-!SstF9I976&|9NVTZ#a&Fdwpg)QE+-6URqMA;vkzGW2le2q_SLfO4RCx(|u=kOFJ)=O6X4lP-m2$!Z_WXFzU;4lhR4b5OwXr8%hL*O{kQ+{qyG8Rb3^({ezCw}pGN)t z=->NbHWnu`ll{Dl#g}s&TjPA{FT;=X^IjJF%RV;Vr$P(z0gW}x&Z6l}t!3$6|6*}7 zI$8QWe^2T2l`^eMXv|q%jl`C@bh662uvhz+CjEOh3{|9m-j|hU<$UU&KYRzmu~;e`3AD^q%rpc$k@V_mA)38&X@HS%+KNyBn^_D+O!L#ZFehI~T$I zNWlM2|HGd^PY&ueHdZx!RYlsi8G4;`gnONIhaMLrd2dYxPcy)=5jV}IJ`q#N`* zxgPFp-wXcTNYn8SDL23z*Fo9tP%B~NLo61k*9Z)>@+K(=YAGa9!n>hBMTf~`;DexN ziXk`Ee?4uC8}~v(~hKzH8Fm->o&}g5ytqgHF*c(=9EV zauCx?6DJ>h2i%%G`8C~I>ae@czV^;9rrdO|4?tXFic5cBH}iX+t7=RM>7&1YO?NHY zF?}?6OzfC3diup3#nQdrO5;8mw@xpI`KT-N#2eFYb*6Tua;aS%Vvn>|#7qr9xgKe{ zQD79rQ3KF?pJ6Qac~1ZdgZ!{7lY~ux@`?FSTMS<*1S=JO!|p*aHBe@8L+RlJ9(WEP zr~-j~7sLVy=KPGKF&CUm#>GKBm~L$Mm^vBY>Ks!%R9L&{aVQ}?UgT>`{w z&>lVmSRAlkPxo2GVru7%0Oo9e=VQ4p8M9w-qr;bj%Y=uAA6>kr`&+snU&N6|7pGzR zc660j@!{>BsPS*tiNAvduY*5r2B(SNU)XF0IiJU~Y5W{@EC-T;8DqK9x6#qeVO;53 zRx+(<7)O>_$&G>n4b`orM?+y5cE;EzR9FiNF3f**Qa=^kqO4lgFDwl#WU<$o1oA6I zc_wk!=~ka$ZVadtTr)}a#sP(Y%gh0V1m<^S4lokcC^(cf>Nu((G?rj25S18{xwS#O zN;Vi#Ct4ZAb7%h$~%UB z)r#2dxH3Ien@EpI8bQV3WU2`6t*0#+JMCLrBKK={d2-*C^O(*{eR;=A0 zIFXjCJ3EEQ#4}(^Dx{Rd47=l0CJfsXLrcx$Y=G8xP7O+%>Pj ze6CY<-#$qy%3T#zTk@cnr#$wpgG(S?`MM_N1gI9@cKloPPc3*V$v43DAASni+!3w| z9VsVpJF|Zj{FMr~ae;mu9lI!n?~r)~oqr&bg$(}MwvgCU$buJp?8Iu?(JPO;u>Z%n zvZMq7`g;UufrW6DnB4us&u_SFD)bZn;U#!qq`-Xg3-rD@Kf`_82+Vg@CJGGf;coc~ zal;$2Y)OY?=novDq32*ZqN6^pi&elYp2p(!mLDc7(q24Wq>~~YvBLF~p)lIjEzm}E z1ihv*2!0y~18m$1-U>N5lKIWF#4V6I5`M=!$7aDOV0XaJXn_t`4p&p?B^DUIT)%h% zms{{64E~2KI$$|%DvCX31%nA1n$mH>=r~}I5C8??S_|I94KK`qQUG3{@xIfmP-SHs z3L6*fOJMbYU;N}sVc|nMSJA+5Xx@c!_`C*6L>UgTr-38qDG>7tGkfGbN1(@?PR&QR zID#GMDR55u&__>AM2AnxpLp$rIhEm)#`$cYvXI8@CF-&E(&Ea%ym80gk`@=DWneq% zkKkR$%FJg9+Z{?iRu&9aCm;5@F8EK3x33HSqp)4gMKu0Vm|Bzg{W{}eL%h%S->)<6 zH!PiC8J;QXju7`>|HcGq_z1DOH<)G>-AtwTOt=Z2RV0n&XpBSH_AbIpCH3~AF%047 zRe*e$+B4zgg~lMA{H2*J?{6aGd~nXgRAhz54df)q&$4HtRxnf|25^MeR%ShJ;vAMf zM>1Fkbsy2{87a_yv!9!8#Ppl&o5q3xU*&@~bjIKaX>mZ6R|mx8g@BD9A@dvP986Se ztBvmIXsFk1RnrIah1O6o-K-0jH82hb?VaX>sfBhsXlPd`cdnQn&aGG~KSVtsKWfLg zk)h5N)wz$_=YofF&&YE->4wz%T3+r38Db}DLeroZtzA_?Vw9Vj$!q7M@B`+6VJ`Pg?- z=iarDB&imXP}n&%OD&cSczr`4rXN1q+TrK&^10gJuWO=)i;*J>qK@}m`cSZTUOQO# zU5`%_ZvC)DtQHcYslovz$eOJU`KoQSloF~HaP43`unXGz-)z3~u}6N;URrQs^ypC| zcH#Zpq!g0+&iefa?SUzEBi!G=Qs`P%KKSca3pYm&nfU%Uv3FL;QDr7AkXO@kCKcE` zy*fR-Z%^h9LyJyo@gTgeV-@@Ed1oDeusU9M2q)7SYYb!JTX+NpNQA+-~>D*mp6pYhi5Le&M<->i~k}mLYe)P$Y_L`iWe6sPE z;FEYCxsSK)sZmZt`_W)?`lz6M(&wkZNaFF3wB5BYWV@>`v1hp2EQQT*86`cRm)x{G zZ*#-xE_?#@X0lj!fty)PH7uy5n(AGLhAG#8;eJD(epFA zRMGNY`*q2}xFfTV&rSm|y-z=8|4ySZ?%6-`Hfn}$^!UgpF^(QDG_qBK9)k~Ken5io zU_+$@p%cbFUb*r)z<&z^#lQkZ;hp|S9+ZlcxeJere44JZex0MMEak z!%mathcm-;jk?5Nfu%PwO1};ycwNY1KgEUmcIGvS#YqeF^lrtvy*KLKaJ8I=eyl7eq_>JO146V zR3%5}tf~cs7VK0an1Y|3n_jhJax>CXI|K9igIM96aJaaNx zCYemmoO32QCKHP#sNcx*!aQfpdI`?xXnTD=!FeLa>?t?G8Pcgf5wPsd%H6mdBB*US z4>ZzoFCkIQ^zLv!2d|Xjh_}zRqE@S-O8t9k@CuBe^%@6G%i>S;<ou81pU+KakAf7Fya(>}B7QfgR!*Chu`xhpLBS1voyEO$lRMyh*t z+{R|im}T#3am-?v61OpCaY6Gsrs;7cBqQ7@a68#5L894>c^7rJB#mKu)9?+on@AT> z*76)a?{xl!CCC%{5thMkkruBvVU=pruc*)hn6C(YS{-@BV~CAURD|?e!}-Kw8i%JR zE)lYHGpHXXF3HSFpf1b&GJ)8syWmHC`^DNxfl(<4Nk^3ip#qZce$yqe*Vbb$^GLhdZe=a;2R~KF*W}6YA=f zc1k{Cnl4EnOQ?i^LkR3ANf}J}LUxd_YEiBOeeD$@Xw40v_UR6ZwW7t1rMJ3-Y2;H0 zWsfIBl#G0uD|=jm^SQDXQiOv#Dx{mmHMVw4ozGQ{DsSzWb`^Je)XQxgsaKJyqh4;s zjIFV!mXCE%Z(giBoH~F^&3H7@(KD^Y1D(mbVmn!jsl$-hG=`kT;#otHUoy-%E6r+> zmozh)(js`W1l79!OK_PfK`Y0UmYEZXAEP?jr~^Y17DFS=Q!y@o9U~C?269ps_11hT zi-TiyrAsiKBiXdvSdI*WjsTSH9jQ;K<z2q&tms>lYnsBk~{@#!e@@2a@Xor`v$ z*00l^EhRzM&*#F4n*?uu>5$|Lh`~JLPghDlG1peT*C2aHFhAwcd-%4-`PO!=h#dKd z2jiUN1$|18Q@>HrLo)cnhC^f3vk`%t{&Pkm>mz)Mx#LF1;h%7vXGiI((vH6WeA0P; zX5>H6I(wH)>o1Hd&t=QB9q5lOMzt^#JH~6bq8;e;Zi&4eBhU^s!tz#&er?IYb3aeD z53^%@@^5DvjVgJ5{n_2YJ*&u5&%J*$F`{D?^ui4bTQ4WTqD!;8Rn1!WG!K@F^9zs z{il|X7*Jp9Rkv$-v7sNae2sX&ViG^pB_B@IV)Fib^{r%a&J)4uGxYflrR^$cdAzH&>93M4{`bSNo9bHG zI|j!ow0w;`i+Q};Fj>deH)4+-+xO_}>!Jo&IcD>HcWBFvWyaJvNz2FVtE_KEnH~$e z0+WO^D%yN(;(py&Hor+r^xMZWTj}eSG&GLoFQ&-KHB!GD{TLRmix-L6?292(9@dI9 z@}DfW`~NDQGL>v5A?$VNL}`tu|;pJR4K_>C5^@{KgI@pT_^DML1td{MdV`~!qR{rFd(Y!*1RAPdgZJE;+!bFl zWX_l!*V2lqJPzY)Kw8i6C|vVyv+>0+<){4-5?EQ2S z)uR5$Q@8BdHc5fAM}%EX{d@Y0UV&5&^%|$A)?K=6Aqwe0x)B%tx}&{lC$tXji(eys zQFqY~ZAW|IPeyyuE6|I!7wMsuy8>lX(4v%<$8F7Mfq}-N28-2#aldGU3C%FH$%s+D zFmyR8GW3`h!GD34f^{EDlh;Mek?_}8I!X}ol1O|QS;bkSi}#g?h+X{4C1P3}JW*nS z9dwgB@V}IVr^R*Yz*h&uH^z0XZyOeew0G^q?+RX>Z>H`EnW@F!jLlTc_RIOf69zVL zfDd`=;0f^Bdo>N$-WxIa&U9Z$j>Xh;4b>zI=TL(U0%yGos+FP-hz=)7?MG<00 zM2#g1ViFd;|(j& z%+aij$+4Fru59*C`_Dra`3&_&#iLssvVz48Ejbf~@`H*?zK$9c9?TZ0#$33Sw`eZl zIl+TCl1IHxwzWo%#hf(~`4;(_a*KSUwjU6cFVuIY7Aw(4p{#_Eg6MFeY#|Yu7HB&3 zC<%Wq+OB&!^WLs{+|YZv==oc_@bq@cw|3-r&#L42M1G8k7?UB|OA1WVP-%Q3!F(c( zmvl2EeXYn1Wb3MF)))vm|m>|`3vOA#b>{16O}Z6&FK zjTx@FUT7#soG7>6#9YW>`(Y)kp$(QTTpJoRB6;Yc~!s3TZjs8q=^oX&jP6EayyG zYp5$*-=0a6&a1~%lIbu`y}{8ku#E7_fbj{*Ho+ayb%}>{5-~)@(iWz zO^>~TajlJ0uiL-&-9&Q3c{M?OL2|7+uLjl9V%I9IJ%F(0tg1%jt$Lws)r?Nxy)#~F!974r$s zV|K;94)}c^LHg`9L=&zV>9gR>xFSBz0pHty@<$pkU;X(lT!bITff5LrtH5JKOjO8ID^A>uhfRua?EI+( zkA#jh@&b0~MnB&n5zmk0SseXw=0`H>6;m4*HsbdUYttRWa-m(vF*Os9V2*dkwH@KK zewDA%EpoN`eI7O6WyT-?1np9fJSW48PMLhXY~ft8B{Uu}w&|8gKlU+tk&dJ@_BJlV zYWLpJwN(&h@;# zs_omo4v1TC-y#pODM_i1!eMm(!?($3BHPzP=<` zkEwK@qeh9wm?Z7;_gvJz7w{>>gqqGeqD8yHNyt)h=g=3oLX1JfIBGM|Wn-rJ;#P~z zoUW8WLtC0-)uv{nn?DEW7|;29k3h+fulxo*9J-jUUww);E>K)XmBQsdsReyjkI--X%rZ zn{;#PE6|wqUQBY*L*fzkUPfH$&^fQhjangx4%gziG$;`gw~;DR zT|Jo5wYpj%j~H59(Jp2Rm2*X>I_nv!#ReBg&Bt1%7S<@yxJ{t}Ug2t1$isdvQvr;J zJntp(y{Pg9B-nP-ylxEBp{tOIG@n*q!eS=T%-ST7p+#ikmwv%}T@jbXOt0&rA};;Q zfZ%kb7#dE*A0b7p9XJ7teG{HN3*k&~z z@ldoI#a;luYv}j7APoJN*WwniW5BCX3g`+EtuCctx;wcyvHD7< zcWd37gq4?GxA7Nx{HFAiANJpgmAfbE{P(j6kxHx8=6Sr!SDF{SZ#}*nhh=?nhmOM6Vtxk{|J$YD5zEV5I8*jckNj$D#St0|6;W&KB6YkO{J8_?c0EDmG8xO9Qz32 zB!z45Cv$zisPlVg@sBV6Y|Da2XhFj=8MvW!CZ_7=kIILg2Mr$mp(A< z=a7H<+i>h}N!VXHkH;aAUX@28@w#I?xypN})#^Ew7-iD>^qx6%KdPWt*Z0Xg_}czC z7>N zqtfFl^jM0Xp|u&k@kht;xD3~NV=lc;=hG+^jYchou!x8V)LNjv!WI^RVLGgQ7#ftK zYNPJGHF-Vso?@bBr8YINfW-w1mTR*M7VOrX^+(MQ=*wx~Y!n$6H!z+o;eC*^=ZXvNldwe_Yb>!un&6Bb8)p z*;Mpqp;`T(OuCQ(Xb+;7PY*343=>XGkN;sjJ)J$zTP>k&RH)1!RNMYx8QS)fNKKln z-H#;dCW&`xaZ-FrFOXg%weJ(sYwAUGv-$21wfpZSZK`Km7Kcn` zv9#h{t(>&tfNoJk_i1@jbg3rm<%Na{EZ>Cs+)KZ_IYloU|684=-A)oi*8lv6wgXK{ z^zsSJB(`0-RNJ%iU0FM6`)@TtujjEdBbc^JHA%m{m{h30eo4v&b+CMm?DOjgxkMv?o^qGobn}W;4V7R<;og)zL7044$rhF?Z0?sRTbSxJXFpk9bTZvyrI|nWg-*MQ{ z2q;8BdxxbG#wD0K2E@_+8?pl4xIfrRQyF4x+F)T=My!y~L1$JD^vY!*9Ocwn?Jqs1B~n6)#lh3@Sl@@#(7>fbj! z`+dVZS0ND!yknFK#S*n5b}L37B^n1J6QRUOIWOVndo0exa+cZ^mf+9CbGPyc-Lk19mC>A#^~j|0#9!*N&=-xQL~}VZ+MZ? z;kh!Jt91p7i*#K@#oGLkIYo1|IA^hDa?auhX`GzBOn2~-3o4~A(YFujY&#Khm>xTr zQNx^Oy1a`T<<#oGqMC`ikBZt8jCxdmqEU;{j*c#8ahhiI#Vpdx8JJvAnoMth0X-~V zUq!FS;z%R4&mGtJbJyVW$g?<6mrHch`yovza)}b1EhdC)$Kv|hI<#xBe(SGqh5J^% z=jR{Q8A*wKjGR$@>l^W(i~6W)@BL?@L?T4z#eeoD-P?F?fBcUM_7>bn_Rr5$PN;qN zoQf`SA3xHo^1y=b7()K#Pum|7WE_XKZK)x zBz4dJMG9ITrhoej>N{vXNsFVCajLnZw2ubUdJ%)VBcI?M&}ZM-Xfxt}ZwbQ?Zg}^q z9XMJ)GQDfd-gFH|o!>dDSVTq4cY7Y82514Krh zNf!PM#2i{fGEf^UWE`?rpiU)K%h9@%G&DffyTxF@b*)bfnxJCP4+Ol3T@^YA2yKpv zgkaJtR7|vas)$ytHaav220_JDD~19wA&nE#S@4I#MuU}}c@B+P(Eye+i@Xub*B6Zf zwEe5WgtO9dxN(E$`6q=6ULLxrMheJdQ>@grh{d`AQe+?{f*25L<+XU8!BApy29k9v zspY2L&{AtwYGum2Lj%gQQp-9+_cfY(bFb>3(r@0Oy;G?VAKDGOyfqho1Pc!BzQ3+~ z+)oD<^EAGz$umcM!j%(*C(fxofx&eefwoo%7(+sAB8PmYTx70 zDZb$?nQdN5LhO#OV{|h;jy8=$-kqS?%AF8t8sK(PvuYJvxg{EltsD}UC5P-OMml_m zsewAiXmN%dg8@|iT!xGf{soj-zDA;#!wSKg^5c&x&X>R;r@V_4t9nr=Q(nGeNBgRB?AEfJU+|>Sk z8V58DoOjdZhz$v_vO~;(ZZPnCyzUxm!Q}y%n@hE&X|B}l;eE}vF?e@D6x_`W1bZq` z(rY#ObT<{>zt z73p`#Z=OrtU15Hzp<`Q-&NQ!UToy7>%Nv(b0~J)c!_U;-a?&=Buv4nlz~Wkm3fnQ? zuq{$I3KB`?(Gz;OCLJ+I^7By%#CuVFtekHP-wfp)z7@PcRaOuv)%q+Ek`Vjrh%?yg z>shK<--EUuT7u(2Pb%DbzvOX@I99#?0iyWrXVr=sI4U(_-S(J?q$r}3<9yUiViyck zM|SSUW4w_epLB>5Ipe9WKUUlE{)-*7O?5)^d3BE}%_PR@-5r;c0+%r_CNfpB4m2B7 z3A={IiFx*IZ+#T4s&qV#+$%Odj zRX(oZFrgAH;CPa;W6wiBMv{&$zVh7e(?lw_=5dFw#QoX~YO{RexT5OStt}?Xo}t?Z zSDc~sy6X*UYmSuiO9p@DT#91i*C#%mf{#qujHFI|6k-qFKQJ60SK@j7%MWts`0&@S zr|naTr_0eF=3}jRhWmFsNqlAHYd8o!kh^hkRmsoW>{Uc=Pa$FMrG-KB?l` z_YXWlNQ`GjlC=-=(&Wj zG2ZIY4aC(E`Bv3FQil4lA4U=EFy`5fZxND<{@-EKh{uEF9$W13$Kb(;K(FXY7*c!jxFyZ07^T)3E_C>09#W&AQczE1= zZ;__mJMaEk6FwaP`^*}v*?-p94{6+g*5nV8{dD&!v)N`7TXK?$*#UI$x3_L%Q&uqA`;TN!O9~Eq+0Xw$3oCDd}ysTN0(Q^m_^U zg1X9zTu5WNsK(hwJT!Tpe|9WRT`SMqGCNjmq>1KRiBiZc7GERJd#4C_IJ2mg1%}TQ z;=>ZJ;ljoV%?0nZ4zf7t}n-8&LGddlAjR{3ob95R?pX1{Mw?aT7Kms zFH^E{yj;5CG07GQ_rYOS*|+bp1lwfllL_&YTgXyCGZ%6_C-oCW!G_q9v9R%4MVrna+>3R z%_6Vkp6niJe38p>S9TAMS6q&6HAZthp>ecA#nYsUk=YJ~7e$OYM0M7a&{%R55iu_N z!nR_0=!+?mL|7VAlq|N&;Y$S=ds`D8<*}Yq#7BP8eGKcHu6J12@3TSpy2JTJ4*MLC zj%SLFv>1;nxw$NRh|*?HriczF=hW8KwA>AEa;+uBgK<)~k1ytZq}_?1<_VHSa_?6= z5y^-8kE1`3FbU&>M@}eD^pU*CuKgsRkM!S_5~Gy)P_wd4kv%e*cgLY^80`V|7{?Y- z@v>?Y}n8*R=km*zQx?E1bZZdw*9z7>PpDNQ+HoS>r>wRs$=Ec0*tHm{+CZ!5uZ~N zs&BvsEYDW)c28PEuI|@<37u6B&yaEm+PCceAG-(hqjtZAr%gH@JN*#Vsva$*Uz_(` z-KIw@;>@qYo723#`h-C3{)3IajHG1R`RxQxx9^^aMf4MERQJ*?ImS`xR%w(`?y%W` zr%5*KJa(AC!4)UssDabfC#j0lyqP0}JoT))6HgQ4&YpOT?r5E;j+xdR$~*0`G=6O1 zXCrC3ytp=fY0Rr%D|mWj*>dtNby^?CW!Qj_V0GjJWpLKe6IaVnI(1?$^}lE`u*dRO z$+P8O%>Sa7uE`AGwFzfy~XwPecpSDdc`KnM^0c$Rv!9-U;)k;qQMi`UVC^r^i0DanCVm zIii!?TA zE*&MjCcENumfj@1=5@8wl;D#$39m@5(YGzTY0E3_-ZxPnareHiD)NYy_clL~xWXfb&n|#dsSi@^g-T)Kir&=xL?2V?Et(4z90IP9mq-{i7?gsjW1fS7%b#^^hfqCH&R3H;buDaM!i+ zWQP`0vyrcn+le?;O6PXU&`TYRy17j?R>{{Oevhk?@6en^-TNK~CF&$-+xwm+Syrjx zRAIot@Z^mfT-3H3Ux7C~1akz#hy(iar zC3z&xhPXe89Z+Ah7N-*En#`xvY7kuMH0E{jfp?{2%pfXHlItq!F?H1H)BhfK)T%OwPzB+!;92`6xt)6Tl2vp{Iv z$SaVqLcVtc*CF18{ONciJ=I&Gx}I`hQ1Pj5T3&pbIsYX+wm+kg9jQbN!^gE^J#!uN zjk*>PKVn0nrcLur$!2)3fXI-8i)M{ce^WAn@Oy2Fn^UruQNLCjE|ID}y zk^u`R>KYozu~>7Gf!A#gWs4eRFn&gQoKicTCjyy2>XmkFgaR>ahSaQ2a7^&C>(@uM zCZ^y|iebBqfx&;ui$0Tz(~oUdjFOytD!69U%loBZFt}#%obazuv0(n{_$drf7Ua&T zyQ+;R$W6DbKADgk6q1fq9Xm5CP%-7&_rIsd2R@tl*skZyt;o;^z{dx&`;=` z`uU8p9=OCSIFn_PTW#y|DV3bH5o;=jDd-T17fe0(_m zPG~!;##R@&gCE~J>TEZ{2d9bZF@B6gnrBo47%7okVXOX0K)E6Rcu~R`CAi^Zo^>SQ z{)OeMQ+A!S2E|vm?mj@qMu>_n9G@|gUApG`UEbW?@6YkZe`Z!hG5UOqS&`vp_vq)F zm12*PF7u8jT3X$A7 zqqknGpc=%2@pDOdk>`qiUoBDy=Q*o>IU`nX>GPJgEWL~ueb;UJWf`Y$;_)!*OnCcg zQ(a|bXxvDA`D=NF_@YokF4FZf$7mLi`YuGQ_@a2tvU;vi5I?Hi1syPVg;MD-z}32L z=t0a}-krwwN0+Zn$*&x0UrZg8W?%fwT9@v^4onNYHfH5Q3pZ#CEU4q|Aw9MD(8tLz zc93o>&AFC%n6lC;#$R>l<4G$c#MZhaTVpulNzX-$g7YTLvW>cB@F(*oeMx<2-lSdB z@OeXa!fTkfgs-@Ir)}7bTZ}8N-eF7s*Db<|K|6%>f3+4~hVs9f2`^imf_!(3(33j8 zRyT{?9P-{60tpEzvsf08cVHD!0hH>ARjtKz-tU=2cB2a`6Bno8;~z4cbmSD76jO^} zjtsppjmpo{#p9~yr=ZA|y1iC)JisbgMFniz0=(m&zl zqb?~v0nQKSYIF!Xh@6z&5483d7 z63o{uTH8W*p_3^-F*UbvNzvoblfOjJeH=ztdGa3@TEJoXa|Ng7!x`~n^QvK-=8-7! zaK_DI^CLWYSiYGwgR`isd19iLF@4nG#8l4=A|MvzIw~x#r;JsO&!SAl6q3j^!X#^# zjV|O{N1Bwl7AZ^|q06RmNP;**akkLR@2y)%oiaFL_B3=6nqrMGQ=_cbT6q@NmB)M< z!@Kb?TIfnG3|8}a!b4X*tt_rp(&k$YQq~rUcfpJ;3A{@*dc6g(_*XTs`OGnf-dg!F zhCYfh*IO{g&_WTY9Q06I>gG2#8S{7NKdr2&aPu2CKBlbL>gKm>eN6E!a1cnE)=9Qh z(D}6%Rm|4nq8&4Z@_Z+$u}X`%qN*aA=ZdB^bc;}tzl0N}$+vop#hj1|g<{T$_1Msn zEDtT(dKQ$BSnmqy=r^7_9`KeWC!&32z>NN>6~y4X|EkI!W}_&3S2iofJ4*h~$0u&} zY%BL$)dPjW3b&GHZT2?KhDcU#`e7X5!*KlF(T|u=Gp`;}a;493d_ctJ$d2PD&V6VF zigupc)}fsP`oo^Ab`T4WHynN|NI3MP-GeUt)i|!->B`A(oW=3~&Id?t1o1uJ@*hSV zz?d|3;DIyqB>F)nI_agO{7rt4J_N_Rd>X#dWh*kBQ zJi?zQH@xxHQ$hSTdG?jBypFPB+<&|CMpBM)tv38@s35%${J4;~%SqYmiDTqrL9!{} z`hizu^skBCgNKiPy3retW7q4AuMCy={ZH~tI7uuHINbd|&0nOSw==4F`lC?Z`ExAo z->by#-i(KVcC&kp)Tr~n;3qvHKUW`kfF4g7^2z5*`5^YTyCkf`uwCSXgXs4FO=@C3 zK|U{LZz_$mzS!BE#y7kX)A|=Utxt=~=#Q~HbG!c7%wl;5ADDuV(E|gxUS>GM^~)91 zyy#P@v;uE|;`#y)2e0$ax2U4pvS1cGR24N9PY3NM5~@4G|HY|*-B(i2F9~EWDJY3R_SDDsGg>8CT+S@ zS@FLfh22F%@%huHhSIkgof`VCo+j^Inj9-v<3Rm(Tvh|q_1GXcp`OMzh|Lcvv6zWg zS=w|_@r#j8d=viR@xR#nL%)~5{*t!GqEkq0JoV`kz0~(>O*u(lJl{|^%Xe?WwJhI+ zgyoy`XZ`jh0|Ih~ZRGP_n1ebgBBS>QP7RCWAY8aTJOalpa8v{XfZ;F(9}$7$ z9*AvL9N6G#XAv9`G4T=5Va9O{8XAnoaDy=-!WfAI9>&n7BlG|-#n5du6;}HRwXXpt z-m%yvl?WVZ5HF--q``%}-&t#p9`kif$9x)(g(OFk$8K_tlyhA2I9)H?A?JwRaRV-- z)jMun3(VK`=ClYPUXnwd0?Vj%bKRUWYcMHJ@7`6bs>NlG)pzyN>JPLWynWh|++C}m znsIy867Q~*r&>>kH>W%m0e!=%>tlQww$@aISqyE6VGD8E`j|NxsD+`WW`Ca7CH$>4-yaU_&2&KyaSoMere1qSeM zpbXMP?d+yyqNRf_=|H&bO;3tE99c(G2L;0pA4ubgpkqQjR^?WHHj~c1CNxomUW}^#wenkfCsmoJ`TeF$6NssGVi1{>z zyq8)ML*)yYyly%qk|{aeR1(8!I%14IbMjb|W3g@=RTyguX(>TW#+s-`GC^CV_)u=kZF8->b#iXY z&T~5|)^R@L3Ke=-tO56m1G z{}BvgT=IN-P!8Vug!x%g5)97h@bT*;5aqdj=7-OtD?;%3^%1*?XK3#6>SO1yZ42J^ zP`nGv@ep;dLYKp<0fi`^1@K(t9S7HKZgwP?TOhm;{8tn>!94LOo3&b@$9)}k+U7GpE;>^sd8D3?f6Qd3kK z&mOtwQ$kLlooH*c6D>pg+k+C3PUHM}!qtvmBH4PhSuSmmdg~L;w)FfjMBS~BTsPXk zny>Qtd^nG+1N~SeQu^Z0w-7w>9*4vQ_`IWi>UV{0>;&~id-l_MlKVW?wR9_RUKLa# z;TbkL;l|q3q!OF|dre@sZ$bmhH*&dtJHtyCHVOS<=+wv(mS?tKsK+(Z{yUhuHn1~` z!Q|>FFMtDjUEdPjA3yjGxYL~KdQ`@qeC1`$kPAoa9;f`BPTZDL6-lEjAqLX z+58v%{>&#DcsGliFr4N8EG^UY`|B|2x0%4oHC%^*a1o7?Uapae zEPsg#tXxBFyuO?o3s~Ghk;RQv^z!1x%xC4;b_Cl|Z3;84ZEh(G9X zc6+vch-$*_L-capvvm`hQb%++PxHb^iuOdUxV|h4Y zT5=FdUZY(fP6pDpp0s`f6&D7(igC1X2>paZg z+Rku|Ery(0(2m?lj2FNtW>rWn2RXusBVZ)U_C| zI*oN5nshIf#@LB%q_!zTUQGvZGGnTZJ#iY%uGSNZegvi=)s%UE!;N~Zd?}?$ah7j zZ!C`ra4rLrZ>*cn=hJ1k!%JK|&&1vf{<*L|sB|qilxDQGk9of~_3%z5DP;+^kV)R& zTHL@PN#1oNWt|tkO;Xm6ACeT%oMhPZC`m~jKgocj!O6AW&EgB%E;U2Tn{Lj?EyC?g z!*vU;uQ5pTh#Oj7RA3?nS*G%$g~U*h&X;eACWcM;)xRb3f+{RuYb-HrPUrKt#B$La z)1!3xsC=2m%%Mb&p@HUW3(D^lzIjBs}IYN|t;`UCJ=qVGK;;);a&3E~<^)0!u-cPB{=}Sbj z;f#82`jr?}A}>Z&AjHl zuKTXP=bnN050rY2FcPCl{Wibz?73?cvQu7VCDv;{;4lsZ88*GEZC&h$j3Fo0R@2O< zPFES#eO!`hYL!_v^CyF9RbKNSQ-=q4R^9MMRcmkVYr?QCd*VsJw`}z$(>US_R^{=w z4W*^U#jkDabg*ph+|y^(w$-cFy?xrO?ke!)-s4%lNg6jk*B{izw~&Zu%A|cSDn=3< z@`;M^QhmW@RY%Nt9G;?dj5`*z2lFCY^&`Id!TW}F>rQ;Df^U5z{1b8cHz)QUCmfLu zSH;|i^4=M35A-2Qv69!~>o;jt;;_v-mW{BDz;UkX7+OCvt#7-_h$j>0m)-gHh|J;S zfh_UmNSs)<`~&lNg7GEtS{(2Dk5(Qsa^0ae(Pj_YW3=f-^Pcz2E$KKE&y>GAnr`x| z)x9clE99!rwl6L1LCDg_`B4^p8;oAXC$@luo<9!WUUcYryvK0+E-k4B^_%e#nC?p4fzUviu#tQTF>1{de>!B>SC# z<5v1~F+;m<7n3dSzZUwgqf|8ym`+>kyNHz85<`M=wIcKeGNdIN@}_5O4ksPmiE*88)s>ZtmtKTAaIaiICCSmkEbxe&8H3gvNuYiYA)e zzjGI2F{0>`kjD@^two>cF8+);|5cBeIF{xQ*c`x0x+!!J<_hQq1w=7(RhP3^p{ zV6^7zrK2DIPwLdJ)U~NO@Y;wPdup+1ZO@!e$O{EMy_$SaZ^!3LuM(hjPU#@RyJdb( zX_{uf!o&+r95Pl6wsny1NY_eN9F6rA$kyj?M%Hh^)#{)b#15g3X?{IU^K)*RuW<|4 z%|_$xmTuwjMtC)ms4p>(-HzkkUvIY@yWQuOzuq2R$6OZY%4ZhSe6DMT>qE$_5XG!*EDZK~rZW_()M8YZ<0Yn( zqFr9X#mBun&&x^bzU1TH;kKzsN%c)kD%SEB)Dm$T`AVk<2Nah%Mbi#G%|@-Dikd>L z#8eTLhM1U2V7Z9x12tT?J5J?Y;z$#yibMW&owCp)lNgGq14*RdeBTs3P=>`Jc^23D zo;IJ4eoyWuh{ezk=kbR3{B9$46U?5+a~IIe^5*yT`Ov^^+LUYd&r2c&n{o|Xpt8+t z*dinlW#NS!!}4PY(dJWSl!x8@hAj?FKIBZtT{O=(5dANazsygJj=Kcua_G*@ka`tM zZM#=cT?x?Lmb#1C*%JX@V-@NsWy9YU~6QzS2r&zA$E1tD=|-(`qro~ZiyS` zn`<~8R&6cxl7M~X>)(!1tde!~^KBDXm92WC)tO2QXSeg`)TCYoq zk{R(M9J#nB^%#-hKNm!-MD+&Ri+uA+T9A-iR)4b`E%(U1H|~B3i^G2A_j%XAsPhMn zSS%U0yj3!aV}BG+8f1QXs5v!{OTJQqe@}nfE5NZO{nh?y5BTo|{i(0OU%lQNF8|MZ z@C}z6ivO(T|Gk{w_6mev|Nj-&f8FVj@3%d^zf1rB^a^z6P_K~{sQZ-Uuxl#u%aZ;} zM{U_PXqS9`c7*)eSz^b?p?|(2#n9d zkL2Fgzyzw>>c5~pS-z2j%Os{A_BWaEVoQ7lr;go=Lc8y8IkROPj zoveAYZt&*WliDMHTDS1@%`0hKczVclN2u%S*p|icLF7M%FRU9<`th@{H+)4?`uGtT zCrIx{VG5_@%4erlr+@_9C;`9V<47s1TLalV0!ragQL479o zI=GX1H6hzuKQl69G~z9YqXyN{#A4I(kvz?Z6nrE3G@NZ)@OcPc;|xKPsj`4eD*azy zElDnU%6Zo&6D~v2O_y@AXk5o@XuL_v(RI~$hixO__H@YYV#_C7DHQVwDYewtmBk@D zvsiPjCAYH$<=0w{b4R;7i1^Uy6JIDpa zSkzj+Mk4wvZpLT}FlHp-hDb1*uE$1hV)=%?tLO7#6H?dMiQTO3ELdZ=&d+l=-daP7 z?WHfK>>53E#0?7H!QaZGUORf#j#6LwstxpbjK}V1lV?8Ie~a>&O2@H?DqW+VRGrh_ zm^)v!s0aDpN}Ie}UO158^UYU%t6bmte5=Z=>i317;K|&)DC=8fReQ8w*zp!#_ww1p zKSA|mc(B`%UmT>u6ujY0V{76*4nYSMWU{3-DA zr~3~`gn0kGxq2Lt)=0Okj#-W^op-|HJD-is7$N_n9;m`*Pf?m}9Y87!Wb_N26K!G) z8U6gBL@UmR^W1BKm-fe?=N(D9!A+ifwT(9u?H*34t)^$*lZ$awTcce>E_oo+`mzUk z5yzv<_XcpgPrNo!A=hpE`bldThKHH7>qTtmNY0`k+l@fW;nkZu?j@)YPz0BM&uT&* zo$nmE8k^@l(~td~sBbu_CRVM%r}I8fo9-Tn*UR9C-j%ybW%$27P2{m(a&3tmHc|@R zw;bkmVK0LoOBqD|LB^7sa5;hu#lIVHeVEpgl=h3IXI#4x^KK-!;W~}eaI_^Gx4J4U z3?qQz^k!O9Qk;4oSHf`oh4zNE*=TD~?3&P1q2UVVnRq&cs21UZvuX1&1}F;Qm<_tt za@eae(PAPWt{1dmHjj_~uT5Al+EgRH3hBhvs$D}&$F3&{B~*@S5E3eAY)DQBHWGup zYxx>Q7T1*vk~o)a=!ssIEnzQe3RSN44zCusxnr#6=sVi+-S85QzN0_i&6+L4sBTtK z&YRIc>I$oc+FkeUkbSP0OY?8vPgP;3xfYMwf4$~8>W7w+bFE*#zau=;dIbz$e5S>- z_b2YE@zK|v%MsgZyu!KsEaIm$hufZti(7uSbhznhSiL94bY&fnYVp$LNB0QG!vM4_ zU48_1wJc5eI6ySFz|P{*1ju&~=Ok?LYf2DJ2lJ((CKRw{B$7{bl^V&PF-5ZdnTtvf z@9-jtjKujN44P4XL$Me+l#8Y=sBvg?g_*|8yWs43;(~jIMsH<)KKo%B_xN_!r{dOo zuIq6V^xr+K8+0~l&tcud==|huuZI<7#ZfIqSvUHVKYZSzWDWHbYWh$aipqeYgz6u# zmGGKh@Q`E=ILY%L!?cr${Kto=?f8!ek*Q-&{fbv>^XDG9TADiMV3&}^<1>?|x}WPp z&2^Wi)aYfgrZ1&f%1m~3~x>%uF{A*s~N*712b4pnc`fGHS z>9N!2Os09KFRoqIeCE@N7GLwdZp3#A5d&Pih%XfEPH`sN7r0j#{)Ny`S<8>Wdy~#* zT1J?(*fLx*B4pg%dd#%y;yfX_G!MO|G$QljOOp8*k;m$i#+FNlW2nj&>-Y-cwYY&C zjf=}A?iGa}t7w|WDn5ePG&Wy%&qg1yAg0E>w%km;;??EmeH$^PsA;9S=u+K^^889; zQ9TdF>hZx=MMLTQi6P@?Je8jqSA_3Ds(>S_xz>B*;vKTpE8em$-komsL#HOzXJhAT z>(`ym%+jUned>*^yNxh|E734A&#~_<{Pr~t+E%?}ZEWiKyY3BJO-76`zy5_#>2q!kd-2^L z_mzsoz^Tc^gLb0|_no|IE_&9gCi)KL^UPnrEukA{L6@rcWP@bNmrlNu7`BS1_rA&(&|2MAjmN!az9i;F+TN0Ub5CFWk@In% z&vjZ=+o?p34&r$!#PfLtQR5V%%n#1L?=)?H6*Q8R;lJ0cpAZ{o^5k8@pW?) zWje2-@${bGyha?&gVVbN*I}zwnPmUlk* zoY{}%K_gYHqpZi7nuRMhU4^rC4XAm-{2Vw|cZ()BThp5`leuwABIbJ&(saiR(!DGD z&|@mnWICjuezC{SQp3Y0}C zn}Q%rOM${g4O$ctwXC97uLU<$q|*hg$d!Vqbq|Y*g$o6ji+fl^M8&e`MMMmXfG8LQ z6$>=~&-cuv8{qOhzvuP;{a^oQc(rf7=bWTzXEHf+<}BUz85mG7>@!<4@`Y&i$Bv17 zJwJ47T$vuFVqVKu4^mzp?)S$S6e`7a{h3^<$fF_KvHodAkg|4u(*z$=^bh09@_V07{FAczX`K>-T;Q#F#+9{Io8hM}p{&oYmYm8O(=v(}e zf_n6nrPM?83%!(v5VvPsiF+zJ_!Q$^|mEiUYEK!0i?pTWMr8XDZE4U*mZol9zj0Fk8%W865GQ9)au$>*?CySdZar$N3Rx6FdaN0deK^Sm|~h;#_m-|l?~1k?#zt1!MkX0eB#c` z4KanLo+9|=RU14-Q{dGbJw@H(6MvhsF^N4}&!?^U>BR7qjXgi?vn%n?lvzDLXvVr& zS>Ede7NTUxS-YqGhQkmur=ziK!Rj?vW`#Km&#`lKW|^^sfN4kOO-G+?Z&pI zrs<>6Y}Z0)oKJPMU8KVTG=8>|c@2HbOq{?}*sX_YX_#!Q)gP?B)75=?Q~QRBw5J;~Aa4eDh&kcwh6b z`}x*!s%z<6w!QnC8`1gh-S72O46h^nQ$cHQw3ot-jJY((dnzoQU5EQVyxwXK+BvGj zTl0Cmt6@JCAb-hQeZD<0-_UxyrLabeV+Rk*r&a?vKX2OC%RaV|Jp9vBPqo?VI%znn z0{*2de?Ucp(s5E7RvG`45lkxyhm*tPYgWzx9}i_8$hxQ}E?YzIe{H{& z42)w7m@0t5e8t59PaeymxWv z{m_G553M2TZ|PCt!$lOjEj0a-(7(wa9lG;2Wrdzu>^mE7nvd1oF*BOuLDD^FxA;7t zG*_swYs(>MrwA77O@`s;X+NBe=W(+B{8mTariz}y6&o6l=kz73lW(Gss()J*`zux4 z5KzxL96WE><8y!5$sRrXZ54ax?d`gduQ4?`8=gfKcFNopZbH5psCaERsIm(EQhyy! z!7FIUj<3rUDyBPT{8*0l>1ld@G|e^k(V88*50S4D>+HHF@p#%9sY#boMTjo{q^db- z__KsH)cZ8FdxUT1b$S29mSL4K)$rNF+Xa-Y^m9xxA19^IT7Gy#qpWmT!1+;_LBi7C zz$I2JRBWm#S<0sF;y95GU*q!4FJOG}2$A1pP6Iy~v49`fxctld}l&j#Gr`fI8!=T{(L8-TjrWd+JzLwNzzQQ1OMWtb9p?uFbd;U zIEGn5?JqmQdR!{oOiL6dc24Zcam40Ue2yNoTysw|QNFq7DPg?lDV#R2--bV8nmt$L zI3eODE8g^wnchHo=MZk6B4}%%+luSEZHT#_u9{$awJ8nE6%ATs|3e?<=S4Yj+>>3m zB!iR5W=VgjDE?Q!$(b&LA0v-DNAWu3aojJ0^Xp5KZ075Xc(J%X$1sQASF-Y920yRV z(>gEoR0fJT;T$uq^z$00W?#kmHIfzofvS}coAcu%Vx<;t*Pt2|D^`4Fx{}vWZVw^1 zlHR$#6;^B+W%=+07018(;R+!={F=DrmaTufd&(>T8lE8Q|+ zY3=tZ)4F%Yd=O|)NSQzsBttqAC?}1^tUBh`6&>RpP8JXaDDQ!@%lNJT+~N&mI3|3K zD=N27kZ7N{YW+!EM;r9 zjHW5>{e_N`$~Et_Z&d>G%%N*YgSQwpl;F!X7^DKE>wUU%d=ZgP(W2APE0=yTD%{m)kX_qQAxiTBL*?U6h7C z7gvZD44%*HDlx;?2zzmMdTxdgWolgY(A0;_46rN>Az!O6xl{0-*O$BEy?RP4JUiH zGZW_lXPU;%B+=$P(v|U~2i+F{x zi&a+yb`9+dSF-Ib+nBa#(+lZUL))}@-SY9$x$CNi+U;=L>09jf&x>QWo$g~V`h0n< z$9uU7KK~(R+g{V|dwV$%JNidl*vn~fTmVP@AX$=d&;i(3HuPYvh67y?bJ0#F*E7+I zu~ZRG7P+W?u(~C2bwq3pwUXCyLHniaKWG1%+et4!AfTn>WEF1%*yw`UIHJWuiK$H?p)kcYkD$Ur`!YH9;nC7yy-KeK}wHd2dQ?4O3Bh%KD_yFYwq!%c$FT0WwJ2kMQ z6|?r0Mq)=fH`u^j`$10Eb8Yn`j?2>ITAc?2OF6D*6PhqM81PeTN*$bQsn-}3>3X1^ zSvua&El9RZ*kHy98;H}Th)GrqePgMwRAYeRGN^A+QYBpmA8T1j12W_ek2Nc$0fS`C zGPj=CzmGMm5c~O=20pjgiF%6a{Uv;Ix{eF<-5kg)r6p(l?urOi&DaTELH+e(C3LK=1Vaj*C^7zGTfcl;)R9srMny2>45!+ztO%hUe4;w-G~c>+hxgejuPx>&Z;O5^v1q^Oh`+3jw*A=gxS`n4 z|Mk!rui~cZ19radkULWUZ$4ju4-Nj~Uue^_&MmGChU-IY0cBy zp}WUSw|u30tcaJH>dWFq95Gbn)Yfvd*tzr5iaFqPV|SgA)+fvm$4;h+zQP1cU5r zBO7>KcQd_NitvqQDL-?$TF0AlwCSXXovgUi*w4BAq=+3e%-C{VjYCIrId{jQLmGIc zh|@0TVd&&eixDbmG2(ct)g#(HqLy0vFQ8$i8)m1es2XK{dWtekB}0VtXZ@>3LDBqy z690t9=4V(+=k>|?W_rZWt@v}Sw#<(o8IM}$pD@R!iE`&iW;#aNi2MtgZRKan>7txc zD}FlaILT4H+3oCX$K zv1NydvYFl?qVrMlJRJrX3!~02S?RWj(h!5o7ZwZ=`>RI7+@FOFEGjkA8!CmX(Fh>Z z^{eCkN(|b;lf7?|m)kX(`Lzl^UH9*OFOY7K5$(^RR?7>{+arIu7rUCm-82iYub8eU@;L+b;%w`Pmaj z3Cef+X(OlKuu@KD*X%et8S__Ae#=I|aPs`Pmq+SEeGYu}7zObDzv`Jovylc^GV}O|T zD-&s#kxp2=iF;+Bg9aupmob;QWRqjL=|Zx_aK8+!#)eTOHd~A&J0!`0CJyw>Unra+qxzpo{w~xI)Cte?2;NM-B*@WvSZ1%N!OJn&6%}) z-K6VF;EHwauV0!lXV#-NF8s=j7j9pg;CblLKwXoH%s6TB?7+hHemSYl>^ZNjW1ogt zjxl2L)1Frf4S9NU8S9vyoalMD&~ULOb)2TDo)W`woktnzcBESpam>S^*lZz5+1P71 zFGxDXR-+lB=4);dt?*C4SI4}uPLh8|ne&Y(8iekXa-f5LIeP#qUaw_q`9L3z*+9fk zt+=kKC;J3APJOL+V3Xo(mHJxGz@}XI`o)l1X>~NLSorwG0T`<_eq#fqbBsHfW#~wc zJ2)7>(D|H1I6rIZfq(Gutz64#rrP~#kkfPPnqo(s82m>{kv}MWi1k01^0+^Ey18~= z_s2uQuiO{(IxBxf#B{U#i1Y;^zt62fzoEKuO9B#GY)a?TMse6ld`T0>zaRfDzq^$O znr0XKpQm!8slK4bM`^Z*g@FbBM-9Y*1wpno>z{3WBXk;@0||jg{W5J1;OuCcVVZt^ z*LKU0xLkqIwrOFl8GadYa>m$(29chO^gN*iqXWswuCY0?mxP!rx{kRyB6gZe&fGB^ z!y6+?UeX9LOci46x{f$QsF}tR>%?`UFguo7aSUTeT&9`!gbGEENUvj}84o*GJC{Sc z1e>tFJgt>w^I?gql$WP9cf<6$sv=HwTiW27yk3cg>8ey7Xq-5!VnIZ*VpIPDVR4Pv z&w<9H#QqL6Wlv$e>|8JUOTSm$a@mVV=T?PX)za7--kj0?r#?iZF>bcwnQPI`knf6l zkNnHS`Ay96xcQzFmyxpM*I&b5KKP@JyyfrN#)X6Ld`96aBYZN9`M|z-5G$>D^Alp< zp+&30`HlCy!{d0DudUve0Nv#Td1^6aMkAOiR51yw1wNC7-_{Br-o6UH*f~? zkEt&KIQ%zV!Vhx67za}@j8ZZ(!}RFw+=Rzu0zVf&zH%57U5tkp67IA?U3+3r)^>o~q zmarJb9rtxK?b9(~702iCO_Bch1RTfYXcqD2YnqAp=E2tq?;Xd=YQ&M_hH^}%xL_N{ zpRBfsw$IJjw$-$7>$u^ZUpQ}E*7?2FOfPjeP_bf5!_=U9k`=Qup8p(?B+@zWe|R&j z=MU5sKU~BS50Ji=^M6n>OrtXPiul5QYvzwQRNpu=_QoCL^ux7^&uzp`GhG#K^8Psk zgUxblYec05+uFeqZDyxkhoE$+CIe zoukuAuaPRnG5ykS z-r#X4kI!wglXNV{wWCED)(4@dp895&_`KlH*H8u81->%Waz9=Bma`R=2FZ|Pbn^T0 z`IUmF)o9`J?t2u)Pt!(vR+Fw{eo9X=sR4Sd=ZC2LecQep z@K!h-ZeMLEgwMUL;|b1epJnf! zc(N<|U@z~#b@>0At-pkJjmJyt{=Rzjb7jr#9*JTZ{$DRol)|2c@ z^TgWMb!J=DxNW%^*V@L{8~NLow~4Q7)jRD){GPCh9bVeIV^iU!DW?5RJ3`xg?_476 z4_hHNZB*lcXL}*Nk*zDc2$uEgnB;Gi;(fYTT0{@W&-7Ac_OV_i8No&=uE%=Lf#WQv z!s3k3V?Q~zN9jJgiwc)v$t$By+#c1sB#Sjl6thlJQ;cpFN&5F46|{y>q9a(}$ZR-ptI zqr(3{sR^3LNG&Y7rW>p*T7OTst#JMN88E>#YC!{^u;PechtE+dXBi&b=xuq++y!w`|Ntb-zKjIQA_OCG0{V(0p)r0(V?O>%t7w2o8$c4d6 zFKKp?3%Y1FVJw+?&JiC7Buh9XutgNX1-}(#6c@xw_U?~l%qr+2MY9igu|w$z=&rBL zo$`qE{KH*fH0ySEQKYArc5ydn(^A~cV-(^7<^>zIgijuUaSX>^)4I;lp& zNC+C8fL~#hMx)rt8cpk2qin^pEw`tH^aM7qC&2_MFO?G9bxgWt1+?k~1vVdZdhs#e?JmtjZZNeKGbc;s)BS*xo&| z-_L~Ghw<_6_($Qfb`iRJQgT%thCFF)rile9Wuio0;9?V;ba6Z&4Ln!s%CRme9|_rp`z`@fJVd`@2sb zA&1*H@!4H38iO1@QSZ=4m@X>8dS`L>k-EFr=;jT^O_5De0Rymtyei$}Tx218qp*z#~lHbr>5qm4L#xW~d z7QjA;VbdDZJct8L`=oi&*uEMp4K%%;tq3%}KG!q3AU%)(4;SPa2{+{?LVv_#@+a1RSR;ve@C-_<81%$?*t}xUe5f?{4~iECKZsvDxWCdW_<_3C*j)5p|AJ6fdi$L|b}e%vcyyQ`k7uta>_hGq!Ze#Ia52hqwlnpc6GuNmF?j zL7Z6#XQK-!KK77r9{F|Tv_G}!Rw-7+`$&j8F%c(lI%24GgA-YtFknizH&u2KR40O@^#beZLVw-Xa4>WTaE-!8L!mOYsN_OnS5Z}%ns!tq60d;b!ChCTJmv3buX zMX{yAlUvk!=6r%!nRhDZ6MELeZlDDof&t-SR-t*DSd8nhF@a-sUN&p5Rk30>++mMT zj2N8|&^d-#HLj2E&GFCs<2%}u0$IQApWo31@7+JYi>qZF_nGmXb#10+-MO!@iz`ac z8XqxG*u@z|S>ppXjQ^x8yQM8aBE|(JD_c^H_+&u3x$)VMH`D!r?>sr~N@>4wi|4M6 z4eU}W9pdyZ0d@{7i1@2<-<-QvY$&+a$eW$IqS(kW(r0TcWT;-)mqdP{R>uU?fVGz# z1}xH+IE*An#SXu4gJr<6D3Q|xF4hiHBH|cq6s6n8!8oC68DGz-N<4K#{yjA|vUPiy zJBb|~KXMG2ET2@Mnb&f5ggr1x_ ziv5mw)#lG8=Jujr4bPU0ppl3BB;TJU%C!&r&&j40BemA{@Hd9fmlz(hfyod!O^G{;GW1_r6l@cUOfwRm%?4gKTF8ke}SK z)!)f_$dTg?|7H(jew7s)_Uxz7-DgI8K+j^DM_-4Z)PIr@3>9bg+)PGPXh!=(Z*o0G z`}OD96rvDD;sn^`>rU)P`O0hFNo+-@2=_-&=`n1W*Q+B}D=Md58`}-93;jA<-imrf zX@1jF{mAVNoeigjrScJ4&ikR3n5QdvDtz`V<;Q4M;e)tuAle_VA zFh6SN0cyUN3eyjLsJguL;lXc@sxCL?E9nv-H1$@a-sjebq2AFJPcLIB+CE?N>GJwl zTWAI;n-4eo2b*tG2C#BCSm1xrPcu5l zt`X;T@w^(j!rv4+a9TbaK7{*L2KwPNZO@8&ARPJL*Uu^CcnRljs+H8xbSu4~A(3vw z-r?;WWh*_R!-{7{JYYUW?{N84! zM@=pe`B9S%(_0%_Xr-Gfw>EH?P2?*V(rx9Rqj(r!LYSLbjQ_y@FK>qR^Dktnm0xiI z`TiJh$4xv9oMXnGxu&smzZK=qb)rc3u59QaEB*Y&tjrSmFiZXFo9D27wlpHr^H1u8b#H{LV{=QED) z`h6XDoB1{Nvf>M>i|2Lp@|m`+uVlqiTUF#s$(B+?G~Z#9xH+$m7%C~^zix^3xC^M5 z>B>18;&BQt^?9pRgyc4N2b4v^A3EDAH`{5JZ)l;JZo7~HE5Ctc#S}32Z;jUeCet2A zj;VZ#srabtff9wN{bjgK*zv<0y0&r5;CqR&g5y{W9a*Nie#4PZ9s7 z`P68D(6RshMTl>k71x1(p{aR4{ENN(lXh6J4EHm}hsFI2Dcptph|AwQxR1@ZpBcaw zXMVF;hc_)doxuCX$pN3Sv8kN9zVbYK)(EqErczUVi46Gp>c ziJ8j>)8!a3I|yId=sU`*#r8qV#?`2t+<^E}#C^qAKeT)dh~`!cJC=~eFjtq;7Uh!d zG3fls_f*96R7JDCPB-YL4+iR~KNv{le3;Zw zkD1=8MqIGzQv$6#9pG5Ql7!h~ZLP99WMmD`u(jIMAwwRXk@CW(v<&&ij5aT9PRmGz z&j|G%&us3Ukvb!@$1_KIuTQP6=XNtr-<}$_^>}7c`u29GZ9Q68Hd|EjRmZn&J+50F z`l{2Lw(i%huKcRgDqH$>ud;>g^-#TTRb}(&hzmu0X*Bx#UAJmS?-uO54ZU07%rFW* zh1;yKA3JT{hSWNGrp?>PaUExwah#=U+B+i6(ztvYIU%tG}4zq+RL>>M7DoB^f7H|vgn_@9Y&Y^bQX}LRAZO<{AVLIS^?uk|NIem}2 z6q4&3RwmbH?$Y~B4WZXvPp63SD@NOpju&hvW2$AIrBA3CLP-l$u0=Uh4_mqycPXXh z^~Q0%Gf4~g8>@ulYYiZM72-e_N@mxwe>Ya`>#8TOy2E(43La`ZfyIL~UeovY2vHtn3V~mm1SsLOI zj*U3bir3e8E0-I)-UD00m~k-8w6QX_ASPEb;u3QUV)9H|)^}lJpGap5QO@7gC#6WR zk&jX&b)3IPJ#GFTm7HFqX2n{MQjyNK!@Waob*b8({i(FnvI3WHhzm^j)zeM;3PLv< zhHxSqEC>y%RiS{06~#1B2}qm|9dNDi@`!#j9?I&Dec6lFKC6&p9GggvOjG$>_pRlT ziMGUX1J5zZN{^Ue#>KTNc$^b4<~zEzUGX(&Fi@{(H?xi&b$B}ov2d``v7SQ^myT&C z_IGJg7x-X$>)wrB%)=0#%CSjHc|)|^1op{lqkIMF8?^XIngP5exmyw(jE6c zIX`MA`4UE6`Qh1T++CM??cpyA200|}Gtd2W{YWXt@pJe;(=q?3cSf6-wUc=0Ob z8}za*U%cv7f#zs({t(%YSEGKbqgr0_vL=+)`{w!C53+@T|9 zemI4Go1tReGYkhrUY`Tejc^egdhp_lE+M01Xv03oZ4|&fVXczH{Nhh8sJagAC86P; z+M83}A+_W2lQ9%fLNkYc`Zu0WZ28*oI7$y`VeUtRb`hArXto#aAP1Ua!pq?4@By@s zke}C&q4br(y!(D#M!7SC#ky-V1>J#v-ucT_sL+}HN$HBfl)KR{2(iTXfr7IF%hx51 z$MYBkZGQOx<8h6woz+h?#`xE}CvIPfRbOf1vr%pNbau_dkXzzod5PU=KiD!W#m&y zQN?twQ@kNZgFuc0XNS2jh` zabFTodm-&2UsBqz39HJNv5nc5@b&U#0pXEll?^;@#m8H7xg*O=hb=bEUU(e+G8?}b zQTMOHFV^_Qb0m?zM8B|j^$DEMruu|QmaSknq_>iN#K(1Pt*RL}Hgz^u@phzG)-Uv3 zRM#sS`z{J1{ffppUF+${=^|oBPHe;gmqT1f561@H7sDM{EM?22)DwF^#C1e5TydCN zL@T^7acZK0+@W!*c)p==2@Q=Z!RyG5OK4~ZP9GkZ&|xW*=OrYkv8xi2C;y%mP0W0M z#93C{&_XM{MlBbwQ}!2H7UfRhczKb27bKX&cDD2snaAn5X5n~L$Wy?&Yn7}xISa>` z+wgSwJ)@1-TR0z1mhnc6F11aTaXd!PKaX~#ueMEg*0IIv%*lm_8^QT7?p(9yOGxjL zssEnp13EojfRDY1f4AaW8k#{*8z?t)1eKYpEDe41p8eao-8^raeHrO_m`7pP^kr@z z-FMaT?nIU7u(8{q+&scO5;`7F#RHRuae2c0o*Yjb*{VDDODLV*Ylor*Y3MfYcj`xe z3O?8TwCsK7zC~`d!;?0E`zMHg8RVxIUa^1eaOh~~|2jWWHR$PQwv1LZ1M^t+=t%JH zi5pjvVbE3UW0F)ftmNHK%e-h<$@lJg7rTvCZ1{1!JC-QpE4A^JGScJz^R69dVkw+t zh?mFxH1IMn|5(BLAVfudJ{-#N^GjlPAk`#wK)?ttKM%8^a_5L6E{m<1*kw`Ll0G(vm8(M6o7u(|~3HeJb z55Rts-)_3lG_KZz_Bg2@;)6wT$u+7AB@w4DB(^uZ-t;BQc5oEZh1x=D|5>x-Y}n@0 zW@B}2mC9yn$5s2ME%s)nv3>Wh6AoaLqCd4f+TV9ro_*nN9{c8uxXG46}{ zpIV=`UO2GxI`)j6P40h2@>mXEH(bQS*JZJkRXD+&eDOOpxbT2Gx%(U%{3uNKz+jEt z5O16lbazXSwRA#%^KR+TQ&+5eDPkMV@)*t2(`{6!SQ6AQj}2xFw^UWcc%!Mj5?ZbG zcKd%N;3vb=;chU$ZyQ=7(ls=hAU{*1O^w`AdSJXzy;G16#bSu7?}8c;6I0dahS<{w(jC4Q2usaWaDc=7rA~3A3vkSl0CnX{T-r}RcFGTtNjYOU)vHosIU6|r|R(D zd~`ps8T75r^Njx%K6dsJtdCZ5L+8)1n&AG+`IPCeL1@7{Jb z<|_~NIC4fMUu(1jwD=g;ndoQQbS~OKd{yD2xZZc9OJP3GNNV1+{hc{Kzh&jy=&XS8 ztR0E;2`YcSo-i@@xjd@1AnAbamehZz#QK;Aqs|=z& zC4_z#zoA;DqTUntG>`jvsx0YCQvArrymI~^-8;HjH_Fkmp4mn1X_SHcS-U1Es8<<8 zfpS!Q-ud#E@7_oKN(y%-8-BG(Pj7tDt6<@r4f~Q%9YlZ8He1a4RVvm~!>?}kCqX}o z?`>R<%DeCMGEV;c?51fxSdG`mu|0f|q{BOANaZa5o@`b4SakrNvrXpvEgyyS=xs(M zPmkV(KD?Nl<0>(~Xbz3S{gmOj+rnQrV2+Ml{L8?sd|;Tb(R;wegz7lk`2jwq+c zr653pgrc~;!V`EzVO3d23>Ke_iiwJLU$_Pn`DHBmZ^N6a3C^d%ISCqe5eg^d3XO!j zSQVne!`>4y*^Fn@Sd9FVgc-@REH}!Co6JbwmC@g=r@Bya4j#+SISNZ;5`ZKOmC zn_6C*hInwlI7o`I&1U9o65~|FUvTXW^;_r^-ez6TX^_jx^3oY zExN{I=`?dxdi3EVd8UV9cDn6IS{-$ceOOq6#qeakn`u^`%-I}oU=!HDY@CnTLaixm z7AT~qxy8k-D_GZAhzaTSG@=5y!4O2xgWu7Cd?kRlhX!?1!X|0>#+xR-pR1MpCy=c& z4m4Zhzp@J#C)n}lwN#{Qh3pW+KdTbA--hg5y79M+MEPc65_|Bsf$SQ~H(BQ$JAXU) z+tm%cz7^8%JNV`Gu^VsQdEHCT+~|cjEopJ1cirTj*QQ3-_0+YgcEr<8UE3C}7mkT$ zlkG|DkYrnuot5gE$8kStecq|TmRYr?N(1vw=}`oI5f5`5xY`$Ssfb6Y?2-Vh)bTp? zUmOR8@{-_vr7QJB**!Q8j1547nRm3522TfO5@hDg{5%;=gst^aH0ZR2Ava> zV}{W(Xv$~FfpU_FU*Js&0FNfjjvJRj6A>W5*p2761 zs`hK6*~kyw=i9oPg3+NBAIST~`mQmUf0Uv_`QA9FRNOl{w8}uaqQ25clpNAHzK6Dz-6P)EcaXC3+1O8==zQ%nH{hGJ;x1zjLfwQugV0#^%9Z?JpspMBTst z9w%KF6|_A&hWURdbh~TtsM)y3ZH0n#;}TWPcOC2fe%N(0AMq zcI=C%7UPlz^SpiiHib^2KJ?jAQV-xq|Fum=m4-KnOQ=6o)hUn5q2_1#@Dwv{;8ZKF zHBOY9`mnGRPQ>$+PW`88j~b;cPVezSMAWuKY})1n+JJanC)0hMHW;5E-q$gwzDka< z8|nKMpXF7?PQ-^4%M#O@=foRjNI%!^R(kgm&c9<$_t}Z?_J$Ux%*NqPw-;-%@i3;2 ze7thZdgS^lWX5%rxg2W3=M`DR`MfpMO#Lr@m#xP|M6Ez<5oeOlvF`&ZlO6L}!FtVf zzaQTCV}bCd-?0MaZu-%&e-(Rkjf<^V7#`=abQzo;9@o$zR{9VF7a+*5@7ZyKiPO(y zHZ;>pk9ZZG73F-EYK;$ybd?%&|a6O)9#-sgA+=OewL|FF^{USY+I#I71tW%$F)5YA2PLBIxEfCS=kNwj_(_|%vl-7=}=-kom+MO}nla66 zpu>u5#cdaCnCvj3f&XNp*HAs>0uc|h99_o%VfZ2y$PHa*rAORo#inZ=mM%+sLkFXL z8LhR~R~K=qnj6jOMjf4q32{TkFNXApT#VGoxQT%+KPDOTS^Yo{jkW&WFN6Bw?v@eD7XnJq$5^|_$COx}eMf&YUC(1d0@v5)Bp->3pDWttT-^ZAW zDnA87xL?ufjOr?-WdTLZ7d3v7CzaQmeQwDoWAL&m-}zI2iXp7S67zZquJ>PlGiofK zsn!L5`a9oGxq4k^bUGm#yTsXsJ%e#60bEx2UkQKB*PRb!MZJcaA#FYOb)JkvVCJwN z|K|7OK)^VUhlBVDvRlWG!Q(ns=fa0?{Nc(+2tTiH%Ecq$<8!2I`N%IwRtJ^9+e(hT z!YAzfMu+?1Y4N%H^}q47G^0b)H#yF`?@T8h-Z3Lo&KmJm>)8L#Z&z@;##ecI^nsdR zMUVbLp?+w$$i#gXo_lmS+AMgDm0@VT7>551g*E5~aAJ+~^95E~3~l@hD~?MOz2jEBL>nnZqO5Jc?a%D^7<4_hj5?y^s}P zk5Qr`U9)Rm;D6;ms`VgCt*$zs*hr&@V-e?(Cb8358>LUT)w5!{Q%fFOhzZ~ZoAw)= zDq>SxMXhby++1X<@C>e}!Lh5zX6ZKmnbp?*P?6i1n>EOE!XWz-c6VUzZG)UoNbbNR zlOtZ<1o4JPCJVEQAXd=GDoPvPq*R#QinXV)%5ZzyrX+ZUP*JY5uR*M&Mf8d|JGs_3 zOOnclBYv}_tZaBMuYF!w32U2Ix{0NE^Vt}-WO%PA;q=BrWqMbKd|9$Uz%*?;*v6XnQRE;-12LFY-lE-pQ4+@>c zzCxxCv!S~lV_!ZsomE_?&Ya^@R{B$tncu)7D=x77XC2+H!)G{#@4#8j*=AC43&+wm zrX?@~F}t_;E8&5eUpeL=J}~ne$DFs1HaqaauM%6mmi@u&90V3TrZf$^CG|_@_fq>Sid5UEl-HhAlDyO^8k*st}{5gz(zY!nL#@dQ_ z);88Mxxyym`r2&CYxwxLxs%@_EN^F-Q0Pv6o+!goXZH{mVQpa2EO$4gXPZj3ZfxG| zMji3SR_Rv=lhj%Zkj`-(HzcWpIo^<LaobjN1}B4 zZ#jNn{7W<1#J}}EuG=0MbhIb=boq%=*Un`NyX^E$4tyqh|fd=<20wR98L10Tp}-7Cl8 zL%YO@MT!!l#hTyiZGO zP6Pj)|H#&ZFCaI<@>Zk6immom^+FCY%f(qXi#X6sZ}tba%rwizT*!cx-#|~LnI5sT z6-P|9;?$jHxpt=-xW_ z*7G!PsAN5_<)zlkK(nUUE~skd`~HW`v7X=ZV#{`8%=Grt|BFtu9%tFcGTBl}GxPsY z%`A7J?|=08Wb5bD*xP)jWJ@jGj3cI5fAehAflReJQvQ@C>Lk1w9> zYYM;a$%NIbRES~unI~lGFmgx&(y_@8crOIf3&IGw1s*_t)*5Rah?^| zXu~xk1}4q35#}6JrN`opSgd^>F-pWXwZ18FF^|Pp#zrl&ERTzMvOD47R4E zS4Kx2DPW_bp|^WT=vgKF)U&Y0X7(cmh>u5%L;R6v;S|f{INOnefM;QHobzzOy3Pe~ zLW$*^OvJ046Cyr3u5&?$PVHO}WoocnrgpZxZB7(!xo(*%yzAK1$#Y_Y&K&rO zH|T5(hqDK#>ceA$&bUc+)e)y7oz+;m#&K4hBBZz!(sle@#C9+nM(Xk-8Mi1gnh~Yq1Igfn{l0Q+jIpbe7 zH27?rSBYR!eaVXJYC}0JBJ`>aQ|ZH1@pMd)6MDc&u=_k1Whuc@#4!Cr7ROT$DN9u` z%A|)q3l~3sRu$!SbO%bvZiM5rG%Gbb4jRIMsjbF=BHb3K>!vu{`te5Agw1S;K4CN2 zicIN`d0n{ zk`s;L%EP_bEMf0I*3IR$k}O_Nm11IEc*?BLo8+-lg zJ$^KIj9j?XI=@kZkq6Xqa}Kb!)Msq2QX1Yo{V|YNs6hQ za(8NHhnxJ@zLnOhTv_Dj`z?Ea`YFikpk}5OYWZ@z0kQfw`gs5MsSb}rpS<9!m&m}# z8Q1O%Ki)98ue3^08~bSL)E_VMjqstpV9z&LCzcHPNPyE{d_5&sN1Si_ZU7;_Dx6wP zfjqjYyvd@Ht1!Oj8g&(Vw^HuQ!9_k5)G9cWH|n#k3jc`{P^AP z`f)`*;)5@rWo?*Ws{(i2^IbdN^Nxd#M(-a+50z*Ajj!kVQ=WPLyRX=?C)dSj8keKyk&XZ3P73NGJO?S)S-8xV;{MApl+D1Gj@n6oJSsB= zl@S-lv!K#}sXH*Z1@~1jO*(f9Gj zWG3g!Dt^4_IR2cHRrx2VcuG?(+f`v?CgMpEtF4&zcq(qLrZmgmb@U^B8e;#=&pW5H z<68@tvIQR7(!DKVOySbK&1T(I$8wHKrNY^Jn;pEXG`TQ#VYBc(?5eo%J(YEQGcJ5h zWskzxH{;Gm99-GM=0yw}u}*m9o6>{M%I=V+uz%}AQ(U3$jRrQO5f9~ck85Z~Gx9x@ z7as>zA*y0C^VGNuA;qDGpgJ?1x(WNy6#UK9nf+@V%dw*s38hw)%dr#-u{D&e^z*25 zd9u-;RTNJX-!XOQ-Co zIk6I4EX|2^9O@GBzQxj`98X&;&5D%|#ZF$FG0QDatTC;P8K))Js>p+45f>_QmJAJ& zF=#zG18+8x(y9>tbEoi?fsNwx0kE#f$sO!hRP zF5H!@N2f`9)Tfg(-HNowXS$`v*&=<5udXH1MiJMV<(q|aB@)!e#nw8(H;eWmj+m-* zTt-4qDYea-MTJ^53A^!scw{ogC+Vm&s;J6qCT z{x!ex!G3z>O<_4)EghY4^z7+yN_CFc(fa4`$J4RCe9W|w^6HU;$njfv%ZzfjVJql- z>_g0i?7sZt6V3I|ht7h_) z%lG?#B)=nc?{&vk@%@caYkqx!G+ll0V%xtlo*;DpaEzDHqC>BKDorG9W+=aP;-!2) zBOKn5Pr3Jn+MhV;Ar<{Odv6(vexRYsqe>p;tMFHawd!S|DvXDzYJqj(jLU*mXqV&p zLcf2V$GPl1b?_G+pB)Z=o=>Gt%=g(A>&|51zQxfUc)4gVYVn|s`yTga+h2>r{f#%a zeshS_3d(OHzfWk&ZFWxN`y->?{I%61xUD=RrNyOu|0KWZ(SGlD^kaRR zsv2RmqFm^45_#o+Ld`03J~^%47ye=#cZaF?GF2thgs5{h)Krzz z*&H@8RUH%6L!HMqV<)I%TEL_%%MGv%&Fi;G9n;sYu)E>$ew!m|yZcIrcWHb2X4G?Y z55zrpY5VG$x=UNsEhBBp64+}H+@>wMdr+q-OSTIq!j!wSI!!Fz?oSyh48mm-i`NHJ zKF|98qM2*q@b54BfSvQb_k#@fNAHIj8|>`*jE(jkbAF6npYfS+APmg;EEY-ztFs@v zg(WlH0|{}BvOAOJB9;|Xf?mYvct^34fj>3Gca~daxiV8uwXBV~x{V(0xnW~&mOY~Jkr~(cofQjn+8A)l;W6#uj?Kb^ z9p#6|v^8!~cf7JZsqK=9r90ky2Tqum11BC3_8W9y!vDkGoxn#;?SJF{CbMOx(>5(F zw55f#lv2tzWvz;mmI7r_2BeCLSQZgcuSLBA3N$SR%c2(KdJz!^L@w5?fO@@d3>6gw zwIV7g%CHJ3GRP|1JfCxt>0Uvv_y7EU&;N!O^X~WLB(o%wNlwmE81=7rGHM!m#d{gq zNRPZ?X-2l6*&<_uKe@0;Y|{*HesZD7-ZXWiVM)HZ&?GcXEy<^#a}g_ub=~4zfyHid zb0S41E*dTlNF=e9uBp|c7#5pMi((>mVFCty7zU+8QNatHFC=cpCysky;>DpwF@LH8_#MdEqSJXDY zq9@YJwVDp3a>Y;1CIc0X+gmr%uyZ~0c)rdi!!CJRYadBTUGk_KRBGx$oR0Kb`ly?R zV+}53r@=f}pplU0+K0A-%ndcoG~!HEPlq3}`EVALg7)^(V`yzCaR->|>SXCC8e}e2 z2Uz1_i=bIzSbT4dTHyN_pQTY8v!b}RMMhjSPfv$211Kz^Xr2}`kiw#M!=p8)CJ&_W zZn`10984kUEIpiCEQD+Jz2(kgp%-_aaJ44vEf1}&%dW@OZsGY{0yAQ7*N82`iZvZ} zp?KuhbAh%Ov3H=oZp0cGI{eLqXOnzofr93-Z&O})pu%_IM_eWxIfL^M%nvygID8@F z#bw9e{NmydXvG>nJ?YE0Jf6H4|5Ef#nc@(`HFK&r*U~5S=GRkOE{k!jd$`Z(u2{}? z_ag_|!f=gZ)V?GPUin5sUbE*zZ!-m26yEMB39NeS%TbjUy6t4(#8lK@4ZQl{@v-=A z3DtzJ)Nsc~fpdW!)8%kSIP3Quhq$tpZ?y51rIjt$tgXEcPubZ6=gytR`XirP{ll(x zZw10Aa2_ElMPatqjXC1gX3L&&SZMg0M_by_rN^}Hau4KeU~ zLY#OhRa5YXLRVo25ljt=p9h|fYptM(8uFtvLvAuN2PXs7E;C}ocy9Y5-JVtUT&Af+ z*UYN2u$0Fcg{7G+J)=-&J_1J(U0K5jBWA`uk}-oB`AGhZ=tTH;n9g+f4%!W|__f-* zTQ~Tn+PgJ0ovar1h7fxPpj{R7x>{Z^;tT8b^b0eBj%>&BUzjm+0`mw=GF%Q1A!fcX zqXjp0MvKubmY}Cvk;NKbmoy_vg(S_8u{U7C&1kOJh{(d{|Av@#Ig?^!swHl`u~?MV z815o9J`XQN47ylN$g?7@wq;%lk89~zoZWI1(j5ynv>erj`9ZJR&g{hEu&w`iHIrHK zkE2y$zec<_vkMMWS^wB8aR0ein8;#7b?shZ3Yt$Cc2n2x7H%SfKHax{I6Fri^l2Y$ zXYt~^NM>HpIF-jreHUy*KBODcQjN#luC3z?rTXIY>)IOW&}ETm2~=vxu1Z)Olz&Gp z#wpolh{bUn=Z}~3;PB$}#28HuFFr>yRQl*K6j^!=vy3>a0{t*1_K`+cG{@(hE#+0j zvCkXE)V7Glc~V|&2j#K$U#;`19Oc0Aqjg@kZoaO?WmsOvHQ9+Bk>3K_<7HddWD86Y zcBrkzh;0wHMxxn`6L>_AA|0)b8jTomkEfGJ5CE&Z1a*8fF;DQcbjM)X@T zd)KO)UuW@m>pZyRS-SG;5gl;-KjzuQWZ8`NEIlIrDPvE&@ojM_e&%btjX0&oaFC466bU`q&Vh&dPfmSc9Zis~ya-iFE z)Ehx=HUoX411yeW3~bQfF{wu08fq*FaV@<>Uc$Ek2Xv;ekpx4Nu4F>TdPtW{eiAfY z^cb|A#X;vAFLT+wRWoyLJ%}oci?(&i#jc7U{QTMQJv%`t$ zNRNjR+&$1HIh*ZClvmk^NvSoA_UUm%Z6!fdSU!BP3M)|5wUWM#f{I35QzQ|K*%VZd zF6*(>$W^NE48&w*CUw+U97Y=Y?9kLODx6y`ZdAj}pU?H1HjWOTwWEfIjJTeA^n8c* z^y2wn*wgFDPp}+pc4Tgz>4gIY(MM-1rnUzjHk`=p-FBsJB3J4S6FL<#^JC!{W}8B3 zRN^eS9v0`vrf^lRVCLQ~TGUwUdWjihUhmv53SQ<(CULbG+U>j`3R(u!s{1Z?f9Nw! zS$s15nWho&bsN*gl?`LGeClR;rfH0p2cK#hgCjyWQ*6*EBj!e%WNuWI6eZ(hkHWyh zOj#teA%1RnTuUTf&g2~yLXul>dWWHNYNi{?f>UEg7{&^N5DQYQkfXW7YUKIcOLY*OzwuYsq=(ciK@$D7^=pbSRCq&4-pgHN@F^W`CP@F==6>V5mR-E`Otut zT`}ye<;lk_+i}`pDF_XD_i)*($II3wjP~6(e%;wX?DV(N9uMTc_wzIUS9_hjYR~Z} z!rk8R0?Uqd?0(J*?{?aq>&|@nY@qc;cjypHLGvS5ZTtLM)Elvg{u8?bte(pAwwRVn zJV$TA`h^Ea2Fgk8V>*NWKG=OCjlSdkm%X^|2-^4J#B_7J{bjGdH+0!crth);VjQ{T zi{_%Tvx7IrJ*}b}uj@8lXzu^w^AXdP$hR=xxp4038_6=n`N{`@F^Z4;=qF6`E95U! zLi%_#^viMWX|z*u_;cSAo+)=({Oi6uaG14Qp}>Cj^^j-ujNOFmN&iFFezKO9hx@l4 zJ$VePMfrr~TzH5g7epoWB<)pyh0k^uTW-=wmy60@^v~$^$pG?n^7s1wLNx1do%Nee zv*@@jQ-40(@D+7`BH2Aq?&2v8z=J|j#aqtvwJ`5A8qmtrqyUo zO!MxRKc|y!dT5Y+Es9a-bfCpFiq5A&p9pd2*PULkwDJDbmcQco-r?}jyuWN1N^TY9 zBi|H|icj>9PB;(XlVEPdVk5oER6T#OuDv*o$FYqVQ{0iyZ&b4}pHlREbyddkba#~x z8u0_Q&C_GmFssUZo-Zp;w{KlX8R=C{!=v95~Jr+B#kd0)HrA3Y5&fHU@UB^Qcp3lGeAsr?Om#;PpT5GJ4IeaoAZUvZ`SK5-zd$xU5Z<>zlSB1%^i9O)Cts^_&3!l&6lI0_RM%|w ztBR?vV~zAWN=7V=b@9cdLQ$0gcS$~$v7t8W-aFNYf zs5VK{TwlG3-)oeEgzompgx{O5V^Qvp^p+X;S)#+|kN-*@)=&9oi*c5V_Av2N`*=|@ z|FzJJeV{MD|HT*y3W2XRtOmbF+Y6YQ6uX`|dU>l>GAwX}z2|{5u1MI#)NHsN1^-{4 zP|h#{?@`|;|C4W)mun=VzD8SL@M%tTvQMpiZg4hqy=W3)Ia(3{aO8-IKC%54*%- zmQ6xu9X7iObu&=Vfv!E!dk4!Ynoz`HvINKc2)@wYBiq^7#Kl8BReh#p7^5mY6U22l ziXNeeHhVE%*j}dL>tWAYCK_>D2hvBZS70f9rz-_Lxn8*%>C(yd>eXTSrP6-r%wME? z-=dQJ7WL|R_br;e-)b1f-LyYUZBhKoqD?;--l4mhyJr#fjx4VB{WdK$#HG4>n|y@$ zdu?ws<~OB#oAqncwA#wO&2B)v6*jH5c&mR*v#6BrQOvF>-4hmYQ(7)KHl{~ch1vT- zS8d0+lw_7ZniZd_FTTjLu3czbrgh--o3)zC*fF5EJyZTi@p~ z)2d91fg73SBP!t4F{Ohla=1CngqTvUB}SGiazv#{^wnOWB4!d72NhD#i*IdFtijW< zJs_Wg8es#h_t}m$nbTK*{zzt?t=!Nf>v6RXHs9ir9oL!GD}6jJm`TiVlPYFviWF*6 z9Ss;`q(fr!W9(4HUSqeC!%KN?yVdNdBp)-bk`C6ixe?dV%45N7ZCOqmhb)WI^+Z_{ zSPY|U*_Fqcp03@a-=vM1o}M){BhKg^onbB!JQUz0a ziI3?<-&@ux&5DLHCpIg}qK0gO^i7!Gq^K{f+TMJ-ul#4U@9{Tnfdcc6b!G3p7`SE4 z4i7~OH)_q=ykG8F{_4BmbU5TGAzR4B6DB_$41Htk4?lQtn&JNF$L2RXn%S=1k``C? zN?G8$Q;rjld29jJ?gNu$@W!a8hlYP;d*kz%J3M4HnFDg6A02WOh^=~+y|pgz!aGMt ztvdMFtJ{yCx&M_todSi)mOeIgr!JuWfu@md#+9G<_oJf@@AseIwHu!i8+V1`$nnd? zgXeMt5?}dn?>-doPL%@tMTIs#a4L{6RZ(5>Yquoaj^{tWqb!zT(pikxRx0->xpvFGIFEnTnS%HquOc6vL3zqZ%_EguySe#d zk1zEM1-{o3TD&QtK4&SBr~DzbPbT-RZq0JcOQlq<2~AWyAL@Cq_x-Fpb7xpI{%{qv zNV!V%7x*>%q4jLOh;}>5sDAznfi?ls$YTvOFRCmb=raZEh2*!X*1)d+~ z*0U>LZ(?S$?x@Bb%jc($GE@v*LGR}CBV2~1wT&(P3!i_J;qba@`uySboWPeGt_4lw zagOFN6uGKqNR7#}k*f3iA91AOlc8W3Vb_x{t;{n<+aq*m8_vCikL&wKbkIlixV2&O zBc*(O$+PNcHDYs6jptM7a`hVdvvpY^BPPX=7dj%&GU5TMzWu7qH{vQg^5qBQH=;{V zceqk`K8Gv0jtY;-bqV!cm;D&lrJU!rrZ-~5YJGk^6ZLdiKMq(~Jt|Cf6^(Qe*FpO9 z+bTxB`l@_!g^cYW=_c0G%=0Bmy6KIXW6ZColP@2!F=&_(*H!=iMR;}HK{pt29aST) zYtAg=dtBF*`u8{|B`ALxl#`0BUK6eb`jI#2I-RJSU ziE--p?vXv{@w(6M&E#eMdF+@*8fq{dY!<=Xe5eh&X=n8&!j^^-p?$@+m)ILO2X+-^ z!QMMsH8sO_{J|4x6>C(wu0*XWG;8dv27 z*Qh#skEeF1-O`CVqS&`TFR|!@mL`4hZveiZ*N{qGs2g>r43uT`rGEHM>T!3ZcBAgp z-HGl-O_D_n6U{cs9LCEgc$tQcH8&ALSsPh1#!cZx97sUD46_Lxk_vV)Ttbjq!c?^gfXRNu z2RCkQVhO-ab^QwIOUF&$#N2g&ITQZ0>8oZl-~H?;OfZa}`TR$pAU5pF-Lfg$$)E}F)Wq`INYPUCW-<~tlv zt)a-{&mCgAhV;)J=5%ee8uf+4JQ2!Yw2zutyhn|EKY7@w6??L{&+idM7FX#L#l0+s zKO_&k{fO?^BVHa?yPe0`M;1kjc8!^4+OXQpI_MSAms)B z(FJ$W@)=f%+E( z!WZkv|0?QVe4&y1YyWg5?Mm{5_+3B!5RUe;{=&rX`=AuT|L(Vk51}`9|KuBdI8PUl zza^3S9Qp-0y$0J&Lr>QI=tJ>#IAKNjIN>`i^^F*Qpr9-gv%0YMinB z5{bSeK+$Bp26jhFGC;gt`+ z9*h3AF+P0?#;0fFvO8P=6$kGiLqhQe z#;!-36VVoC0_aRbuvjt5iqj&Z^$Et5Mb}^Go0Mg-n;dpAL^SKgeZklCr>PlhYDBS^ zU0UJf2I4^A5;p|Zv?fFu;s)KmRj%jheK+Xduwi#e6EvrSv#ZP&GHxi|Jv*Y0CF2G& z%;i26%ia4_?2Q)b2D9$Xx{jM^Pb|{mnpiu0NQTY&RC!ij6TxC`%UUMd5g&FfNVH$m zM}&Elal5!Y<4Jggt7II4v+|UTA7|*6JyMkM7~--=W~4rrm;7eg%o(Zkf{tEM&f~uO zn997q`#fbcO|@M7fk`PrI!rQb#fx71tsWel=d^n2f zGQ-wpxQJVBxMrRnuVr>Kw_Lkq2~19a_h{+K3CEW#xf{BUFPW$NPWzzkdHOr;omxb) zve)qapsywK^!G2zsfwO&nr>_j-@tlUTxCiN&n?qD@BcHU#WKT}H}B%{%O8L1`wZy; z=IJ}0r$!7H1U`Pkr@H`-SrEt~U)b?cMHno=8+L4z#&k~1)XmI9Lt@04s;&UB zshy;a@{R*;B~LOQ>OcA;-sJ?jD$kc#l?jH`h~ncRGt#R31zpe=tE? zQR#!R+6wZy#;=lpm#M2<+6uwzQs%gdb(P|uc+BmrII1)wJrc3`Zb9P?5;T#;!!iDe zY`Ub*oAi9nn`}J4^Ck@99@#|UE@(o|o5Ik#HiD#}5j@r;SiB%Y_sIy&9ua0Lell8% zjp$fgp}nbiR+JWNs%3={R~aE<{v%Np5n`%o3s*61nQ2C>G}&Tl)GRhX$6{tS%Mr|3 z_lZh;jzE1!ivpt-1;Iguo z=D^p57@s6Cpz_*OO)kh-#9PsGfkrC?+YyQx>u7(5(25DunK+wJmQFP z&8`azS^r)$dUV}Lstx57iM`14BHDd4y9?DcDy@O&A{=*I1#w5OLhN6^>*8IcnEX?F zxw^6Pi|MU=Z&5jG2g1r{NO|qk&&SERxxUPAn>|V1>AnIn0_7GV=pStF#S&$XaxsvI ze7W8!Lxj#~SK_5WB9WG;&Ke?pLjHR(-fL2TCV#I{APHZkGPEytwJI4tmX^JAdL+r` zY39K6E##4K-gn?7y}V(E#`-%gdruk7#z!fL3wfI)7ux$C#{2M~pRhB}QaS72bZ<*$ zNWf>J$~>Czs3}z%?xn@gIKDyg3GQQ|-!zv-Q?CenL(WBuXYWr5x8lATt0%nfRg_Pd zj`k|UPyL`R?c;FJMSrT>m(OKqzU=k;u?6>7?j|oAUuDl490xEy%a1$DFnfACemM9P6h9z*s%vA{H+TPAPXO-{QhEHuOvj{Y(P z8_GG*eW*o1ALdRc8mF*^CajAM{3VB>vWC^LXJLlI>DTJ%SpA@WG9rwi#CiKtal zLorLtXK@n5Wl(9-05Lx_hI^lGWMdA+{P{@(BB7a?Rjq}^VrV=Z9w~N+xBM+BhTAMn zw@2Edrq;GmmLBbx(UvyVwlLxr);lehHdifLYCc@0z0FmnkDCv-wzpYZ`nctA8+(dj z-zVlj%HqEB=RazPSo36TT=57|}Y_sg1{o9Tz)aM7y z(;R!SoS-{QS7OFhI3$e!SI-!v%Sh)cwM6GzDofYRG8DNeCT8lz#Z10ogJPrcxJsPb zqWQIi3xx(uj=?U5bTswM5YgqB?h~x(XND+L+un$O)50etK7T!Qp&+Zu1rml%EEm@= zkZ?XuEVC z{EJ5(%F@CHg$*f^U{2T&5jt~FFxi0S9DLES*_hazOa`W)di2|mS=tu9o(k<#SR}#m zVkRtvcpdN0f2^ingH4 zA~2+`YZff6CmHE5l+EYLbtJ_3#TEyZnknC0rD(*^sy&cxF;Qt>?ff8{NnBLzPlGU? z1>!A(u-`0V+gIOz8urA+Po|boXv@3i?q3ycc|Nyfwpnt}4v;XjU#}wo*f4%Y)`g;g>pF=<7bnb-b!I9)55BUOt76Hl^d?hzo zJ|*w%zB7R>(~`=T`{v!dXDI4zcG`Y1nFa%J@VpbGR>icGtz9lJ&c3mzlD`Y%o=*`6LOWKpUr0IR~_9! z1zO94H*#O+W2@I3SJhxQC}csw*Sr};o4FHLwn&<5RQpg&H$ z4_v9t=y`4vi~ScreF*hiP;X<;Y8=SvzWdv@$J5e#-+pl?J4okGIo<9>d=Nz)zU_yQ z3X@NK;D)w9lBmMx%BAm_6CORZih0TDR(8K-q^3IW2>vjjWb@t_yhS(Kxei5txYyGy zGzyQ`qwVYUxVLg6dQ2RSGmvZWzbB1GbQ>O7-eJ6TY%Wr6!Z`;U+TI=-YGdQ7gxaLA z5ZqL;SOmd={>j-%3hMz36B11(_?Ds=@L^qAL*Vhk~Y8e5$<~V z5$Ez&b`563!u1ihVayM7XK$!w;p`3FY{SZ)gRL#Y(qMM$KEu+MueU?@_`~bNp*;Su z7hWrm|Ck#&{!?$bx%IURa-hPz4kiv-kTa;tFczzW&{15Xa}AUf#km}&GP4J%ilW5p zQqZ18T&BVm58xzGA8 zX||ECmQVX69zX4s`e6R%r@g7L;A5`v@Ij_o__dZ}dEB;-ZioHwr>DKW_CJsaD|+oe zG4as(a}~XQIDh}92hM$T+Yc9xduw>35!Z9&Gd$l7E1x<}QF}icTJ+Q}WX}6Y6if4U z(bIxC&xVT*SEEuMj)rV2D@-;8)uTopt!%wQWIoqaZBwLYA?}RVF~Ag;@6x@mTIHh& z*0}pzq*RQz#${!YuxGqg%E}O`^ck^lds(y z2!ir_a&}lwrjP-h+^)JaUDc|Y6oWWaRSolMC})Ry5f|4{D>YkKdTD90z+GgxT@kz! ztTClfn=q?NnZ;?dh+76XdW6EamNukjq-%wX+5BZy3PxPZoMjq|DW_1gFx5iM#w1v} z$Z$BWG!QQwjvgQj%WVrM!zapZQOqXg`u41+%Sbnjtny5t)rGORk-^3%!xLnl2&A3g zgF0eIFMRpXy6{fx4xamDvdy0#&{_nt1$XZCldu1=^E%|8JS?m;Zd`m;(cy1ow|QSZnMAp4}tkOukinLp(#7R zacNC|dY|N${-TbdACYU9>+_$F`C0mgJr{n0a$)P87*}Mt|F7d5ohU!xdT8L0F6@5I zSHD>9!+ig>n-dmOWn8Ipy6GHzRr06y=-Puc)&I(d-D9wLrRRlDZ`E)Db2^ZCAtKzI z_bb{5$?F2XTh>M@SYCN#7=41*<13xs^h=!3pk?rJ_S>nvv!nT)cpmRV*Pdn1=M~xg zki2u+_$Lni1Mf$B=D8yysB*rh$!99iEj-#8g`Xi?IE`7>DVcRgTOcJ}BZpe(uDNGd zvHr_X2ewW-6aF~*_u4yz)ExA)y6a_jo-|^$&BN~RJU+#Yaqdwqmfdf1=ac7!lv~N~ zrUyqJ!u5flZeNo4JSlk?|IXA4zu)x0_!Aw;tJ2gE_4 zx5s1ZC>e23)X-t8#j&A|dg}jYk5!c+HT@|gtF)q^3q3q*zfVyMkD5{B|C0Vcxh}SK4qlWHZ)=fPlv17@}Uvibti6L#^Za= zJonokH0B5O>G7zbd4+uWQM%4idHOf6N>z{ZC@9O0BM^VQ{D)Eb5j^v#Aq^^}4|#&k zZ&a5tzf@Sj^VRoBJ>ArpvyAzMMGcA$W%c_Vmy(Qp^<__kN#l=omC@rAn^7&!crit? zLca!e^7N+UOl9+%nGKy)j_2u4XKllbxXSkW{4iZxSo=J-ob9^WTCPjw=~WIk;(PN) zvHZfl`I$_*%WzDU`u;H{zqZD97x4SU`bn5Fv}tEFIYYnQ$FFDC*Q1yJvVwWTJB}8F zJKP%m;$?Bk2m8;PaI#VkjKga2 zc_6Xtf0^j*?O}IPC83}6mI3rLL5r9C{0&PlO8?~}^5Q!Gs%D#*=+CkX+hq1YiijU- z`t-;2qwJes*h=h^$7~<=krz%GDz0H_(FfjF;e)pwaxw4t+qRV&{O{DrCooz>xkf(9 zGA3gy=#%Qy7U4Rw3-!R`V3ceOMu~-+*B%`}*=P^j2Y+`*k#`2lHiqEeaP;-n6Qjmn zK`QRIT!osXSF(P;tX8WH#S^S6a;w!WSXf$UC@PV%J1;1^U}YL8xd=s_Qgrgg$~stK z2fGPlK~accHsjt5CiEF*v#>u=4uYZE1l+wr$%i%gY8zAE-SN#ZGSpTz(z6WvR%t?4 zPcq(Qm0A?;6?{l0?x$LBElP}`&#D}w$Ax3OJiTztM&{-!M;kF$99TGx#bQX|Sl#u^ zjG_=`WX24*C2FW{3^Q}=mYDl8-se7;`hJtlt{Y+=O#P@!W>j{Ft~unvwm)>~A7u_H z;93VV2gufeN1=mxr|ywE4fzwoi{ zY?xNkd3~YoZsB7xrIn=nn4N1WnOr=MF?EIXX+uV^bfIfW4d)@I2=qqF6ly35W+D$2 z8{!CpSi#|hi76;6YSgE?8p)SL#=(nCvI9FWIv&1+P4)S5)7TK4g5`6^56_Qj8XK;_ zxTdi!JvD6+`>bd3@cib?hlc03tnX6KLox1VE$mA?H**(yB4emoygg{KCsL;9c)PAs z&^(a>MN7EmfR>ud{0^1JUs|P@ERiR_R1lN#>A5vQ%!5Oj8rPKPVUmq`0|wR3^JKwd zn=k0H(Rnv$vmW;?+mm-&G29Jju7n3_yPn67`qm#%G5=8yyfvt0<)ef+lv#K{wVRn9 z)lTB0BydYEDQRTq+vzeN685Nb<+zgkgymcpGuBb!y1I_jjJT!=juK^B5zS4IG-WDo z_9RGD zrDl1Dn2Tb9UWqstaRX{Rohq2I9jT)DMoUpdnCz{qd6J%Po)pUG*U@L5WEZgJ@>hQyiTfpI&trVrH%St4Utt%nCeB{J z{?})+adaB8!5P0UoXlEG=l8SS5hpxlr*GYZ<5!dOU(F(TD$x4E9GhEO`A?@0%~B;c z4q6sFV`B9^*_l#cm&S}c>zByy@Q)~WB$l8;XXhUe$|x6cPn;yS$9&hreK7u3sl$Kd zLX(Hc9fE$&e|-k+c=I*+mkN9g@~+dz@o|uR-^5Q|mRuYCht3^A|0V~;Iw_%v(idhF$>--`UI$#;E46xQ=sU+#Mc63BDV^ZI884xqlQ z@9~Gjx6$YOy^rSmhf+izb?qn3$Kk;5&7WdxN1EXEym;nT@;R14kx$nmyB z5j13jJwDoOqk@F5-xgIH+TWs|b?FvzfB5UuxfWDdt(p|$uLc$HR@nS1BS~QiLeM6x z@h;*f#;LJ&{ny&?b7iZpjl)9+%9hC%ax0 zOJ_GG zMtY{Iu;r+}Jee|cbk(5!^%%{-Fo|-N19=>4zRhqKYi}(+Wj3@{$!=VWPg!>veqYD0 zj5z3%JdQO7T@b^bAeKtG_C_SaSSx>E>Q9R@u$cIJwTt?4LRJZOHC)+dNlDNIpVLjf zgk#zF%tS-ELbc;qI^x>OMqJm(NX_bXoo)9x&-`bWN@oPaveg2BtHeJD!DQI^i zPHDsy$$I`8X0sH;hC*#ajoAL%dW`u&y+*vz9x;rqf0KPa?h01h--v6-_Y1^^e7jM9 zyT1L3x`ty%$M{1jB0dlY%^KWgON8fVv|mUT1WPu4FIojiDxtv}tQ0?47L(a#vZJqZGsa<) zP;0}C{=Cr77aLigRn`#LDD$j_DcZ`SRt783K#%8w$lBG~S!E5INkCub5{7QG$QIFN zwV0h&JKJQxXA+If4U1|yY`!Y0CT1QJ`bC@%zq{=83izPV?SUE$SlN0Z2 zQdwkzXPTe_+noOKXCf;XnjY&P|7K+6Yc*_`E4AtFbFHO=rSXT`TFDnR%vz%vpIR zPqoBi%}g|WR-RbX$wpjLLcZ}xZ_r{RJ*b9_hWvRNwiqUrKh_uLzWT&eumF@BS2RbE zS5}4v%_>eOuS*#=x;S0(mgg}`yyd=njxo~56tCvuUL0K8v+H z809Hhz}-`2WS*9n7`116=R8dzSyz@Tws%%F1-h%nTj4Qmf^@vYs3sNHGvi@WwNiW+ z!~)ruaHZN>^f)CHUR7~@N@)D-C0BhqsZ-GK7IT+8j5xgI+>*O=wQ3(^>Dpb7J=j6_ z0o{4Yp*(i#wsR&s#ioTMIg{}!wM$AY-@iZgusd68%EHRpK3Um}r6<*MtBs{Qo?HMs zz%{l|b7-mK$<8(l*-IT2oh>uTKEKK=9!EH`l8_#&t5!LYr>n6{Dpq6ao1>>ohW0Ak zQt^kZqcn7yv$-mnc66c?I2Ca=4u0_E_STM@Pa4qaa1FqD{CNkz=@SxPNaS)46U z26L>SLQyw0iN*@vi~-IRQlh@hP5E18Z5LK{=IPvIw%u=8dr=|(dDByi z1K*HV@`x>vD6R;j&;ondHI$~<9)IZYFpDKO`d8FjD7I4Q&95ExQ28q1;jB}y(~zh> zrvjf&W&MKPzWB%I(S9?H)yK@EP?6n-QBi(zHt^Z>GMq3KIF7$p<`5Pye)^}IX}Ea! zwikX}j$e86Eo%a(Z9q=X3m3oBaHgXPC(tUDl~=^Go!d2{{Kzt#{#M5Sih7`xHvfz( zdTffW9PBU7{1)S+IsAG5xX_AZi#zP1&y3<$ciunPiH^y?U?zPB``<@ZfQeFv4*|7bFw+!3h%5i*5vhV-!7wHqrB;r)k^mjCmGh=;J8($2p>4DDP! z1r~cR(u#e%&-hN^c~I{)As}aq>g>@{B~_ZecbAykl9y0FBOb?`Ebo@XP4C5ey{145 zAw=k-1<>yqP8&5X;p=R2TSz(mqXXqSwC`7G63R1BkD?O>R7QObtNA^=ewvg^)`_E*R=hh|*UbO2$j>KorWvecrYBIpNApC->f zDrgb$Hv1U0(4$%8&OtqnpKeWCz6#^-eu(lVH?=?dBhC`t>@&W+3EPTiEHs>7$Ip$p zN><{I>GPnO7CpX9j}6iDANySQ*Pve-vEd=Zm%n7!gBSqlWNkZ~#6TY&LrLT=ros2Rd(g&4b;BtB0W87 z`)`_R%x@QUhUaS+b-qDc8R^yP+qqSgvAy-}KJfv2d&+dv+AS?^r~G`hr(lQAa@5S*5H4qfAbT_5>c-41nO%%T^d}I z>U?eyN-lb##3CEDG2FaH1}nQ5z(=Id!Q&vzWAn3k8f%}z@@Avt!;Q3l)W_jO(S_A4 zIzmJz{-If{GbDu9$}pq20$rM8czP6Npya|7g2pG#5EOWbxRyW}2O6ZJ)%-zK zW4U@_=R^4$soC{aWQlUJMRa8yG=_2piIgbAYl~=PHECRh>ufJ%scJ2e$0FquJyL!u4cY? z{Wn3EZDjHN-z?i`TBIukEj8l0R=)dU7vvWz-z{tPjR#&`*6J)Xp{&(K&&I;a=(KiF z3+io_*6xC~QDaWi9@923OCHn4HM_Fiw0Yc4)8@UxV(uN)c3|m7bWN8{{w$GbImMDlJEG-m?4P}Kg6zj+e2w6Ixf*Ro;B{&G6E^e8!-viX< zsHw_hkyJS{Q*vOyW4USX__X_JSWU0IUl>-}qIdk2_jhlJ zauPE<;i^e5|D;U~5AC9BNxzZBp#nTuEgK9Ry$3~@NGHcG+w8Zi{B>|n%KG;wv!GmCUZlg_T2&7)yb1Lhg&b#&%we7m>L z({6a@)Y4TXwqoO_tLt3pgK1qK(J zJdERvsUSL`rv>$(Zu)0POEj--MS*0T?qmuDk!^aurHF3_P%og@HDz-O=xgtClxwj2 zK4l%t90hXf)n({FyzE_hw1O3#2^_l6)S@{T{mro>2lti-Z#whzFtcUnkPqzfuUq=; zWaG2t3Q;Fmy$G|V#607QkNfw@&CNa+Xgd9Ehg5dZWxU;T@X z5C8Wke%S8DD>@xGc!6lJ|HtzeeAZn_Hp zz@%S?RNs558=H)E{=16Cdj6~aLDc;m{=laDQx~cGdj4<4f9jk111qb$|NmL>A9yMM zD;D{)KA-<7$Cv*VA9`aia`BR5ynNtd;QIotu}uF>|9|%r=qSXi&J|kCH>f&aD!~|R zSD*&Q6}%U(t}q>cu{72?h4p04#%Sw~9yYr0JXhmy7GDuu4~8|fW0&G&4a@8mO6J4iRjj4#|Yh&Vx(+d8!J#Op-}e1Y9;@1Z%_I4J#PwhEYQA zM7CU1Vedp+gT8O1&uG}$M*358_4$kDBwE?)STyH3rfbQZwbnVhO&_b{vqW8%IMz08 z$$eWlt+qSUp1y|7-`TO2O|~J%crPYqG&7|Ulb^mO9rG)zeD-G-kDsZj;O+Y8&-pmj zGqn5k*zR+E!qTNZ_5Ddt-*S*EZ`o+*ts(7^2dR13+RNoZ#7u%JH%AwLU*=?%SVq+0 zU~0M;)tbyGO>Mui+GHO070069b^DEdlg6UnZqI{#lWu4J)UU3cdHVT&o&SpY*DxO^ z>UDmw?#{uTyJ5Ta+BuNha%V{A^jSnrcG`7^!(zliWqaoc7FX*s;@@doBY%BkSFrWB zb?Lqkv|Nw>^{?iyVSYr=)fb-TG4vz7NwsqkM>JV|glp23P31h6O`aojupMQS_6ZYr zVK&WiL4h_om_)Nhxk2qlI-hyz2W@UrBy#oFUS!J51kaB2qJ+WPRI#=7gT-Vs{6CT~O3ZL|lO+W1W zSr}rmOq_K3CFIY&82F~ZGQ^TAqP)TErrfx*O-d}emOitFUik^B(v5#9Xp)A*eXd4( zRYzG_S%8i2j>E#mz_$gCG#b1_yuYCs$XEuv=Ra9ca){AI&}UZ?jO0`TJugD$e}2KKWxe6+p-ZS$6;z|m;7=`14kDgxo z`7P|afAGc@&6zj7k=hcqAgG|(=63`OuHP+Z)}b{heS32E3A34K+*7|k&z#ofgMYGL zx+513oE0G}$6@h=$B+F3t09fTy$+3VKkBPH5zy7X%?m|*kYk?-ZH>k0`igx^nE&IZ zSV2*pFS}~KF4g&Z2DPSC{=Y3`|JzU+>Xx#<)9|RrZH$^?ldko{Mjw_$r^ zpX|Zy`#ZhC$RBj5zFmZ^#`d|C3yggKDc2b5uj^Yz`rB_^Wbd2m`l6BEnC-6AmkZi9 z?JQqUTa?GL$u8AaFyhAK3;o8!?_HnZ(4Rl3EUJ9%xI@;Ukw$vZ<fF37sROSpGk3 z_b7e&?$Nj|z>;7 zOjn*~{c)rOX0|2_d2;$zu@V4S|dh%eUB^f9$>59^k?&zbV3(8EoA|?3WSW&1+uuGC);gvt65Y+Vu z6GB2zCDxAGAU5=vi;5s2QYhNA)O_aZzlAJP5ov}V=tJzQvbUtihM8kxc$_&V-tZaS zz27zHkmvMt!@Mfje&->gydkTZLcV+TcPp5~SD)l=Uwsni7efB_a@}vrPj+Q7#Cd{{ zfA-BPuQ6igM5J>M2YqKZV$;O3I;QFI5Qy_;lcC7Ob-w6MduP4wYwPVS?zloXWySkE zw(1Vkr9pMP&RWd!(WNzuEMFL^^3+r`;wn*-hs|gG$Z~NAkXVfWY^tlC`lmjs3SXVV zRjHoY9?aiIimo~;+s9({j-Y*avpD+>?*0uyC-L~?hDqZvUtk{N_R!teecWmm@9WC_ z>%Oij^AZ1bUlcdJzDfAK5hC_Q>8>zL)|LOL?TzKzM|EcFmm%6&2Q@{uU&vxpT*rlL z+5S}JHA~*+amyudquk9D2gAAZo`zyvOI=6M=0@Cn-cwv>89%5QrmPrmJLg*yq@PPP4CUs(t9gd-lQe=R zX_ULRiS8tx9#k=6-3?N0v!!G{pVxM`On?>*jVNj+ww)bW`gTu)Y`a0e9qXD}p{KX4>tD2=%2>I_m&fAnLwQN= zeG}t5kcDzDpuEE3w&dndM*UUHXZ>ne`dD&{vp*Urh;qMX=YF)I`~>-u17fPUynyzu zW925z&mNK-%N=EZDe8t{>b0`N=bKGo=^C9>S-FXT^*ELjOlI#-nmy)Ix8%`Qv3| zP@zX|O#75brJSu_FT&3uz4=AkA4t*Yj=x(|Sb2#fu%EZd6wq%5JMu1j>HU!+PM`|J zuwJa331g`=&NRl`a86 zi!?HsY&KJ<#c2u+x1*-2Bh>74nx&91dzi&$#|X4GtIcL{SpJPqjD1VkWQabJcvxko zh~qT2WN#Bw4q9r&hEb)xL)iT2`VQ37udfovcZyzk{lMA|9mvWbqZjISi)z@`QA6)q zgM7Cej*S{RfyLEEPI!aG!xsEyrS5dyCk{o$;s)$kAPC;{2J!Z0+_Bn2?K_$1w#@;2TTi;*n=|WHYY`#m*xjbWX z*k!-drTc%_yA${(s=a^wXC{-SY1*WvEomw3bODMYq<~tHWm?)&kR@V^sHg!^;kuP8 zC@RotC|H3j1vgaGfFiYm6h%cu42y`$g`%J=Dq&F(Q6tLUHvi8#bJ8VH!R7Zn_x1em z@M_+CzjLzAWHNK+%sEn4qdCY+8TJBZ$E;17J)0?Bti@*;-qWk#{$hA1HN*-YH|RyV zRBL**uc<~JLl4adHArmvrW&;69aKJAzvm%ZYw?fS!eXp8-DW=^77|JRHL7mUG@8G= zq|HTmoL9_&jyfC*3DXLaK0)02SfFz)PNwXiaC>4Gw0acpoNn)kyfo_`(@;W^En^-t z;_-;|)p5sfFopy=17f;ois@CHS24{PomhkOv!>FF9r@S~^55b~NXLBV><1IKLr;mY z>wW0G^I`ofX!q%zIW}ni$!!x^~&l~BkBASTWT+YC4Kvoa(n8$CsTIOUBK);C!R$2 zDCAh++eyTNep_#B>!6~3sGjbRlwV&rm>m(~#r&U`!Udei`A}*4UKGpL0-TxYaMr(z z+1Z@Nd}r*TG)DA)^gIszcTu>>q{FU=PSVq#f&V8H-7e7Se3oga?c-1p9{f#)yS zceKYbEazsPTZ!lYr`7Z4)sNF&m;T;ArohXemTnd3 z#Et{0Ygu@<>v{gezei%w9_Ozb?CDoPJsq9&sDJxgZD1x-fTLbeEWb-2-wuHy$EhU{-~w=asH^;{C-^WNA0WKA6GJJQ}FwFfd;?N z{6LTVVWYLD)${e`PZ~I=VmSLiiEh{XOwzs&_4*YZWc>aLfj~mwH_zl!A&@|HH@l}? z_x7$AOeP8arSH66mC!HQ()M4Qu*6^cV9zb|_gX-I7QsU#{<-{@|Kv~L5&=VgZ1p#N ziHhZmlV~r}p3qh#7vtY$`0Iq)5eHh1IPkwNID2y`JA>musSYF^xsb_d!tinETTBW! zh3So!2%`bDBPKmMW!D=Fs58M#gAspd9ZMT1{_!cek~LSQ>nt8j#XhL!B`Q_Ek4?SQD2ZKkQX9mOAtmGG-Pb_MxR@47SsDbOBxf(E}YRP&M({ zW5S0Ul!pJ$CL4cbDANDhhzz9$=?Jzo<_eAq8e4?!71c1ZiT>$0{y}_Nb1wIp(+?w` z(w&){(tQ<;&vInCmZ#!)E_tMM(Z{fjOKk8H#I0On=VtnNd@l7Ue;V_H;DDd1s2^!Q4<{jR**rFX`bQ0Jq~)it&D%H; zA1};zK|^VtM3qVfO|=EFBCv8OtI+aX@cYv`y}lSF28L+ywv%Z7T+;n+nx~ed*X~Sa zC)QzIxwjLmKP^|z(v9ysL7s}X?w#m%0`{b$r@gG8b9v0Qw+4^aqVzUTUsuY#wrCOlel97D=qp;N#UK!t0ORJ7vx}Rl&Ce3wC zwYTcHt^j#^w=k}tnJVP4JQd$>`zT#be@&5U8>Kt9x9P+|Ud+EuryJygqeZT}!913C z3r!`X*9mNS=W+*5Ns)V?Hb6(OpT*U&zfSJ_%}P{T2%}y)^&&oxYx;khWJmRwj@3hu z`-KyM4s|$vc(la)^%|lxyVvfu{DAj8s!aK208xl^ERbA_(0H+ znb9{Y(mI1S8~?>bhm653=HSGp(TLNYXo6zY=?&Z4nm;mO2`aSwxs`BbH=W-oz7vXs9@9>u=?N-_mK=2JUo`tn;%(Y)qC!Qc5(PyV&k2`}Y9Zy#9nprC5X1db}Yo>~Sm_zo<*0iD25 zKY`<=pcfUc?@f~Lld3fJnp2Ebg;SC5ZLFlW?$N~3B*fvBg=!ozt_q)5L*gVuGouZN zEBu7oW{!uNe5P#cTAs!hlsUVVeOc7nzD zF~}f_-m584WugcwlTg{zl6>|bL1cwx!jxnT0j6M)%Os(Rw%iOBYvz)sCZk^+Tkf5N zxuL0f#J1sX@=v%e(-jsEsX!IpAy^Yx{dMG{SE!J z{niRijNtJl4lUnk9?C^+yu?A)3Z0{WVz!Sye2ES5ZPAC@!R*_j$J|OCB{D~jN*!~n zos{kyl{yis)X^9}hMH?4^1e|Evo=s~VLF&*)v@mY;_!+d`+iw?#m4Z8-qh)EL4%%- zTI><1Dchh9&QS$7l$$8(Dw0SAPP#I2Ox|c5Hm5YE;uk)sO=7XYrC-k9n{=P|CSU8m zPt*J{MfZ76GE8?q10LPyccEOMl011AxmLHglDNl`*1D(AWyM(hoN$E}B*>e`sygYZ zL&Vg-OpxMWcA1bKw~6^u^v?4snX=y!4wB(98HDPjqIJ~oxrWX!%plE`$rSt!+X`j! zV1qS0cA_6JnujB}UpA;dguHZ8R{P5~A{`><(9ZMC&6W{Y3Tjggm4aKSG?DXZ^ayil zIb&KjRW_y-#gRj%F*@CjIPzssGmnFcJk~Ywi_7S8M}HxER%6%@>#@Kt>{qMdi&yS& z%`jWL^zwcEI5zt;j|IL)UYc1KXkDTbTVTZUI1kn*>w=}RH@);Q8B!wt+(%I@*0(=v z_Z_+udes=nz|Mp1#T%CzQc%wLOMQnk)a%}Amy8wDS`ya2_o|(`*(K)IYv|*=xcu9_ z)S~x)XzTJ2s}wkl{aW(Hq7_GP#zhJ_#y|&=EGsRP0=r9#h-^6?_`de3fIlOmK1IUw zm;z{@i}I%0Kze-|dW@(0XXZy*`zQ6WzDZ=+Z>g`H`h~~pM}P8O90xi2*KBzjqmBC~ zXKq_boLl_|Y5%G=QuC_!;{CGP!C7KG@E*QR{*uUyUPM}5yw3O89R$7Mj;OZFr(dJk;ud`B{i*w$5Z9M`h$xTE34A|B}pf*L?clDKV~2A5L=k*lmZs|K5Uaa*JY__ z5K|Yh1fVPh&d|I(?A`rISbu!I_x>D%V-h+3bOb%6?|tcwtv63N=J1x^@ZA;}v1@Vo zLVW(-6DvmTz4MCViUU`E-Tq>D+o+KybqCW@ zu|3p%HTYgwDsq{T)dTlo{TUea(dvNU^sv0-^f~B!ZoK65I*ReVZBSvcP2v*8Yr}k- zqUoSc#EbPdaSV?`&ER<^*NIXygZ)^(ES_qx zs|^}y9FIZ5nB_sE5DQX#x#krR%USAJPA@ivCIJW8RC=&2lyx-8)M+W2r73n6KN7T^ zUG9YWk3f7}4L;4|r`w6N-KVD<z!ukm~ zkiYky+Dqu>WZVSv66&O02i~IPhOOT5Jaxt`2{k&@ZvI?%{4`N2NJ;eZo}LaKl6k~z zvwA#e|4HVN6c5I64@{P=9x^2@Y}I2Id(dt)aVqh<$Pcxq2UOLA_8BoRt4=SOc&%l* zQl=1Npq;1?)IV5H^diLynbNL(kGwR6q#xTq6RQ<_-JbR;>GSpeCx}ZWZ*A-JU0gdt z5>BHpOe0=tUF&H^IO&SS%dWu!J@qto@NXr}S8nKnSh}in!4Xje&G=KD_l=-J*1;hNZ8qgz|}SvmXxn zBs~ESL!NjvVK_DGo0z%O`oN)p3wpnD99H1}+BAgLYaGVk(dyu9P%Nic;w<7-_#1?e zT*2{Mf7VZGE^0Uiv#3AjWn)e@tJAm&^&hmJBL}q_bQGC>BsW6R>1kz#gxUvGX@sF- z0yPv8Dj+1(a$xWmbgfFOF`!Wt5aT=y8xWTckBgHsIy`{RgxU=hiu^0>Sc7ls+$yS; zZz^59G-i64ikGUAfgfoO+Ev5iL0{G2(jJvSfx1;wVCvPBtzT^u%Jphm6LsZA#FMwd zif&tbU9BtcFsIumJ;&*0!5^~58L4Ne?U#3ZH)~u6*mYG_X8T#4VMgX(v<@Yv=cP;Al`sM#q!$dpvYhv*c69MHxFQ5~h4=qh3E{9eTjqSu^~m zb!geW(?5K0(2t4Pw9{WOn47-|@us5%n)@3qC+5ga`wMUoOx=`gA!4L@OHfsd)0$}@ zX{v%v6_swf&Q{0s_!u!CP0rXJM^LsHB!Y=97CN=0Hur*W@m14KP^y{4D8RHOhhp)+rnlUc9 z=kTf=+r`v{XWMaH9P)~-%5vdUg8oA z;%2@e%C#gbHGH<{SWPn0bXKaICZ5|4Jl`O( zrqc2qNZ3N#3Wwb8TzYsusEk-adDzPv_Le;r){xb^#IB?*{0O4e=(+W4c`Ro-pDyQG zYKd9yU1gS0-(X%NdQ{`-K49D~d z3L)0O;+3sj`+s;fK=-FS2~wT4-d9FcVji;T=Vyr=F0S4C)g2fY)@%sub`#ZP76acA z50R#1e;F5xen`zf*Cn1NW+EO7B-Hx#!hSXB#0C#BSKhQ_$AZWm1mnT9`@9N0l%5F0 z)LCm*+0Fzy)e}|rtNMB`(YJG!Slaf+ew_coL7Ck!u8RMo?;`HP8}&bw^8>CiQIt1p zKD!P3Gpc~|E;s7W;0>Vv6B15JVoj^(PP5{=pHB<$K1ji5f}J|%aYuX8a6OR%T?85R z3a@=&yanUuVt=g-&Vw0A<*Fa|xlylBxyroHj2))?TuBbP|EEen;q$QJ{7)2~&x!q< z&H=&ZBzMgEyb=S;U_bH8=P}F;xmy+RcAV%(B?9|dHng)fO|hc<1A}#M*q|YBK&10; zc;m2qV-d>fe(dwYmkLB|g6e6`udAYR~c)Ze@Po78H2?EK>=aqC)M;^W3Ys<67`u-k$T` zIPHE74&yQR!IsR|$~}02_Tc5RxORhfe_K#||LA1n#)L`_e=x;_I)mqD{=Je??eQ*D zftL?8isu`gsy!|n9~%E{<<{cpx!mYKW&$sNKFz#bgM2$==9(PZ^P1#8Cvj=_JD=&i z+~}aAwfl_@n#<#Xe^Ttt(`od1R8Y@HCn4w~JPx&m<=T8XAFX$sDb!;;7A5&obPI-d z3p?H~jzkS&p6(M9)>u{8so>`Y{cwL(D*6Z+^`iboy+Y$I0)eLj2kUcTiSD~TE|vD@ zWqdo2(E5V#o9H3KgwaV|aCh=xKg$XQmLzO{SB3wb{zE>2Y}9K+zFMh7uE+|$A|-OQ zf|?9EzO6RaZC})4^u#r`y&xSACI^34BI=1U3a!OJJB@7ikGAsYi#Q(*Mp?B6q!k+y zYd(rT%k-$;K>G?2l^5toRnnoc1u8O7)uGc%VQ7(oIt`Y)%>w>PxO2| zqH2EsxF=R?PFwwW+p5I=asOKV_zSRb_L|3ESd~;zIQuo&yI?$b((Kn_o?X*}OnUUS z?$7q_%6tsoiCFXHMlVZ69&sErFHC1!_eEo!`_l7C#ORxF-Sk}JLK>^xn0l(x^Kw1Ogp;}%VX9_prku%5X)P9DzMOKo zXc|cE2LGvZ$gtp_IaEu*s2qpF^8A}V@4C({xe2L<7VdaIFCChiNp?# z5i|=SQeJ-6*G0t4m){E#);i%uP^8RxL~`^doMrLyPkr!c}yBzARkj zwR}vy16JqDX@U{v~(InsX>v)+$hl{ z|HG4}j}pho7cb1z$BSdgl}+r3xX(f?*KYx|`~6Pmppp`))%yyu1Zck-xw2R?{GI3*Fh5|ak1bPd zgW7(3I_CamRuXOq7~ME8udn_nVYK43=+#yk>x+F=fhlRbR~7rzz|!^GZz(D&x}Wxw zRwH)_&~~SzKHRWu_)0lhiYd~Y1MPJxA;)icqn$i+LDlY4f$!?D8g;5i`wSZ%_~o~D zx~djdm6zx{_Gz1SrPFs(IEMNDo0c3&k%`T(is1?=CjKAmj1Lfp&VTg=|9osR{kK2W zuDv{f$lHFh7ZaKIDp&oqZ_%P?wdcNFk4HUmqdF<;tG8z-7ool&O&sIfLrT+=KA4?c zLfV}A!L$_v_!W9>&$&^5@zmU?;Wkx=aR5{6Nu)&D{}zw^Q4?9eul*0yryh^H4lf6f zV|F{eesbr-rdOP}p6Gbsi^-(MM*8mB@nn_RLl&?672^SVNQu$1=Ka$?j6;j@b#W>7 zweLn_+!qh-FN;P&ChvUiIFzR-|isB@^YQRx6b~V*dafHM2ad_8sew?Y96*P_^a>Zq)TscORD|#1DQ!9GH zxF%+?xT^!E#)+>(CDovrMx9ttkY+Gbhf}jA7VM;EOdPl~Egu#P*_n2?`Sr0uHyP(4 zUUdic$o22u<2BCfcH|-WFms6a;iqY=`M{Obu16ktXo&i7l!G}f%64S(L;Xspb*nfs zd2GMZC%V-fx%4h$>6_hZmRx$5q4dqPnk8@EDVDBC+YU)-b=u}dK^G0wVkkJ$Hn&@p ztN9WdTsTrTw=1IxT(VhDV;k8lG>94o8!qK5V_Jxcg@(Srm~?-d#&lBI?caAJ%5?7; z?Y&%d{0>E8Dou?v5igA+O^Q#E+^$HA;wzBczRp1-6yHES;s_VBcZ6#{ji)qNd{~RQ z*5bpP_0xTmtP`QZH>vf+!y6(b@A%deA1sTI)Z(XL7<_DEg4)b6Nm`tdREoUoF^GOA z$r~AQRN>c*n9>BQshOpS7a(8y3wIx(adhcO60CZO3Q-|<*_riP3cHUT3!>| zM$MMU@|vP18c4O5#?UvR4be;{!gQh9HyJ)Gyv|+dnw&&qcY}8!o{IP`%ei&ojZ76( z84@?nz{(I60Oym{e4!kXpYjuz)sg9V%BT6HkBmf|sp+_a*|pKO;lteNumCYtVdDB+lkU;!6=aj5 z=(bE($>toFZnIm>-jL%8AL#Z{t-oh3oA1k}-YGI8sQEcA%NWtszd@|uV+dl+kMi9t zZgjfKGS2S$$S_^DWJ0lwP|Kl|X_u+w{E}HHXN$5^1t!rf6V5Fz@nhvAAwAX+H4%~g zlmjhZq!i8c-&e=yGqK!t-BU^;eeYFS(OyI7)W^Cb5U5IR<+shQ1HNT}g|Gd56T$WL@`>FuP?2Sg zt^IO3woJmyZ}0c%)n$PbsF!$eQS`glzWYi2}8*B{JNM(ec+HFnwREIxV^Runk`85 zI3Le%DT&_q9`VffKQPqX72Q|*Z<}XmkL{J8^`B-U{&&Af8-w-`{s;SSTY+{Me(zUx zhe`ER|4mCH7bKK8ZS}a0Wrk;&r$l$6&&-aCTqBkz%cfYXq7Ph*Z`Ew_{h8#6w4KI` z+ghV~0Kc~Zqna!ylLv-Gqy2}Oc&dzFq51{wIJUe@JZ0p%w++d-`h-~PW29uyMATF4 zDy7@$2d+8vI&G(MD&VNEs&QZ(ngcPkJ;umIiD|KllialQSV|P;Pj=L1MHM>9;E#4a zYeF1Vw{IG~->cqlM^W#Xg?8D)W%BOMl(EEv`VsL|AI+Het4e&NBvTrJ(AsNp<21yKjqHVye-80_*cOG**TDhFc5cztP z6XF0K2Q~Bcszv#FWxh~D)-Mx}wO4oHw*r5iN~rwxDz9jd<>HPB`l^;6!`quQIF;u^ zoXFz_*K6e_2K`WrvqI(DiH7zv`C2(!P`+MaurW5BPaKVCQ&5S=L3tP?m^Sdl-zJWU z=w!S=S%0r6kKKPZ362o?_D%Rhj=N9Si5@nVkdA%)s0gp#^yz5V07BwwlZ>=qPEXah zUoE_dkduL^I(mX?;cK7WG($!|o!58FByj2auMQ{*+GF(pWovBZy~Y=Oy}{m6SoEE#x>yZWGSN#%%Z-2Q{EC6$Gri8UMKCFSFP6>DH7wH?t09a`+<+Jeej-UjLNBQm@FlQ&qZY+}l6Z6IP(Rb0-1xRg6p(?m6( zGK6b>OpDErm9u;ky(p)cpDJ(AY>-ErpW3vx(bUo5Zy`2f@ZGlttc^5^qi1#>ur@-^ z+}EJMW5F=`k?sTb4T7cJ2WJicCvU#tH`ArIVmQ9hXdXv9PVS}oxS-W1_wE^AaA{KY zp|AE77Gx$>SAMm#P_ZRdmv5#m#G=KJ&^#J75YkM- z<8z4Axu=9eOJupCK2KE@b&b8yVn(&q*^c0O%~e@cuVIyAghfZZan%lthY96I@jYj8 zP?^Uenq`5M#mTPb5-pBCTalKJCO%y;Od#GgXvw1D^689os$+RSVZK;H`qKEt8kxEQ z_M)!5|jA2?{eQo0<2V?cIf!aVuKsIIQ)Tj>YaN>XOHS4~a z?~IJtrjiuz)a=Q77Ogn6SJ^S*%%omXZ^?4m;=4=DRjBLuL8&je`(nlfEz-1q{8BJtd&Ks!3^(AEd_^}DE zp`O81A20=!#VMx!r)rO*dSKI;Ktz3-i8yuWC*yHaQ9ZWe=MEKwI7%Y+yhn)WFb3L- z`BN+W_m7CI79(`UCHjm$uU1X<+%q}lVjCgKu|TUybkH?bvMAFQW9bEUtB<{3it9o$ zyhZx9QFeuK}Os7c2*;PFUI?VjoM_w$w2UyQ7(!U%tXgi__ViA1&kxE|NzpngLC zo(&C9hOXBqvg=Y}PW2Q`)wpi?R0XZyxa_+xaXjZFu2;l`?YhWXI}p6l(su1WxCn1( zO8Tx<#EH`@+kTm6!p398mdtMz#DC+&b{6n>Es0f^P0bYvE{wr+w%vH1YvM+5+XlT{ zE7!Kq?>o5{n@{S3w&ih9-mbg$Us}F)R&#&TVj<{pE!GF!$m5Nh+5Pk_>E`p#a+7vH zchCYJE1IIFz{NU7w5cWe?7s}YZ*00iv3+J!s4kx83ZvF)j~esa@) zcb^uMZ9!ME`D9L(W<`(R*M_4wI@yDodE8usU5gvl)_X&ucHnEUoJZc&+Hf9mOrSmg zA62{8d2hw>*zREZ(>nS6oo?-Mf+{>Fp=zJg+1m+r_Y1h;>k$b`e{kq@fvUWm+EKe( zOP2HbT%o}$*!_g^i6Iti@r>VB&*$sIwQ~A!C%a#Z>iB%F6qHvFNFB8GqruC0JR#IF z_B@6Onxdw^{)-3c;lfQzlT#F;px(WmAmDdF2oP0}RZ@}|@ve=iWY~N9=W|rVQM=xg z7pf$8=YiIwS|u;N{^Q<77pY|G750xT_+_facqH@E>Gda;WqEkzDJS%1S|4&U@J$_& z3Axz+T_heBze7LFv*86z>aq7N`UbDsk`hnjXJ6<*Pf+}K`k(p)hS7QrAKGgi;@e65 zutfU3=(C*mUEU3~8NKi?7pq1d+G6CRAM-2GzxkCIo%S+ZnUaOu0^H`4Owt+m=#Kkc zj@!%WfQM+UA)(#^?J`7Mc_JBb847KTfe{kXYbZJpWo)$283N$fsL&c(OA{HZy znfPymI3l1SCJDvQ(k5tmbd|y8H!2}cC$=z2vS7J4x-o*r-NH9UM4oL9&!5XMlt+F` zm;rVTpAlw|)0NA(1~ZK%#UP$bqU4J+!wl)vu(13l<`TrCY4uQpGy9DeoEy}zXFnJ% z)ND{YzW%{@&9%_0rW{>+A9tAW6^n;OedVo4IlArhHqUvhsn0&AGWR@3sxy62U*L%6 zH=8f>sXgzRsLwXoi^q+QXY<>c#@k$unY*cqW9ESL24if*F|!~Y-FXu^459_QEELF^ z3M7a_#qS2qYs>EfZ|8b4=iHttP7mtlvD?$F^!9P0K@D2XW9mp^P%GY8a0_+L8w=7# z5~9v|%#)Ul?LpLBPihkt#0wEOvoDR^vRl)ot|KyH?z(1XM|oYU8=Y|k6}+x;8tXS{ z<`pRAZd=Ff+=86)>CAkpkXvw;rs=jTXx=p4safW{LXX(4X+W=M@rdDYXo1&p>*6IA z*Ik#+?KIe8ut{~-bs8XPnx#ma^rN$rS+dv&6JsZ~py@{iy>aSRxgTAA91p*jT5&F0j3%$Z69L+AJqv7q*J_r+zbrcYAl| zZ)2Ud^tck+MZM%Ms0-13qf}LT%q6Y%toE2mgUC#=(-rad{KDi=SNTdlL#{_Km6v0AMrfJH@@aufd0RVOIih1 z{4;k3;mycj=UNDrq$0Qf|*P#I`F!px1isP)FkxZ zPUd?y-GFX2(XJzI8r?+CUtr!t-FqVXnCG5y>~E%s9#1(LcfYw4t-n~jlDMc*`26wm z$*^~$k`&Z${7}2+6wMzxlZyRrwEI{hEYKGhlL0j+;%PgMGl3LbHzSirMkP9lLnfF+ zdV}c?YRb{>7POqlGlGuN9(6`@4Lml?)bhre#jISYt$F?|A9k?vt+}m?THH!s&GHTA z@i@0a%ZFOX^M!NR{HVf5&T0I3E#F|V7Kd?7#acZ`qY>73>`DH@jg8+o)EJ&`G=*hiSpUGd680=#dtFdF%j>SwKB-2vPblp=ZvPOoc`W?Cq05=j-*z@# zsJ!@j(8b5H5@*?l=i9_-^BcTOi^GF<<#7w@cz$Yg36IItRCa&j)|5h3we5tYx>&iO zF{;AiP!m;cxwR-ZvUEypNlBY;51Py4Tob#WJw#J(Im??yHTM+uAZ87^7JEZmSN!E z585QsW{FeU$6i7G^yj1FJUA18uI#;}iV)Z0q*PBOA>;0|cEZmPxo67BDn+677jcoQ zik_Fxm`@VucGy3p-hUmshJ$m|Z;Gr0^%+}Z{BdFettec*Yui}*L8lx)HNyw% z13OAQaQ1^;wyW?zZBil+t-xy%{?lI91;3u@I@D|Yg7zAxCn{E50llAMMP1ufgpP7D zg4|4Q#C0a)u}b8^tI>1l_4s!++GiBtFN<79ZYLASeaPR8`Lw6eLbT+#iq=lh#u>Cf zP) z+LqoL2}N}Nt|=x%vuLBDYR8t-VCO_Frs|OI#2lQwgzCJf%~BZdyrf|Cm~kfqz-@?T-lw-;#7BHCz?-H;b6}bqdYLu^TYvP;u5Bx>U-jV zzb*Cg1I2BZTs2=aT=OOB`r@{p@Oi$n^%JSC-1U=g^oPw;m>nyqgZ!5;^ZZMpZW5K` z3L}Cl{-rj=16}^51X_HW5k4nkg`BOjgnrZ!K4q3BVd{NckM?URgh*ss8k5X!Tb9rM zJ=oHhwCWUseL5C1k;JiuiM|AACuPuN<2n)~7IVWK3F(Nf;f|zEWyF-s6ivx9X)KzV zFN;aZGrK2sYU*snuOUCXd)(5WbV+UCD==!dWc;4H@34>*WtEI@L{uakAdIjXB;)!4 zLN3&=ACRIjug|3Bt*1)*?DfMl71G3Yh!w=^oZ~dFo&b%?x~K_0g&1A#sPR58G5W*? zC$Ts>irUX-i<;!|k|Er!9-9rZyanFr@fA~NX_8qBJTxZdr6CH4RpjBvBq(9=CM+XW zkc%}*s4kv&m9uihr9y13T@*|TyFL4+WRuSS={tgO_a@Q*>8a+n|KBZ)AJnG$L6d74KI=WBLdEl9-sJZZ2eVR33tl1}rQ^EEdG{gTJE zr2Ryc+f+-^nZJ?rVtm;?KB&3Hr}-!|g*51*dBo7gjFPfpq^qeTiIv6KK?NQQMlCNG z<;hZCak+LofoU>njzwIcO z*JLfxe^1ZTTkFs+yCx?m&Dryi>xE|QFz z`JPrcsG^}d^?3bEBl-_1dO!BL4|4R*njZsx)L;1TztQgyoK9znzHJ}9)9D}h@3eSg z%PY>=9Z!!-{8AuHKr7>#+vW-F@B#SqzDR71eD%NnS^Ft{z9E|;+MsP%4ec*gt@eA| z-FFrcACXT5!s@JkMZRZ>p-Lnbo_BVH9WoIN@)w`!;SIw5VAu<5}s(qroUd zh&|Az5L-}RCQgy$5LEYauYjW@o3Dv6qb!I8Z>X`US{^!F?+P0(O^uL)%CBZ4ZtF2E z%--B~rxW*Jj>v~2yOZ46t4M=Lb9G`J4=%_&>v?SC}Eup|vmPsm@= zZScHN)1!mt(AYISCa1DnK67QaoOv!oOiosVirr<95Gy8^1iLCG`5ZcAxISv$tsnYf=e%1tHkwW2e7`PR>Xet4 z)1a75I_dL7e}ibysLw`>PZau?g6P91O!Ne@MbMy2i-}A<@3hl=z%GpN$hj5nJ*Y;ufD4c$_IXq8|o$j3rJ(o&whA1=LjkcqC zFDs4mTfwdP{J*KRqvP7giGW zL?C%G^;DpK3U%4Wx`VbuvRb_<{GjNO)snWm-i`G6)d%~>clQ{r>LC@Xx^`Z?ZM2Iy zKF{nxz0^f9??`@>Gj!=KA^&6DiBrUjt35w&-&#^IKkerpE*KX0da4yh)di-}`6ZEZ zub~8w7khL)J#L@e`M^qQNz}<+IEedCn&?jnokGpf(xhVxYVEHG+MsCDpS2CYf2$UZ z=6S827~2gVhl-`irvEMZ)Blen^Ji?ybUy#mzu9*D@q*%8#-^b7_OU6{;Oc*&qK<=2 z7i#Ew5sd%ndcmLe{~0oWi6~zaM{>B7gjr4Ay!IyxiYyaQzAOudYWI zy1oU=g>DanF@M|mG1I0$<%z(N$zyQ;5=&h#`oQKAxPLU&5dEq~19nGMzfa68# zf2aRrpTJO2R;(7B6NznDuM}%r8_sxKjy9cHxHfeVT65Cgxr%Teg!VDskKJC0-`k$J zZgl|8h4jZ+6xuI&56sQRz4C|>LC8t&{~O<>WAxXwfghvnnTNdsdsIo&NsTYMGTMC9KpRa*7ms3wQJy=F%=a| zTSY~O(YpqYw#}=!eDv^w+qms*f#z*6wTWmh$zmqam7z{bNNrR_ zOb|11KB-Z*govtfmOOpo*!UyaR2kQEq2=LOdU$>TPC}#iX>|cfb)Yxu(id&1)iPXc z>vJ?^eog7(s6O>0;j*BaEMBmTxpQBG<7ggn6R(-SY2@s2y?bAixM^hMxSo;M*r|!l z9m!)j)V674(#S+ZG}%HGqm`{AlU#}9ocg{*e)h;DruEi1&2~_TR;(T3yc?jg(RGMj zh&7E=v?((hpna6iDlx?nF&6g68-#gja&qn#W}DnCWy1V4nK_F(SQ45UNrYKCprg}Av8=AR+$D57YqHm zP##fXaYpVOZYy=UiN?(}v6whCnvj=CS#xr zs{P%j(hg@fdc+x(B(PUSFCdN~o4#p7JSN9ab%#!4ZFW??8@-RHr1*HiGT9|8E}qou z-*1tMtNhD0nhuic_x-A_z1QiPQ9a?7E?r2m)&I`t5n~9(hc8KJgX3JQ|KXy44?;bQ zKYxb~=L4+%+Q20kXD!2L4A`;XT<(cGa~M@zX>y;h51}25nStCZuXX6S2vibR`t&5zr9jkZdIoZ*fEf(W^&`Qt!QT|p*>4-CcUn7(v?Rq zD=I1`w|Ht_A~q-LGd_Eg=6m@pIO|`G{>L%iTegFA{AFJRE8kQ9RuSf(4s@x%(Pkry z-VAf$bO&*66!1BCQ15}|Ve(+S_x%cu^?_(Xu2xX)Gn~p)Nr`Rudmc2PQBfI#%V0@~ z-t_*$h3J&<-{H5ujZg5$y1fq+4kKj2lGb0NJuJq3`}vk~8@Z4P%Y#iVsPKHKE}lQj za`tG1oq@%F8=S}Up(gNr3o1Mxs=)I>x3S%ncHE+V$ z?E;-X4s{aG)B06RA77~BwDP;Tdos1SCHM0Ad!w{+LBHTJSKOh+x}Zfo&J)=EbPa0j zi!PU|{6Sq>d7(i!dwjvwT*Bk?Dr;}+LT`0MyMs>F6hd|JJpUdGA(AmFVbdRVBEMg# z!+AbbJI`y)Y-u|hZL;(A&vqW8`20`>o^LQwdt8BA8nmsJpBid1&-41wsX?#d`4Fe` zI7E@hjrL{t7pLE%X$U%s#X2ce7te<(^L&Wfa(}SEw}RH6tHx7nMGs2S-(M&|{M?W` z%XP0f6+(9ZcKTNWAHFti3w83PT`ngoL4CjO$rs?1^c|~c`^{&cOW959&DGejzVtx( zrPrmKskeB#rqd1o{WrFC;OWSHH}9^d{uYR@t${1I%ipQ+ztjKlCvX?7*JyiEv9{;G z%LMH`J6GTcuRF;^T?MVb$VR<}b~S4*YA)zM+7nk#%*jF>MnBYhY?r5}a)AUMVj=t__uh$ahxXK+DbS;lHXT)iK9Ji0=?-(5S zjW;sP*2M2<{LtVyW(hOGRx$#yF6bq;DJ<4puUm?^_2Q|zrKC4=5w*iq-6E1ys2Nun z+u;qwapPkQ0@ z@;Xc1IGx2MCWXaK6;v!O9fH^-7TSkA zJcxPK!-M{b*hCGZ&TeMLRkI_IAC_^$EPZsiI5gv-Sx+VCQlmRPWO*vNZ>qV|xTY>N zcPgfFGoNhBVpDS+S}e(?=-9psO_EH|MBA-N+=RtMZCNZM3G=lnRAfiy>J0Mc(m^Bh zbw+N#bi|_#I_7N+PDQK^y3IiK!1evoXUT)|#kF~cPV$ZUqBq-+E?*a?=?wa$7TeCL zENgjLcF_5eRCHcX#b8pns!(=3!=1-;IOgF;)httUE5c=)W1cO&B0R;`l^Q14QiGQB zxZJMgO)wtw&uw(7DL#E(yb$e%E2A15>Lx6PPa_{i?GAI?$7efG6?}Ht8;wB;e0I`k1dpK$`J8`|l+k9^7mM`XeHgW0l=F5G|+*Tc8a`kz&0b6|@4*bPH2DT4^e(daD78Bx(tqrs( zAkNzUseiILX>r)f^R1w+yVE>fvGgQxvF^~BdV{;_k{*<+SA*>5T2Z$|W zQ_p?Y$MAeB>9}CfSrYr>YU=x6oj8oXiJg;rA9@pU@j74YAIy;^$HZ&fXJYub>ib5S zyWnj&KR!h6#>*_8)?d0xC=_kzZ>*l=6*?p9>2axGdvwAkx_{}npZNv#8G@4pzQX=o zl}8A4Kswc9sgFlFQC6kn3SPG7mDPrkGi2G*{vt02+ z>_C2A&}>gc5sjOO)kB<@=ZS!Zd3lu~R?*lv?}NiZ<8!fm=1n`yv`;%6m5=KlsCYvW z4?cc4lRK_GjVlgwKMwkfmLEWE`(VZO`xzEqYEv6{y#H6Ma_(`_GGnY0!rmaI(n zLR(2z(AFxA`($W#X(B#WTZfET`svy_$U*fy#+MNJcCx0vxrCKM0g0Bv6sndz*R8^n zAxet&7-uW9^VgCxriqfG#n3edhdZ$NoM!WU_6lu&c6ITXAt;|+?P$@ye14ARKxT!b znF`{e^m&`;U_(q>KnbDGe*|Lh9PhgQvkppn=KN4~(oysSWEIy7xm zOg4upMEVt@d3p;qe&@EpElfr0uIiu)j}^C$YRn<&+z^#a%8Gm zh1-5dg84Oc`xex|V}qP9oaTcXd2E#JEN_%!nma~|^#w7gcM#2x)=$94FUI26>QA5;k9>)y7$KnUv3NlYIit7Ozm?bB#G#ri;GVBs`$ba_W+XZ)Oz zZ%?1TpUg)8<3$l&=~igPmYCZlyu@2P`(Gk(!fl6_p;;%!C$*KEv1Pda&0{anazY>- z7yl6Q$W0kr&1O`u1ma5Qx~vKbUJ*MEC8DJdeg^S;+e@&T;P)|FNB!jc$h+xxpd|cK z*52_|@19ZkS;Xt_`VC$E$2|7)%hah?Bz;7^?yU|O2ZWH(FC2dfZ;MR0W&1L!F3`FZ zKRBc$e&@SX5?~u-_PX%2cs=a`bP@dH(Zh=+*!}z1C_D{jM6hp}Dt)8K2ydN#Y>@&> z!jJc+8UyV_>VNEXJ+0U1g7z9&?CsFksaO>o`q|1ty+#kxmG{xr9qlscnDCdQ2lDQy z$52qO(F-j#Xun;TVP03vqy2gHK=v}yM<<$%w1PqxW-{uHXs{t-$h2^JF{<7Wu1BMd zFaxWjKs^UaNfK%*&^ecAG@y|NKySaO+OW_|RYjUWm4_k1h#tzr#S8o-ZCHCG6sD$& z#Kg7Ru-2rDCKwJaRD~Kt!oAd3xP<#!Gp*j&QZ4pNNfTv@y&a8Hs1e>O*goChg-KjZ(J85V8S(eXcET6QSSv~nE^~awn&3w zBcn~B^7P*fcGlS=a?ysG#-^q!h_POTDd=g)9;-kpsKs7{Z%d*P=1>#4N%jdWhNEbn znN7WQzm+fomb+Y^GpW;uP0HY)(T|6_Mq=&X&|E zu9u}!ualZPbzO~a6m@^i`u$8l)4jhMzMV^57}Uq(278UB^A}h3I_TcN^1$M%w*EC! zSMFOp1M156bvU;^p7(T^5ntU~S1wa~3MN?(yF(Cf#T#OQ#D^79}i7QIu)RQV=C972Lxjf^xOE0s@^b zKrJYR3tmL71guyQrMTis$|AU3QBgreT0mJu40~Dg|C}=?p)IW}*XMcN=l{Ed_TBF} zXOc8CnarFy3-+tJ%heX!uuqyiwfXAS)ES#!Z*9BxlP1N?4Z8PjXtHcD(x2FH{j$La z+uEM^A!zsggZC!S?2_GOzk6e{78l!jr`s2_lh&wN=izRjZcZ&z&;_w0@>7N@)W$W9 zqOm7YjnbkTkJNSQI-DcjG#vv|xf6*akI3uXtsOD5QPJmsk@YL#>xbs+T6AM9j=}** zui-i#uN$PNuNzc^e5-@1=_(;5mX3>F_;8Uz51_u`8dbO~@p)I1uwGD1nn`F(RjBz8 z|7f?}^J0T)W?515WudT@+|IKm%u?81wgnxwChV!gt7Y4THhF3;-QH{H_U7BY>B&DV zIGr($&ra<~AwS!#8YgB2Iy-u%XN!TBw0cxH7Kl638|Ph*Jox)- zl8`;*Y@m6rnmsuCZ&MGxf@TTtWITR$|tP%M+O{LV=m^NCn=HqaQ??dTXx`@1YFN{u?Z-{{pO*%a83M@ai3 zoVTDGm%}k7NxF`_Li}T=N3|x|4)@xs@CQ&2;D7Ai-&PQ#;65GLbB?rG>^~RyIhPn6 zIT_oIG$LVwXU16T03zml`)_R41e?bD>E48ca)hXqZ``(<98=W$o^8^SC^70yU;XkN zk#O}#>xYP_RaMK6lUJvb2?Lalq!|5lqEB}tVi-xNZfvz;$}J(#hLFKjoL=H9IJZ%U zZjK*5!+F#%_{VsVd-QCc?bTW+}c`$Glo)71HjRMM3DL8L) zB)A?g`Vp8LFI{gelF6jB-`-J{ptoOOH_m5el#jvV^Wy@Z`NAbhVQF$w(&gEa zm#<$@j$-)`e>ncaEPb43akQf?i_gpYorvR|wXJrJq2&xGFQ&}O2`A+Vw7l@1JdXJs z_n$Hy6b95rs**;g)F4y5Z6}XvDAZ} zR%5BF%2ElbD^LC(Ls9gHuA{85?JUTtb!@}q>w+fJ^$qfMm#LMP4{6XJ$I28istb*w zP|GYHXAvA1kbj}Gd3tU85+igXgL!_B zdYv~_PcPK!sVNP#oLNlkdF#>2(;HC6agnYk%jthE;rX~0 zqUGu6K1{*FRCS4mpR5c^)DKP#IgaN)l1LA<=sM1($&VyHnpPl z;Ku{I&eC>;i^{IqF7(VH^Z#+~HjyZ(=ZGZ^RV5Owe_y7degl(-DjNEM?p{p<)ZD+9 zI7~&ItL>cMKGcTr z%`>&s5dU}i^LGIC7ZOTf%khQPYp7_i(WfYwq}m6Rpnl>y^eDLwwH1BQKBEt6E;3Mq z(F>pAo``!PkG9t6jvgpC6FTQw7sRwqqbvTu9zBlH*aG8ZVeD{|F$@!;VT`SC3^y*& z$_rFfm}r{~y(fwZA$~`u0c|``674q7)&pad)9xzxfqxjEOE83^$%f4w5qy=Gsm9CK zwS=cL6+%MdejVL0H#rOrC~VquVWHZ1`6RzHL0r z3OX{;Hom7x{A^?g+vpxi8EZ#AVjI(A37ldZ(=X(}wVhc!Bxpg}6x+UjONJDrowL2d zeaeQ|`LhlFP%t0LOHNFm|BO5Xnimwyv*gMXMrPJA>U1a9k+oY|P;8xE0*jf;rDBc7 z%VVIaq|fr0d@T%?g)Bf^j<~j|Jg!%%yiaP3K?~zrByS%jMj#fVE1=ajJy6gKpbNyd zW9g7>Eav6@h~g_yeMDBG@Ws=h4%v%*(dV#uqn;y4KF3p)P2Q7}K8^ZxXTp0n>fw+l z5jR47a%R$2?&-S5^Nnw=m{H+A7^_AJDl{^sPAy0yK`oIw z-AkP#B{8F@v4YAaQIdGC6f2NUk~mCimIui`>LbuZjioLQIgZEm_-V_=iwV-1!y-u@ zjhW6oX;?a9XN75)PhxsDN*ax+*Va&Fak>eT&dxf@s-8}C9gv_LPH0WU$HYZ8S8YH- z)7j$B5PSo>`pmjn#!ZN$x@Lvz_6grKP^gIN+9%w{t)Sk}C!A|r>yFHGH0vX0x+4Wg zGmCuFCTi;o45R6>t>v&1Xrwv>ht%2$F=jn1FR>JcOyqH*Lr+&6VcrtCP*NOrZbVsm zd?8WX)R{!tWGnO5Qc9&U*`zB4^-HOxNSA83JN{{6s+3Z9M?FQAM@Ct68`aV>vXMna zK9^X=vX~hANl5on1=Xa$6d|Kg{jQ19)7Ri5WbB)g7;=uN7_BLuH6<75GFlay9#TS_ zUt(~Xte)XZO3dXZv2ys5P3Cg(0)4~1*sh}J8(v1YFI+`6_=dCXZSXw|C%Bt;G-90a z9=G9+kwH&jxtRTPi4dV`c7bW!h=IH$Q?XEy>xbdT6`K2M6P^DUzDb}-?e_htR;_w$wz8GtK zRC?>HKSnLHYVwnr|9V?%)4%#S?FVB*LkGigpR%O5EI{iO6yh3xYljY~{_wBbY`u@p z|K9660oVCZ0 z;`Nqy`Blc7FZx+Mf@1gYJAJ4cy+OUpE99N1C#XCXXm?IQ|CIZm-kVA^;`6?G;x6KC z>&x6){VWcmzEkLzvc%foX9)b1mymsrZ`@-h+~+fB_bmH;3GOp#o%WwB>FNCn_ld4* zDQ6i;ey7$Oi^Vo1|Jv`fXg!26&`u~$%E!3j@s&mR81~+5b!xJWOznO8b2{G0lvanj z;w~KN_}kA8dcKKvzKw|2kMp=_%y8o;Pxg@B%FcF^l1cJrnm=sm*Dq2}1lpd>4tJ1c zAKNg0d=8%Hr#!9Li}6Op=J>nGKVLkE>nbn$pO|(RFZ1F)+Wt|-o_Ncya1m|#iXuK_ zbg4&~r`HjGBrAV``t{9BRlnW|>C@w)x~}8t>(bfd$?JwOg`k^xtV@KT-;JQ& zqOZwtpssUyI`_-DK34A2y1vEJgSs@mTt{!cZqw7n=dW8r^Tp?{U&?)_e%oHFr|aWw ziwnXUv3Pz^E04tnBs@K&k*!~()>Yu?^{c6RdQhUr`1)t*mu+MYj;B~6bIdgr8|!g{ zKF9N)cd_-v=M|BzFD~E~<0dJML$;tH;udT_IIoe%^-J>g^b4G3X6q3XS87OuUQRL; zu@@kTK{XzGxs{ivRWGj;U78+V&Qs)}&m){ShsSQN{`vOky#!INfp{ZN-$bheh>(s( zf7AgN$hI%ibt8l7+g%E@$L&9rblq)=Ze+mBBIg{2C8&9Oe$jV#Xn5z!-cPB*n9}&3 z5%E1k+c}&oRW}imzU{y~8qb;-F_@Iob`%!0>qL8tdye%-^$5mq`tBn-4pbmeP3Yyw z&{anhX#TlCd$h+S_-Z}r!`BgUoIPzMD&p{1+6DmqFn^pf7(O+G=z}9Yz}3X3+W8I-}JF%}YbsRIJmMt<#pInj*qYa+o>H zZnvOA2}aZqjaCe`fdREG#&C1EC}To+I@X5GhylXUIKv=WM4Q!QveAj+%@Gd!W&Rm; zi)-xYgZ-wi`K)A?hHq_rg=P8bu@JT072gYTp(>27_*t-V+ZC$mG;TN3oj&?&X2tzq z|D4xu=BBFqzy1mOtL`6grXu7EG@ew^jX9b5z<|l?C%xc5^uRdXR^$H0;-Fo|jafhG z*`wpQ9hmPNg{?PCI!v`}xD&>Wv-A$RQ;+Z7c{G})-@5Z?ym_2#9=LO})%+N&n#&v% zvXRxCE!ziGG24F&4cYS5%)4|8bR*!6Nax1Yc350Q9?hrTNkSG3ONhw3n=~&N)-0$^ zyPd@$Njzy(kdC>@&MO-9SQOH2G@S^lO~M&77VMcp`Q{`IS$99EH`Z8c77jvmVPZhg zTDo~mb)cNvDfW#lQejlz*{@QgAAIxG403z(=!47~JHgDanf48^9X^M2;JpWSaIh2wK@G`yo&Y-@6 z*u4SNr_lZ-j}NoBu)Zn(D8=uSf68vH7cbzyu%DtGEX2%jjRQ5UqRmFMW=Zom3 zK11g}Lks_$C&Sv}Ws*IoBga!u2ELn2eQ3&_WmL`RS7|%l-6zAJz{|z`u6BuXXF^_F z`QtrwJdLQcd)WEou+rl%;%UgRQfo5!zr0|(vE~1D|I@bN|Lfc1uloD{)3)pX-(SyP zwcY;R$N67bX3)3hakg8Xb`~g4~CH0}=N}xm)q`R&q0Kbs?HDxCWgBo-JXl zKZ%hxyTITXY*I8<2=yD7{aU7NHuT0Bm_W^fG&9O$08uP5+TK~v(!+{r#1LXIQB16+ zSCa={@}-JXRJH^y!8H=5VSvHUnCxdgo>A)Xm(vgvD#A>%DW= z?pm^(TA^D~v0+Y`xSq_fSU<;0{gk?n`W&?gYNmBW{%1yr(IunqJtW~V z5mBW;J2j!N5KOB_dS2{AI#u6rL9tEP=DU&~n!b|Zyc)K>gjg%AUoT6~BTRFFcA${R zG3El_fq#&uu=2n^T-=ULJJWb|$8Ne0*Y(CnS^neQA2l0L?(yzprSqo0_xMAj5fA9f zZ5_0ip5D60Ls>Mxb&vkc#2%SUp^oW#JcyZGIgnb|g_~QCJ&{k-sZ%`EzB8vN)SG5b z5eKIC$((UuO2WYOPuI>kGbO>HewxSCW}JDD#xN=GU^4$ID6SI_$><%gIvi>vjD^&wm}p6ud^LcM7w#*T0uAk)~a1 zKd=W5^_O52F9-3qm$Z-SMV+l{m>qTWklb!n^zrbD$+!e3X2B8uc7zOo8Uhf|- zmS6qz>!&adsI;g1G;h(0T(jxM*OS8Sm`~%Q75OfEZsWKQS`dOge^ug|*=En_nDN9@ zaIQL#ayHx{OUcLDgc0Jyz!N`fL?QlM`%fPc47@p|^``d-+G{L%J>q8^ko>vP$3G;> zEI+NskgXW#z`Ex~d`#TAW(nr|{pSvxqU#?G>^*~e1%F=B*FDjg)_=#F;R|IWaVcke<4~J$(0X% zOw-@^eD5#RF29=QppydTaoI766kUz_46_e=CmFFgy42#1LBHo;U!>!gK2ZF1nLKnb z`Xwi!eg^w#`Ejp|3Xa^S^7R8Sp6Ri`wn@*&UB8+bl9zqcOeX9i87k0a)1rZm;<%q++Z*^QuH%(*d_NH>>b+IWMj_v4ar5^=()<5X^N-#SYNT<*+wben)3pVidmT%+&FxsP z^4yMezmvG8pcXydbY52vlryEzy<8PlUyo45s<}OU&;a)`H}_;N{=Vlb#De#mi@1j8 z2hHGdM$aO)eo(cC%;F1_SUP((C=inVUeB^z?ZqDV;+phwKHbuFOk-goYyS{biYXK| zJPpKAwT6vm>hY~JNj^;vI&dZtX?)2p=jE^Bw$|wSWMCaLcpNm7$LW0k6cRPM8mXbm z+K8r@zLP9m(s7&H=tjTFy%roT$!W|puc!){|Lm!) z_}(LvZmfRWovo6Iy`$cuZavcW2(7ng-_u1J+H>H(19pvP=NPvThlcSxlDlJADl(_= zt6OEHPf6VVz9XBEy{C7KzXk6w>t|}jIHid#uFD=m2*%-_i=zzYo&P->7e*xeZoc!$4IcRa z%+iO}YdnVb8dKof#^@X<1K~iV>Z6A0Jx+R%ThI?>5NbGX zVC^<;L932*lOk*WzNQr<~QTs7V z;RM7>Bl;O9Q>}Lz@0vWuur&To^PQ8&n5QrJDQK%ZbNkS^)lc_inF|&y)Sa`i)n>#q z=FCqHI`{4@zZS3>Sf;MyShTq0tH^=HQ_F1c&RY7fJ%&Lf*n6EoD zU!<{EQz0vz#e&KuH5A20Xe}lV715glW)a$$F(mdp`q5Uo^fwyUl7?5jD0EFHQEI3O zEEa1k@z|84327+LR1_s5sW2=}aVgDPLC+&_H#N0+t6!VIwB}u4L46 z<;w5tbL4bA9X^YE2b{*-;F&gaKH?4cO`Ej}Q}u4heP)I?%jw&YTO77{qi$6V*K{v7 z&{$W7GGcC#toafULsgeh7o(?qP|TCgVkml%&cu}qX(VIfYKVF;dZhpJ<^Cu&!4kcA z{^%&Rxh2{=KP!sV?;M`~EVX=o);r|m#?-gSvc`WeFHt`vWs$QTLFe+gwt0?1q7ERcEzyOAL`E~(p! zdvff#(c3=^C(=E+);~}WQ1sPHZ^wFinwI}@E)a|HLZvPk-(iv0yCQG~?MYv^68q7> z50j2&yX}*^*xrrpN$lktTJK)=S(`HZxwt|V<6-Fa5TfgNU_a^+h%2|5AMGK`uKW(0 zdei!ZzUb$I>v?}}Ofy`U+WoJsJu`y%|L{MV(e4`J@9)oz3;U3!FR!rfB;Ex7#J*p> zN#{YK^)!SOp`Kv}>NoE2uKesb)Dz6A9JqF0Q{r7-$(~nR;hQkd0oGK zLB@IEqL&ZeMIHCJw4Aorn9}xG*X#i~Xy4n?pm^M*cofFX!}*{kkcjJB2f691#*Jye z5jtn_dyJuG+M!qraGpjEcVBij!TIKoQ^Kr>jbW>oEu-VPH5%pE_~*xli{z&B z{L56}d+e zSGy=Jd5McTikAz%P7jZRsyuE;{<+6>kG`;RIq>V{8p+qcQt9O@U7R|hbEM$8QXK^z z3q^YQzg&$!?nNZQ9V&n_Yv^%Du5sgt@^zJqM?#O=0~)c-F3N8f?=0_ruURo8s=s(ltZ z8ezs075z?B{FVu6eMLGVI>tsOOUuOdW_SFaiIfcd?2WvhsMF|+e?254oynCBnL(#L z!7v+`%hhPaU>m6LKw}G|jA{ToQq^tVu@aixK&{c4pc?5_IQg-TE+xjaR>(iJZH zx*CBva5bR9)1iTuQ_3{mVbL z#e37;m>nRaM z+8VrRDfR2->E~@;REqNMMopP&qo#7HhKj(8=;8U)11Jy4Sn9KoP#wB+>Q~|E${bfL zEuS(+iME$T172Ug^o_TxUiJ#gDQ z`xBN}?W?`7#NDO5ydpry`93jyR^YDJV;6rS+5@y7hWoMf0e7uPPK?bq8*@!Ds};c> zSn~RwpGUpEA|MC$iB5<8mt66O)kMn;l)l+!NA{3F;P$t-FN)3XnSBQBC^YSL>(EuL z5|%A1J9QlG9QN4BbwgKeSwzPPtq!z0ORSF4(l>s%2VqfeT#Ih;QN(Qs{FqmmaIvP z(J-2s=N><~bETrFI1$S=|M0Qh3yB#0R7~NffR!30NfwimfHr-o$>n*MR5EO z>q@-w|2fMLdcS2>nQEVp?SEh&Mbcm&NKSp+@nAK-NDX2=zR3_jO9l3 zjO~xy+fa(0y<{MTijl#>B?C@K45EyCDzt?tz1@OEpm$*CXkKVuYL?JrR>CZ7v~4J| zt#k$SqP0o_y%U4;7<#h?lep3Ja14rWGfKgic-d-vjf*Jpd}$31PT0)M2+ScxV`$UO zk66=##`*TOK`{kUgqYuc?FC9gb5nl%f_~xY%}sB#H&7#V<6-m{;py-ySkWZnjn+-R zxQaR^VPkvPaZEySdtYS4!~1k6QY$;yrc@=o-?p;D%{6^Mk2~r%qyFvgs%9NWRIZ%R z6b|z&o?wINF1WaH&`uSustLA4OQ(voN+_Jy>Fdg3W}@M1r`k0JcAXc1k$$CB8N&=y`fPMXIS#=rmjk+L z80k2|Vy2MgiX@1+<;+LIr~(-jR!*pyVI@R%37*WbO$5z$Dr;)xaXh>!>}wJqnZCP! z*fw%~WcnWaHYj+gT6e+ohyC>DS{E38s*cq+(e$sv{mlO1{xBNXvISzB-=ZZ79bK~{+yzubt?LjtyQy9>+hhQJ?Zznm!8iG5O6>pMo3*e!u02BP#l zPW#>BajnKc3mkuBan-lG?sMS8Oh!GB7k4KDf$wsO!{~q2w`(W*oBAIgcDNkP^ZZki zP4CaY`5O0w`P*qf(wxk#ry3Er-CvOQWftaj!}wpx^myoc^Gk9s@viVs?A4|-yYcOS-iANsNGTx87o0qtQn=aR}(WOC<2 z@6!IXjN%GKjXwR8+27eFEm&Cj1 zeVD1e_kK>>%$k40XPpce7oA{a+C458sAW{ z-h@H%9qIZ5*PX=uB)lVrN>yIwMgL#CUv8Bwezoy-DxD7pJ2=|kU}h4;SG?fOXA7PG zgf5ZX#tnEIPrpso%U`Ho>2ll1x|-SJ-!{_7oIG+W^D*wdkLvNrpl|7M$_1`x>!rNK zEw9kykX1b9&Kai1b3D3#=N9O;Gz3j0bTJvYVRhBd7foSxb+Yxuum-g9bh);eDuk_v zucSX0JCD8uT%EsJigHBY`qC1>-UiqeY-u9Vr1nW@d~>5 z#-K~5phYYsokHq=U#FtlM)NqjSWmy0kqg}HfkVdU=VjEOu`9@yPFX?|irix(67gsMvg6DHQY!E3=e?BRXKYksJJg#eOfnGjV|2UJ# zOIBekMv6(7rGuAuh{M@>X&Jhy>Xn+o9;c1!tL6Rfbu12vgEU?4A0Jdde%wEP9!t+i zyF$D1^}79&J&xL~SuIIBv@@Vf&=ej^+^PC;K$^<8-<3I~ow$mWYvhHOKD!gm;mF?# zPQK#65x4C9)GB<9;ky3&)n54g3vqW8i}b!s^hDaY$Mf=UOYpUY`}i220-u~*dGC!L zA0dmEZ~K|31pOkmzd?OqrnM{@xZ?(Jp+%NJAP&(oG`FSNBY6Lopdqs`gh_ z1=m*XD}AWHP*F#53+gK9eDl3nZ`c9Y(l?>rq91a4Aip;pf;x{rXi3_e-EpC9OzEAM zUYKHyR%Xz;3={3{ihxbVW<>i?qhO);T0{)>g}K^ry9L!4sJjTGRUx<;V@4p*iV%yz z9FFod6QN0xq~I&O5=FX_gI>E{nkKxff`iBrb(MJf1qvu1DMv_o)U1d`HJzOmachBK zaLpcSyR{(F;GI2GzIBE}^v-st-a5l2df#@ZK0eE3^1ht|J2R(z6|yDL6~x@Pn0x<# zt8*0UzV44JdmlOqpHTKbNIjwK9lN{bzUxo8m~G*B#Bk&Ibt(JWoamstVdcj0zLb5| z4J)z3*DyV0c5eb~yCeRLxUMCRv;Xj)1)8X+qC*|0pD5&TG;7ww}E;9=`mlQEL9 zNCa&oiLscNjV#v^A#V$5WQP@79M^{I!s9NQp3Zb$Yv=ZVf~GsW^dF*0RYRTaZyw@3 zuiM$WUp8XWm^qGgg{2Zz9YAdz(!pa#pHkB}p|yqDA(5J5x`vw7LJXNaMrLuy(LB~Q zwi+utHVR@m(_po?8e8PpNbSe{0)`*e=QmlH>#gCBupoGUn!3fIEQW26UU^=38+(LSIY4x`F_^T<^q>YF zH*7~*UNE>j8BD&)sb+U4(YUc}s&N3!Dw=9c4;sm1t`M0|^QSUpI6L3i*GAQZzP3W$ zh{Aa+jwqa~J9Dmq=10`;5v1u0K8j>uD#Y}f>B*eY!Ya+$06x^e5IYhOzmPsBxPEtxGB5*oz zmRf0a*bFmfuopj_N7OSfh561+S$5Q?U{@E02%jpCfhfyE-UOwZc4?_ z0oU!?6HVNLds4UK*Ak<_eei76#7%Zgev^5qFHvOwl%&e{Q4Q}Oy2`o8J5a#9^P4r| zE%QG%>R&5RAK`y>_1T@Yej|NbWh@#Rdo|PM^q4ONZ*?H~tVT5N^lbD8@!B*gkStg* zgx9;@{B)nHX4CeW-5tb?`i<{Vzk+_R(YroGKUy-v`&DTXZeYZp`UPi!b~3eB2h>~G zF;02lS!(H<+wYOR#kkLtf_fVp&ii5}6-UUJ7kB3hT2C=_)!_tM&(ZkQ8LaOnldnDA zMN-|U4{0`ukv3+qq4+42SbI;Y!f#`|9M ze>!tRPotO2ow;>Lz4aQ{xNJ${w!E19d!wbGvXTwkgXh=p)qSDdT_{x86Jy0UIN?$z=%mVW83;pH1p(9>@ee5_pa229}Tal`a-alQ>gA&tq-6sp@$`;S3M=S}N?|%0QsntT36C$P#>;7?`r~t*b(FP2mM?4Cl{u5I zH?v4zzpgf(Zo}~(i_9hMoGcDGR#OJO6J94XrSNO_@?W$z>(%b&Z zS#avjNBZGw3*U>$Y#j55bImY5CL!aV@GmDCIzLs%Td29DH62$J_XS#Yp*!&8tHW1O z&jy^iD%8ZyD)qn1U;GYa(Rz(H&|c$%wBRGF_O&WW#mMBXNIUep=!!v^x}vTk9X()g zAni#9jAMR1a@u3mulC4Mk$x>oU&TfpBWFu#p%yXgr>z6&rGzp`g6mR_5AQeSoO@r z@Wjq==Is`AlU-a@NR8N4RoIt0qDo>8uOxk2Cyuo1?oP2jh{jdkT_FeZcwk|q`H=U;)o}VDZ$V(-Oy=2vHsuPQ|E^F4LXU1#?Dyvtc+B)58~iwCkT%3|h?&*TCGMLhG1KKNoR$B|H)$@p(44=RibgGo zkT9JPeT}~fsnD3Lan_XZn2>z^Q;F?#xl2bCj~o4M(>Lhj!zEM$w_qEK^Qf|9sAYsy zkT?Curc0PZ8|6pf^FG~a3ObF)Trp^&ogNpQqHa^&XWHq`Y$r+y$gicAC)iSuuH_MG zoTgEYahf-a8o5)+l2Z&^0j3U>Q>bZFp#jq!EI&QnaX9TxStk>8`GhlG?XS?qOIT6!eq zkQxd+hAyOggdU=4sh^j=4TC{8Jaa?dG9XHNUiC|1UOrFLCKl;gjJE^DHZJd-={Vth7gj}I% z_azwDJeQnUO%&YG32Z_AfMPWT62x-i`|+X4M_x6mZt0FU1TPK~dr82CYJq?(2F__X zsQAA>9Q!c#RsTQcIc|gQT<5`Vusk;!<1=Yt{z=_hT}#_dtf>?pAYR%JHh_8pyZ`m| z2Y2BphV~eH%ZY~DN2o`51CP`5hg0Zktof%6+H^Ckzfd;6Z>jF?fAb61FY?jueq%rp zWUE6N?ECef_+Iu5UvfMJ&!+|g+cdNnA)54qis}~9)?bYZdOPkHDQAgf#`qvj3f^ULvrNC@aX5={w)X_+ez(l5xK(pb$;L`CWW>S@DurJ~};&xV<>P zJhYp(zgX~m#~+F8L%-X~<%Iai<0F#Wlc7E`v2P5zOsgcn^VJ*i^3gx` z<12MHU;lsGO8&eTa~Cg{`EPoq+dsG-fBMTNxPDzTFY)gQer{D>{;!rl>;*KmUtVf@ zaK8;YpFhK@OBCNnYSC?to_D~sNd*=o+*RKP<=9a z)K{3yw8aK0FlfINS+CJR6$fjBfi5Zr$tp>-O2aIhjHug?WfVsv57cm=mkLG*W4~i{ zvt+{6-M_skglP0Z>KCVGET2j%D)WdU>3SrK<|TQuXKrh5SIsNZxT{tUM^jDkE!c~C z1Gl^GhtvvrEcfO!-HEGLx*UF{yTm-%UFz5?U2C@QYUtsPhb^UtKY^bz^QnVA89%Mr zr=x5ke;xlxvkJs;9(B+74X|Hk$g~af*7oX`8A$tK-Ws?%=yg97A~xJmQ&T@fV;Yq;QS+pt*6WYqOM7SkJz1%gzRJ| z5{*0Aph&bxh%FM7@c6tk%SVe~T^xq~f*k?Rs4x8VNuz4`j~+g2tLd?#k$hZY7!*y* zCoXDUExlS^G18O0T0t?Iz>V;hP>V*CGiQz{pIh*ZR<7&gZd^sWsamUGe?UcuTuw8Pt9$uux89&df^e zcMW2$1K#;q+l(k?_sr;-eTJny*2NGlha5xWWVw&7lW88|yy*d?!~XZXt_f)z;p|Og zi5c6O+Se5uBaLvt7)ct=^*GFZ6;xWn3|&)5yyKGUFwrd_sidwFPiN91`2V8I=CL^u z(KAx5n@7TeTG~WC4l0Rxsn(+-bD-S!=*Xc`65O3Kv@j{pl3tlIR8y!5RS4;&u_mN5 zNyq_S2huTHs@EYil_**Bs=@#z3dfJZH4W!+ZAnzP59MZ)IcA?WIC36TwZSEC)zrsh z-=igOiOBaoI;W-{#HC_^xwd&`kJ=gOc_xpl=w>lR57m%IeU>_|pKb@35+)EhP0=-x zn%b0JEVkh*!Gdx&dX6zCfgiPUht0m( zUp-F4U7y@A<;Gij#$dfRO_wZNcKYzS-%n#b!=N>PG)47*z4T4z4zeoZ)ZNRY7grMb zi#NZBzgN{XTh!T;M%CrDz5L#uO1G=?ftTJoc#o!t%lmI^+2pS5Y>CdxhJH+_ANfh^ zUvUKEm0RB<4nukO?I-6EM_joSND+wE-LEzKZ5^s(a?qaRtm+W_xy@Q!PrUc!w8ab? zNH4!<+5RNWL;PB_JrnC=+_5lRcNxmXKx0jH36)3A9XmtR7US9NBkRL4C*Cs^bsj^e;j^NK~bab{T6b zn>>KFIT*)0b`G_C!=aHDM-Jwv-|HZ7ZB_Vh58J({FG&_61r_aqfBrZ-QYGUD{qn}k zc9p#U{jYn8=0khlCfy^6N?II^pGkZgdGm`+_YMx$ApH4JLeR6d%Tx4O*m@VLKda~q zdb3_`W6&>o92D(Mu!vu9`_(pz$5A#tJ!Hhc*n07Gwe$3#W*(bw(aQ%l=y6&dyBYei za^0eB%p36aOqUz*ajtDU9&=sY^!Nfh^K`Dm#o}0Q4A;RWbq(F0rO7K`i1&_VtShUWnK0C#Pp@E#3doq+3O%*>I$E7vs9KIsb0k4 zJfGV*NsrH~|HX}yf<_U#IC;YXXGeT4v7~i;mirDfwc|yM;`u=xDf)UuSGi-W+Lv6g1a3XLQpbxW_Bu=lg62 zHMo9P>H+G1m%sQOxRbWm_zvwgeohNMvTFZTMe7SXzq||EG83&dx?wzx8_{;78|pN= zl1^x^aRdHcga7G>=jhgow2t`Sh4^}`k%o#14E=>l3N*f8O)*SnBd%&_g$5n%%Sbz( z80o+nsO+G9QVgiIz&gmnz~)vo;J|-INyIvs)s3x-ZY%hm)_??G=OwA}QXy46f$p4i zsjE+A$)tkmfSNoUe-~G)=z&;sK`(P{FK^j!ElmsgiFXx?;RvK>*0Hx97xtb;^T$JF z)qT)JEgD*AnoiwPXqu&zn~OH-K232S}Gz;PkG9vaON9S=_${f@M_7D z(@EFeMQT>@U`lIh!GkGX-A~v;=CgQav;5ICo!U7~x0`F=6EmBNqr16oe`2PS8&AFC ziJ8}##&>fyp-xDnPBe|9E@UQLvv8t>IH3cV)YM55$ij!Eaohn;+zgzXBSlhUxDF4E zjl|(05be4ktJHw>WSMvfzDyw7HZ^3M4Dlr`3M(biqZSq>j!-p3T^5UV6^~-H;#H)0 z66@1M)8l1i4U*5OFs3i~QCkLyYgL-BTW$$(_2 zLJmX6P(?YsHS_5b-KkXn)^Dd%t51IW_mZKDz11C_k1la8_HJb6RbJG@=b}nvlv8R* z@+ymzqNoy*=fgc;M+v=PfeyN%A+}P(vW=+k5UiXY6JG~wEgFqvv2_t&(GO?fa`YD2o)T(=|_LfLhTK^smIf}%DSv6fJeqBcTnhV%?=m~P3v zbCHGRz8PEn**w5iIs`Kxi+Rv3nU7zK0M)sKBbG_=4BYQZr*Q?l`gPpN;?Mc%& zJ7a_1OSkuCXUx5u{mLQ1O$-xzA9xG%t{8K}zx~MFgV+=3eSTcH8v?Ba;&2G30^d(T zvkK!Q5Bgt2y78kQPmCc?w>>fLaRBnzBXDK8vxAz}9*X*=HjQ%0-w{9*c8r>gmJ+Keu-NRn`Eg7%R zfADPJ6m{ii-`(32<)*Z1+Lagu|1F=jix*|9_jsV~8B!ckIen1jOOhYqUAQQA8}>q_ z?^m0jLyvgR@`|H7$r9of(GKKQB~gnww@)GoD@d+m`$yho$s{ka#d?DALMLB?A?&SI z^3K;k-q!{LjBCP9HLMInfCRSZk>Y325T(g!QoIc9dfQex(9q(+yvA*aB;h>rz#t2W zLA%_>J_k-XGk)ChEJ6Lpg#Jg~j72?%6!;y7em8mKp}4E)JUS`IlrR->SYSs1$#&p8 z^7IaZkB+*%Wo!${i$C>B7gb+Tt*z`r|dT)xp*?PmQYWyheIF@7lU1(dRKmUaQ;qT7|~qMI=0*IQ8-v=?)YhHx1Gkn69wg}_gV1pkzmD+Hox_NkKf(GMPK;v(w|?X z=0vnD%tHHL1T7*;oyIkZQ=u7K0_MpASAFF~TRr?7a&aba$brjvO z_2?L(bd=CuXq`dFIj5t8(mC8{eMS~qa124+Mpst8C(J~CcRDv4=4nHv2Ca!eeFm+c zpp_Jur%jSkUm?-}wHkC}QB-zV!dUHwj9J^zWd*%hLt|DFov(gpailC)5j?b!KMrExnP5eZ2L}h)u zJaHQA`|WFqQ+0)@nk%aB-(Mq8>^tW*k@^|41Nj#CUuvFlnGOE%jicBdEe+3=K+sP#_^Q5Thzp$#6OeHn)<2 zD_W`u#q;8h4Ij+S!B0Nmzq=@y{|m>LBJM^Hy2hnoo+VuaH+iE+Bnc@bdCTyS8jlOR z)AUhdVfPl?`$#9m)4Jbh(S5>l{gzQt({rC_T*oHYZ@n7nCz^Es)D|^;*pVj9(ru3E zo+C|~3O4uj%5Q@%vLSxC^4s$oWg5@2!B$+Q@*5T_m1}}dw@I=aaY#v$H`DkcZs7SF zdPwt+qC9-=!_k$WZCEEg_u6=BOXi++Nf)&>%I`us>dtjZvs;h%ezs&?Qc2=?vT=z9 zhvLoG6soLfgo>m81rk9|KY!Z}#r|ZOo4A8CmWzbBiQ7qJYLTIY+NO@niVU+`Zzql= za14AAPQY#Omy67KLk}^9p^G6g3qunx#W-4y>`~){+m&>(CnHYync0ZBAtQ#lzK%UJ z29xhmj{2-Jfb_`NtV|yfW?Z0W8um*yb4zVUY-BOPep^WQ=S03A*Y9rR z-;Y}gyGe^8`0;KB^YvdHE#$r!YHR0t360k{T5zukX~jk58b@d4yrTCrM+|O}H0tC^~+~kg8 z7vc@`Pww#bD(ahGNWZXgKNBiHAdY+d#{-FHh_<4V{ayLi%8VbsdX~sW+*eA%5mO*{ zym?%4CP)}R-P(dy4dQ!v|zzJkKW94~)(HsGm3_5Y>V98P2cKy@m{Z@0&%%F__1# z?8IGUQ#s~oYhjR4-!P?JVp=TLdoW*UL+c}QO~-C5PW6(n4g~xJU#+?F_aD=K&LdxN zr4;p5F)mx&-9*#K1b21ESezJseQ=-O5sM>%Dal9YUy}pd^8GeC?wg(UbH4fW%6L31 zNjkoJ1|E()*>mSxcsMfY2FE)p{OcBxKhzaV&nmEpLGyULwvM}a9P~FmCKq!*FGuUG z=rU1Jkz@2}%%9tX|h@ujNr@(qbk8C?vU{!L5x`VB~UdV^~E`j`Ck z@4V!K`W1M(p&_mMdWMFSdH$u!UxuNM!Q%=~{_D8h&s(s35B?Ia)RBDsd%43d(?h)c z$_9Lsr~j!--`!P7zX#g?{)e5w;ia2jq~81d=@!(Jft0gE!%53B|MoJ1dIYEZAw3`- z3#6QJ!^b^GTq=CBS3+m%f0zGb??5kFuTfowHmzyFSFSoNDxqyPI-?CnH`G^jLmfsp z)Lo>b9-|BXrQzQ-$nS*lHF_iNg)-M5x(>A(*Wx#nHki$Za2x(Z4TVvlBWf5UFfb_U zHfa3?7Rf>f5)Ct&Fz`k=sx~Ca7=fw}i^(Witl?18IO}7a&A%<&1YCOKc{ehHK_3Ewh<$GZ}W^|)=U;lsY-3fdX)!sMoGm}Z0rfEAZ0YWJ-ZCT2mVpUv#X(`YG zNJ`+Bp&&~Usfs>$)k}(81h8moBuf{X_}eLoSZr5Oi#q7=uSJkR8&E0NtcQr2a=sCew$?l-up;1?6P^N!$N0$Q6;Jt;S{<)f%%r%UXhV0rxpA5gZRK^{X~a7_ z=;@x$BiC9s7_KXem>9J>(Gpf> zNfDk-6hq4_st}UMLgNiZ3kC)t7EvP%C@Ol)NMp@5)JVQ2PbVs&;vp6X!?>N|!^CaY zxThPqx|7J$S9h+}gmNp};F3&Rt!+Ggpho7!!<&O<=9T4Y_M1Jh@-pW?QN`2e56>dm z{9zFL6L^mX3ocW=N2{Xr^dV3v7)x&DddX4g(E(L2;{NH;>AGS%zO8Hh)1kfAk4EsA z6*|Rqw_Pxdk`ZUP?NP2mC(Cfh8r~T+iPGgeqmnS`K9rL*ncXFKC$(^R6KvrK>ZG`_ zqkGUtdF*lq#TV1Y4p;X_NhxUJM8wIGE3r3MF=WtGm>e#4NM#{w;3IW)>2V@S+YQ!l z!269jC{w8YVPyo1oU*W{T^PoR6pcUsk5wKm@`7KfQPGiM$}>N$HZyZ(UUF4>X1HWN zS@Y?WjJ3&`H~GiGWunJ6ecF%ft%|Mu;ezw+H=;p6i%r^^g3Hd|aya5yY?88h;BvCffB}@VBZ7tMq#%nm0muRK4K; zcsBDCYxlkx;lcB1PpTJiBo1ZkHp>&3Vq+6lSQBWUIR2Hc2`oxtW$TZPrS=Rh>;QJA zZp@eQSq_=sy>SNfmavg;pK3+%^7SXj(skuWy>;+qif6am{SM9JnSXoDjwo|BE50uV z-AuADul(r_G%v;A!}$ z9P%Zpfi5F%XtI&sP<`A7P8}Ng4UIL@8;VvTzTBudbn>O4#`iq-&)RS|{;$Ty%eyL) zk#jv;8|D68Dpz@hM!OQur0L7Q+h)e^3E13-|ECQ6ea5f-AEV*+d!uov@e2Q|wl^Lh zFwTf?)HX)>e|5XO-u8dJeB))XXyeZRX#=n4nriZQsf8$!cWa03w891d=b8sT@Z9;f zD$Kj(^mcO1yWfo0;QucFkG%t9(5@ld5>+{RQ{XRCzCS%xo|I)st z+>L!5$s*#7ePwP%z%==0F>PS~iZuBx!!x34zx$0encEGr6#L!XBxtfn)bRwx@sb?y zbHsMUh76)dy~HXZi)YMBVu5@e*Q&2h?YebRdYJxLXjJH~DR%O7;c|R4Xx*wMZY7zF z)wTXh#?3lZ^7(`Pe42rK0>wX9Pfb~JVB2r&&P}acdf@QK>n=>K zeDT29dFw75uYCD{f8GYT4=&U#3VQ03%63;TKJ{E-L~l(S z&en2!W8^)>Xge&ln=}pI*e+8Nc{+o%60s@*^6+{$OXzt`9#368JPy-5?T|jGIk!-^ zs%TIQ#Z^UrAeE}3hhmu0R`CjtIvLKa!&s}*(|R54s)?5i7@@~^)|c_vVP(#UG?NR1 zL3G3H&In0#NxHHjuDQX}HOP>!(RPeEm9MGEf_Dq$bO*919*@<4fW_r4ABd^M10Y1j6vUK-zvsoqd zHp`YOt8!Dkw^iFefK`T`+h{%Jrg*yZ^;xUSh%2~)vA*Fimp=a!D_iJ4bbi+peYFk# z$M37!fZ>mQ3VQA7U+gK5{Nsd+8=86)e_rQbK8(@|SLAh(KP*)$5`Oo`UsBOvuCV%q8=;N+QNI&E6fO##LRq(f@O;^rU_Vz(-%a_wA^iG^Lay>ET zH+-)R@in>7=xL^~?3@-|?qHZdW96I2$5FXIrT>8D6^3~>T4CSU*!TIbr+g^q%8mc! z?@S4C=XDAD5U)!P@yCnLeB>|5?Q|335Z{okzL)Sg-!~^6)1m9>;g>$c%c)wF=lB}2 za@ODSQc`1DW!$+{#ktHh>fpYO~x zw68SVllFMzP?p=iRbS@biTUG?bz+K-l|0q$E9#FoyUlm+P`gS&%IS}oEgRRr^RK1& zM9r=zqvG7`*u^hqZfKm%9-6lwdpns;oojB7`CZxMMJ-m-IFxFC+@%vG9{dS6xMUgS zf@*p~*0mLld@;+*%Lkpbg~yhLPBqe}ZsYk8Q@8Q&c@b0l>JIE1Lm3fQF($n)Kd&5< zo>ot1dLJG;)1v||qvvUs5)}~Zo5#Nz9wMU#M;+$gWB6sWpLksBeMZcczr2mh78@?= zuE&eI$5A;Juq08BYsL18r6f_8DLllc_g~a_*guJ6l!q$atyMJQpee{kzJ(hbG@_Ey zW0|4Yz;;I5&|XHmp|jU){yb2i=X3U&Wte?Ec|FE?dv-lzy6bU_D=7coiP&&jeU)k5 z==X}swA;BJ!>XV?d3x1}D{89p@pRaQ%3W2}*Ouq2zIakl-3`>o?FcI6Qa*?AH-Tb3 zysAd_RT278D=D6*zu_pHho!hy{yreC@1Km=5a(!lzTD96MtXOYz#@0o{>t5SndLma zLeuqV#XPRHP>=HhZs)O<$8|JNGU6-frN>E{Mdh-HfN4hDr>>|=@VM4TjCfW+GnKcq zSzOyyeagshpn0aAZk`s9uTqZ5M~4Hf$7>6go&mSMf~<>CAv z@P5;LOjcYcTxF9>O{G$VBXCvo&4YBD(^rRwQDp-e~s4N10__slghfFF9!A4xSh9S zP>+nB7-2#^Hq!8uffkM1(Np7gv}({yq10=mCw_Y2=T_wPz#tMVbc?PRbct5#s=-?? zL?Mjk&Onb18*T);EDBmJ&{YGi8T>!Zyn&G`m~RxbZcws-dw3{1afDO*1|H3)77V=H z8&^b84L?`XW274j9()DGc5TwNLNj8k2}6Zp=sjczX$=@+#6ew@f6N*}4mp+{;%diQ z4EY(hfe(=ySJ4fFV}@j;hbgVKG1D^!l6$A8z+`Rj$&`UM<<8~%U<}*^ol18&81Zkhrvurnpu{3u3+mG{XM|TGNwb(`cf^viv2hgar)>Q%6dLlFAfgMjj!dIrgsI`q-M%Y(~G*|wLKTl^F5y%K09Ha;CiH) zCwD@gpxhnh$*tmQ8W|B)R}+tqoP0_S!Zmw31^BgwidgfC(F-*y4xTr8} zE`G~yNeye=TwDT$Iy%`c{VBdoHJPW&>a6}k+vcLIP8}dvnqvbYW9mECi08&wcz#eP z#TnY%5pXh8B}q*W)$-D~C0pvc-K@t!=kQqCuG?ChQ{qjN%&oP8tEw9LYFZ*Mr>0Fd zC!@TYHpARQD|MwyGeWqnXPRJkI@Tx5veuQAWYKzlpOx_S{XUc7m?D9mcX>!j(;h$@0p^!`XS9@`MhKc!91V-v>2yt^(Jc-zW*N^`01$^ z2JOq125paM{_Z$g+W1-IAfo=E*cs;Yl`obt7nC$^$?Rdvz7H)m7v zx+=f_ha5bCB428WdZ()7h^Ec>JE~?UT)dqrao*JLf1&mbb=Z5~EJM9eW&XX|V0S;S zfZux)TkTER9oCpBoxEimejXd;mbJW_Y*Y`|9((Z8Ys_Op`-6QZj-E`jewi|K^u280 zC)OBMh-6wLQ*XRJmfthvs#Ss;rNPK|4s+YI8j0i~b)pk4D#5iTEDxFJ4OhMKZ$66!Z)ncK8yM z=0(RMrKq|Y4b9Ti2k53XaO;37p1-w!)g*eJfLv6LV1_KSL4N8WydIEKpIe$m9w(+7;c7{X(?nDV1%S@N%> zJS&2y2aKAb$GEONPmfA($rWnNrRNdSV;bn>@3@eTV-L^A{+-mnhF(Kn?B91|HJc$*%J-E~<*T}FCP{d+2N87d9M#|Mk51l1@` zkGC~Y_2}t_Wrlfb8KsYyt2<*`%4&*dY#Yz5-Zp*%#TSE4GvWqrowksd+d6Fld3tM7 zIM?gq?ldg%y3CZW1yp4{4$AbH3SrUs?Tz0oQ9Jda(pU2Zbt=zaX831yD*t_dRwr?r zut^d&VwX;%F+@SDM7zLHe528#2wTw~wfE8o0oS0~Q&vAqzN9Psg>q z=zzeWV#r}Mi3AqmstQKFp{;@FrOF?FnIF}$muZX>?TCA{1wx9$@4y*~7~OSt}ep`x%Q&&7^pvVwMkI2`S8e0VAP zdm&S0i;GSVPh(zI_L?946jiqG?13{3hq?P+Jo5^=Lt;E*$lKKZQT#yUPP*>vsJ9NZ zqIQVG7cNwjD=Kc;M`r%5eG4mm;@l4|ukAC0H{KHoHdp3F_K_u*>> z$0>5@694y1MY>}9J4x4Vp*3~ zf+z_#j zZ5*n~R7xjPp&Rx_ou-bdy(QV4oyDNboIN3+m^x9aKrC)C+%{42nSB$)&n6b=KDrf7 z5ucfEn0-)>VF=P+%RX2g;^`v38c;rXE@UNQI2W#$4<5~1`Ak65`*PLMMJt~%y(916 zVHmn-)k4HaLl?aU9S?>tidbk`Iv++pEz}x{`1#zW^Fzbmh;~l};rHxw%q6 zXFMt_xw+=piMlN&vinWBmF9-FGSaz~mF9qvlwO$<5;9S{U#uK&B3a6KlPONSU+x1H z-2}rGiZ(&+6K8^A2`nScka0zBcpS-==n6|ZcV=bi;sBMpZtC3GMe%!FZv%HmA-#K< zW>~tj8R8jbrN1wIuXC>%WiS1{bXKok)nzZ$82Xn?>ecJpA7dMu!_%u`;T=7{<=)=y zTQA}?Ln$U2YpQ1<6Qj{Js!Idg8gWofFNf8{*OjpfZ=7SAtx24a*aZ&b7D9%&uvm9H zR1pVEr+9LgWBrSVDR4^v?S13b8Hri_x2MIcZY!J=aDo@{sO^(-MA0{lgcdNp^$MDfKJ-!K6AV1)(X`a*yd||se&C|C6Z`sX~C#^yZ=tis|=1MNu zM$$;R4Yp&&ka2Ud!`vyi5$fudTO+oK+yYV(atmx0?)YvM^2dkO2K5Bob){LZc&lbK&Ze~k8bvad3I z=lqVR7}_bO7FM^%PQ+Jy|4BEK`}n>-VV%y@5Z{b@61!Phh_C9b=?s;7dZ_d%c0xX@ z{}?_1g74+kd-pOQIyUgXhso(*Hd?|wQk8q#iRR2#U6r{xqy@`m9$r6UENkB6$j|XI zy_fv0{85_CdlBPrR(npWG9c(u6SEyeh(Y2(U&E~BaI zG`qDDuM_pbT^iH6vRS?CZ(%SCqy8YQRb!?#ZHgo0X3W=d$b{L`aNbI}P3TaL`8q7S zF;0fP`Kj+Gw5u@we6ob0HJ7&F^{_`D{t2g_tgN*7li`@wh`qS%#J!@nhRqn(8TGex zRyg|PAapxnzg^mYo~@SgI;vBcR)YC8cIEMQkCwaMT`)q%yz~)YvdnC>e^ke@3Ll$0 zxaD1F;9^gV4y|DL!~W?-F&+`nW5gaN^LrjyKasd5=t!Pk?l46CnU?ZvE0o8gy$5Tw zJoZW5J-V+MK4Z9SFP~3AShg1j9h5IUrR%tkLr-CSp#0DS0rPpBwt~yjR^WZaq9*93 zG&J5w|Cbf{?@5WbTvOG^4@#pFv8aYjG!EMVdG2*@1f#??RH+E^brr*`ps756f*5d! z9>-tL=|(v&OYK(QEk;~xH!9Dzyis4T5npaRFYkRrytaSU^D7(p_jmL7 z`swC*JbmwG-af(hzHGSfDvn#Amv7)-hU@7q`sg;pH+WhCI#oRmXf{_pZ%d#-7Z`d1aG4Y8+Cf)72|@b?E(eP-EOlQ_gZswkgpNqG1W&N&ahwsy zNhv%#&H|AY*Q5`r#Wm@tVb7_xAwz`1HZ6mC)Tb$afZMLVMTnOp-!OMUNXz?|yPt-! za20gwh8fzNk04!p4(=aTraf2eoDIFt6)%}Rer$ZuxAb`XTZKz%ke&`Nl4syuW0mw1 zhK~l_^d6;8EZp?cU3V)3Cu;B!ZYdm%xHNoH;^^L5_tMBo-ADH>fmz+h_AZ%0_Uv6U zQ`bGSwAJ_-J$pAW#fWuB^n@j?#*-N(&2{DG6~DxRGqwysq} zY(Z?e%cIA;$|{~+fb?BIR=obHrF8~8@egjviQTaDM^Z|U{jp^KZo8zC-`BIWN{>sc zzM%ADqyh@5)pqAp-xY@}heaDMZrV^6*0L4p4ONhiwIb-^?)4Y%M_bJO9&;pUx+@IxwPQS;oP+%Q+@Ps* zdQ;pbH8+)P+gnQ=>c|(M=#cZSq->-cddax9Cdl&QS_@umumLrS>4c}Q#;~73tjREo z>?FnMI@*xVPBQ6*t+SlsRWw!dd@DCO%Nj;;a-wzK4yRo=Hlm&%kEWQ-(^cjT;4vK7 zQckuh^Bx))Ejx4tIR#?Dt=rLIs zEiqVG*u)Ig9fgg}3RAqWPnHzt-Ck&JXg?#pzFu>`EJvKN-sAYZ3Zm9@c8BlZ>7!{O zzM50#C97Z>@y_XGV{pLGnsxZ#jt>_451tQyV%!|Xv~;O$6j~ROW)6w()*(*|(Hec# zJ7@C-|M0(tx6H(NMw$;(NsK(ZXwI2k3bU3C+Ft#l5&e1Xs3=sQ)@n7+sQp6c=) z&La%>Er0d1v69M^RsZ3tO0y`M;QyIE92)btW*xN7TGpDQ@eGII2`i#jU|b36Pa#h+ zcO?7zMAUTV9*6lUP9I{{csBLvlWS=l$Nftq(9hM4`d7=v)DG}){#WHt=$*o@6k+3fcF-bw3(-$?VST4^5l?;jy=`^mS6Y;qy`X_km;X=P#y zGGpgAOUWT0gvOFI?z?#--y}-?B(S_S?ERD3s-8p6Sb3W{LD)->Lp3& zLP9fqCG#^e-4MG_8@UZ7C?%Pfrt>klew)>XZlkD%Na#r#8j6vz*p?(ra*jF)&ahZ< zsd-95Jw!6&p9N!M!=;drP?I?nt$`r|-b4LpbED#@!&G!Z)RiqzRR!5%!9l8?N~j(y zpJBx@TD7>9Xz*7InLG#pzYuXBKtqK~DRwTd#H8>F^L@ANNG9 zcdXlZXcOX&>)zf*hLw}M;oPm8PV5V6T4UK-@$5dIX_IAZNy#qq?_S*G|HI_^(EzoT0KskKvD$9=~Op;dtFVI1uS%x}udW z8-KvNU14R*?%VG@qDW=S9x&_|@Mz@`#a`C39~quUuE`>IRC*?dZ-FUglf$>Q?3a;J zHs!8e0ecP_iFnLcxAY&`yKKOiuaeV84k-J$j@w5L5a*5g%C&t&UH2#yFC16Lp%0mO zJamd_;jnL&!l4~ZF9+Qbr^h>lppFivR>O*fLMdQ8Vq9HZQcNuk9Tpx74xA`uk|OXk zVkFiI#RQ|tmFuZT*Sk$L#l=~^lQlAAB$*)8)yfi5C^p0{l*i1@I#N2bD;-n%al_Qh z98CE(wO>G-?%*+uKza(grExKRzD0>pQ_v6%y(-sUSL{FR8q&!E-Pi(EVHQzw=rTtE zL!8I8TJo$ELpzMG=UgM6Yi(<{Xm$sjFX9!rFh!lJ>=nOgTLa=9J~`shwJLxq{q$*?Es~Buh1qMnr4pwl48?s zak@*P3-M(t`Bh03ny^$cWhKd)P{b`HpUYc_wpDHDr( z+r<}E2g^KxaVn}iGxLdiPg6Ts<7KZL*`vDMjW>SgKZJUQ@WiMlNhm&i(^p3(XRv2C zw>ceQZyeXuKV?C5qQvCP4cm54Qn3DVq-MR!%gf5o_;=4L#}evqZ6fpUh}t322?y51 zi}h9f>Cfyp^gAiu`lIi-b&aR|_JdJ$eU|dBzxWqYdQRx^zgZPexn@43t}8D)l4JU1 zb73({)TD5)!aQ=0SiKo1ncT}(9USe*OjC0r;(AaY`EhHT#tX=p`t<&_=ncts969o9 z6yoXAtJl-+FZNucBGe*SL7zK5mXW@E{SO$mW5Yb)$Fs}IcQ&e6PH~`LXNwrB zh}`$$saI6l%_e%ztz`J*j(P3(=WK;Y?VV>Q>pk zZ$v3L!lIt^-BXRtsHbdg8tcG8_th=O570sQ!A16VRMBvM*r>S*Gd_=Z(tdcT_xxYX zD#JXoH|;IPiqyhGx6m56+dLuw77 z*+`S|b2CP;+=g3fAL)eqdfkM|bwwJ@N_q#)%z@?)TL>;8Yon=0t$e79NOO5$LJmwr zidsuZs0F=6&|relSYovJkt&ph&*YZCdMiN^ceRKG}kcbEJm$tGj-D7Dn*33R}yK3@g$N z%^k1c02HaqB11KbS(src)3PYWlErWTJvZ@;sr1YZ6cE#9ovZ8N+N71_P%=TQW7?|R zj+!tu2Cmc1C!43Og63Mc%{B4(!)>}Ba5L|KJ95p9p$N^r3~&8l3y)zmrQiHPYV?kx zS|3MTxuCK6@ekA?l?$TgJib3=NXn=1M9L7g*4)QVq>M`YG%EM8*M(7OcSYq+;x@{i zl!SOtquewo-Q7r2lcL5-cQ@C(WdC~J?MMwPNBSdC21#Ko|LDa-1* zlgD9h99Ndj+cCiFmDsX{0goE-gWQ)EJ~%)_e(AyoKO#$B4tk7Y?dA5zv@8dVxeugQo_EHe(D|Ae4c9(OfazozuArZ8@}DeO6lFH;OF<>^qMd`nn?po(N6 z?KenV>xkL8`1}Wq2rE<-#P$Zp>TztWX$|Gev8F^_1~s};Rbnc1!~SR(An$JASWJ|H z^`1CZ>2SigfGJHkJUaFGYp9)MzVMgW@B3t`gLfQm^sA^mx8YPxy9+~65n1zY%y^7P zG0*NKba%Tm%|o_IvF65V=IGatd)%@+Gt|E~AJv)69Py{k;%h&h&oLd^T!wndshWg~ zR3D-7Eo4U@23;shu_hi@8R{Lk9R4-R=JAy0ghchU6|(ZLe2u12xg4=kFAe!Sf5F%j z#Z$O^UE~-E^$$%vr<&oS{EvruY*s}JJD-BBT%El5=hX}a(!V&egz6n*UyX|!s+!q1 zC!0-=LcJuv$KNxk{xM<`YemhBt*U>s$wCOri45(*L=p3SqE!I(3r+k^MYV}#|0%g+ zIj+yTD4(Ifk`33r!-Z&&Km6Up%P=7p%UXke9;g_MdaLSXI!?y_emo4Xhuw6nM%K{p zBJrdzTuqbNpzT6)6%~eC5C6QV4E2ufhnxITQLJD-P&pxrsjSWEQ}f-ZKx{wM>R}bX zx&O{zmLi$0e6zzoJQ2Hd|L#%-fA_b!C@?y|wI>(V6TI%K_a&0;_fn-oi)gyVLz+&g zS zQCX#OBlHtIjuBFCgsrb{8kL(gbVS-jICbcVp}J1p%vvWP|KYpjp;Nd~Ze5eRoW~~j z8Zx84e=y>QGEA6{j4_CfM|VCqpK%}5opQ9+0_0O%O7356xGG>APd~6S&Vl?t9ay=J zoF4Fl)_QE14e59hbc-Ilw^Z6~lOZ zmMD(LM~qB>7TB(FDn9(MMQU6mx4+@2Bt6!>wZ#qE$|%=r)azk*zFS+xaGM%7ZW;4B zZGUo0XTUf0*!l*yTfjmiF3i)@YfYiYcblK5yFTFD&nf+l_4Ra~Tt9aw(np+J|C#Sv z{`iWXA8?BiZ)u~a*LpX9o-J*->0XxZ!tqYNR?X4#8>$JrRlLo;?t97f3i?6z@?*Rx z8vFOH`j2B-ILhsty!d=OeBa5zI9U9ehwq*ITtZtWC{b(QYqX!Sq*f0Xe7}}O^je`c zYWo3W1^cEfXq(6)_WinieJ32pS^P!+Z?iEwP{AX)d&<$IKAOg@Vp606&D`3{tam6Y zUyt0!*k@I1{`$*!CiPMdo@;&<^#X5BNJ390_4KBH;YU4#c^`QGTw9E`(cb;$%gH#) zhw(mvF5vIMpQcc? z1=nTAcvkG0b{szN2*roaA9#Z7QFW@6VbApOQRlC;2g_;x!@EqxE1+??*uwnu`mKnK P1;_M_xQj>r-?{uJlOzVE literal 0 HcmV?d00001 diff --git a/tessdata/netwts b/tessdata/netwts new file mode 100755 index 0000000000000000000000000000000000000000..0bc501bd517e21279cdf1a789c2ba3e406c502fc GIT binary patch literal 1369167 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

4j6HT$JBA%CeP{J<` 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%0A7L)$-tI5lKg-l_@L(o|&PVm_* zIMj0!Y9e{B9@#-w&v3GtaQP@$4);-uiHq=8S{r)qtHhzwG%Co^hr+n8q|Q%TpuQ{< z)*dqiXX|~i{+Yuck#eUVm^a>z{K^vtg@Ob)b0G_2!>_SY zTNaXgu1e%tT9&}|xfd)sBg^(~@B$eHo}=12gVSd3bEEhEa6 zP9n0vw%n;0;okn+muAo`3?+8z+B{R88{ArH8 zA$?%x#*_)quoL+lR{i7IFxnB=hSLsA_u{dzFL^haH?|Pgc3vdUtzuw&RuUXNXAP%$ z{?#RypMo8qQ{Y>_Gx@r0KAzSZPbVG7r=eDjoRoPl<~o#<|8_XShr~94{kt*<8yo|AN;cz*!ZQM;~DxtcfO;?psN3wK)lMJx?zmODD3IC8)`H zA0jE6Ot-&YLEc*M->T0Mu$v;C?fllubLx>bYsbI@w#~tZ6No>Tu6S;rx8ED)%6b`}IfY z&i8xjombNBM&69uElJWg{Uz!BF93G_6=8m)wD62&58UB422uuY@Sa5r82J4R);@}Z zqLL8M2$#V0O?fcxH@`N%N@f4>Y^-%29^4EgQ*OnT4AeU&go2(1R`hHH&+%3h=&yMz z@au7d#^ziAfetKxCr3d&jr=>i3S!!>lC`TpkVU%IHYLu@#MMfMS^8@xIkwl0Jd9aD z)nkol#`Q=z@X~;MX1YkmQy1#w)QMq^rTCHA#xo&q5pfwewBNWFhBNuKuIPk7vGEWZ zWtZbELs>W_5e^;tGtlefCtkua#A$iCa!k7r@Dtw{fz25wpH7LI=NvG?wpZ?U6}=X`{w5cfvN4L~9vR z*1Q(n9ZbQy=L@JG^dZA5ji~I~spPt87}*@s%xE3?$MlD8C2wwKQ@giMc;>SLIKSV^ z7>Vs=GLJD-v%3wynYUnio*_3bZ7#ks9pWNYIc)x=jdqKRP`voBK)az(u%VUrMjQQt z(*BdQ*+i4GacSUIYaGNkC3o@h3|~C^?pn>|Gdyeh`fUidI06L&W1#fqD%k6o2FX`^ zA>DjFsIHYJaiUL|tcomxBORo}t)2Zk!HcHlTp?G(kFh1hgp7zK(p4+{=p7HB6_ORG z_uxIIFKxhMQ#7z<+ZsMQbBJ@)GsfP5L~hBkh1}+!yi?-BEl6hyNxqDNkavsVmjxY| zv9gP|K?IODd#s7ilua0(FUQ=u(+lCR9)e~;8l+svfHs{-2v1uBTGs4?`>p|XvVZ4(H=)mwKviedSYA#-cw?npbV)|fxW>5%Qn~#j^4NCq4ldrg6yy%|)4<4?^!Wx696rNfgoY{Y zw2Neln=a8#RJQr@=LI>wqZ48lBW&NzzsClhaKSbXK1G`H98Gtaw>c54Z1hRpbtCp# zeZTOglM1QLt0M>8lq79ixNw~vR+`n}J{=BM#uRol?_Y$F~ z+_(_SlpL6M`{X&fhB{n#_7S$v*XHK$Oy}G;?&qeg-h&^@QteVc0pD4zCh% zC(5!4@6I!oyBbhZN>zBJdn_sLtEKIZ$MM{4!cE)C(uVJwarwwyvQI*jB#(C@XzoBX ztZvg6D&-hy$l>$7g`C2(JPhrYfKwiG`2XT09Je3>lQDX5TBefs*IU7*1&&ajKL*&P zE8tnfCsgkj5DktnAtwMjIIrNy$E_p|D zW_HkHkH4Z#>MnM9Xdol@`wm-Zb&r{}BY;h6x=eEyCDDJp!zx{l(wlr&e}Y{T$&PFy zm&0U0_hd6FuXJPWult~=tRwxmu@}x<>Jx|^z675(nUO@X?GUxCljQ3Rk**~yIriru zS|`}y^oarFN0c+X`ga6Qk31E~r|kvL;9=6w^YKlsnsCzQ7r0$?90pnUV)W+e7*Tm2 z&wcw!Ys0>w(ML6OzN&$X9tNS{x)1HPn?b){eaR+tgtA`Ox>)tf5ZbBzfq16N+Z4Nt zQ(=_@DYM^i^YfA==+H_Kc5R0??w(-lF%GJ)x6|k|qNH(s666KOGXeW`$*T<~Vbs*s zaKFM;Aa~gu0==bqK5IGYRjWfS!+!kmJCqZxy@8EVA+}ym!W`LasCw}Q z9tu8)3#o+n1MFPoQ zXUOqZ05ii*$Uhy9`u27B*x!Bv!c35}hA53am2a(>spiFiO#P6EG!8#7S%Lq&q&A?~9 zQJl;R7F$>R!8rFyTtX{xos1Q3m>7?%9&X2D$>ZqrCjPFC?-`ALJBg-*EoN#TxzZxb zE9~5fyffphIJInUCfAIzVX@;U=75K!w1rp6^Ieo#T6l+g&k+HSzco1&ni)lc?VuLZF56ijgqgqva27~HxMuQ?pY6`x*X zY-AX!=MUlWyUCbT;f~4shA`FXIPE`^MLR4K=m;|pl{HQX({**2#5jHSdH5_)$j&22 z4;HYohA+u&XE`#ebcDQIe}FANIg1EvQ+d9#HF`wK)Ao|@XxiP6R>fyg`rtiGZW}@k zKNUEjo>+dDgsbJ#P9+r*& zNAt^bNti+y?Nrl;!0?^$PiGLL-ccrVFpn1IZ^DX4PAGn?m45j%0HVsSWcGnxQr-Lr ztW!U+LmwX!aWaiWludz?jcE|q>_WVs_OKoEW|LzzIUwQ?LHa|2X<|$>eOlGV{wFe< z9zT4ZjdTYx>LA0k)J>#@)L&HTHPibfVc9wIdi!KfjPI)cm#Bf!iIJ$mgy7-;DUdj;35(RldG_>CbP3U; zBoi*ctD zJUIDm5j!$!hCj7K)TGG}FIWt^-Ym8Gz=74M6ezUb50iE6 z;Lv^E@uSHzyY`*}?doeZxz8D=2a2)5Q`J}+7)m4-tYALL50aLp9<+9bA}f05J`FfG zhjzNip~`kC*7Q;$)!1CfjdB1?YrF%Jil*paqK@IF(wxWAgPgr_FsEt%4Yd#OcQYOb z*syp5Vtervtlah;xU1e!QvVe^Cd)!%0SB+&pM}JZ9&l>@1*iOWK*(qzM0K(tzjzJ# zXgZE8Kll+R{lc8Vg zc>7Q$`>ipa$s9eGcG|w6j)qIg8{>`S)}3H%xE_a@NhPqbgu#T(<(N8i89K*JMdxut z%)LHW+CICDp~|xmr`R_ZFEq5 zKtES$(Nm&>)T(PQYKG3FwZ-vBr!15q>VwCw>iwB=g zLf32aXv^FnoEN5#`zpT(|ENs>_fb=EeP27-B=ZTDX1@W6MxIR(@l7B%b2~h{ITOrw z^YgyIlMtKfK?ZU&Y2K8JaPrP<2)mVydzML{m6{`J=$XLSfi|?gqJ{f!EE5(lyo+j8 zW%%OMH$0a#3(pwvi~-Ll7~+$Qx5Y9s&b|cq4~C%prq%R|-C_(s@e8LbcA(@sTNHcW zhQ&)-G1zV#^$)vmvqx<%hLzr;CPjMS%6lnSL^wnC?^VFYeiV#xSOSlvECge|6G-Ek z&XKcjF?CS~pa|^+DrCelNK&6wl?= z;~-8#DyE5-W|ZQ|m=oyw`#q|NKV!Ze@1`C5x1)W-a~vE011H+s(Ao3(oY$NnniOyv zrS&(HV|#|#6+b4k8Hyi-(U1Sabt4c#`}ZJ<{V*T_{`}#mj&cRr5%iJ636tdSMbf~^_*gR6V@6#aUzH1vCH-c zj%oJC;AOioB69_8UR*+K=GxL~{U7vP*K#zM*G1{6-b}n=6}f(Q7fr61Lc3BV$;jg~ z?BV2QoHFg5^~}vPA*#U|%6U%WVg5V0IM5A)KJ?S7fAZ*Swg?qQ>0|yf8|z-aH_rD!@2h;DP*UE;QecuW4{?TY%i!wZnnna*|MC928S76H=%Hl|4ziZ@b7LtZt>K> zX&P#{YUdGBzTS_{oIf3>XfLEu7c!Y|>vM!%speqpbP06|SK+!CcZf>e4xtyTp{jTW zq)6;0cZZxn*?Aou7Z?V4Z_4oC+Q}qn#Ga6!cgX6{dQevvA)j~V!H=n-FnlqMp9QFb zsHF&GRQ-euB`pYb`9a9^YxHmQM|y2#HNKFS!6#WWdqR;= zL?@VZ%f14iCrb28!*L9JvK&R0@jQGtC&E6N$im(I0@CCL!$}M2qI;X6a!ffCJjoVF zdpd!W%u!mS+e8QW`JfYzQIi02*N5A>sk-b#uMUq5I>KU)MFMdnEl4t!5Xh{a1ver?=(U)`mG=KJk>j(;b#5A2J^B*Z-l>4DbSZ(N&|2WUVmfh`IR!i13!rH85A5E%h?@}b zol|^Z0#R}UV6aXOjH67TUGgTBXWoYDJzccQ{S}>Vz7Y+Nwh)nT+vrU(YgklpjTqbM zQJxgf)a#_vuh~g>M&&$hjO)NtF+RBQ+;Yn9RHCPvXJY!TY82fvj-H54BF8=clIlC5 z%p<=ndNElXCBn0WhbP4_o~tjy?R8&)suY3!taC7`)kR?b$rYF1yntVVXVc|(N+D{0 z0O<2>w3GW7IA0b?{6?*TBXTB~$vScphqRE5LH@jRrn2)!v30`+ z^n?e`l)UIn>-Tq}%$}EMDwSk2+%k@YJ9g3wYnA9TDN!CH^_#>byONP{cI@P%VRW~a z7hYWAZsYy59-(Ff8`D*a+TVYpsBb){#Z9HLmv7T&=3DXJ&AIG>Lzkes#gp`W(uKjB zO7Q1_D_eAH5hr!Totqi*j+3oFN|M&Hbj$8>cvdWdee2~%V$N4S(@v zH-G<%mOlD)eDxwaHfuhOiCVzko#0A~dB!|lwUADHbb>iMb^>Wrdq7TEOH;KlMRL@M zXHe|v#F<|FGx_dGAIw`vB6?RMn2(_MEDh!pWjIkU8#q1x9B`{XkZ*GB%nHrHr3dz$!Owkoi6y7fLLn-D>Yi z%eaN-M3-Z76roIB}1V9qX#ywIY$b=2Vz|7W6a79L(9o0vF@}h zjXN(1&Vv>FK2!`F?5$94eLZ?ltrv!GG=h6a9t%XpQ=s9X3=Hw-vsY(5F~uv1Q}Hdv z;l3L5k`5D$O3C0o<{KeqFa)kG*a~~2jzJoI3ao!7jLne}N;X-rU2AR;E`L4wEhbIs zqJ3$Z)Dmj^Yb$IlTt_wsePnt+G(rBGIpmkje4G`zo3f|Z(=n^-*hJs+tlz&kP-StK zj5NHUr8mdIC_a})%PYYq|0f7#RiOIpO@Z35jX*j-6dJ=HW8|$@n3ww+J6L;~qdXD5 zb+6~|z_Vb^lMqM>m51A7_d>o*80;=Bh2!J+OkT1fsA){(-3CgaZskRI92Y8OgwZ+g zGr{kL1sFWCgJ30P$jrJ<|MK2xXl(#x>w1uXZ4KUM8quhx8Js>>5)Y}jU|PmI5)$jF zU0x-B*1ZAWBaFds*LUc$4H8(sPe8*lhPbYka1!S;@Q;xks_(uH{&$<;c9s;Jm|hIE zw$Grs_B^D%SPiGN@*(F_DfNamS|XjuZsMu$MjhaC`8c#!5tp273yui{L*QOp|q$0+%56}~U&$EN4kczbE}M70q^l!&vMM|;$UsCC9IN>g-AZnAlBOf*GwKm zxM4r2KhG142?~Qzw=_ZS&3n*j+6A$B0ibSjm~hv-K-((>R5oTp&ZbY0@>vwBoHl{M z*DbJU$A1v+^bg9+J~PeM&#=(H7tcR?hu3$k#G5h6cump?uFa?8;E zs1b0R198{-HJtWG7tUd54W|{l1bg&Gz#{Vns3j>-Jps=M%NLaLz#kBG#EgNna*Z--v-t(xni%`4JwH~>zUaAKQWKRTVleH%)uUb`}%66Oe3l-%a|?XT&`-AfqtKAqMNdcvrM zGUNms#A3%Vu8<1?hvPBK0sTlp_;^df!o8ye=F0-%`kp}gT)vjxnw9aw!a_3YkclVu5Rox>>)JDk8 zaRZFb4n5Ku9sfL~dt(U4GA}>v?&t88F zk~V9Yf(>afzyB1Sv9yt%h%-Uc736b53^-)8(s3{H+oeW zV3sJ)4KzZ5wu>?RF3=S$7;PaK9kyNAT5gC(-rYp!$s+i^Pl&hd&e8{!yJ#+o(BBPf zanC7H^sviBGLlN^$||0lx}0_5ZqV|b7DOpJoO-H%r$l8f&AkBJ^H~90#qB}vf|f6L z(ZH6=bC1HZ-(Rry$T28N4JOsgcaczS4YD`rHCa2jmNGB53zv2aNTuFkI#yDTO?&1< zHC`%Ot1AVvqo2&AS3+b^b+!h5)$*J1aS*Z>mKANnD#l1QD~L&r5eN3Rcj-u~Ah-aIlHPxh(+$E{IjER53W}}rP`gu&<}a9u2@?!ZxAzV(gIVNn=3FS!HYRN! zgZa7dQ_x)U7UW+t*7LO=ql-ZX#_yNGB^wdNlHSs{^@oMRNN3vQRz{PnoOoZ2D}A!% z13k`@95S5#VDigN=w)#kMe{sK;Mh-8cxwuZM;xXq-VB{JxrNF8^P2hu|G>ok*{CM0 zWU61PfSUb7IF()nZlx=DrPw-pu_6%UA8f~Pi&*B?k`S2We}MeGAqkI`ec^dsuCRUo zAJ}a-!sB{0=y-_}=y{N`cV+EtMs(jX@BDW&&zDBgYP0S98S)D=IlGgUlwU!yE}6#K z_T#cTGhBK`nWnZ^A+swOk48meQtWs7Drq$>3#}wu&+KBph4OndHx;7lai3V+dq6Bg zAH#m@6(C&p0vubl>Cr76{5`;TdL-}&Zn-Cl#+CgzPe&TERTo2s$RDVgm;9-IO@EiP#zvF_QBY1t^Ax?b!C(hk43m>}l2$YY!gZr1igJIEclq!$J-TFG{uqmD% zp4G=zN%oV|(;Mrys*BU^&o_lr?!TaeCOVWHI!6a%Zqg&i$FOC-FRAc>8Zp@s4ca4l zq&rFo`jK^bz@QdyPtzk6dW6)r6r=az0i62EmVQp$j;BQXuxwEbid;$tiSOTmdT5d4 zx@OF|F^iMPvf?BhE4UL154cgUx*<{HD#S@{p}8f^xGq(MQ&<^*36CexrqcCnpFTs5 zZJ9|&tm=>~(6Kam~rE&u===?H0qPS&0z3e|7M?Y31i|RHIHt+y#oBEl){?Ltg za!%vJb)qz6x&h4EQcWcvE7MHNLRvOp#y)cVhf7E5IZ3ZV8m1Er3x@ZASH)Rj;Yyx| z^JypbX>P*J4pmsVD}lVz*0&0V_pM+UO zA)J)T56ac?ob0ds9kRz)_V^kWdw(}!N}~=H;t~4Mq6!t}?!$RLo0x;6R*)Ntr^({` zvl*H1f$WSF2EsOMgE?RP$nyzlAi`WGS@Em!;5Ocgqw|huu?~@KhShX_$YP>juZN~r z_Mq6AM`(O+AG)|7#QTSXI8o7L8ZVkg%c&ZQ6fTBJjfG&cf3GmMUJEo@^l(M+DfBuw zj`sX@!z5N7?_9FLwyO^G!uW^uZDkGUFY;vm`>sl#E#4tKQ=Y*rjL2eB?u^j0N+l#x z>MA*B@g3|I&jw@ba^6Ex&2*32X(jfF_txvB;R3}iTWqLCyQOR$gOWr z=$N}MOi|_}2%gtZ;`QIryz3tLjOV>dZ#o8Y!VtVLS_3OOhq25r96R|t7YE4&ctT%} z&N=T2iKk;YnOUW%e76?kHEYQCbWNJ;xK<$j^c-xFYb7UNyV02No%VbIDU zROmx6nT5=S!MCt%%XRvvRhs!Y){9)5Z$w&GUbi{3{R>IUX=IXKY+_i=qwHqQw{_g+ z52RnW4d*?$h&pc#FyF$Od~YxXhqb#gu}v8#u5slVIqG=t_$TmEas%&^YpDW7ddtq7 zlPi6STXM2!x5jn)>jlz3`uc2kUn1z;I1lqT#WCWO6kzn3>Zy*oV^8^4|HKc+$!M@i5~Jjh+v*BLo9{m2HAW1Eh$KRGg?Xc`zgh{Ne(b=W_71F4PE#j<1R z^!2|SoZC7Fr|2BSQ2rggoaM?kT&u-Mht1@&tviXY-9hq|(!gi&91?w}lm?vfh8fMx z?B}z_aH3HKLYs;~%etQI4f#u%97`gTKi2wdObBbK#*mXveRSqTW#*bvCT=lm1+#`Q z*mj8b%kMTscUyv9qwiV&I8hA=bpdpF$vkp4cn1w|i9?rv$DuB%5A1IofUb`<0y&v$ zWT=P*2ZLc)|K#fBogw}{=gt)P?EhI{JY^>- z689!4>vllLiUt(e)>K1R<+qA+M$yqOY3b z`Ri9OyflUcHd<1VWoj6eaF;0Rh>_^!e~Ih1qYzv?8t!&G!0V42V9jq6QaHMm<- z>wXbtw)!7o_Uu;jvMZkNfBNDKnP+6==vk=B^#SR_+Hg+tJ?xz5Po6y10*QHpr0D4? z{MWM+(=M!ov+q3c+LTFnS-1*IUunQ;`#(_GJ17_{^%XM2grLR_0qL?8C`}y^h?uPs zXxH&f&khwpi6fx=FPh#$98;JiBz7F6+`N5fQsiE@-c{aLvns!kwS zXm}TH?Q(=|X5$6M^E}|W`An$hJJ|jDL(J7&hFZKEv0fUqnpmE^PdpTU(%EG*!O|g$ ztX)}V)B8>p=7c=}U9MT7c<&iv3O)LIeH_4 zNI3?;xo24dxjJ!yP0}iXT85**=@Rd~HIanH(vOKSy&8%dCJRi)t`sb?sT4Th%7hoW z|3Pw{wm|)l4&10QhFu9e$P=gQjNtnQ8trLF4xCpduigPuu<8>D7e5LbT~nx@{tjCD z`7Qa*YLTLIB_y`&0ahHiiN_RlNQ!kM7FbxLjBq-5E)v7a9|M>(oDWe>4WQ`X3U^oT zgd+cg0^M2Rfk8<$y>hgHP5k=>n(q38!%)6J+rt)0?}dXAvlFUMEPx|{&b$*q z3Iw-YK-G#R`DyLs-mN9f_Np{0az~f$5(^TknpOahhcl4Ue1h5S17 z1!mOB;s;-Q+S0p{bk?1~DM6p;%_t|PcgbrEJG%p8)LZ#EQaF9l(+IdK16K#`#Wd{} zj5d;j_?7@p>1-u7+zw?_c*d4p?@vf*83XmJ>frFv+cp8?13)RwPH5bw1LE+4@m+S` zx~gt09ox2zM$t7kEt69~%u|EzJfO_9izZPCj~eu24CqG*6RZ184CtDO!czUk=-zCF zhZi@JOvVrukDVY-R`Yyl@72tHn?Nl7X-gWvJi(&b>oIbW=P6l_=0yJcjh@FW*i2t3 zPW?HHnce$wKc7!rUHlVl2a93tnIsy)`$pe84&XeSGTJ%y08i$0W8WxsP^~R%X_HO@ z^L>RO)z~FTbT+;rGCQ6#(>_lYUi+V+^YF*24dXZ&WmHl`vN9@_l?KjzKNW?F(ojNs zdX$k0}pfanL}wyaJkeG z;pyOJjGkk`zK?TdA7ltQMP+MltkVI!)3XQn__{Oq4}L?-yaHT$I2u<;-@@?LcVO1M z9R53R3x>|wX5F99LHE72Z1$yHZ0wt##Bn4WL=-tHsnv^FDQo zVIk|kFF4`eHMpNE%|tJ@rn29zl17naoLVrEU14h~P|x~9pSY|Mh#Y%~T6_4p!-SRi zQ>%z7iPfTNzb2jq_4q~=xFDLHni_s+_D5__GnnQ=sZD0p9U30_v zKa6mi+9vjzP8TdP)PpVOTZrwNQW(>34Jt;a<{NEBaLngYa>nZdjZ*$c*YNjoU-g;H z1^-+ycs@&*%LP-xQr;sh;))?tIVSpN3sb1W30Bv=7EC{yg5vvcP#L|wD0;`4hQ^1W zOnW+~uKWOBERg4>ot=ZJOFgh@ZVB&RFTs}?p=d5}#e;)=m|v@k=h(}_M(3-rJVGA4 z1FT6v4MXx%H<>fqZz`^=(y1(6Y{&j~)FnE_ZRD=bZ+iXlYdT-Q)ZDh}G`S)37ezks z9S!X$+V8i3LH@gG;oe4%Cmf^cyY|s?dnx++Y$!c47)b2{R#2Oy9+IYEh~wqFvEkes z%#?bLAq}(fcJX0M*z%Yo3RaxfSe}C%Uxcp`rlXtK2p;Wu2|l|WV6DGDEM9Ypne^O> zNRZ>qm228CW2`jPL}nB5O(LLfvJo}61%uVR3iurVjh6Pjr&hl%(M9sSFU~iTbZI3J z)7UwbnEW9ngXh2`Xf9nj?<&2#KA4W<-@E+&HIc`jS77*OJ@#ar;$6ZI(Z68|4ni3| z?LNzC+S_tF5>+@cZGTRB`7KWDt`8RYh~OIE5xDl`FU)!U)!a~SFW==n3K2hJ*!S{Z zKsE6edu>4-y#MAa{H<7lMgu|+NkbSGP9`62InmO;|1k$Omhx;YbL#O%l~yGE;JHgz zNRh=+xMd@Y@*b^Z#cr0y>dvE@4)f>*&sLNj`yBU`OyRUwZ^WyqGqKeC8{Y4##M}J) zNZ;Sd_!Q44dP)I_ z*_Te1r5=P+2kY^|ggJO-so3?&ol~08j4w`Z$0%O>Tb@1*D<@`PTB{De zEtrIvR*rb(p)@p9rh}--RAP5ao+(K?4Y)u9GK$Jc!%YoUIay_Zjt&;+EixON`YwHUAi9MP?Ihha&%5AH}x>$s}1+KNlWbTC7)^X<$D(y z^~X8&4PUwK8L`~v%E{bRrSW)Ebs?&B|AIcA0i`7S91RR(ac|69%qw||BBuRp`t1|U zs7fDtW=NCiZ=WT+rS$_QdzX<-g}+$SFl$obkVXo`dPrh-Ch731Azy}F;OwLi| zP2!jH1MF`6A<)!I;+*qH)U(S_v*|8GE$d((HQyxSg!6Kks348-e!cE~H%2 zpNyZ)&sE-ipijXIjy#J*+Zl(^dihhpQx4Qqc#E!ot}2kf_y|xm|o%5i1;4Z{k3PEx9HOxpDkL~|$AS)<>9Ms zZdSw_ZfuMwC++ePiyD_>skQ|y__q2Wft6R_l}E`Cc-#MozjXhfso zYtU?8NpHa{3l}`h|J^gbd0?_I z0gK*wVJYwJO_$=m)?0pK&RI#kDtZFzuD`(i(>}PdSqV?OrlOTmKZf47!iRaQu+G^B zeVVso$;5M*_Ljr!ub0sBV;as9ErP-9MQm-@ewf|16B_rAM^~SF?C<13wxNBIx$cFV zu+Z27tc?|*H)|C9mELULxt2pl_Y3L_1i;&DV;sw82je@6G5*90B$2PsUBLz;lKI|R z>~1WR`H7tmPUGW)4eVF!a7-NELfU{hM%e1;N`QZ?RxP{`* zhj!TbsST6Y20*T*Cpw5_qW!x@PHgKVZo7seraWkm;Ax6z{bLr0wV9%9@E2g~n&IBPAtJIbi#h#14!xgmLXQ)- zvHFD{mL2$pzs|40PO&fe#AX)0zh#6cLj9Sp{2^iFyKb`W?G>i|PYtO!*l*sWssNc` zQ7Es`2dy*fz-5*>t0!dOLS7O|f8B~oV=BNvzmthm+X+{%K7k+Gror>Gd@gIz8RpZC z|Adn)8o>JIZSxtEp3~)%XV5c$rErX_F|JRn!EDcBh}NnG?{n8lGI*f*uM(`yu7`}N z)=*w<3Q2BFB)WPkYAZ7InsqYO(%VKK1zaZIJ^ew$J&c`IItS8urmJ&Z0z68wz~lon z*}J>^QQB4(;@2P;PA`MNS(fl9{SL$#CGhM5eYiI5W7X3S62x%xLK{v*rm^?-AT2oxz`hrSE&Zg258_{9V8mCJ=g@hxoP^~l**GsDK`}tZ}+BF_ChS#BA z#7$JU&_VSjDSuvBuO(D z`WF^p@^dMSj5~lAPcNpLSvlr46;(*0K4WQ!Emj>9ARJqX^X~WI<_-hM>2XC-#a{S( zu$An+oeDHFiAnku0Bd_aQE}igXaT#b&M**;Ttn(&)GIKKz4K3j~hpH2r$<_N6l z=wSMJCjSLN3FJInk6O$V4F9EubBok5X|NGZKORJXK3}}fX9cHmJW|5pOkpEnB`2W8)OWsM&3B7LmGR6+VIw<*_gh~lUCe6 zMvsMs3dRiO2t;2M(j(D(1fyPU5RCr8(Xn!=Y+KS@D&#pOO}9g+@~(4qq@;$%1o@IW zi%?p6GKJpQ{)fp=I>Vm#nGWTOv)GH*5e@s(0eX(X#nJ@x0f{6KKNkrNe>@@0YcAc= zUPG_NKc$(P6X>I^38;VIAI;e}p1!E8r-e6H5YMkAu#$IweD5v?NXr#WJr+WxgC*HF zN4nv&h9r8`=uxq`-|)PI3j{BUCp|Iy1f#Bfrh}V#LJr??4wA4SfBH_5HhG?FJ=&5c zX{PcW%XN@fc%3bom`y|z`dHoj7n#uU&!BdbG>n94o8^w|XYD6_rt-4}NcZ(YI$G-w ztE2sqrmXzKdrpf<@ZK8g?W{nSUJge6wX5;*wM-C^eg#Sus&t*{Piivq0oqILxe1kH zFiG|_4LZ_spCrx5a>go@aXMTtFg zF@816#;GJh(A&>+rR_=TH1Giq^Lsq`x1!XC&z(k4-W4h`4Ci?^;r7+nss66bc&6Hc z96HuQk8BxDkK6gt)dtn{Ap6>U+Jm?B@1{4j=u{Yu{Bwu4@1k_^cit)YFOT()JWS=+ zN6?uKZjkYcK*C|38TWb>Y7IM(J+%((iZVACc;i5BM6YEI#>Ug<``SG4_BapWmI$!eAO6lyjrj!UU+v?ZFK7 z7g$)fh5T^&Ku_+eA|4HcboCLAh7VSNOkXE?`rL=cE@=^rzFx=yHPz-X^dfGL&ViKn;$$YDa%#DYS*%zQqmRIJ0B>RLZZd=&^g*`spR&*!tCKV>hI{y+9v*HVipV$w?jR^*ooob(P^BaZ!V`)^AzZ5 z30$sGju8;du$<*%{@%~-WQ<4$LnPCgMU!;5Hl+$GKS}^<2QhJhpgSD0Qr@rz`i0 z)6e$XNZaxZv?2fD*VBfWWOoKlEw_^iw--~bEj?_Atu3A~$;B1byNIanQdl@GnoRdP zPWn1U`DZ{7X}kRw;zd?NOwJmr{9c`v8Er=P+1ZkTQ)^&ip+2R7f%H;U6*jos!a#{N z7$RCk67_80mwqwz9M>c`bol~}zTN>j$tv9Xn|ry7v)AL33-Q#eUQaMKeH)!=d5&IE zQ(<}@_u#NF5WQoI$nQ-*=^m#YRBg-!^8L|5+-=~)+`VN;X9^6-(nLeT*`*POm*dF| z$yie9_=<5%JV`FF!&ES>o#5PARI#*;^*h7zjEjj7d!UcFF?Mx((mLl;eG zG%HlCrVFe0&?kc`G^cex4S%*%D)}kFp9t-!#W1`tGO1+&%^@oz+?15|K zXKM!x`^wOOR}xHJYywW23-G1oKjFdoCh)C)Il0v9Oe?wrsK@LXIQ9HQ^#ytK+42tG8Yfe zCrxWj*)QK4`ON2Ewz>HeQ+s14RS$F`7gJ{7vmP zUOXvu$;2h+e!)0qr-%O#Gd8lIlF3d>ScbrQ2fY{P;Z# z$=XD{6pX2>);$`v-kmC(c}?Y)yF$<%F~L;*leDoqj{fvNOV_xUkf@MTRC<{v&iXZu zqzrqL+7J%T)%vj4y01Xjf?68Ge`hwm-%d++x6{R5AF1H%HyU$b9Uaxbmj*snqDNlH z(g?$=G%>)N9*Z@m>YvqEmkt}E_~sqC_WBGnm~ThkdcP$a_g^rj5f|vxKy|1!?x5!K zw`j>fTiQ4)fCk?2gISX;z$Im$XNH1KmFQt*Mzp+~4rgYKlRC-JX;i?nwz$i^4pM#8O@Lf5Y zqajH*?p;PY=bJH~N5+wln@SlS4`=L2UNQB1333Br3phD>0h@h z>i&#p%0((t-@32VQm=^C3J(h=9TBJ3!grFmu4^>a(1A8w&7i?yhP37Lc-l1DkUsA3 zV8Tz#2W$R){OVMBd^>U;o?9NJhRN-uaj^+0pHT;^c3mJJzWso&1G+Tg0zX%8$R$6Y zxsbAk`TQ>QwQypf4m3T z?0bP&jb4Re?mV0M#W}hqY#u#$Q2|%K5G77^DO8=+B_XY%^zNdG^vld&)Iet+4WFZc zmwSq_bPjS0c2FEu#`EzGmUA*IWOygpZ|rt>O+wn1fvnt1l4^R3$lb9)Nsl>X{9^|u zc2*@x+I^WEaJC{pww)tw*#;ojejc(FaT{-cpmab6?`@@N7!F+pNP!xf{w#$F|98hb?XXouId_Gyjm7N zYiV=pmt^sBnLFOEe2jjF0V5iH`Mi5H9WB|87NIlAt?$QK`-y#I<<9G*{z9iPveb?V zP1yyxzIz~edkZ{GnGbs{WZ3g7+HtDKW>)paVRH7)32OCMfi3u)2kZihXN)?S;1)&r z{c8|?9Jisu-cBJB-b1W1UtS2`}5I5tHbw*bDKP0RMI&JW>cCi}p9OsjD==@v9vBwP`og;_pJzE&Z930o7o3wiv>a zWO4EYF|-+)31;>WKrmARWffI%TBAFtsif11d(&y!(rQv5dzdm&h2+J|Z8XzLVk!7%pJ58Kh&izrT6d9qFa5~8r&&b5lfygPuHnOB4NiJO64oE7!=*ki)WI)eZk&|=c248VO-^>!4Q`#`Tuu&+BIfJhQ_^x0kH%>AV zi@tIgYFvU>$3^hH>4j+5G8HPnWuU=4UsONw9$NEH;;tFHA<^J9NbvKfSGsa=)@(?4 z{q9<5DqF*}SMDW3@Mfk*T9d_1y!h4xjShGWw_j_K$4VCeT zTi@dPixxP3lmj|l;yYv6Kll#xA6R|C7E1FJ(QNuY^WL|6Ao^7|=(b;io~_r(Ic6K2 zkGgwP?5q-3aQ!F#RF_?}aL@Kd0i&F0!k!k+?$A3<&uR#r0(~K4MkNu3M44}Jb0z0o zoP~d1ijt|Quj097ddO>9ENmS`>7T_UE3W;RCvY;G16su0aC9f;Ic-(Dfk#ryIy{ zf(k3zmW@(tCGpO+Y^?M`jK7k}Da{(@bR3^^^S<%tm5L(^w}ii94U2LC^BbmcJD@n2&JlV;i32>%%4>7fKHB0_&C=6dC1@1>RSi|=FV6h~Z@Ec=tGpU}6AN?)d zGQZsXg2fhUST%=CXq3fOsy5W>);Ph!sD*-w&v=g1lqC8xnWyw>PvPX#`FG^=S!i-? zBhLP!h~}~PFi-0RMinl@^!Q9%{ko8SGmHNj7r)~aWl!+hIUcGE`XKkPmDx%!5hi~8 zW!Sm16t)Qq`I_k!QZPZ5nb+S=>c_2y#GP}6i<&=yq6A@c7pjx(-wzPKhTH7tMm|px zbcbzY?oj!Qw@_gJ3Kopq5Ud`1O)we8)6rafV$h@)V?g*?z4lgE@z19>t6FO zYvuws#P2J!pW+OlkY>X*)Q;ohaTqLXy(1d zGti>f9T}tD(D+6bSAMC)twV>wyW5pKPA5F^Gq zkVoNH@WTdvN3Sb}%35QwP~VFi|9v}g7(0$L5WE2Czb2f{Bmt)&y&G-kmP1jm6`a#3 zV&8w6fZK~TkxX>N*)7g+ck*Hgi)K-xZv>K@dLj831@oH>>|S@Ar1(yQ-~%4ua?}WR z|0;!x7oMXmQH7FoMtHIFIG$em6hm5{p@kvO^ecRXI!gzjT4xc;i)w=G`sXO(*@SGb z2fJx+mloNx5>&`XT&pnIFzSbC_qvA0dxMZDJ?9e1jHx z5BNU27kG836KzpFQupT%msX3BfW?LEngZFdB(Vh^SzdJ`^4k%r^n@=rnfunIOBx5go!d!gKShF!FO3t@4`& z?t3+`@>vw`l3}^olc!*^(qa6uejDyluwzc9+$24}k_oqZEsh?Vjy@Sb$Qjd#?CWhR zyf0#qG)_6fIvzPo%H8}xOY07l)UJoKB~xLCt0q(ZvN1!?*6hVU1kO9MC}bJu|UR%^b(4I#Ztq*D$5wwLogfhNc;b!iq=FQB`jYrvDuW zFDDyut7G)>QSTA7$ninvJQwQY_nan7UPiv}4FYglB#hd+6#{$$p~OLw%xM}DDh(bX zhPU0w<`#RpT^|U4KI#B=p&R^JL80K$X$;c1hdB!T%;}FWrW)SIge5K5 zZf%d8@>YD)D$9-YtmbB?U&Q2<`c&!h9C~rIJSqD+F|Gw66cPr!JSH+GCxxvhkQD7?zcy9VvM{-7WKg4#tC7$)8 znX<>5NXTU=60>`QaBbldlvutVr)kJT__QnRw=6HDqNS+$asVDInT2w?Jn!q#OY}Q7 zjKar!U-vGH*^L{y@x@4klJ)}E^9at#*uedA0hXVRLXE7QFlC%RXRw{$k!pm|n)43y zjH(FjOnXLuEQzNVM!$p5PxE0gUk~0dD<_GE7PHbD-!V-po7jQ7MsVJT_jGuMFqTzm z1elLxt*$rh*j^9XRF)iU4~2}Hk-mjr@b(gB2}5F?mMrN9C}d3&e2L_d!&a?ZD7_1Nv+5tv+a7AXIV6R6K>r_y%0jH7NQ ziFjiVcXr6bqd#S+H$RlUw6le*a1aqL*?tEi&uoS_-#?qHu8J_vypx3@X7bP_HzJIf zf0~pZsRfbDzl>H-K9N_;CP5FZ%(php!0jLWajQR>Et60|mik~A|C zV$cqsmv4sVzA-p0zXzWD84Xf}Cz&{#CG2CKawXN3#ok)4L`lq@NRAdGn{+_ zpO~qU>z|?p@?*9O)Z9P8*$4v~U8*n8afqZ}mUmLKx8hW8gB_GA%fPSGb#Npu2A#K` zhScGU&~IkVHs_9o4?-<6T6P@qIK7o^&?shpy7#jVwR_m6xkDhAr_0`Rc+3X+N3bm; zS4h3aZd@oL%84}FaZ;mdIQPlt@cyz&JY;Bx=}o;fGwwN__&%8v2{=!U>Rs^k=0Uu1 zak606pF( zHu$mi4!%xZ!6n-Ja0&~iax>T}&bVebd)-Y%Fq%41h1L>*jO041=02C~3k{$%Pkfe%wyg{5UsCxDrBt-jTYMC>}H}ZFF z^PTv!+8p0ZTfiyYzsy~Dae$23KS;~mBj{Aq$CNgkP>tji8p^v$%mUreM?r`4{~bux zrisE5IsQFyc_Ujoz_V4>G3=zL+35779+w{tM9s`lC_Lg0eWI;I-*5wy_>P#CZ)srL z-gy%1Q6ca+c}Haz43J%~Kj7>}o)tRtB(C?MxU9t!q7J!}w}HPQMC}Z7TC1ie@G5pX!2>D<`qEtRIIV<>H(;rusb(AOf zP7xSo;7N5N&XS&k?xf!B8?F|$Ci$0s(EVG(!7?a~_6ZnL+GmcTbzYp+k5}A0#Z{izsp2S>=9))eg4-NJ2MAAtRaRhYSdG^Z@B z3(aMxIgQdwSkD`@ZD%G5oLrm)#>%isK%rkj1C#SWnyY=2{iHkoee|C`kMx_g{6Bk4rz($o~>4 z(`-lXD|&!W%Ua5Oy+JG*b?F*`4IX(BhWAP}u;o+<4$L>^_6_XC_Upl%Xt5ZlGa(wU zaetsq)0fsLet`y|`enD}4$kLmm~Na_WOjDCU@)okOr46hmqIXfR_ z589H!uh-$T+e4_``UEwV$8cjXh7t{pMd{1&&C{0Yy+mI5~c|3EKzaKQd03W#uxD=~Lt#mu-gmeAGKjS9}+oTC&zH73B z@^|60={)ooJBrC~I`Aa_dAZMXB5rM-BqBSEEYp%g`L<0s-R3qKeeWCTJJCibx!(~; z4f@bGf8A(CUMRg15l54x-!s+qy3lHrgQn};(7tdNv|n3}(>6A-50xyyqC^9-#^@1i z<`g8|AA!3chRjN)Fo2aAFn8-Z6uGQP%4?jE9{U5E-d$n>ocKBD)HOuBGYyIyT+w*w z5l&u7$uH?ver7vDQ}f0MR^GH_e!PE9Cq<^Bb8s>lIBG}+27Z&hNt0-Pbp^dtqD_~$ zB8~9%q0N2Ig;S)4P?z^Vt9Poy&w+T>b(0XPH0I-y=pqOaMnQLst@*S1?PRioElj`b z%+y7t{NgH<_N~BWve+U+L zeV}_3!vz<&C<%Ouf6^y@qxgNsUiRtoQS|lLDby<~oo;tZJM$SxefOp%QUoRcr~>IaTm;O+K7a9X4-|UO?uDy?}R7E7=t`OM$8U50b3~ z?D>!=_CbCJpQ~C9o2s_6KespWenD^Cwm}Ta9$X-P@>9u<<&n&??lm-TxjoIhp-<;p z8wtX5Is|EUH|U!qj8N-pvCX&t6}0UW5}4Z2dYiP;fPZTk$+ol?z(On30W5iT92Dh^Vw!- zznFybCOaW2shzo$l@Imv_u#x6rZ_fQ54PTpq9V~ptCIDt;G)lZxc6j)hRvEr6J%cr zmQ9{OQ?(1J-rat(v(pZnLe+`#VWrB)vm2=LMm4<6?-C==&7sTKU^wy72OjP%p);+- z$rZ~nq<=vb-C^2E!jtB*SNV65)atpAdvhWCjr@hg|KiM}?fse5+WA#=_9ww2Ukrle zMuD`gJY0+u2M61k(9v%MM?OqLqc0ZJb;V+6U-|>KJPV`7driogDf$@r-Af>nC`q3b z)SF-a^#cuG#Bquq*_`x@ELPv50V8iD;>(lzXlx-MqDJ%JyVxm4K6?VGu_`5BF1gal zm)4OqpB+SFk_$v?9-0md{*DEN~!M$C8*U z^>)nqF^N!ldNSF3H41jRd(m+VN08l&?C=H=?Aqu6+uuCK-2SChL23hJPKd*_$2&QX zxyhV`;}FmEoyF-l?Z(KwXE->y8zX*f#gu1S$_xA6ISIrt?&~%j)iD@&La}kD~ zd}TI&`VVTSZ^8*RI$+rO99YM6cE%2ly;fe#emR|t;$oR#c`1P@)K-C?Pj&FvF?Upc zY)`Vf5?JqiS7>}c6Xi=E($$#^7JplZu~ON1$smjDl4R({*D6?Gx|v&aHi%ncbBQx_ zj>qor^Re>cK2G}OIt)!o!YLLeXi(P+>C%4W)4O?a@8C}|YLJ0lrV~-&L@le7s0-7K zl41MA70iG2%h(_*dAKl=04WLT{OmT3~f|{m6y}$ zs~m`HkO@-ZkO$EZo!Ge*klqc zyzwOfTd550`1cu|Ur3;=zi#!pUQl( zS_}fs#U$5xJ_OYs7Yg}v@4z)BNDBN-DrC-($eXjNLRSc_5bgnwpS2hsITPAS-g7Fq zm2u{FHNmLRAi>O}avEMVnOsmTCMmRnpSKTk;vxIEApY)otF{kY5~4B9IvmfJkK+4} zlDL@XoBfWN4t~?S+3@R5pzqcLPm^!6AJW6gyfqRet3-zFi@iX0%Ort_`BL_o-)DF= zc^IA@9Lof4k${q|k*Ga$cGcFh!i^gM z7wauGZ(Ymt+$)qIB6KPlJ^mpiJlF{tH(H6MT^l(dcuYi-^B8xV5t_Tlj21Z@$Jv_* zrkm{Z}7805j2|m9Y^h+jKSv%aNC+cILav& zZN97qiH-!i(Zhjxerz-OP#}p@R_!MS&7IU&?HJwZUQF{(vGi>=%1u-^k#F1K(ksyFty8DFfX@^-jnyI4TfXsq`pAnRoIC$_zWqjo6a)|gCS!m ziX^reuxdS4jIgzt(U!gdjbnH=!RR=0%JC`xyN1A;C|Bs4oQawCmFO`d#88W87*%P8 z8m`ya-wKY*0WDdC|Mu}5zyea^&37n=L;Te2uV?nhY_0$u$wW&g#Feg%@2Y|HB)51wsk(7 zKktC@FS}spw^hWH_bV#+Z7|=V$QwYEHj`-06(mtHLH-&w8K^3bn|0E%Yet=-t3)r&p z5!My)Srq-PMES}IIwSP}b-Fu=Cg|KD#YNNb!M0e`HyT0P**hH@$tiO{qj_x8w zGDU2i!gFDe*c0-&Fb8H=Rg-g0gXS{(R<8yn=Wkn|> z6v>#!-b{e$hM%B5tCT41h$QnYyx3a6VmRgqp~&c_A*-rT7hqg^pHBNx^o)E z2g>kj?Q*EO&UYM_j>5s(7|e4{<-`K7ab^pybIR;uZv5!ASm0^_lN&|pK0?j zLC*h~jzZHO^SHuVlnV_+QRzJF^v#EF!KXO6rQY0(-gCsncA-G_fg~+ZSW11AwFL@0 z=Fw@fE69($E|Roj4|RBvgken|U?5ivM|VG@%j#oE?QxcU5^@hFx$++DQIfdKSd~$% zeGQrA38Xo;5;j-NW`3KgoMZQq~6|aCCyVZaJvGh=m@r z$MlGpIGJ%l7esGPL74%*dz7qzo_3e8y+a+Hw*7=ZqvznbfNSI_zZ>jczX{^Ts>63R zU3_pxnq2y|7ZtyMh5r31IA&HJ6`%Rsd_eRw7%1cl|9p?f#SR~E!@dC!D0DLE2}Y2p z915K>hsTg&3ADouvqpt7`;-wNJZC9m~FlTPl#9Ic}W+Xx8oFi8klEprSco1CYiFO=B3ao zKMdYC4-w|q3s7%-153I^sFdPAI`LHzdHH-TNY6AQ!J_$8W{d<~QPD*1WNe^|7v7)_ zw>@aq&n-0M)(G$0NW>Lej*-7>k5k{XZM3bhiB@RTQ_U$^G7a05{)T5q-NHD*ihXJ_cIA4ZdF?yQ_z}{;`}oX_eyfy^5gWs0CT#)2MEj1G_Y5 z6%*0qN)_v0vpd^T=-|B~ft;%*9c)Nu0;O{ZH(((UF$tp$BO=uEwg(0`^WLD{zL?E( zpEGy<<7Yvw7%+Pi`pk$%HBBQ_-0}%#o9eL}9$N4_xJN`Ud7LS&RO6-eG9! z4pcrp0v$__;F6lTF!*vY^ILWcxqdSeQiDVxW5p|TbI-@A4sr;>^2_>yar*TBbX&)|Krc8ezu}X8W+@rkn-~{V8bCX z6#4HiHW;6Q`rvph@mKV{QRu(tydkxK-m3bEVO1x}jkHtwBIpu5_PR@2Z>@UiL z*c1L3_2M1Mob5r0gCEdtOebf2>Lq^J_yfBiEyv%|`dAe&iAjHc@^@q2kvF*(*6JF8 zcUCDCnHLUAdkaC<+Ze9&$3d!FDQsG#fnr%tp}yl9-!sx=!+l<$$iKcSwS&t!sn|oD z&gK?$|BwV8U`bYOf5b`Li>J)&ZFDv>fMx!@cpt)LTLt^?=hKk;(=ha?3wP{CCbzP#iaVUfa>q+3w>&g~o73%K{@S1dU;j$M zMi(n?+@Kz(*Y%ZKvicS$ve}Q544s1>VaB+kYCF#{)xb4-_!&=o3mJM80c8=U=Kn@t zX55~8z~hP%7$t2FJ1_sBV!M3k{KR`qWA$UKA97=ob)2yHz#x3RTtw#Vwu5Se655s4 zOJ7Q-35-pXssH7foVJw;x2@BTlXr3Ayf^OU;zb^Eo(o&J6;ji|_nr>6HB@j$;x^nQ z`%BzXt#zF7o_=nu=vAzpB*66Lu6QkLJiL(G&sJ)Hcr&Ib_v5Z<@hDhn0@8uosE1=M`BwRk{>x(oi&P&8 z?6NIsmfTxT%G;ikIcLf#wLRwI&F*nK!w+*&*`3&QU>TmIGcda+6f^Tzabpy`urtw} zQ#s4;fiLXGtr`{>zfcLSd-tMeXB70jZbKQZS8V7!6G-xy5BXymoNVS)fYaMrt3EPHMRC^uz9b^qPAWty5^h z<_A`sMAUcge~Qk-5zF@rmG5`AkG#T8^M_`Agbj5H?30uy|Dm85JpcF?etT|1!>o+S z>XCwQcVEUn+o_=6ZzV3*vIVV-d|}kJbgo?I0%x~Q$e175%KBS63VlvPT;lIW6M|ld z<PSDqrom29{>OXUVbs+0x`!{O`^N7Pou|E7EOclFVeLqjip@Hf@LBBWIwa zdo&pRc>u#R%5mzhUVI)rg>yTy1~cG2{2hCjj3!sXDXrf)r}wv5$y*c4zOKdK1%_yJ z#{_p*{9>uAp3tRL4`9rUJ)ut`6ojzh*-% z*74Q5`*GRUvaBgcjxP@dcIV1HS8BFhl<|UY+rS3w}5WqcR?2@~9?ePOm6G zx1A)`kMZPz5ZIWq6VHlcFvRW=EAUuO>amB>bLnf`m8OY?WkRM$_Yd>d@5eG@v%#Te_F@B=9SUHgO9P( z3*P#k8~CF22b3tTWiHXlc>c30(&cI>Tl*9G@9<~il=NA@Rm0)lX2yp5l3!r668@n#wC%8o3h*jmo@rO&actDtjSniv-*v82jTlu%B zBmEphpTER$GH=*ndk1zSl9OmiPnPI!`Ash!jOorxdER4gE%^TD#j5n@iDinPiyf6T z1@&qL7BuMKt+{P@*GnI-wQdnhGgC}Voyw^fjAsR}3nAZjGF*gnkhNeTC>q>iRbvkD z?`K|x%-Up>H8O^Z=cz0fR78dYb;U9>y~Il9cd@{EAeIZW(|~mi_{mTWqto*6`16O% z-u;5aGN+ufeEg|*d>zd_)t~G$4$_*(h4Ab4VrbpSk!9dhvCXTE;`uvz#437ySh~Ym ztl4UZk^>DW`C*AcJ0tPnMt7XNC=7laJqtU;k40DKbi=X%!oK3%AO6v)SvcmK9xPj0 z0haZL$*^K47xH2pDs}Efr(=JSrO(2{xdNxT;WIzMc@xBI{-Kh=S4eHmPdYz*r)06~ zEAaKnqLSu8jNd1RXH^fA)azk*Z$vjP9j1d3Lr#hp*^L&j*3rVM>^B&?uM0ph zz@nP)p^kDEE})ViYf3&ipH6o=No220mkh{LmrS&pOe3G&guGp~Fyuxi*VwZi@L7Xc zL-T}q$kt@B&ED&HdvPM3fNkRbcVytN;P8}tkOggzKQVXhyEvoh02LQB4DzEEBh|&CkGEWK*}EsXwlYq!tX~Y`2Jri zezB6S-GRcNCukZP?mv+ zUJN&qj5k?EW|Olea?Uyuowt_kLv9H8d1v6s{GX(cLKi0JJ-$$n6A#G$g4HumiH*IF z!kNXLWWU{1A~m7{^umuoNV*}e_}j{R7=#0s-GtF$1}N8X1(mBe3w^mt_&U%T-0f!a zQ96~d>XMakvv*-$+jp|3L+`<8`4#-x|H|1b^+D{`{()@bs$PmTUPWX5%IRhPMajT@ zh7PWIp7JVxW+uFFK_zmKmgrt-F7~uNNzDY1m6< zKWrwEZ8q-vEe}6R)Ilcc9yH8LgRiewfTeR2?s(ISrd2^`Z~F03~ z(#4LEomh2k5ysSq3!Jz^7_(qA+N7_>Gfg?DJbAT{7ru|<<@NCCn6WJLbt0;X9k40r z9zyOoba#;)Vu(FN9AKwZ5vwj3EasurEu@2FItpXz>UyHu;}hPE@nE2wELdo0N-};;LJf7CH??EY<{w}NsG9p^OX38FClQgqM6HkKAOp| z{|%dD8(HGRVz$r!6hHI8&_a!)+nLUs8ov6z8QWiXl>51avA>5~Sd;!F5Kp?!9?$+r zDu0tm_n8!ht=>v!YGdf#jPLaC>TY_m@hd$szADjQx>cfHvWfl_4Wg$$K2){)E=Avb zO;^f=4r}IBx}BClx7XTJjEy{*uTx^nCl_#iZ*JhMd9y@+FYkoP- z&S}Q%eSkB~b00_h@4E4I+YeIctuVUwe1XK;c7{ZCoW4YFmI;;AoTOaNlqDV*NWn#s z)G9bd?n^zVPZ@Q@AWHCl#j+5QDNQuY`C6!^mas%mSR|%}0FJVt2mp;i}i9vV`Khg&Sfbx!=+T z5Z2y=q3(Ym(bTr^-kCvgeZ+0P<>^XxOxGPoTJItqpK?0AHjV!ITd}*!Z%F^$7x>Zg zoz9$*r^r3U6q-{?OHHd;ik>fXJ+H&(F1=hBo)rOgk=wY_mBq}zTi)Rd*mM1!-hzaQ zAK44zPCl&IoIMa7h9vEOEU#<{E@-!aReTIMBY8CW~0E+K!#p?O|7_U|kSB9&(s@gbw8M)D{W6W*N#F;ltc^Z{fBL`Gj%b|8joU9}R*m8+aFEE7-&KZ2b zLO0f5K@F9vBwXIhkdHRiBwq1D)L=2TSJ+{k$x zOMpQ&72tO#0N!{`Lzgf0u&1sJO`gSJo|zO`p1VXRe0|CBvIMk@g?xz9NVd-J4dVw% zv7nhfnEWIKnCUN!iW@ z2Mg}w>q_Xg;1PT{zJ^VIWdn8Ur+Ag;4+^Ue=7O)E4YT|q3u}TrVTE@JoE_K1mhZm6 zhC7yUrzIJjLfJf2;2Y7XT^?>pKV$9V&)8+;$g!kF_o=YH507fx=k^B=;sP2L<7oA# z_~4m2W-DY0-qI%Eb^GZ2t6p@nYN1<;5AsQ!%h0O#2)@&C#F;LKaog}us8=q7O5=5K zfWJTFgj}&IY(W_n z8a?73m$vxH=}SFePjopiOsD;@YlLQcs2SSy1c&yc~t^$X6S6RT5rPl4mr(&y(c&f zegXW>zyOvbW5oFObk^+SN(N)DflFQ^n@4w0H})=Q4&IE`ZybgGUl!h9xdiTPxd}s+ zo`Twcb`TaNp)6-}dTbFYkq)YpsJ1E6pjaOko;jI?yzV8P5pJlY+JU~I;aETHB98lX z9Ycf+bp64f;xXPY#FL+l6f2eQ!5vof3ZvFrqJhFQPWz%ai-=HVSE~zHb5al2ySI@~ zx-yMx30O`;RaP_K5j@!K35I+BHpANmzj0i}VW=OZ$*R|A<2lb1Y#3RHBX%IRd3?q& zRc}zjUaI?;Kwn-INrt2hlPHeK^}?GCn5}j`v$i(T5e3eZSKA16Eye2{dWL!n zOo(H6ptziSTQd-%o712tWi|^OeSuw28&r6s(iOIkb3@(3X0UnTEn$zii+p9``KL7^ z_9y8KIp*(|C@RO$kF^;RtC%k8GF(W3p6Rst(|tTA^-jDrcAi+HQ5F8mpM%r)Q!$`T z1J%>tiuV`r;+pyT;>9mb#KG!vV#8e%#SU&QV$Cfw$m@(i>%Yd($(yjW_F2N4Y!-T} z6vBW(r2`L*%J+?&H7Le1NEpnPz)IY-B$7rQDK+AN@|o1BOuU&sO?$ z{Fp=<)=9SRG?0ucxGfoYu!G7|RqYyfiAeTQ75#BPE>S#CB$@Pa zp2T-v7geo_l#H8JgNnXNSllvDtaD--rrMlmb;kte$>Kfqa_2A#*7w5DR0T{J+lpW3 zoe=kP+lp5*kK!afXHp$7l&k{NSZK8wt$%xlUS%2L6pcLaEB;lOSo9j=b=Sk8$sMi{b}8m=V0Pu zfj+51Hb=IIlC?vrqv@ms&c#Uj`PI_SN?n?oGmeW~C2+7Nd|~m&8u{kyS@6%U0(_5( zIDzk3_;2A=&TGRz*utK0|AwnjgwaKsad!rl-BqD*kB9WwdL%`yyDU+vm7>gh=5%J! zV`{H>h+E=QSimiz>#1QSk=OEp%IeVsRT-uNq-qIQU`?O9B#VlR>W zdvnP;yRi~2O?`>yKog1dza_MN;W!$6@jsS0Cx?A+J5SOI7nryFMUlmjZLooE!?mu} ze721%A9%0<@~)b}eRDUO7(0UPeLtH<8(*Ud2PU#GnJetl)H42r{(M+g?~5|$8sScS zKB&pM(D5#j#9-ECmgF&phAux!v%+?=FWU;JF;p zWJ=rkrsEf(?ENqbic=>2Zbi0mgl@t6r{<7sP=sHndEuoi|8ZY>FEhK^LAdMtcHH); z4o(bT+o3&o^_X4ZZ(yhPRx;5bfZKF>C(gXd6MvQo3+xTz2 zDcs%L2id!&`^g2mS=Qww7-Uor9m8Y9N=_M=Gc^l6Pg#@HqzSm7Bt$&aWPo^p>}Byd zuNLH8x3Pk&4k#7&jkV=2WG8->N=CV+N#x%2qb4!rmvD{_?OXHVRqU)yg?#AyNx{8#nUC#w=}_JiDZV>Hi=0`oAB`b zDjBh1DE*U}Ls67k*aa6!=CBmo=k%P(|C>8dna~8{~uS9!c zZ_ckx-~t+xB*xT_90k5mRQVBVw9BJ!fu9Ov*7nlsjT0p6^F$K8tyvN)salC+v4%uv z|09a;o6QP@jO_ayEAaUu3r%Bn`2BNTU~lbUQLWMmR_@RxaAou<%3~@GuYQYm<7Q)? z&jn0v$u>#8QBI2<3%*cJtA_iMq4_{M+ovwE3~ZAOh(1f#a`Po-Y@9^PIF6=Sx`X&t zFW=j>l+9n+1=;KJc}dR%z9#ms=-gwA!rDz=VUU&&8&o6D-enBMr5!%Fa9|v2dyhtm zu_Epdy(d;(zmT;A_QMtZMx)WFJi$A=7TfoFu@5r^ZG+&QvxvN3k9C_Bw z9fEyF4A)hFq-#r1# zSFo_4RIrC8>>V47b-y!c=7ob2ty``VsTbPR9$vuOI-cXG&8ukXxnMdp_&y7KZwJ?2 zt-|7pt8DqOHPF+#jkC)7&7%C=V2`W;B{=V=XVFq*V0WI?j)^B1b!YbMOab#UTFJSX z`-1Oe9({#bhH(i)tAoHTzO$Hp$TGnoc|S}ay%__BOu>l1;gIq&3y&;&f!41tczp3GShVsot}*sNO%D?+@PCK-4#%;-uPc9UoeJc}*|VD~MW|2|!Ad`@CiAg^ ze?<3=Xy=PcTz>XCy5ESvuH)M9?1HUC>Y65t(bu5QP1;CHa-x*z$^e-~|WEr*YZ1+s+LqSH{6_A6vX~=RXpS)P&8yFVi?F9eUSq4(-{r z6uTa-!1uRWQ79opeE3^BcYQ0pUZ70nD{EoI=TG?DF%X+a2a9Ev#G(*n5KoP}j5&QR zsAL?$Kvj>o4XTBua~DARsRS-;6UX}L?Btd$GC`MmJ=E?RjK)Up=y3WZdr+_v2L??f z6pFBssPa_JS#buKBh!pzz`8ZuYP(j8^3h{cbzj)-P zTo$N(ldbW{hR&GDByanWj1K3+H{rb*)TV&%#xE92eV&8gj?0S;Q}^JDNIjf;Zv+l| zmjic#6AD(2?SbOy1#rJA8a$)TM1PG_prA??&#wI??A#tepW@HL(%UXNRMmxxu*kHx@~VK`^zJ*>Bp!$o_Xamtt*D3|dJr;o{p z`@%cC^u`^gyWfy2%$N;TE&tGF;s88!x&;@{G{nuu`_Q`PB6Q|{M7y3|%ALGghQoLb72fqp3F`P!TMLbbFTt#BvvI23T$Gs_f+Oa|qx=0jz5pSfR~V;s z0NO_hd^g)h=sDzyqtYU9%DWC|e(THzEf~yijJ`rwoSq4t`z37i{&hIYG7InZ8;S-e zMlzXq!)Q%Tro>|50?A0zHhQV~Kw>#FO=4E`QZhUosbiC10nI z@s<-bDP$e1jjiFFmoA4D!{(#Ro)qwPy^Z@sl`v)NWj;nRlAX~GU`K=QvWWftz;SLQ zCeIC>@gGL1%!t>2+otA$QK`9tRxPbY8jG8%4G!n`}~kWQFw{+wiQuoqc&B&Ql<)-XoJO;58Fg)_dF+TKRcB&RtnX0{zu z{yY_O4qfNGmZhTl{n@DcWGENBqZ>`1%Yv_Z9G5U^Cb+bI0mt>5*&dwAMynY>+8`l2 z9{Yp6Dj&_qZ;(fB>INM5*MZ%9&`$FH#09= z-JT-#y9MU0HNAbYig+(+x^#olIMrK}LeX@J0%-EOM4H0=VtMs0;4`8e_I9vFkRK@Xes)0R1BhO@HOHC#aJc2GL@f(z--Gxn{I&2#$B zzqtBlhL;B}@D6_k$@`V{9tcyScmjLd>*N|U*y19jl_Q;joS zmxGEkc(x*_5e{x0%!OG+pqgnH4lZehmMd|%CwL!@Fx6n!_l$t-yWwctI}ExPq+;X* zZIqt&4RQmoa(iJiO_J(Gzr#1smHor(JG#X~ggp7L(_?Y}tGP6_xROLsTPSAV37QlA zn4EX4El5h(15vd}sM9hM2c9rPy>uP!%eFB%aejYTX0t-rh0TRqeb-oNl9>>7{80X5phP+qluu?S;#mlnM;gvtGY@Ukq)vBS-MH8J$0CmpPl7Z?v zVW-GZuKgZ~?h6U|lpeu5UzUlDbMi2v&5Fg$I}bPf&%%+}jqIX_3*R0d&;ALVZzXSi zu$`I?39GN-$ROvg!r+ z`)5H;d=Hpx%0~159CT5kETNyP7+iJ1XO#S=XSn>^Dm+Enwrf3%;?9ukr`({>HS5I>31_&z(~H z1-~qA3LS!IaMXH_SA^_))agI4Xp05B5p+@CtIpD5$KAA2!Il23-at`~Gb9SwN&Y&3K0M#V}WPAl#hHzV#0)O^__I(jgYX-igfik{#2YMrC3OK}*x3PsFH zDH+4!>~VLMuB{vb{@FE!4+V$B{c9np{oGY_Yjcx><>eC5 z@;%!?X~6=%Yo!_VpIFA?KCA_)={MLTNMwyHg_n#J&U?87zn}liwCb%;Ds&-!7?y$4 zyr$rdg0c9RpMkr7nDGX$mmzHWOH%v0(7o>q8}0j*ruRxgmhVlHUtP~1@%zR<$$pI| zI=A4>yx-7T7KJMwh|zk@Aa*5BTXZw3f*qf90(?yUaMt8h=y^Abh1eKF%=_=Cc(R0F zyHb^O)?9)i@6roT&eG>Q;|}xlwr=9rpNl2q53^{M&vE!6?&V9fPvfG93wUO=o>;9=lWIQ;D+b5R`2#^0UDZtWY%^eaEpw*0SbbeR-)aQY>fw^R5<<9=~gctcoK$qF`->Ti2=ugsp?aLlz zU1NJ~1NiSdXTv#q4TmLLP{mQ0ss7&)_ttNJii{L24|tH)6@Ce!q^ zYLdIYPB=pw$=ZD)E48a<)nAUWL*;+ip28b!a@9oUZly&-ry7Gy%Vv?)-S4o`)|m}H zz;W@INvn^J58Cgm&SAdk*D$; zZj8=(yf)CAWhU1%gTL>g*VQrqGi;|yWpA|I@tP7}945u;W$Y!jQNIa-FEHDMEN3Q> z|9>77e#(hdf7_Dgr}Ad>Mbl9o5dOmW|8tkLxQ=tS;V7XXyE-<$o+Y-0({4AuII^8 zvsNOTuTCrnfyNMv-n*dtlQ%Ov+4_{9i5NJe9~Vs$-6;`f`js+M=FgU z;>n-rQ(+k;_qbD6+S%!EU%4mqpYVpw1#FUeD!dTbC|^fhVKp7E$ZY>{rmg>qe5dNr z#WsPzV$CtI3S_f}xw9g-4K%Xy25nRW8dI7M*K?A|y}1o16r6=~Uq<1+XDdlFd=Hk6 z3r2MlW!j{Fo>ecaX5Xd>xr-7ZwI&r$Tbid)z}^WIWS>A`3k6?fOA~MONsR_)I?+t^ z^CV}R&RT2=*jc~xFz{p}|H|4N5|ji_RVR8Yp~$B zbvRz|CrJs+@w=B5phf8k7eCbr58qRu<(LE?O}pW<%y}&QU`}Bn4S2g#*l&EF#och8W~{L;##rt( z`>Z75zNl+)QMVIV=EqWc>Z`@G zS?*F58a*?dRrM`or-hlNl>J`HJ}448Sc}(0)#>X2b(EISwC$Ax6Pa4^~R zkH53O1*TqXgOJS+z-ZziQ{L=k=(E1oQIo1nGQwfqc9OqPj{^5U& z*#+aKBtc`U0t`KMMs#A?M`*3LB(1kUA;w4-KDjvw*)4apyBkhnG9fepia5h3wlv2< zmNT)rgagxOV^=>POdNI&@~2KGd3WK^gCpjN@F~0e7sK-eP#wzG#!Qm_DOix_z7$_6oY|b zHM3Wp!c>pE1J_`4s=ND|UIaCeiQ6qyeo-@c#=(xW(`%ri?zy``c zwBq_^9v$Xev-;f)Y|BA!TH0L;!>?TgiQslQ=59xc&F|S9l?~(>If25<6e(WXkwV>$ zk@BNstmTkA`|_}zRmt@VuE1aHO7Ut)E6{>tUx#r_dp&2(orbXZVLV=y6$Ct*wMZjGbmxbg+x|TAhEcwBT;OvAg!6Z*vtOM@YwS#yf~o; zi+*&|$&MJha&0;*G22Z(&vRLQV>4@+e2|r{dq~^2ZlmQZHnVeTH^@zPKl?7~pb3kH zuz%VU`5B%A?B5TX#bU<=7v6d92}#*@IP{+l959~+KK?I99xu3~~0MegZ%3QsY*pHt!=Gd+fp`om|8E zZnx4f15-NrVK_xU-^)L7G!nj#)of+c7;=0kL<_G3@|SpZny;{r9JW89A-Vq%W`&TR zoi9sW=ExOQ9A~F$C$m$QohU7MlVpD7@ekGR^Qp_zSkQDci1-)@yVWm~@7W&|yd;}o zpbxCo6TT~Yas2YJpCq##=Tb>{8vUs{LAS2j(DT1}Brk8p^0z#vP4Zs!dR8IrGH;}H z|LtaSpXR{UG57gzQmxD=W(4oDn+hLg#*>2NF7whlN~0FdAus1l8eTA#>t0vGnfxhd zs-rkIs5KB=El)$hW+s}my@DTn_cLGO_M`~VA5lWisL}~p!Q5MAm*h+Wkm{|Zm6Y~FBZ@b-xY-_sol(Wfh&Z* zbA~jNK9sRu&W2~{^UHGlAbF7w3;4W&Wou|KUsZ2Xu<|E0o&VUA>X9s@<3B$6=u=p2 zQ^5{orNLr9u(!ya$UnI{l!aM&F$cE}QM>FzmbPaA%__Mq8n5~T_Z{?tnROevi|Tbu zE9kLAX5)NP98|;>$Jf#CeV^!59!c!lA4*1*E76CWkrG7>!KZV&hJX8L1w~1mt(K4`b;I9Ad z16vofvSg6K;wKcnKlc(kwaEoZ0kneGhr9) zGO#UllicHOu0Km!k@ML7yK88}__adcx|TcpU@X7sdKa7VLX*v0AY^C^hJk&|R~GX^ z_v4a$JKURhP&x&73#;Klnl*Rx z{&CW45&GcfcFfi277lVb&NT#0rpHObxjngz?MrWrH+p~_9) z{gRzLCb+i;zGNr9$;>SH^N!a{*PjEBf0SlOQE{@DZhCACb%h=4$~bgF*I&A8s+ZW98b{2q z{4OZxd2%26bb;G7pL=yxk@~-kBGWk*G-3LKqt7W-Is$yKt* z%7R(kH`c5kLFzrjxuzviqMCcLywaKF@Nk5{NNY>rVm8!}W!x5r_xih->*ZQB6|3Ut z)L2$1vyo;0Kt<3O9GQO=pS3!m%8fm2_j(2PJPbZb7xyu%w&K2#)yVZ+79g(WYEC<38WOqi=vv2pw_)pa3)5N3lMmlV<${t z!+klocK4lg8}gSysQFm!wYEpCIDVSFk4 z%1US35`EZ4(;jv>DibC>-@?i#=Ru0Z9JcOUz+aR5&KieTaC2%~;ED1U{*Y51cS6Yg z+_zYT27&Ej#kG4dbo)Y_6I+1B*3HoSav}@akjsLvZAbZq5$JB}2MuG}aYulz=-R~| z&f}aV;&5w>(Hf1Ea~==9tVNdt7I^2`7tBf1fUW-;aAZRujtsXzsXsMne<~OaZm)&H z1G&6k)lJd!jH|pj_8zpjZ({aO8bQf61&*c?d#>g|$D57{9o#e=uYCj0&RGiGwK|xZ z=LlPce0hL{0n45whtp*}!E@_TsLY8c^m@Z$e-*G|gS+f>z&-Y3`988=T_N;Gn(%RV zA{PB>#gv9ISakLkMhni9&0QrpLeUaUS3ZaDXTRZ$_LaDCa|Ks&Qb*Js^ceKY{^O&b z&t%qX_Coj?4>&lm8zK!K6(nu=5A2Onv0vI8<}}|KI%l84ROjt@v2F#v+GB$Q_c!C0 zsh4O^-(^-@B}GYZE|Q0494XJ+N~hl5BEOpBq|-Eo%wR9vJR<|gA2i{A>(3w?b{L;e zJBmF;`nZ1EDzv`rjc#-Q;IicD=sjX5PW)t!TZ-z@P&or!M@6wIC(>EnrvP5gauMv# zu;M+w^uZQ8Ri-QyQ#&=^=Ts~FT^RpNIb3SmRMfzGu_s# zr|T z2v-^`#UUf&(EX(;PG9^-l)hn_D14A7N`0LGXN5l3WWAA4zn~UM9KP^%75&-BT~6S7 z+#N-(UkZQwa(G7WH=Y@E6d!)}!~LI(IJaUwyv^B*4Mm~&)l-CyQ5DoNeE@x%d!IZE zd?>}~2=y8?Qptfu>}ypB1FbpS3Gcm}gRBTg>zlyWp_6c{p}r`pat`q8H{$@uA*k~u z0!Mt8f<@V%3r)4R^IXObs0c8FPg_p`TY8i){C0wm9X*D5_soUsDjtyezL_l7wxI6QniEOfpOpQ4sQ`rXIi?^J+;xBV2|U%Q{{ z$+v`ks}kAe2fBRMR)5%Z{#>B|w}Hs0v8WZEEwje>(xfn1c;r$Y0`Vzwl5JInkvPKW^f zWBE=s5@id*W4~639g5yzL-L7HHuI69s)3(F3cHC7ha5@ zZNf|^ROc0m_l%%^3+_PW#4YUHs{<66tVQ`_55R;=GeJph6AsKT!PJED`1EZcMm2I5G~s(Imm*A+lo(E_^4s!8?MUfS@~mHG+ZZUe1)mR(=T zH1bTjqBV*RVM+5LWAA8430e!QBW0PogAopl9>Nw@O=WzS7auSwfL(Gt!m<=I*wv@a zprn(HOI8nM39Am%>RXS6KHp|h2RDqQqJQFA+lP4dqm$4BnvA*1`|)hZ5%h?DPG*(I z>00d|a<|ID)CFcZH1ImRqTbHroqw|E+U0QKQ7;)MBP%=K!2(paz-(H|-gfr!H!_cK zxoMZdWd8;Ji&qfm5Mj=VN-~*ztuynfe+Z6!B4%9c$|Qv@Q1;iKecD#VI;8X1nvZ>S zGGHHVxp13Chuso91PdtT=>hJ|`n9Zb&NH^SRGuA_zQA`*H|Ko*CU6&4s8eZfJu5Zq zMnknjFiGbvd)rz<8aA&B7h@1jjLxTp+-2G`=LOTLm*py22Nt^YOJK1>XK`6e@343o zT}Uap%r7~i&n-N9fMfh@!9VQ-TQ7NVVY|;k*n?rb#THKzx8|`DXJtCL@-<}~n}|o6 z({QAlDLj1=i~$`M=s&pvFNg?Dg|j%+ZHM?|MJ1>w+#v|v#Ro~*k>AP_XzE{2Tgo?+ z*`?*IWK{z54R{SP2UA%?h9Pgu{S}xeg1d5YHoFyc22>8#^79LG_}hp7fTA%6*?DT9 zn{tM+*FoeFt4B(D^*DUrA1?dg1xh&`LL=1`aiohom8leyZ+8(%tqNg*Bb{(;LJRy^ zYJ>CqDo~c$l8@J3P<^2*HX3vXOM^4fihD^rVG{n+n+OS#cgkAqZT7)o_9I1}N*C4caOfOF5;DXAa)-#9tyuPA0Z zfz#O#@`8+JDgM-%&#*SA_YiZ}C}5|FaEu-u{SMb82v` zfdXx-)aAd7+X#Q2oQ8Mjwqc2%j96Y#1m6eDA(yxV+)2?I#{M&9rD|pn6!V*1d*}@z z)k5CkE?bNF@w@8ioAtCz~aQ;g4aPEhTGV}a`Ut7!LbnX70#Udt5UYI zL?5PWr~`)Hf|la((39y*QMOtzpgIYE)PIGY7ld!Z+)dCt<^W2=Rp@KGFR28zC@7O11!j1O z1xruid>v+?LFrT2wtpuWy7g!A)mQie^%Q2KV9Wmz&m)a`Z$9tYd{$rJ0ekx`!#kCh zko8vr84LB;-UBLlS+5GOCmn^ztFe%5?uvW!i_zbC7w)ZCK{n;;62)oPC~Na&YTsf- zv$P{f*>DxDj`lB%QJaCD!{5MyYQc$PV}M=758!2nt!TwSL)0t$$mSh#gigmAD7tkK zLUNPYqHj(-r?81dM`yqTheL2>=2j;4=K@Rk`2wAk2D3y*S90BV2$#y5KwH38j9%`^ zb;-TOY4^6Vpy#H1;vXJ2&q_z*KcBH}bp;=Jq(7aBsgms2+f46zbm&f5JB5_IL$4wG z*w^Ix^u4C9hLAM@D>g^;+q zkexpEg%A5JjmlzE80&D2`3HFcmX<@p_Yhc~J)1Tp#iB}S7KJFzWadw%^63s;kT?Gw z9=IEXw?|!ulDc)cw{5F zMK8ykN0omDc(Ud$3IT1bF`5RY`ft#%<|)e``l^6Iu1K%9t?3#5j-&4Fyglz zZv1zde}DfH^xu+2s?#6R;NSb$)lY|6`O8kGtbGXlQ%XfO#}Z*s&t{ZadX#OwJr3o! zC}L7e5k~J>ggT@9p`}5s@aDLN72__D>8$PSbEqGS8vm1de&F!?k1ljcf5Td%&alrt zwWMS@4RlhQ+2un$_}(Q0_dNWFAJ)bR|7(MU4Bi4hYer8kYnkex%Sb0Z0hmZEWLIUmv(tMIIG=54_JVAUS7e4 z;iGwl3mCiR2y+_(t-Yd$_aKad_<7fB4tr1jg)2#w0f$)eY_xW+`{m zMrm1rb>o22lfuN-E6<3h-&-SA;+w^Cf=kt*I|PG-eY48gWw_*gJ4*{FA@2`n6!)l% zCQj35#qGwdtluJb=D`iFlLQZhRUebM@8de3hH#U|H1WPh`e;+^3cm^;;?Rk!x%`~T zDA&%xnFBk-LsM#?S4IrG$DgD@Pl8~=Rb{b;qYZ!B<9{5ThhI+b8^_zFrJV4|M3IELz%B3C>YUk$))vansv#lN5 zamEv?P1De;T?+Rt(8Np|8(KnY=*v;9sL=5XW!}%Df0sUF96dJDzrGyV?);P{Z5f3| zSI>}CHB~0OtDn5SxnMV^rgIoTTHk~5jQ#i z-};=LAHyApyTn~>{lT3}8O4>BPUK2t{kV+fo!t8H3EXy(8+PuUg`drAke%L)=K{y_ z%!f@hxnva$vGStRP5GCazmp#OyoAjeYG9%SC)ie_W|DHvhk2@MO%mJ`=z#M+D&_f! z`7)saCvO@gLZk5zYW0U?I<~Nd)1pZDz8`peiha##H!+g&&k`*)KXV$}3ptIb-CU6S z87^_qi%aa!<%;iVa53{=a~Xv#oS)=5&dZ~p+h%>8vs`L}_1cH9N?CMy0jr}|)b=Q2#xiUWs1fZ*K;*m!#+)q26gRlb+-{5!{mHw0mS#b(a5Z7H|;PCDoL zs+8Mh`x?9b zeeG&;uBw!5y0iy{A^Ef|pazSAHsH=@Z_ujRgWn(dkq2MjlczhT;Wnc&^zSDpy!=lU z6ca0me&{|p9Ule9wz`8(R36*hp9k-2G%#SJI!n+d(DfDy?{;M&FH7V8=b4 zMay@W(JvusbY8nNy%!yYJ69CqKHCC3Xqitt@)x1WwQD#t+XN?6Zs!?EwluN&7HN+@ z123u$0A@~t{@CL}qvqWZdo~eOzFwjJ5vla2haRWzc#u=ReTQc9Sk#tbA#LQWwxmzn5|N9Bp&C-i$)7BF|UG@Xl>Uo zG_5~{YEyROvv^yCX@T_1`cp(@!bKSAAVrp|tz~*Tc~;g@9kf_88zgKsAbL@TaP-Sk zp=thoAy4~-2aX>w|MxrCBy9*L#y#XFr$hhFeZVX$G{lSc)6iM?5O=h7pnB3ORFaOS zYghQ-#^PHTFfkk3X$t+&;!6LWeS-v z^V;l1p(5@%G#1_Xdzxap8-4EqqBPw!_8Nv{^`P3QB(jO0wS}~ArM*5PvVXrE zzZYLjM_1?azojg5cXcVg+-6VLM~0%w)5&B;lO7bEOC_nz=FqV204NTo2}gMw(%Sla z!V$espjX-vj|=;0V{rnUn3@GLu5!>QSPjvKve1HOp7x#e!S8GI@M6(@+F+l@1pVP? z-0M>2_4Adq&4HgI7@j8H%lDC+?OU0P@r!BJ92uHfWsZqHC)n>n*7(_G6_!rCNiO_c z1x4FZ(Kl{D)rlYMwA~627r}ERa@6TS<4KrevK>Af_TrLF zB9Hl5#qMpxWaxGq8R_Yt(^&IXv<=;LnhwfXZopOBdIDAN1evLrMto?)4M?q z$4N_5FRe+m%&wfe`V!jLA&Xv%&d|))`#G8OUlG1(A@^x6vhp{;?MEN^*%HJWZC#1| zRiDwzR1H4m=u@FM2Tii`$(8QsFcP_h0brt@i#`b3BN>(d1 z9Z^lr#m8cV{VH5GI-6D*d2?b`Z!v24ASYpP7+<`ei3#Z==mn!8eD9{nt@aD&Ttu^Q zaKd@sUFk+r;UhMV|3*XJg}@tIUznMD3k8o|d5?QLbbK0sedaHK<87F z!=%c_VY(zJk^5rFf{+P76q3e*V)AC@-=aLyt2u&9yuaK8O7~O6g4#H3!nj9GTsc=DXHpm*zJ!R^1JIh$2GIVpDr?_b=*soO{46P3?s z!M{uYwI)F9V{c*TG9&oA`a0anuM=*ju@GhO5fs)$Lk&N3c&nHO@t5?3#&&xlTzU%m zc_#z-4=WSF+&SGpfzq8OIc~&AKF8z(BRvn{*au3O3HlD-4~8} zXbSI7-w;lz*)5!L;Dpd}RIQNw?Gnm+8VPNWWeGe)Dd9O-xl?kUc`vM0`)9`a#t|&+S0oaUEpt57G zvE`-JV3X7V@Am(O&PPRXyY@YNIr0hmLj^*$U#ZZyxDihK%mpQN8#uB-1+3;>0O8tg zWD1xHX16S06COH{hZcuvnASKtXD5FzS+R&2C94JyXckmLBPI|Vd>vdx?TmDzAso9A z2;G7I;M<`L_;y^D+1WWBTXNQM!pGk^nbG?=$*12*!~6`$YuF8g>(@eIMT<~jl_j)R zNWjV3XYhQblTa~f8GLGLfg$Cm5EI@CnGtJX{JF6(U0H#Al@^dR-(zG%(iL*fV+}i7 zdp(+MJ%dKqoM@VG2@SG3Ks=tkqd6L(H6dFLfzJ5>c>Ssw{C0gK*Oh+KRo8)Lyz*o? znVD#lQ-tSImT(fzV%#b{BW}BWBxm}pjdwuahO5~IsO>Wav#-aX>%C`mMX@1BCNx5D z(+HSXT>Whc|3RwHs+K7<=t#c{&2 zHg3Dic5bxNTj310AB=Z+!HM1sO!4lePY?f~3EIQ-_HmA8IX93myCm3HlgXq?Dvs|+ zTN2}k-ArHcb*5By87Ww($6OoPLi}SYsiI{QegASN{VB<_ne(nOqg5@@!c2khZ7e`x z=NXJWU(Y)Lc9KxFW-`6{2XR%OM4SR*&^ow^4nHhHr@*IZ@TdnJXLZv2NvqJ{M*t<7 zdoLkFxG1rVnN6pc=$mt#`2H(TfEg3jXErfnJI+f;6 zX=givUyyp~XjbdmCAwo<3B9;Mjplo_(zTMYxMXqz`mtj4+59;4p3#jdqS=_B{D=No zYKR67O=zZMic$}sF`+Z%P+4smznAyHH5x}S^!!c6S#c+t8`q*)zBf(oET+SkkJHlT zkF@O4SiJD`Cr0g>g`Q(7a7}R?+Kn5+Fp=rOqpSGWz)+c}ANc6Ar3ifv*3lPsl) zgAYX49Ncl8T0M=O_Xh=*5j4xtizGd*BW7cQNcyjpw52zYT5Rm3A2W6%^Ws0;_^yc0 z>+n3*qebXCaR{3o7otJoDYSg*frtJU;kdzL^w!*O^!&tLVt9BByY#;&G}%mzNNTO4 zS_>$S9qPbQO`)h9bb|Iaw&VCMOVMhr7;P7qVYP$RaN51^?BSL>#PG~-u-^C+qDDKA ze98ABJFzj;dw3STR(F8hh}I=(_I&4Kq#=$FIiakl7#5FA!22VVP<+lS9I5euMh=Aw zIs`lDMxImhgyyohcF#jSSq(g}>=5ctC-k7v9yY&W50m|{o=q&8PuFS)iFz^b2O2kq z!o()j_;V5Ea^|6IXCF!ghm);Ux$G-G1DYxt30Z-`us?MN*ykOGIfFT*Yp{~Z2y~;r zG}SS^B$$llSy5*i}N;}U=08&9$|7OUP-|EV_IMY=(=UIZymBH@3OWjUQ^d#~oXO`0=8;@yHCVH38{O&f z9~cB@!}xUz$;0$-IR3FD{lv3+#usnEH50d^otH06S4n_l4;RyaDl&9<%X+FlqY~B@ z8{a(5>!qbHOy4Flq zuPFhcpp8AqxM@!K&%hnNaeuifU57M^D zFOY|Sut6th!VZgeT4mjgQd)s@-Jx2%F(V4ACB-;};9Pt@I~yy;#^Y2c9W;*>uc;k- zjoiGXL1MfvVxEm39;w_*ER^Qb?>)tgXtxvkygN#-$_~P=%pTI@aRkP`tE2nR&LGTS z13h2RLj%3;i>m%@rbiQ>p>F0^;<(XSB>SNqRV-f8OB$c)tH>nOyW1y_*&aeSTJNd3 zdi5nS__!9OW048S>P5Z)fhh(P@Ru9U@lce*(E9D1cBvZv2d22}=U%v*I}AsH*I@+T zhhJ8A3@6>o2jd}CkV&3LzgjdA#v_tC*6XA+PzS6L#w7~-y}vsltP1}8(E`{ zi^nQRE&ULfK*fr~EJnw&+=Nf`~+fPwzdmEC@^S`FQUq#J? z&h*vxFt#t_1~Jo0X6_4q;ONT%qNCcQ@zk4Q#3PERy5}qj7Tzb>wT7fFa3%Tnx1FsX zvw+f-*C6g}9?Z$t0-!=yM=d{=cov zgB~Bum#{^hKp*ytUMu6CKN=RUjs@q*TCnuLj}X`N9CocVhL+hu)a6Jqj<5K}DfBGE z2W_*+?zzuM{<9IN1aH|(sYbN9HwN@p0*xrXL*#VzV2!#rGwR}Am@s-JtXR?yAyY#^ zQ{^;lS?U4_zR3{b_*yhC_AWd8rWcjxKA~Y7f77}nQt0YZj-&dHkPtSMR_EJ`xIlo&!-y1{{0K_ZaJ4am_)5UI!;m%2 zo8pY@^@+H%$s7xfqmgW^LjNo~bdKCj?7vj;tY&@C7L^@V{!)+D>*rqc8f^~dLrcjq=}pvy&&`eU4#I^e_&zA~ zGC%g;htf`aU_Y#d<_nH6VA2Cl`UmN=Pp;VdV>A~KZO_e9D#IC*-eEycBlgTV#?3Gt zgP;DMz;inP@Iu5@dViiDnIIQI?Mpi8&MhvWFgOu*F83o-Tt13Er0xcr=)3mA>Q1!p ztdM@XFH5n>kXcdUMM|wzQD$@-IGikFBNxtR9~`lTy>9u;P2F~CXs!UeZ#^S_K^5L4 zy#x`@S{R;9K+{?s?&LY)!n6OO#w_3_WzWZplY%hjwhb=w&cO~JqRiib$Z^_L`~Nt-?S{v`2vKR~XU>wsFgGOcN6X%s()SGZHh ze)v~S9$h!3Z)(gzzUw3_vU@_*6hFY3MSsXHtv%$_o-|-Olp*VkG7QZ*4Z%ru=vg(F zxiF;7vp`%iB%~Ur`?k`&EHex_`~kCk%{Zm5iJX)FV$L8xozo7u&P_{D;l`Ckp>I_% z8vmHaJK*o)v6VrfZuy&haETzV&E!OWU-RkFH)50rma}UmE69nBKJ@2}Wcs|em?)Qi zrXN2GXh!oA5;s&y=e_6WAi7HAdd5O>^xJmwU@n2E_-R5p@nS*S+xeI$@sH(pMYC#5 zCh2Y-33A)Nlk(E1%*Pk*_+-gW&h)(_w@2<9wiyVy37RL7w*A8!>JI#S?nZVr9mf4% zjH%z3>!5#Fk2L!)Bs0q<(zec8GGSUJ zxD6lv#XwI^06G2D(!OnvFG+l@!{)}X1Mv%ixV*=Lyun)V8`uR--kV^P@hn)c|4k^h z&RZz`?F#tL)IqzM7T7y;4kumMhy|17QKeUlRl9nF&%+yIfy!UHW&TBUnYtb2&Z?up zz#heNFVLrdROrKh-ejKD$r?9JKl0z=bP`eT2nNazQC)mJYu5FcsGeF5yB)pZ)W{Af zQoRB*Jv0GCxuUYuDayelSyT|f1(YqKtnuP(&uawSZv&Bf}hZWPm%WZ%g+?W;)YvVS7sm%yc|Lk%!mpzEmXMtSo zcBf*)u~>eu8$})u@oncT+)&s~&!$)4#K-aUO_wW8ib%uhznkew^^>G>=3>(Q!9rl2 z>j81Ee}dSjM4DqDM2HYZT@08UhT3bV5#IZsd1I z%JFzKZ#HsF9kIN>8b-~012J!WA!)8JOxl1$@sqIdy?{cNb_`$HMU?xpZof2Dvh7 z0#LbG&|>Zmi`+kf8}BQvZ!jjqi%MbK@F~2MU%+R6cu!1(EZef`A-ZsRm{DMki~sU$ z%q}Hb(fx#ykCh-(PzrGeE`jszMDohvA=>&B5#2UV7h`blN402Frfogz=ZLooLBZ!*SvVU*Q!0IcV3unYbsv z#T19dq~7QZ`0{&;QnzjrXa~SU43|&*-z)urJIHP=> zeEC>HKK+>r%U8x=P~ZSgJ9mzenl(gfvRct2{}evh(Zsg>8;Kql?_=R^16*b606LG~ z!(2YcH`>D-Mt+S!?byR~R|!Y_KhK5`mPaR=0$G0a$sJ*9zy;5L8t#J zac*xG7~aW-&0mMWD{2LtuwM@|PIN%I^HV;z?*uE}hC=9rIdI3zTBy+)2tOrff!7;< zP+1kp9P=5*?-I|^_c5aEFF#PyQ6^5pVfJmf1FF+HMytJp)CWp2hURPN7%iSlxpM)| zy)p`uyXMmjo|{~N1m1n$4E_rafuKnM8?|EKkk}HKnjFCQb1lh*uo3jMeGVxX&jK6E zGq6~4Crq-~49^ZVKw8H-2vaMDC>I+@`8N}?=9sNH)M;(v2Z7AAy|KV zO$~;#zZ@wLlS%HMNTV4tCJ0!#n7+H$Hgu^Hys2;x=jN zXoF{3+l5k}`7DXzWytB92l+uc;KVANW=Bx;5?DOfNDHrd!Rt;=$Viu`!IxaKZ4Dg367 z^1|sk!FIG)HN#WvLtN(=M-OhggrPoms4VM)Dz8sr`QKn%a&;WZcDH~vL6;!C&>EVb zl|gUvBEZHS@UBe-rkA>rY^yhy4H<2@zy+w6ZkI5hh-SDyaQD=-qVcvn@MiNCXn86 zjefAdMJ_&R#eG8!nAzQdi+!G8t@m4u_@+gRji=D#=cbZ7GNU0nI1U~si^Cs@a#*ok z5=MKAU_$Co;Tfw%4MzARS2rxrp z%%LAIm>1F&L}$Wf9QE3batfg+J{XIFUKxJxxEDvf?+~r97YgbVZ9(3ll=nFllWn$Z zsngmOXzBl*{{1=@^S_tl@lTaFE6|1gkfXpmL+3!ry)p34N(!2M_2AO_IB-0xN3I5p zVNCZ2k+E^tnGa_kkTt7P=nBej_3w+nP8(*rs<7@XHINL)E#rJ&{^@wkzjqgXJrUc*9X?3TG5)W`U zzZ*13-Hw5)-(k)!o+J3Go(?RXL@EzOk!x$pA&>WG{hJ|4^>2EU19g+xyT*NN+c_EL zN<|XurOUA4{}iZ)(;+fq-en?ru9c`;M=@^m=Lq&qb!0D&`b_2+3o4F{Ak=XfjK?PQM@MaC5D{?hc?A;w_SHEO=T2{#9SL38o| zPb^f$<45OX4rpPdt`?oP0%^O<5aTYBH)BgkHC5|dCrFFGM|RBLV!wa)MDn9^iQq@e zZfdenn>emN!_9qS%7vY==jY4Lg5i-f;qM$x3|BlSx|ePM6`$3COz$A`@^_QW`v-_s z_9c4c*a}vwEuG2scuIH2I}+bq30BwZ2J0M}L33XJrkBKK(fGy3Nz>-dqR+~5%&se; zWYZlZS{A*vI&7>yy`9`l1DQyAZSxw;fAbv$UbisX))u3EEKzFk9PRd7j6rXkF!3mo z_xEIAwub^74Yz|^DiZK|yqVCXrdz1GB?VWkZ#yN&1Isnzfd(S5W))rRiBnM1B-%9FU%Kq8o|N&I4D=%d1 z-k)%yp+_y~--wTREieW%9B*OopP%^1=PsVwTge*zy-oA^b0GJ4GaZ$ajik1RE@_+v zmkN)7yTfY0yE}w-a)!bcvFpk5K@JTo79huWD0R2s8e$J5buxM1s8CVBPw?#D&Zy%U<>qmlMZu zvR=`I8>Tt(%4O>#Ib3WEp3FnsDaJkj!t`7n7BXZ||?mEOq;C4x(! z@+?5_z)0c#&EeobV>^n561g$OeVopOBs6e}AUD+B(if$xaO;=9OvslYGFy*-X0`Eb z!9*Q8aJQRDj{ZTsu% zb&olq-SQurw`y{Z0}D7o!Xz4hrvmlXl+ed}wqnRJRaCY-M&jSCW>W8>^rAnHq&niX07cafe%f{s&{)~<-~OIBixD|tWqJnTS7?1-<7=QY!ghpr;Lv7 z9h5!bfiHHCz(2o_aRyrjockviPU-ADkVsXcUo~G6_xXFE`^GGYk{d-i?Wwe7S3ZUr zbZ{fA&Ty`2!FctrDQNS5U##PIloZ>B(RGkdje>x=r&Q} z2;*cm02-%yK>UCkd-|U=5e+{OZRU@t2p%Uppth_IPDXyDQMS|QOXnG|(0By7-_gN_#qHq~!hE>^&%@ZC zbO~2x-@rIhNx!VUh3j*#U`j?29nSuaCO-sB%G)#&A(UK2l_>6dN|jDKfE>Tu9!{XpDn1BbwI4xQgcD3r>txF2YU{gx?!O@C(mByt!%=hV$Ia z6gveBzIPorG}@uV&8>7++Zj4(ju)FweaVeE>eMRDjs0Gi2@*Rdf`N-G^I=^qJ$Yg$ zU3evhwy*=}RaC-v&mLe#b}-)YoQJ_j-twIoKJVJH1hq%o@cq>@nDJ*j7I!?ts*pW2 z-ficMm`6+5&iP;2_NiXv`|n?9F4_r)56!_lcjR!??SGuk@5#vizQX76XVZi7=D00r z2=(_yGK(ior=wNkX|9+ix!2OjI8U7}n)Cetxjo>|-<{{N10q8DZ+@U3G~MV)!v%OD z=`AX+amUP+cKGPhd2UzxRc>Xp2RHG85|;1w#)e<*oQ~vGd`ql2y%p~{)j5ao@{Syg zo*BgS4hUeYbv};q3&J|r>k!;{gEPqM#SORQQ03oun&meR4Jy{s(4tH_F=zrC`=&*7 zTKWlHWNApJ{c0icYXVuSaGIJLsWVxpgGheCdv=A|Me3fMMVk~}V~AM~9q5@wTT^D^ zy4dfWma!$bOgol4Tt1KUabY<1RXN;@*In3sya&~rH93j?JDg!+GI?s@hZ&cKh+yL- zAo>;f>i%7B`s7UV(s4E|U0i@u-^~!D_k5%c2R_o})(>&?6FDkTe~YS`nTte~8yR*| z7aMZ>Ho5k+nn-@Q$3*H(BeMO9)Hrwpseau`O*)il-zI<5eOikF^CeL5R~gTHT*cqv z*_@?!Ah&&95jS;7JEzob%|Wd<4%(F9DIZUKrI>~~2S?yLPc!T;TT0YD?m^1CRh;@W zHGKNlhmO2A8LaDq`58?${a)P7dk1S#;l*aQwf9g!1e>+Vok1e6c50}l@5;qPdM7m6T-2?u&#*qYH z%w?aE-Ml9Q-*MVm@4TGWCDKMXN znn@Y@N|v4uq)+(H;DEXZv%T~=U1QWn&mEXX){8Gk4Z4xUJYT|dgJOxxo10X2R6CJ$ z4iMy4{h%RVYEXUSYz%9;ff5D@$%3JU4oReA&m%;<(Y zvxSzcyU}YXnVe@UA#p+$Id1+Bob~$nIpJBR|4UvI35 zb3zH}Gxvl7sN&>=lR0yf&tMSmM=BohS&j8U?AMgn zT_Y1{-5v>4i8Vr#4c&ruh0SO@=MyS=ucs-uFAy)al{Ads1+M;^z@F?s$o83DA!qwu zlZ-GWQX$%pt2?{sG_zSKz0?q2ji53t*9;g41W*oK*l4PwBP6b zI5K9~+p~*P`@ElgJoXqjBt(Pq&ZRWu)FxW8(TQ@Z)5zsDEyVfTGTL%rCVf5kH4}VL zn-z%{qfl3!p8EWp&TUX7TQZ)L_?n-BTf0m};KYE)F(*tC%IL-r$auu=W{t;RzItZ4Zb}-I&`~}=y zS@Ocal(bYyk+l*G87y?B;UX#e;)5w&Dre2J^2U<8i-Ks_r^6_dZNPqd-H($i+fXwr znlTHPLrIB)qD4}_sNbxuG*ile#@1A@H@?oGq8pEBi*gYwH?fw4W$?4SGcV|aMLL4> z7x@muo3rpK(FIQbI#3f^t;UAG>$9Jtv7M8UOUB;M@pQq)F|g4h14lPL0nH^%Ag5mq z_4Be|Z(kwFT9l3Ar7V3RHlC*2-DjmGglzN`mUzS#lhVB_aja~bpl@7HGY%iY3TL3|?ena&5GZ?4kMZSzW3?D3Fd9&zCehxo^ zzF8VY-%4+!zxTXmHcD(E(&7_o0sD%nooqI(Oe zme%peI9Llr@D5T{XUg#_#MP$oHLsG>%njRmDv3n;|kD?RkF>wcO+MGt8 zEjq#!{ryO%X`G-xp0%-=2c+o2!8uGy!B0`*h9ms>>KJ?7Nte#fzDm!R8_-^`pkGei zr_sIN>BYz2=tS`gBHwHYl6l#eYVkd$@nc^DNmT)-uWLoUb1b1EteHKh8;wRmYhlH! zbnMVrfy=~B;|%GK_-vu8P)Zm@Chhlu*=Oo-)1&h^*Tn|y102cJof(v@n2t>KHnQ`| zTITulSU$4*l*+Fe&-iV)Ney##*%#+rd4Ag`V&}r|Fl*Im`qNSLd$2K`x~&0~W8b6E z^E$x=Zz1{d!GNU6O0wd0e1DjCioKq)AAXGQB+nLHhsjYZh;O++hMxTbM_!%BJ11pC zYwa(C%SshI=$Hx1R{mErto2iiRPc9b?E^`FSjjy4tb~gB5HbT9~r!*(r9`*%PV;*tE zxB3_0s`N{|{k4q9`@SLd`F!3gyc_SgFGC6K^JpD(gWmEx$z=Q)6qT`2?5gQ==EVVRcPuGar~j#NOoL=CiGJF`&Jg;Obz3fyTVrpq3wd_J)rDp(T~@+$ zd48cS66y3ZIYSgo4v9*fBXRa~F~WTx#}ZFhTA6Vf71%NK(u5J{(8zn>iWqR+Is=Wj zZiF!AETm3}glR`JK&xgqgsc34HL1<$Jb}+zPPd{PV2EDepVx`IU8tF{N|4wcK=xk+MN4oOiBf7*_+Fo^R59HKq1)b&MLJ($O~7v^Sm76IHS;t`P4^_B@|Ex9~b0-@Y&1L4>bIcQJ#Mvkkzq=l{u{LCbYpV6g2yUtJH zsBH`2%XDS(J8KU4U{pzCEqDg*`(1SOz)w2bX)0YHmnhoeyu-fLv5$USPUyC%?SjO> zG`d*+G->>5M#E*slE`&K^!PRjuwB&)uVb4*b=M(M(^biQ{*?g-Kb{iWnYjtqY!rut zj}wVVFA-{u)adqCr$l*n%gu!eUakWYXK%3shJV<6iP`M0g#AqD^hieTgbqrKTTP#8I*1IkN@@B` zppmDm=$ar^Nc(Vve1=~UEss{#)j{z9XBH1RX| zL^^eg=(p~8{u_}_or^uFOj|J-80mug$M11si`?LteT>k&$f{Bxvzdsy6Nl^CL%|4jg*OJ}cuLC1q0d{MqrMzeZ!x#7%~t@@dBD zb(2Ai-uT(=r#e9%iScFqg6=I?L zclyKcwEeH~mqFri08O%vV&5oq@aO$R!aQk4>F$lu7+{ zmhp8r!toQHL$AafIQy*x=u|18_S{HefMku3QFReokFpj{OnEC*>|Z9Z3OB&8_=`AF zp%16b-pkgqJ8*>I42;h<5ec$Ej_2 zz3n^R*Oj3Et#U_KGYimrvW^&33ZP)M0&sh$Ku!NT+WylJr9B(zkd!5HP&`LkZWYrA z-tUufX$Prt?U&HYJ!0^HVjay1xk=wd^)Zy+ zaY)6AfhiLKCwILf`4UeccF}I0Uv!sh&bW+o>MdwfXB&ITolt+@RqQdXd+bBM74%>w zL-TmAgEHE2+n4|5R@CLgpsXW?Xv?Da5ohpw?e&&)L`1A7SNwTfN?1SVX*)X>K1^>;nlFnb}!RiH6F<7Um$bk3z-=u zP3m11KukgbT(o=wGnTP1zoi{6Tzd;!d-TYgOKBimwT?{lxlS(Mvtf?wXAq^ugCsMc z0}SGN1a)`iNcV_+pjs)xe3cTAu8%{^t6w3sWa}IDZcQYyv0WoL|BGR7{W`(6bh?5` zQ4bim*^Eut3Kv!R%;dv{UGP_fPD4NdbojqAH|(K;rJxppL( zX8klFOJ&!Sj)jx>J7+Sv(Juib?;`tS%2QmgT8s-;YM?+Wfj+UVrK=j2Qf=XS2(K51 zjT(C3ld3}QHaL*i_UYv7QGe2k@5!CCQVd%wZl9ySmqPLOxS_M;KlyXPgI9xlN7f0ol5*{)>Z{V_1k3inM%2}U$#q>(zRX{8yWtp2>(XPI;c$@KaJOz8S}f^`h~$r@Yh8 zmWICP8C`7}?O z{q!zvQ%WW~t6J!x;#;id&k8C!?@HqfHQ47Ho0-C$48ntg59jiN6k z#W#n@e|D*#Q)$9!eWHG z3{LprimLOw*^b6&bUU7h9+ok5K!TsQIck%xe*_6Lwd_Yaa&U%rrWd%}4>-H4jBiEHW0!GFZ4{TQ)pXGzmV6QcdTk$w2A zkmODq#_SYH3|}aPi~T>M;az>|u0D(T{!1OT3$t-te1diDk%kFi71^(jxmne_jkfqsWK45xj$GUJJRjmm~8-(qSgIhz#EO z1;UH_SVyl*%=!l@^m4!&s^I$1UUY344LNd=<%9wdpLT^Ec5Eh*p`|o(@GXv=I|_IH zxq?n9pE2oVG2Sxq#Qhss3|l-I&y1VI$p-T*{`04C_Tze{+G-tjEgC^Sxl4dpwGm3J zNMvV}YcNsky=k*e9Zhu41C^_@VamrqFnkwCD*fU}{R1G^55|!mE}*%7~3@kOj%fb3pMJLvF5K2NPRr$m*%j=&GGT7)j@%_W8>=_E$ef-fqS0xEE+S z^)qq)^ozd8ZzY+3=hF)(XVUuykI-ZJO6v1xEA<>q$8^6ZIJdrlq-Qq?^6K@7?VBh6 zqv*UJx%$62PGlBIGD;ev&=%sp&bwhGGwPEjX)o=elpPVtNcNVMR2q8UbFPe1T4+zD zq_lUUzW4hVc>myi-{(Hh^E@8tJ8}iSJuI?`Z~TPV+2(BTz!WxPa$npVrOtkx@8%|s zErwyoG{LCi9-C>o8$I;)RmBas$A^b1gJS#ysdKbHd&q^do4uXc^T;FI+1oMbRQ8fa z_KJ~?4s_=Kom@%>QcshAemCd`PvnnYK1xeMm8tCSAnLp&NBIL4p;!A}d^>yu#o2k% zHvdNI^Wdz|YtK50+oel;LMrj)hg10N;6WHZRMY0h`3(O3>_6CV<5WETY!CDLvxyn@ z9m6`F4&+`KC!(IpRBqlLca{^li9Ox%477UPWZAbW`6qrUILJef?|w3pe}Be>Ok49X z)4iN#hOHnw@#ah2Knf||326LL1ylzWFxk4@?D5Nb%#({{vZ3*m(RVl1+6K_?@!@QC zi4z(BJw?_1+$m1Y3x6%>p;#wlT$T!?l3s-$uN!kg{)gDt%nSymD$u7jlyQ;%?2S?y zw{~$aW*XYbVy%r(PGlyguyU50+XLF`j)49mALh}Sh))*uA^(v`_AA?P;h!+_a9j-9 zW6h=2QS)h;r6s0a1n4Q8&D?vQf((?1%r*qu(Q8m%>6Z9>4#%EXM_6k`CAMwIpp=69 zct_it%x5*Cq`{ZYJTkVCdzXhhx+ieThKaE5(Iz(@y!q=-?4H$WG_LPq`@SfO+=K>xpE#4KO#F`8_r}4-*XvO`as^nV zhtV!v0{p^0uyLpq(hMKKxAoC5PBK8ewOS9u2K8t9)hpT5X=fop>mH3soQQTWJjg)h zCtu;?gEE`9u{`~$5FD2b3bIPz*R~W+aZ#*N`5yDL^I_vI1#z`6UNfENF)ZrMN*LV! zi;Yh)WFKyS1?Pv^Tv~H6s9oF&9`Z+VWX%fj-IdIZ^?nYod{#<&Z5aR|$BZCFZz1>% z6x|tXA45(2DCX_bi~X@JWixB1k>_+bxG?@F7I*7GQ0Q-b=6i>ge=%V3C$GctA66{W zauO8Cr^D&duHYSF0#QDzKm-l3D$7(b7%ywnX#WpK_1?*rsdsb#uC8JK_;LLDkMF_c zV>7dnOTvd*TVUacxokttSXQ{=5U4qvWsAbHft{+547Eyv!iJ;lg|ofH-0dv>DbQz* zdc=kglf$@=+02Je1e0_{AZasQQckzCG2bc|b2{S_DM8@+=NN_yJ z{Vt2cunlIoXtx6!BzCP@<28BRq;_FI;3;87`vSq=S6>*c=FVDfPNItS_3UGC9yA8D zu;}i=&^jp_T(u*>GxQBa>1N^ClYhWj={acc)CQl@J*=^y6jI#Jv3D{~HZ5uq%t~n$ zYCUUXK5wNcxU}Kv(SbI$Rv%d9GY|CLyGEL=JDr8>QH7Yh7x;|u8Qh|SGVJ<&2Nv?K z7$*Bng$IeU;8pd7?wmR=cp1JDQr#+r6@M<1X}cTV|1cVbK`Nr-bv7(4nFX@evhaQN zVceJ_OKQK=Sk1w;wBnK{_s9J%3s4Z)8ojG*d|Nial|?Wi|1Mkdps$U>^4)CE%N%xl zo|s9cyJ5*(c?@Zeg1j&~Awt+7awJ!Et5NEVK$6Zmif_dCTVA)D4&BYdzmMBk(_<&fP<0?f zkpW~D@LJ3jGHja0-zM2fKe$g_cevNiuI!&;2`j7Z;n(|yv%nN1_M*at@g=cnv~&li z#I;C21Sr!G#St8M?6&dh+RDvZVUM|S`qtNacB8UXnO_`v9MhB>XlOzM1#K{4{dOq| za=*mSwb+^rcZ6Ec-Mt;(RvqVyKIPMcGdZAPa)X%S@ zVZrnG(r>wZj9WaG{g;GMfrac!tp<}bnZopiMs}o9%6~A`Vh6u!@``DHY+kI+$M1hi zF?Fgu_8T>hjS88@?>sw{OkOWyFTL8at$sJfdh|#K|Fvbx+fPvL&RuA*ZZx^JN~mVX zV>0&X!=GL}lQw8t)50e4ZguJpsxcmqu7^Bm_`bfRnIhgNUnnP)71Kbmbs%2qJj1k~ z_h56_K;GH<9p3f($u2ILgPB_+Y;H!lp!b?D96Drk4|T5L@PGr%te&yV1)Fi+?Qv{~ z>uF4!w*oIOE~nWeYZ34KCZj)h$fTl<5)J)Dr^6z;?sy&lVG)(IZl!rv%~T}4KvBc@ zVA{r`_-OV*8ltomr|XYnFW@`|+Zy2KzF)}dM>qcG4mP7sy5i@r4{+p!L)fBon+<97 zz!#QY+?o-h$6$Vc40&FU_q+X={@?%%o@|ba7cSY1Z5+XV92PUg(jb(+U4-v7;z{w~ zXBrTDlk9_y$)zcp8f}MB`MNc9H)#UJ&Hq449g{J2@nSOQuA^f1PyUg8DmjjGRH^wK%jGTTHTD&q;P{Jz5kC z7+LxNKP|dW=M=|K!FW$fwY-7B&7~CfQHw%sd?=|=lcp;nYE+%)>?59loSR6j8TpvM z89WE06jJfj?^IYqBiNOf?l|OoK5xG32meplicby$_0Ahh#^;?#{c09ldLoxmMKqRHyqf6~EYb@(wJ8rU?{krU-?II}pGtA&-j@KxaemS391Z7^&j$7>E`5;H@q~CeJYBx zF2CUe{p{L!spuNqay5=ma-YcSP8pBwDFRyRt8h2R9A?qa0{Bl>j(nrt5j-h&TCCe2 zalzex*v*W)eAmPX-cZxk`dj-jW||-)vKYRBIgW)9XXk?TDod_Ovy3)8m`pPdy5N`+ zTT1cxN)h|sh&)6svN~gp73;-!#K0YF!o)k+wNM39&GxXNg~@p2a2fVbh@gHt4dQQS zGpMhMK$|mN?799xoUYrzU(i2@kuQ!gN6~MzamIJ{Iapg7Gp&ss8Dz*Ft^0+K#?3>U z#sDyM*2M8rTfRNM7i&278hT3(!9trHm^A)7uX@iG-}((E2m8<1bNCNA884?@_1U!d z;~bQ3n@l>YXZZx*a<)CC!L!_{V`pgumymua99&{T$1G z+`&HjdcxGvrtI0BCiWy%nqdI^Ao#0&|$84)+vUl89_DJ>;$V~2HVOtfrNE!vb@_Vzc(dPWClaER9@gE8i zyB?OZA8ADSDT?;6CZ|DHX|0KrI~{z2yLWUIyEW5>e=m8?x|#+4-8BO|Js^QEzd8xi zO=qy!z5~&7fDIlSb(n^2&B686qs8oey!F$JH>{uS9yV^N3itW!Dd?A10b}+>vWUT( zKrUevcQG}X^}HU$zKlE$!%e5M08@c|X*32q4}X~BunH6#*Rh`wQKGA0Hh%DWNPW}a zleVc34bW{z-;I6nAeM-JJ0%!e6~y+`OJRF{JVeHPXP3O^anUh}?A#K4$TfQm*5aPE zU(6U*;1+||mR)4`YTmP=O-oqb(r%V@cPbbhNdR{{M~MA06%IbW##A)R+04{7c5Zk8 z*sYLf&s@)m{FX)1=T?T&Ns`CVXGVWkqMHo4Z5`~AzYPnBP~y_GPN46Eo7|>f-|+H` zx2QbyJ?rlq2U=|b?7#F`T<$FcwyckV$Pd2^lS^9I{l?zl?l%DDoW0K+Jam|1u>wpy zR>>6(?#(KF>_N5SA;`3gyUXvL;5ugvoUI%UX}?ZG#LZUFzoN?252v%Ay9`;#)c^R- zEw*f#`F6?ph)jqW`V>liVk9=37E88V9tp>x8*N%tS<+0A_-BLuxC91f2Pz0GRnMQhAf3bQ?Dd)0uJ?r<&5r(=x1G!(x;NxZm zPjo7w{!$fNv3VO>X}96<;~I42iX&a`_(l82HDIH%EW*EL>*|erY<{PO+l*LhCpBF4 zfeTrE9Ch*onBwk_%#E+Wur0^u)q6>|y%t_{+=ns7oM@pL1J5n!iR0n4eZk-*>J@QHH zaUR2qn>bo*SOeCvtHJGg0hl+bLb=Hu@E*E_Jy0&jC-;xw=C~nvd1?+mwLFKP+70og z{tWJ5%3nTV)HBhwSBE2hFUBR053s>Q0x;k6G{5@6U8Miyu;bZ9T%8iY9;jEMVckaR z^)DaSm``U_H*c*r|83SA zl=a$2&a$)ErpP*&`gaw%dOu)Y@RZo)T=v~82JH1Vz=5Ck7^|frk=ff0C)Gwu^pame z%$9rn7n8eOOjrzR=A9<@H{d<6_Cn~P_UiKd)6>7Z9EavNq6 z9Nv8w5^S3BLfIeqviKiZOn(gtPOa?GhMTy4q#}+Ldkcvcx}r1rm}H1SEqF|1aQ(;- z*ncq>tJFu5`kHp~+H-&uo(*OjB|5aoHI%u&Uq!tQj4-*|olQBli7g0i!CSkwl2xtP z{kURCLrc=}-*I9#o&wFVd5VjE&F2#f87>TPpi^d7Xx+cTHWHDk8%GNyGN<~(3v`B+ zdamGeDU1E;Y=X|`XCV1mDGt|~gqgaN*w4`yS^2|cSkfIu*-lqz(0l{jQEY^D0llc` zgBqo*96-zei2d5KAUt+;H>sTPmkP#Sq?&dS6*Se+FT{v>E-=7|oq8nm^bYBd@x@=( zOYzyB1aOJ}#QK)Xg6^hgsQG&%%lw{3ZaXuuTOpjPp40=*dBC<^4TXyPYOsCa2M%uS zforz{p=5ypYg@mS47WeV$q7}wSE>r;>MGEP|N7CbsDrd<$Ob&KwM)=54iyZVrcy)D zQ5w|rob=vQ<3p2M{C^dL@yDbZo3Tdbtf_byT7T}bxpn_O8b)m4H)-x;{kA*8T<$&O zZS4)g|FWd@GoQhZVW)YO2_@7!bpcJ^K92VM_nLJb&JbOS3;Azqg*01oiTc&_qlJ9} z@!Rf)6t!|Jbv5hYjobAo_1s2T6Xy$VGwumari#MwcoNixDhlJKEEPsQPoc<^7!3Wj zoAL%;#l%_l7-n@0-~Skb<`*Bc2F;Z`FM9`@{kEg#u>0)swj7v0UJq6U7O)XdR>0LK z`@nBml*m(2VT0Y9+1w5OWO(8ZMT@uJGTeOXc-@C0=WZ27up#u~g9`0DYE7vxb?Kt& zZ7MnPks`ytz|jnExCk4=e z$3wt$s_1DlNv2=2a|NH{JA}S_swrCT9M!a(VGT`7>G~cA!Dw%>%?6RJpdS8~%-1f! zcS*A_`lb$L2e?wH{VTrk!EMAR|3R!-IXFwN;YPdVH1o9sO>zB!(N#74*`RQiJh=}$ z^Vf$h(6?hzr?i>(jsIBx%sb$?Yb}IW1j3Ds>X3)xj5PWd&`MMX&q^?)CQ;*2F?6#=8Tz-F3$BX+3zN z_iZv*d;(@f4rYaCL{9fmZK!aKWd|pHXBs=wXw$U(`pG#0YlQ8MOb0vs{(fvOA+&b-WrbnJs| zJN^;%8qGhQd7AcY`wC-!je>}}*%+Un4C=a0WY`(SH&4q3?N~>N%(f)aJ$px-ZzjVp zzmx2wPBNU?{2Vf6C&RAJbL?vCT$++*L-KpV+4$qpIF)R;?@9JHPeN{Tp=fsMOB8nhgX4en!0O~^ z7`LMpcK$7f?vsXSxceIv4qXXcO&H9dkp#z9_yM`PNsPADN{mi^lMGO{ml(NJ!Hx8x z61Aoj*y1t;WQR2|SKSUWESinAX^HqdCjlLoT*4vGwb;T*`DmN@Qo1lmne1o$hlXVh z*y&nM+AmkL0SiY#PWvX%SvZYFw(SMYj@PhO?DEE%Si>p%DR}$v4?K}`0&EL!Vfr$W z1>@m_hu?q1nPWISWxElgq9Y_q+vK5`>0Kx8TyVa!B1bjP^w3 z;`;4TY>YUwzfD@k-dxe;-YLk6Y=T1iJEjxE)DvAgf_%d*LCdmUkQ)$3({*A6 zxz{55(`O$Z%#S1A3Dt1h>ODNK)Pi1?0+^kXg5pm-HhbJIeoVKExZ4nK@pl=qfUyls z#m1GJdB>AgZ7*cW9nK;*=d*PcwNj~`$oH~}rFze$BLD9Ir9SLHonEu)WZxas>8>k` znrbE_lxPS+Yug01m=wAkITJff)TwCsK%w`M(}KpE7(q=rQCN81LHKXca$(t%r-I)1 zofM=~z+Z?l15MFG^0r!!OwT<4%@0Xz+_RPJokI|NJt_&7y=dk?kK@>+b3V)^V-E9( zdMwpB?a1e^d_(@@htRUr2-NFItU`1z`l#e_c}OH(d}S_JM^6y? zXRRYIdoLO|JcPw}d}ViRr{URvvD9{Y9DQH7NRV4vB#gW?RR{|>APmweq^{1?`#ff*Pv>;MHb- zKI8K-JaB9bd+yZ={mgIU)$%zoJ*66>yIKUjic;G8ONI7&k0pnNb*%Q%P3CVZa#E|$ zKt^1t#As6&v}x*s-1!)&`S}ql%zDAjhCyKI{SC9`MvHuScTB3h$~%ku59dSIaVcAk zC%rEs8<$3^_Y}z4Fq`}J`vfajJ`3@Y>9F557N*u8g6RrnV0UN;e1EFS3(cyM{tk~p z=EZYJa=Q)pl6pb?>uZqby_8(%YvI3rir{dvMxwlH4tUonLvqbaaKG;hmC0))VxJgV zFS>wK%Nrtoj)U+~77*UMN80TF3-cEhGPbr02Uh4{?!d1Y;QSM3-8M!cx|KJ`Jjt7$ z`-q!Y_acp!RbZ>|oT=uV=djHJ{G$b!pXviAYzIlKqAx;C&tcX+)*M#K^+5RaWzfy` zNqpOeNd{l#;ptUla5y+sGWTAZr1$O7P}13lE!kqs9Ni8>@D>A!PSPbP9oqy)4IhG) zrWEvtj%K$S-m^J+nJj(DFn*5z1-v?Q2by z8;ym&w@~}cUQVaM2wW0-p|klFP|>jEj%)vfiyl9i>k4g($eF`UuyB-U;7!T6pc9fn ztNs!v2X!{WrU?QUESIQ`y$_2o4dvI0y+qS{Zm|EQ7L@B1fKSFsCfRJu`o4-|1y?g* zg7;;XyV`@xd9a_UxoyLfE-}pR!f&SER><~iA)i(&&;OkK9B=2$7TGjnN8m&m&6{x@ z%ifP8?Mx@oOMVZ7RldUN!~S4#dp&S(r!Z-y8Oi%g`KICO+{jM{VaXg>cwurIu0Az} zmeaoQ7dAn;&jg6c9Rs_Mxv<3IUpT(sAADlng;}pSGS%LQ`Bs5kgWeD8k@-=mQk{a@ zPe-tAH9agmDv8|rg`+_z4I{XD6;=We;(u>!6g4}sKI=DJm(Cnix^8Y$nR7C`P zacK%gJ#>fc>@AS^`vYVZ)j(m$e3)bYfNj{Y9v+tNm6)qkz)SH)Q_`6UiF+mR$7CPp zT$F&X>w2>hPS^O`z1LB0v^||&a#dXSO~5-plqq_x0UaGsME&XzEmyzBNYDN_>6`&S z<%SogUimI^<^yfwuPwvvF8j#V*OKlp4imfHor2P*!GhYKMmqmsJ0%^k;yiYagbH5+tEy4#y5&XbbgyrWS*N%&5$QEGc`I!47kXZHpd;s;q` z84vn$1xE7Z*}YKQEf`Q*{ARjz;W6EfzDf7@{U#563w)Z9K*Q`GLAB}{ICg50WJ+1E z#Ip_HwdEg_-sqR{kMV|%O}&>sj)=ScPK|j^i##xa@nJU;kx%q*c1L6rmXa& z^~O4kH=INY4+eqEl3L6#EySqrh3u}9F)Oupr=)a0q2EV45+bh%wtBMEoU=qwm|I1a zc`wN{b1lglPsI;*Q}BA>WW3qG8H}?598XGzgX8uX$ zS9S!lc&lGH?R70&xMa`#mc+B`vHp1Zj)E|xwjWKvVnOA17M;+%CFnQTQp*o5n}qH) zT+sMJcDDa*ZpJ2StUp{S?Y$_AIqkp9Dwx_A& z?0jyr?N64mr4x3~i=qt*L7?g01UGE+VL|CiiN?$v*1QtItVfGhXqh5yGNf%@wJ^)m zh*_qO<6^hFGv5|{iEIopr8cqaa9G^;gx@BQ(LR*2%81S^EkxUfPCVlNj-8sj3k%-n zvdmM(7_C|@wK_A;Mk}I*Jx;vJ%5qw{M&*<2`|9I-nDI;Sx<3WHHfEzv(N0z)?(;2o z9${yCCb1d*AUZCVL0ydsd-wG{*uRkhwUh72W>_Po{4IdG%8hX8Y65f{=5q_GQfX0s zHF{p20{yz8VAQ_V=_`Eefa*m27m^nS0$eco;awtZ)aUB4($Q<%va2RecE9YrV& z`_5mTH6I$QIdtyh%@B{`=MgG`PGY_=tb91@_$>?TFRg>4@poy^4j&AOe?n^&oY*^> z&TpvLM1!oODWl*MOELPpm6q2{%<>5!z2={t68Cv-_<9D+5@My>!H3TO3 z>}Ne+jKETDGdU0HPZRq;7JB)AW*Y4=5SlISKh}p(XpJwLs7`?Fcb{3CG?9AMtitQt z60x>To!tAllku@VHZ8a9@N&j?2)9*d#R}c*$x&_5gPqDY+r{8VB^h?*mXFkDf3D4S z)1L@k=UBzAmozl275BWkj7xvD^Qxh0w0=zi-slHtva%b`x-I0U)jG1n8%?=G;pbSF z(Q=yRH5=zdO88Wcv76_yS;>zMIN8l!R4@O+jz;jB<_Z=g_=JxuslX(^a5 z$`hbHpYdY%wEOmQDEFTVXAJ;$=gwh~r{1ACwQ_#Jm#}hp0Y7BdPA*(~F+1J6j)k86 z!7OH8XNN{C68||XxeVuYcDd*e_txLoCd=+Jwi?LVWLY+fyS=&KwK$l&KP-;D*xV?U z9%~_wN*#*cn?ptoTR8dJU>LsTC!c5c2&T7H!iWKn!1-w|w`b7|QV?wL>b?ZN>ct0< zt#*o@T}`7m@0)1fZyQqH)`6`HUX$~#zF1_CPb1yMd&rMD{KJ+hEM2P(;cYr7sD9&D zk30mjdbioZJJb2r_r_w@+Kjh0Y+FEkbWZbHBbYX1gGJ~Y?(tC#w#uY| zKeT2CpW1T?54c8yl1l;`<64UImu+D>^=?f2K_uo|oTQCSVisuOL#Y%0Bd^=K*ih`s zj$E*Ull?ccT6I76X=e*qAQs)3o3C8S0*F zX4gMnf^p{Q?8ccdT;f(In4nr{WBm0$(0RBS{7(Obqkk%<5;b18EklW1%UZHEsePJ3XF4?*2#fwH5L2k_|MpZ8#OFSW>w7p7vzLT>LFb z!-8+?!2I40I1%rM$?MNkpU%&$Yp@c19{G*FRc&PBbvJW=-{g>^`a-tg?I~6(f81v4 zd@nX%`b_Mie+HM6w_u#{8PNPGc8WHIL-}_r@aTL4|2f1mQOnMCu`%qaOfDNAW&kt1 zPO;YwqgdyAJuEV<#&@1=d{fX^{M7aUvlqx=_~JxyehkBgea-ZK=}BS06$e4>?MDi+ zKOh)iTq_LBNTy32DwsTBBt3k0RWO^BOe$9|@=d?)QGT&FSL}Dh*Y5hPV*S~eb{ZqN~k^d5~2=mAg8-xwmMi}Fwb5kIF(6-1@ndrYoDJL zrp_^-dma%~`OuBdZEvB<*<;wXY$aN9E1$+a-Hs=7^qH<-Fw-dTgOZSeVEw!kB2K43 z-lZnkdhQLxteXwt^#+jnuLdM1v_K`hhNZixuwF~d*~jdIpdMVy?p*vKx=lZG55_uj z3DI`!^Q?zVdQ%r_-EK%m<_?0FBDXMj#W0w$c{4_8>d@VuUV`OA83AUG6|A!k37&J! z1j(OJ%G8q;hGqYxRU)3kCo>DeH{Sx|K6hZyO;b2mQ3wS|Cm?9rI`Oh;Fhov#0teWB zsH};Hn%|ueB%=$)6B4<34h77up@>(Vdyu={TlAxT*RhFCO##_U7ubul{h-)w6ZgqR z4S41v8U4sfvOVRMWVmAp%u$^LxwDFJ)&yU=^kk%&Px&G zPUTZ`O%#^5uAl+kWhh&Cf%P12WUqdEfG`b z?}W|uO`xhVgiA~I=FSZc#rHYux%KlVa0M@>;jcj|pr>^Qj=PM81f3+7J5n2Fc5a5? z-OnU8BhnSp45}_G|V){;A^) zR`*z+ef;Ud&S^Zx_5F@m=V%s@R!18iU%m*~nZEG!%6!mUR|Q&&Jm9e2K&W>>iSgom zXxMN?Vrr!i4~D10!H*3Rxhfmz3>yh^uRZ1?8ZOZ5qvo{FX%1*ESi>p(jReKoyUbO4 zENgrhLgs(O<>zX5usdM`+g87YBaw>iu+|IQP!@usB8aW*(g)4hJ}W}Kj1#!3MZnO>79gGC4+gx)CVf3$FP6?CNS;z2lVgC zVfBB$LgJpaVEgeM%haslOf;%+&6hrS?Qak!#hUUe<3#58Z5uwuz>I%tzL~3;nM4xl z4VK_7;LzAd&};V_7FM_s>g$13?bU;(zN28<-g(lX&mvn#?4Wi{p9K%z_PG#m^OY|$ z5a3MoNw_I86wl{hjBaIaZ6W z&WppSj(6lHIf2)g_s4wyy*N@sjC1!|@uBvA9d#4%e4E3s#btG=@EMWwu@+7E`Th{gC{q0P=g!th<*RKNWPhI%~~!s_nR=~b!{ zt8`n5)-XSblJNjRss97^Qn!cYx|z_VKbvuA#$f0Za0tdfDTG5siLCqTVv--;hi3Ij z!#C!}_<8v;l-YNmPl|elFD(u+7cF-(jM#v6`(x-pZZr*dO{0}duCW&#m6TCuPKuAj zZr;g{f+*Jr^R2J^ly%c8K+u{B9{*aYo zEHTnp10g2AsGUj$C8Ha(MaBgdM|nZw+H!#BtN3s|4|dT$6f+x;_A7tKpH@q0`-KwH zy(UYm?#iQ%nCQ={$--lwM$l4?2NZK$kq&nT((#CWc*w7u?k^lnzM0qPxF!nndre4D z^lijfwo_%s3LJUa6&uQRguHXpgkx!yLhv6qA-`yw(6?_Vb@=fDEVGv=-uope&uSn= zdrQ8gz8kVWH^CESGgz~@zs)+6O=L9J48NDYqlnwnX_4gv(XZ%(e`Yw+lFF(0aqBcv zlWRnK&j;ddi6bT#7g4lyJf*hiQd#_bg4jv)B*TS*ifgHL!vxwgFCQ1Kd`T}aO(#1S z3F^J=O>6Ine%{t7A-bwiaLp?fbe?($I`c0Gmph&dS;_K3*!6g!*kLI0_THHUmvoao}d;ew9gV|vmwl-6%LMhC8lvBdTLbaZ$aEiZmbJNoJ%|Gq_vZd$ne zdp#Mse8;C}M$;h|qEaZJ;0gm;W3rm=uk@!6E3VMf{jQYvP)C@sF@@HiETPBrTq*(}k&l}H&;gX!KB zeW9OxKVg8`JX+>^ntv~Qf+F-|X_MbVVh=S)e#3W63;TonY;NOXJWfN-h%6AjefZd6 zFMTQ9MAD86R95nnwz&HVioq6C*)v>F2;NQI8@1@w)3?m7d52&+@dYQ1VuI0gWy!`L zjqvW81e;!^lh@CgBB%9_aPq++A-1zqC|U4N$eOMr#KtxX>${dyLy@M?Z^vwUXlX=` zZ71SMD+$SU=+XFgU6N5fE8gZ8k!-+wY$;hoR^L+bx$`kD*|C)$7O2S8nQBnJsSOph zf2B&}hxq8^J}S=rLWeVs3G&USsjc$>_G|eA3FjKCMZ>;}H`uGo(8}VvH1N0#noWx5YR+$9CM<@^Y{Ka1zfV-+rAk+> zJJHqyOX$Dl-Bg;HO>TS6)5M3bFw;ba1%k6AG^9!5d9q=ue9Lv{2$ccyR+a=T@RY);&>r;<~NJI(vp0 zsj~-GZ);>@yAnb5@D6sdpqwSuH6w3nLgtFP+@t=CU!G7(y(hoShjPHTE3#p12UH@HWYWWmB6+D5&)A~FJ`*E zW!nv|Fypm}koN2;dfXD3)7snc;?^QOqdptca%N$&;}ufh#~cZLpgRg zgO_NHXAl+F!kJv^`Zj_=AJFQGyY<;_Jq*9MlkB6b`3E1JaNNGRxMb*Csp1ZIw!zf{MpVpV zMy7tO^1eQd%3c5mrk7#hwkJIAbDbGEZs(*bPvD++ARJjdP;x;3inOM)NTPOZ0|+-h z!?QW{63r}qFxvPA(&JlU$Fy}^&pQ*o?~6cM@_iJqmir28+*Z)IvOd!D{>32oB$#;( zl;u_}b-<>in@r(XZ|bK#pOlvF#2;L5-a-;)g8Iv#GMb&AguN6zaqhv%p2N&4KJ1C6yj z$UE~e=lM1`)vpm68n4iR{ch}2;&+Ns3}vt5uVQT9A>6j*4q$Ea&?b7;P4+DE70TtotdZ&kZ)$M@k-9?Z* zWC3K^nPB6WQy|-w%^p0|Vz<6eglgGVHpKTQcy<=Dov(eks^2Bjp%?aYH0C;cu4vEV zH}~V?N;l&ip9*|GZvt*!Y=QTJRx=;TXU0#A!R5RI1lx6pPM>DbFMI))&b7nMtM(Gj z2bIt?av7}Ym?D{&6d|!a-e02V5CHLC4zixUk0EZgtwitpL--WmTcYg$MWUbF3wCH5 zz{oXAA^Ow~iT(ve_F}#f&QGyo*CQ6v=$;ke2&+J*B9MKBpD<*Jndq8x1Bev(n@o$_ zvfu$f?|wKpBzw0_OiChKlxxg2AAZ2XXdam5Pi5PD(;@40mdK6^fRIyXBm?TBBr1c& zNJ(UP9sltaiaiV^+8#a;ZuBrzzGUZ`MoRy^Cv=L16r8>Fhj%3?WUNT|< zO3b+_?7RLL*pd7i|Ef%3+xKTMGOB?&8$Q61)-srJ_Aji}SjIfc*6>Q}(Wc{2A$E=E z!1vwbS&{1){;P&B9vSnRoeH!Ajp(^H2aNiG{_Gnte1a?-s#^vrEDp9lwua!1HPCGO z0Xj~90pqDv5O!f8S8cKiy{>*Hncu5zez^>1_Oz?~{=;_i#~*2Nhwg6#W7!pO zYGE84xRnEgwa?(st$ENpAPAOy9K}t@6a4_%u`D?#jitqmkQ z8PzLs_7(e4wx0@HmZOcD-TqiU@f663dx9{OvL2&ZAc+3bw9Ucb#eIW=3teDaUK$&| z?+;68dBtqv2Gg$?Rvd&{ZudCEVSd5Z$O z`8$S>>^#VZ>aD~D9p>C$u}|3laG97hxj^jltEg>=enzf-*?=a z&q|A8J8!u1`Dydvz~7A!AkIfJM@pE^j9M609}0Ob%8>GW6!;Haz*&8w>9{8)RgILDr?6JNG4{v<2WQdPDKUKg#;rJVQ4q7MJA|HjE!5}hXZ+~*k|NdHDc#^SjXW@vCN6Kn zPb(HM^{hjxNeil8Rz)pVcVK;Lp;lKiaq3;(xs#EMmcU}*Yn5}p%aqd1S8Ibpc z)vx&glQTS_;=uzLy{Mh3A6Sif8<%2I^=q6JBah?m&E>KVB#3{zf~}vq8DpZ3p!(7k z2#>EIz5T~-%%8%NyPrtgXAi~c#G<~S6g_PjXmQH=oxz5EZ~b|o{9*d-S-Gyk#Hl`oj;gyVSl-8%NCC^iDdEWIc0ABLZQvG$#vx; z3NgAx<1T%n=|0iq`_2qM$S=U2ba`o)S_XIvvmo3c3F2oLz=7%)c)lqM3v4pUx~r9} zI)kYfHwt7c*09DK);Qbb3!cB0hxc=uu;5NHzothS+ut%iK{*jOq!YQ$bi@PKL&-i+ zmFT7%Ny=VO;O_=npL+&3-*IEAn=+(<4o7gMgB*TN+aSI1#a{HK{KLkcr({&@fz!;c z*`&M6vE7C45WVFz?6PSF>Bky~b5w<_kMFpf(GDb|aF!H6gU0MA!Y6IcV-tHPdqQMM@7S6!Kv%G9#wryaJO-W>a^p$k-t3xEOXiK4ghf>w! z_Z04-OR909xb8FuYTozot6?hEgubFuHPKUVzmQLQa~O9UJ5grB0!-;xB08sjb8nvg zgE=@C9`>FAF<)!oh5kQ?p3nkrQ+n90(wF$eBL$BYKf(Mnf3T-WMf$EHiOW!$bm3a6 z1v}yu!VgvVsDzv4HdN6QZnm#@Xf-)5v`TTFwhUC4g43Keww zkaJuqIs9zI$lEIT`&TzV{^>jzHcGrRkE$Sk>?9PSUNG*_b_xtifcaml;HqZ3r1!#q zK*smMVoP7q9d{1S=(e+*#kZvo?tJG;ADQ9+yO-$o@HuCA6LIA+6Yh?5fVA*+FPq{Q z&eDM^mARy>@p$NG=RRahNRhG&mCDM>EbXPGourH+B@*?V`#$(mDrF`k z5<=N~^Em`l6^2czXJP39%<@YYn?L6w9^C6ruF;ybX!SN4KKu-&U9(|I zU&_hq`8V3AxnB6uF^P;H_r&CZX|OToGR#vL0l6|y%30Ay%|X}cM|h;D|5}@xLMKzA z+g<9t+J(Laym0u*Z-L>q>)`p(P3*6!7rV0W8hUw6M3?H#+&bQX9r(H!E7KNWj;#rv z^ozt9Et8pL?m65sCtvo*8#t@v{dk~BI+xpnX|3d+&%Qs}cCyP23hdF09WS?1NR|sN zHc}yzJGsyraUOOIvV;=*?eJ(%@?*(+9Uf}oF?wUL=5Z6=J$DEX z9bLkDhJKb9HqS}3$2weSw}6%|YNjLo22#<&+Y%$(iYjfK$+G`VQs}2mwn+&X{HGjD z5=4H&4;@}tR}0pOa#R#_jm-S5C{Aa(Gy_inN2%kg`g~uA`Je)=Q`eGhgBi1MSjLXp zzk~u?InFWCmrb{o7;MWeS(@uwR^011t3F`m@FML3vy<<{d5*-VZ%p}l`c@8;YHe$H5lfh=ki$oP?n^&+aJjaQ*-CfQbIblg-Xjh@*4%=C36fT3Q_Nj3?}cy6 zM$-a^X{f}l6edht$l`(|?W?YsZJuw%T@unz*X%b|v?^i3y;Zp3a}Co9>VrvL?p(!X zD~gn!4I89<`;s{isPC_9!t)v(%4SWJB0rTbJZ};El&e#ib_=OYm3n&m`$@YacU~?0 zEkDh0Av_#-4~hcz^ZiP#;gO~-d@COU?>nBcxVJaqy!5>d8feZQ8mz{=A1Cn1h_4cV z^A%GrWb8^~9y3llg}zt4Wpi6L;VZj|TtWX5;h13rZW;Okbsw%F&D|c%G(s7xzI?|P z?=X^II2{6g)|0};0B+acTDqHPOgj|skd~e{Ta=XqyG?_66{llxAjpYV@8Qn-E_nt? zFTO%*ZYs2-9Oq}ZTk{bg*YP%6hd_3iCIsUFygN>rQtlK{yYEOcO79}W9%(pNDTz%? z;jsF-)c=?khdS8;Q_k%as+O!4+%9)=B?^0(LHZCB%N=l1mIgDsBV~}cc+#N!dED^v zzi{RJU9c?I7^j%*gi|FYFd=6PFpUycSJA=rQZty9>NXf?Ch}_e-=R5O15$z*^c_;e zPkq?JFWe{jDR=*d##%+x^l+!f55=^%uNS>q@s?IqKNZfbTZ#Hb?;IX#&%n9o)$o09 zed=ovgr%)Fg+ycI>iRsx=F_Fzj`}916LNxmU$vFBe~M*ZkFtb>Xn*#CrGd!`6@KVV zciv)fGCw^yh98w_3R~tMmUWwON$kHo9Gs=Q%A(m@A*sol+Ga1N{Ma;7k#@0Wa%xc0 z^#Gob#Az^>m_OG%(eYO=8piLS4v#%F==KZTp}ig_D8=E^f;ZS}VlL+yn}q*~qq+9Y z*7#q&1Ewsg5i0sckh;wV?sU#3FxoMPJ9+FcQ*5-xbGjq>f#Y@HncjWgV3_o5$1r~R zoT+>-i%3vdaDbL=yGCIFo)SlTF!HA#;aJ&DQB~8EzRhh!jh>UpyQYj)$d82!ePdzD z$qG0VJQqaN1fy>CXqb6Ln04(uDX-j*`TwoJ#VgjLZ|-ik=(juGykW$Y+)vtB=16nP zSxxR?h7)y<{~_rtSD;&LBaHq2k$Fv=FU?9TVAVfkc=;<2o-T}ns{y}x6=n*)Co{Of zta+5gMNz0j8`;1oI@D)AJzH-rnh0ye5us+NmfDZXZ0l)~#tWIhuR3sweR=KKE|?s& z61KFJlh#UI%qcC!30;_RT0KSymjcJjM8F zQ*qtDbQUsM#%Ar2bi?J6XK2uRW_s)`oQ!j2(S-O@EmJ5PCMOQ5xl7&WKBw-fy0F6X zJX!AOFWT(dE~=kjCywaSrqiD`QPM9jn7%}$-pSpmy1tppd%dQcm6Ivmdoep3BFz`C z`(k{h^cmC!lWuV#2CjZ8yp0C7SVdE)FxbbvT`(DsG;JW2^}CqCYCY`!`7)kz7{hoy zZSGDoVE%(b6o=H3^(7d_v09l4r{+m_Oy(b7IA;1FqN92f2^-782& z6;eANM2_>WOS?nHlB?9Yk+Gia*orC#)xmSQPK|9WXW>KUmt=^;77ueUx%`mz3s^3k z-6iR^150Uw@EjYaRp6e6)uM9AL2*mrTXFvU4k;UOTi6))7e?GY$knNa(4ZDh^!l4G zCa91&@#aT35;7NJQ>IXcjkD-!og+H_IU!ouXNp=TZgetlAl1A=vOb0o!R>`^Zks`2sT^c)>iaHFV9=nySZh!fK}# z@KcFnXSTSaf0o1)-Xvv5FU_Qci<@ZBd^NfrDEaK99>!_rsWj!nGl$oXG1zhWGCMi$ z7oL#7nC0SqJlZFod3AW=(ZiRS%b+6E`m}`xPdLIBoJ?W!6V9Sxup9M7i7#vVm1e9z zK>xMcQI)3>oyv6*K8(@i$0_x<>!UXsOgmq~kFGC#x58e6{I&%Uk<*=@xM>W;XGq?Y zBZt_eJL=#v!4h}Qzep#%?vah39M!%ZMalCD$S?U4UNwD&RpsX}z0X-L^6h`DCO}={ zfK9~_wadAZ-m_T3ICmEOLJgnNafd5W?RY790uAkY&0S48%=GjlDMtM<)fkMRn1)Kq zP0OadhS^|NDF^<3PvNw$H^gs5lnWYztA&{qacwdguf6QhG$R@czU_pzR|jE&vK7?>i~c;C?eP1~j@B$>p>3+H(n*fn zw`vU+mggsRG+ClwZg;ly>Idxkt%neGA(K1$_z3rCMhv;v>CnwTFRS-7{>JJJnslI5#C`~q05q}BC#rj0p8kFoyweO8!Phe5w* z;=R5bF~991n-^&-6W_TDQ9;+(+KpQgB+st?tHZSE^hMa?K0)SKKZUC~G=tTCjKf#g z*P;7wJItKB6{ad%qTZ7j8h^)yT#dKkp)c>@l6M@;DZR)Rabsxfh?`_x+lhOGr=;I+ zq2v|Fz(?y;S>h;1X%@2;@(lxolp9~Us|S5SZj?HvT_q-7sFL(bIS20JIDCw^*qNpy zEU)e+o3nPP!^55D1;zVz_@Aena5ii$O|?D49o#rTIFTBF6D?{%g_eTq?hByWtBNh( z7fH^A9oVJ2l}4L)P;T=Qc4c{gXdYq)aj{!q!lx)o313X(X8V!7VG#b>y$R)J+TyQ+ zw!-}ppP_#Q;(Z6<$pVc zoUmcc6cdW)9qBd8F^zq*5R zrseG7Lt_?u!Jl-RGJ(5p2&tKUAhZ1lYkZK1otEunni)uO=cn=aBmx=D7mwE!>qtOe_Z zQn#LCIXPSHW9Lu(gP~Keqk(xRncB^v(KaQt;n-7@AM_0;8Ete}B%dWreBy^SyPO;@ zephn%Z|iAcu}lr?4o||cg=VvF%F+EA4t}dfE-QAg(A0na@u>5^_`l<^3|p=U;j)88r6lf`g7cu!Rwep zO*^N4e+Aof;|V^BT*Nu|*Jc|(_P{X3a-lF;PiCCi*P-G;8%xk1Bt0ARxK{U#%unGv zv}qW_rP%ARr>`E43QZ@M*RLq)LO#0uT0q;Rbp4@g-mI!UmtES(P^Q;Pqx;0Lg;NR7 zjk&`8an#0>x;s#a*Tb&e$)x_E8=AzrI)qFb$nkoI*hhm@7IWx2reZ!`^EO~AYGZ|b zwOGkFqQKUiSmZF|@@>JrcpNDHj2EU=jpbfV9}iQ1U&MWrdkY(n>XX~(P8Qj5nmw@@ zOxAlMXphvJ;-#~MqEC#ZC;EFyXO{~3z4}Xr2W2ET?ItRo$!0ZPC1^D81=(-;h&jDx zW3%6GyqC}&OXD61oh>RXpe4Ye=dDnCg`n~5#lGo6ThL)FIer_zypiGAMVDmu^2xYw zQ;l$UwheSMZU_7E@+@+w4!ghb785-0!ib4yxXrP(7}}hS_4t(A=j4F5w?1KU3MXVc zdf%afd{a_V96?L(^`^Dq?{J-YGIRJhLzq}>$i)^Bjmmc+vz}6K`^a2!o??mFn_^_b z?W?SBcPI3nbXIt{-H?60zX8oGmSEp%NrxD|Pbgn##Dq7AIB5I}c0Q4q;`pTwKhI5N z6^crnoM{m&yLf_|qZ$huv29@fH4A@5#IS>hN3(r_BUtJ1I=rc7K^|?&R8q=Qs9_&6 zlJzF*)ZO^Ss8jN$N>3NlHKq;`FtB1flFVYv@!tCi7=0P2{wy#KW zpF8yFSs*IDiJ;1x18L&=Q9|*5?{VhL64qd>a3W~IEo-<-&iVaT%^azzmd8X zc1c>z_g{hw&4=j@QyKLS6N3L+j*Aye;Vza<5>{oplO0VXo9&}%(eqHsKi{8@*Zn1% z-k)g9hUxTXzBT>KdPAwN{OEjElT58o7c5dU1s`QKs+ONc&t|NnkN57wqO7a<_H=g& z%2}z;Qu492iR_P};gx~pkv{qs!A3T1P$*y~$;r&wj z5E(@^gTIRdH(jBjx%0tPbuzU2EAxuv&4Ny4(a^M5QGd={xKNmneGjjLSgmS_mogfv zb)ulr_Z57(QVmJ(--G(sc1TS+2fedAxoffKgafll=BVJz{@D7nv#vKGLfS1IF*k)X zhW|nCzYf@DmjDsXxmYk-m!;_>;RKH~thuoUU-VyJwe!W8WGn)j{X?+88U zWRIcpcL`VN{sV@ZG_n&u_Avg>4seQfb6C*B3=F2qL(~U#a8f%1@ogb+q~~pjxn#`l zCswe!ZeP*r;(W~Ao5#)#GGj%@dVuPd4pg1I0haYj7xlh&Q1+aSl=4S`u62B2IZt&<>`_ov=LW}kSN7%6Lv}m5SO|2U z!`<57msZ?2q_ta4;Ggy)S|@x1uib6zn1&V2=_+vWiJA$jW_v|>-}PjY)q=yH-KGA{ z?R0V)LeipeHtxX#dRR0E7Cq1dx%Z7coOR^;EYpJQxb=LG@WbG}c?4*1Cm>RHGq^VY zgD^!E7`8-#sXYQP>TC!7|9DU++6I{|mawBi7rf(NV$0ujPTuYuS;TCmq%CKu_Ffw0 z)a@XrmS(e zFNsBudCMiq@LBB&-(yPvuh8fNPj{_{({VQN=F18&`B}wAK8y}(oX^xfK=K{lqdcx4+ zYB;z+jZ7xbp#j^D(%$o`v|zy}dQ|#=j;TMR5{*il=}<#MFRZ~#`AlgemWCT5m&1+i zJkQ-7Ao+kE1BTwlNQwXNb!9q?tvU^B=ZIWnLmi}6)N+x2%ebzUm)Nf%y6jbBci5pH z3pVTZpjP!4q_?_2M&&w~GJY1TtSgkAGdHGj>ncg^`VCf{xeDN^hC{w~l0#0BDn%N7 zgiZ6@d8HL{bmjI@aXuSHPSV}1`Se5@VE>g@ofS;IvIpYB)vut`1@!!Rh}k_G2P4+JgiUjcg`T%V*?v=P87i+Aw4#QycSl};lFBNGvNi<`sf(b* zbRE2k+6rY;w!*IG+At}}i}|NBZ0}>oTYa)tVnL-0%$)G-uFY)o(1{M7q;D&cm1OZY;no5T=fu zz`f-?WDUK4K>vra*hkQ3|EmA7#(symk1Gzq#N1{G9Qzpp)`Wp9#289em$R4N4zMRZ z0if*;3|Md_ufvB*=;O)r|S>XpB{$%$;ps;Xe6{(*TUYoB`B&&=cQF&w>Hr8@MHMUcwne zWpFjCK;QBiGNn8lrr7Hx7Zj)p1`3i-XpaZ%{M{Q~)TqFGUq|+;JO`F9eE?gX_cIKM z0NG`IVZaazrYw2N!wvm-)$_L~EBh_esvgIOYe?Qnx&OP$<`_Z%*++UkQK*l!6tKALBn`VGT=25nBK`yhF%Y`ETCk)D)3aWk&QU9|Y zg#VC1+oNy%kj$f)5nO~7p7(iqi4nYZ+IHygW&*!XTY_g=J-pXcVMBHM^BP8T?9ZkL zvCnlz;I7|9ldAek8lH%+1KnB3tzmeJ7I8NmPjElC%5h_K$FW}ZK{zn^wP0X1n;ms+ z0=fB_(jndjzIPvk&FE)fVD}6M#9Ls@b0bF2q)vy*U!>zaR!LWQ*b;OuGba4cgji15l ztvB@ZO+denq3rBZ3-0`~YIZ(2mfauTodwLvqVSsGET*j$QziE2e0LpKKmHrF1kDh2 z8k_{BF(+{O!Fk;NiOXn*?`}#PWh-jWd?dYO#npzW&E zN7s#V)Vk6AbPvkwNJcfs$Fds79NFFko?F6xW6JhY82Y6I=5=?4u$?QwR#E!B_O@f| zqZhN!4{gwW(0tSn97tZWBnoR#r!4bT)XUhGEau43%Va;AHT@zbO!z|)o%^79YC21e zyDA9Z4JjulPgD=@qR0SM(FSWV_OQfKA5}(K7iWta(UxN0_I0A^tVH^ec3f25Gm`G{ zp=2)Qq-{f-SnBY*Qjcmgw*M(&`|_;W%YwTsoP7g}05dSwp3IIFo#M_pjsv%aDbPK; zopT;qPCsEIo%XM%F9z4x_2`!n?YUM|InWNl^{EmAIEN8$2C zvFDopl$JJI9NcX@6|B&qtfRB2rl(AVzqiE@$6cb2uetP^9@JrNLuM_*|Nplz=ZV%r z&T<{Redx0^OEw7dqpm-$p2fErPLrn%^s~*4XqAG@O?IX7xAi#D|oFy;$6y<_yF?)zTdG4?C6&D5FId$)@d1vmdO#cvn5}Ydv=S8B^~PL z%v2UY)FBWfWBy`^&%a*(dM=hyi zYq*;s$lV^mM!w~4Rj!^iZ7 zX7`DZ^8N%r{^v5@%A}PQ_OHMRYgb`*(FUs5Tu)=A9uAeg?xIntD@Apj6vuvfB@XY{ zPiIS3ihO)jH=PkI%?&v|@;AEUj3-~7lInr?35 zZ7=QMO|MpQTWsZJ&X4`6;>Ua{X?;$eN<+x`q&!*`TM3~}bri8biT*_`633J@((iQ* zqH5c3iugN+oOWKv$Vi^0^;W>R+9NP@$4OMvmJ^Oi`Stj+LbmSrL_z77nlLl^8~65} z8$0)CB|AQ?pTt;~W9JS|rzX=<;-#*;@=NLbKpR8e%V-F{Z2xp9QacSDF5h_jgl0JZ zC6=Y;cVMUHSgz}EJ{@m-PYo4k$)dXxJJh`b+wDCmJllhkTfY((C{a?KbXH66n|W;) z*+)f^e&{nc#b7pzj0u$q7J4k`>q(G5dKf(n_HwbxVW4z)o-Cs(hz*!sTs@*Jiuo?o zV;k<5LeJsr*`jeGE%!MFDf|G4S!>Pj2wVjJ+!w*e#=WwoKU(=gk6Za+=}+P2p9N55 zI#2ldv6B0~GeXo&drU@si?Aa*kEvuPu^eiY+dm}mj zr%6-(lROkYtJs+-eOb%x@6aCc8Zlqqe6#}D#&t!=%b_r()XFk{LUY>GC(J{gXra$%R9W7bi;y)2r#UU$&K?fWTlv^}+` zuBOiouW3R`4{UNgA}Ey0M0tt(eBky_G0L}2jNPaydc5+dS&g^oR=;_0v|}c(Sp5mE z-fv@Nm#)IG@z?kPI&JV!@dB@QDT~VB2hD4{!!8bxJ@rzo9B3mRY_}8*8uv<`;$3imb`0O`?+d3-8yC-D-X){%OuAno4K6FUQg`#}oh+i!2Nji@snE?N3oVCYY9PP9oiV#?bn-JKyUJz>7QHpq*nPJ#S{g9!TP?QkU~; zPWsTK6$rg&)q+9YRqz@#o;50*MvZOrFgv`QtJ<{yG~bqE>5VTOaOuMHj+^+;>!dJ{ zHQ~+P|1q7QYEYMHlf1O|GPo^AgU_C2D-3s#@$(2;y>2#kL_VR>=V#($E`qMl`%I&s z?4Z3}@Y^ zox<>nfo$21a=hC537Ze=2(gpOIPb^#tTOo(S|1<6-P!hmEiT$hy+=0UTiqAjwU@E1 z`|@#8K4hV^Lv59Ii$wjGUl0z(eWgR1HPnQy6m7VjGCpR~JEPyC&i$t>%DRh-jU_?goY&25x(?I>N@IagFzdrs^IUqn^I-T0{c8$6V>iHoRjqpF3C zw0S{0L^tc=oS|D-)M^G9Y1-`5GAq$|<{&CGs>N^Jf-o~`DJAC4Bd1pvXzF!WbdQ>j z?fa6sV^wx&WO|f6ydJ|{n=6m2dL*-ikW)0M;hIo=`HwI@a{|6JdPd#4^~2BX9$IMa zBF%(kS%tF!<$cJb``3-=_Qfj{mwi{9yINoLIIvpOIsb>=q=r%co)7f7P~!PDb`$RU zDZu%x&+sKh4vxmZq(sja>a$}s_oQSB#+O@?p6(V3UyZb2Fh^7OzoP*G&Ey+sOv~dU zsmJIs!s9O%SX^-oKh=hE&!$z2zK36k^IEh?HXsyA<{Xr=YjF_wTjCD%F68?t#$j~E zK;g*SSxi{6gS(>iTL|e0Wa+W`>}uu!A|Y>F zS$3YXPu!!8Ca=lDKZ)e7O~wVMmNSReI$?|EXYN3bF&b=d7qV+?xRfj_w)fLTW->vJ zd3!d4yu2URXF@wB+~$~H?R(C>rJHb0$|v?Y(vHU6V%aA{E9lp#A@O&|^Xl2RpzKN- zKWpDm-uSZ-FXwy;?tLB)2aS)@iYJ~h05t4l^rv-`d`2ov0h-O-F=Jth9>AGVF9R(%BnjUw*&iCGvj&w(Wr zp5{`xS;CV(Il|Q&iuiVl7xhkW!>?5dY~;yR;Pd(pcUkEIFV#-M#Wnk(a`FuLR4|I? zvi;#sQXfpyd(Dlu>x)N>CWF0McQ6_JgW77oq9!$P^%G>|<}{zZ+`k@oE7u9jhegna zFwkc&F$IemAUztQI4@Z6s;? zyFUxB^fK6ybYt{u+J*ePm8{NFnaz;+ZVk~ULOdJCW*gPAsG&OJ>WD_oU z=|PfH>4$&+>yCNr_fwCdT@)VNL5tpeq&oqzRII87`y2Pe-frnI_lPGBTi*z~k57gj zQzr0gC$@vfq=QuW%Z1V`9A!q%3t_NUI;(r$m#d2Zjji^p+4(GnM}BWWZ;3}8yIVnG z_sOx6P))8&?miCrn#s^rj{O)u(P2VXvy8XW!JEIn3q}0}?oq{a@G3tq6f}LH(i5r} z_BxTSo-Lrz%Z~J<=PG(}d_U#nS~K~3J9x#8ExbivLtc4PK8ty@4cOKZyxRM7yh*kK zWR;8|?H%*jp<9n2fX1+F+PW=GGR z1oJ)K?BcF$c5mNS(m3r;rOWq{V39$)JjT<=H+Q)wKR>XB-IKZb3y$KzRl%%ots@Rt z(MJ%+cCrKidf?se%6N8-zVLZ}5<5C>Jp1OWf@!s-?5bH3MMMSD_#^``IsF)aoV-q! zYqwKu>0GhL)^pU!wTLrEE0a^gUm6^?mc3s;9Asykc)baJyvDU5ywcYSuw49wjW_op ztx#vOxHOC+51Wg<@_NyU4|nkG>_Uv*v=QfBIq9JLTwdxMe+}I#kFt*|P1p*Ro}6*f zbXK>?97kQc&F(FJ$;#6=u-;;`ARaVj#inv#S+yR&uQmmpmqy^MbedW8eMRbxUbO1j zQ_)bZm#AX+pXiX4iSwrS#Dh{t*BVv{53+5cc%LR*TVW5!7oLKg&>A+fG=U;4q;4t0 z_cUsFIfZomMW?D`LhrCNA!F<}PD%hn@3m#@>$_C$PWA-0>UuqU8OJ-k>1*b&#$16- zO$uipT3liJd0&YkqYE=`t&#Q{x^T+x276$#7|a&zhGqXfW-*n$*hxDOEHqb$=A1r-fv;f!`GIrTq4 zM)jF2yz(keb@V__)iY?gdLo+>cL%ziT_dx&=!${&GqKOst?cUM7F3Wr1|A#cvR|tt z4Sql!yW84J;<*;XyW{)d{J^JpAf>-hy5Kf=4!*+F-1ou>Rb7m~K7`b5lSHed@uKan zr=rG*uM}>V1iGHfffkI1unoPTQtB-?leP02aYk_eY6|}TYRug{(2s1J5*>O@A0iMJ zAbFgE$uw~_dQLgb>iH8w)Y)bxOc;z#@&Q7q{uVr0YQW_C+-7keU2w$740ir_1Wu#J z!pU%HzgO%E!to~Reg7Yu`+5}k{zFJSzYRQQJfPXjX43k0D=)*CA=R^sczG^tldoth2vDbiJ$PCk5wr3(z<)K+r{e7#WWxPDKo zlPB=H&o@DF?ky^CIYdim?Ll85nG&rVg;ObJsIStCrXI}@w0ys@#L^t$%!h}l{b2+i zo_Yt@4)8|3zz$}#irB!k73lf;1diRfjAh3y;pX}s1WmU;82a}Gmb<>C56NbfIwc#k zvmDu=xp(N&JrnFMb&(C~Xb z7#$dZ7ao_RP34DpE*3eICucx?#vE8R{jqazSsTdu#3-i5s<>#Ye7Z4VFDv zNmvFqr!9(kUsGnq@2${e(G}*h%>x%G=wMs%cWlxuqtrpWNz1HM7~%hx$u6a`XZ;V- z>wxQA#7U|5HGK|!*l9-hoAT-IjJs@l<{3D5^BSws*$+W-JS!cVAltfgJ)|2?CankG zDDk)tB@|31r%kSSJ$)+Kzl*|v7xlRL(sL%nToPV7PGi>P?)YK)be5^Ml=;LgL~9ot zoOf?2^wLbjWZn0`DThe;uQc4|uRs-Blqi3~J1or_A&k@-k6yCjG^LjuwNkZkXrj7k zxPBM?I=7DYu0BnxCr9F$CRfg+UYCm9Y=qA?0az|&1uy>?1ln(%DUv=@xK$qt-(5o) z_dBT9mfn~W7>AP$=5dz}{$)qIhp=Z#8*#*xgR)DLmvK!`4`bA+_uQNJ_i*nR9d7d; zQ@awMNe&-^X5eYZMAERoMT=(?(a%G^)NSKU%xk@X*Q*E9qYrnnMO~E&K3o#5JCkV6 z&?MURY#7!4iKY#ABOpBYA9%h#CLHEXr5Us@*iU>4VN0H}!u(zoH+U79oK&K*+xO7? zS&bMt*cdJD^kRlX1&lsak2_!f#BnnkSz-HWmfNpfcsTAVb1b=z`3v_k+w%-tr2e3= zi98-lO30k_S|=j!t}9 zORL#lIzHnki|h6rj}4eCF<2ktu{$I1^Y1&d#nX|#7L-#-`C%G*ID{tLT_v0>*eRT7 z4QD};FL7G0@7Uj7PVioyf@g1AqJvKscjQ?ZUYwr6Ru#$EKSKplZ?y#5YJavOr<|5K zq*BJ$0$MqB7W;VZB%U1e4P7s&31?50$%@ZcVxW5nww-pR%r_}CZPXo#TD6{*%-c&- z{k`Cf(Of!E%8NRt8z^qR1|(Saw*v|M32jc!@M-pWR@0R?reHuNtJ zIrm7|@3WE3Ij6|w?(ks|d0i~-WCKf4*vln6g9UMk^UYnqAK@8;H+6iN??FH|z87$^xGALIrWhX|>2E%T)5M^cxVdw5s z;fBxR;DfnT8tY2~3wW`c-wOH}q)&OZl7FS;oA7Gid#+uWf%j`2@K2i^3of$3!6VzS z$@(rkD2$Um>fyq+R=(%7u;gvA+wvP~2>_mxllnVslwya0DzaX#kJb=iuTu3NA&-c#{SL9H<@4>vZqr zXU(qUUFA}F#X2cRF}N5WxWC}eZVkj^heqS`#g83f8@lU~N_&@& zqdq;y;q=g{q}%iYCy!CV0i%a=M~}*(`i(jvs{b~08g+vEsP`UycD@yg{f4k@DJ>|} zuY&HtSfoh;vyyu49SeO}%sWj8^hku0@)F~s_9d(@&Vz*AseF%DPkE!Xsdym0f4wgDN3p=Mk7z?@S73d}v(cO7eYsSK2MEAfad(7M*aHn5Z{tx$8ds z-7l9?+P;!|RugJv-V;jB+~A%>Cra=SPoY!tf6f}e1h>t%kiE!S>@ZV9f$a%zVC6P7 zFzaWP@aejSL&wwvSnzHmm|9%||3}kMJ=mRdk;Q=0uF3Fp%`BMRG!oj@4B%Bys6*2s zb!?kG0_M&g0fZ3>eg5uX$JdR_;#)W_3r-y^|TdnhSX&fu%em*l}m(! zKO(4m=n(v-^#qSBRrWJ3k{^`AKl4gauYNU~ zo24Zze)my$`mz_E8?qSd4W4rT3oISHJ|<#gX(1N=p2r&gHUmFm6xb=Wvd+JaurZ>V zIeNP?pN;a!*(y3M=NCvF{kTKTxsctX!`XjQ*j#4Ta~pGw^b}G~ZejuR=Cjs6scgR(M$48Cr-gL^lr-ZY z?ugyS3Vw!C#Nf;H^KrGfsc|ShiZ2ppcl(2vrLF=4ODkS$pD$;geL%`fUx)rSuNjr? z#=qS?WuJFCvRjSwz-Uwsp7i=jE*9Ic-)15VzmjKp2>^$zjHTIn{&9^B|(1x9t1QbWz#$hByF@BfikX z#oNA1+huTQ1PQigS6f<0T?PYpH{^X$!z#iY`shLo=^6|M<$Sw)}8 zLZE3icjHzq>mNq=-8PA<*!lnuo}WXCPk)m9-<}Rv-0nCOoJkepK90uhj9zTmsS+Xe zvIiEwd_=w8|3)*VGQ5$yOW4(aHQxU!ptRPp*_~9!i#b@6eRQKXf9&161)c%WJxYSCVev@2mfbeTFsD;F-^)4C{ZgQPsWK z(N$%F!)fp8!~IgYZOZLfRlJnb^FF~%`t}YttIuN7DeRN+3ADv!qhJ@EN18d$QoPC?$^3uG|a_{GtK-+xgn4-=N`WQ z(1H`zJ*CCF-Eeu!URrtj7MQh6p}bYws69TEqV#+yG1#A6ve#4c=T6f9;3X^(0q+@n z7NW0C#w+Ho*z$F>V0y-st99PO@&?Z28nQFlIA>p$@pB5d?8+kS%6DUNi!P9f#9Z8{ z@tyOU{*Dc455@R{uA~~=02AkpV`uKIgz%V^tYPtW%u3rLWmAqr)r4Er{63AgvyqfI zVFk^nFVxaKOE%I6Y$+)*Oo$`x-xIiisZZ=F<+a`I<=DTaSxopC)3)IYrtLB}dCQ z{-Z*TVlI7BiZt6w9FosX6c%qmEw}2Z-C;U@4el!_4BCtNV`7+kR2lcZ-%qY9*@CTh zc*!ixUkGv6(^>HCyR7E5DyFM=v9c47uw{@V+dA?X{yZLsKki;6Werv6HTEB#I8uV6 z?fqHGs5y{bU(H(gjHgF6_4H9e3)PxWvBdQ=aPQ0}`@QcaO;`4urtGPNF`fP_OXoDE z{w|^F_z(0tWiw^DNj-#i7pN@z7B-z)Lly&1VTWf8Ui>I=Qj8cI-Ye;xR_75`w78b# zHe|5uo?9_aCl51K@8M}xMe?zf(R+TsU zi4{KjZ6G**vtb44sw}fh6GKLwL%XbNf@1bzcJW8NEbin1Y}?a@-Ma3Oh2C~j8+#eQ z44zUQ?fC^YU4~GP?_I3n^K49Ze8P>|oXfbg~UqOxhaIcW9?@scfTAGsPTteSS$j=3gMKVh^(P8bkA!G}xcokN$J(Fga$5Y^+g?Pv-0V~$#3Wu-! zz$mFN#oW+R^3W~=J;wsjJ~R|2M(M(V?<*i3M7X$aTlLztcZIW$JejQ_!k()->|e-z z&>LRB;+)U0nbUVb!gecRkM~;KTH3%`k2|7Kr!%G{4`5xr`Z}CZokj~ioTyPdj`E^p z6t*ggGMXhG$g8`QAl|~Sa@u(Q?Hmf)dzUm9bWzx_zhpkBFZDYtSiEl# z94+1s<)(|E-{IS^{f7rkR0xL2yS}l{?cOk|^*Cty+<+y$?AYUFlW^r4MRxwN19J%- z!>L;bfC2NR(bM0NU;KH@UGf3ljG}PT#|vz?`e&y2V*|ELxkV*Yb^k}vc{t?szG1vU zd+($jO(KPQ&vPG2LS)NokZdZFk#CarmiDxwkP>N~=RPt@6jBNyvdJu?tl#nlvdH~f=md0c@<^f z>eJ_+s<~fTy=1q>H}p%5$GZ5R__Iia3k)M@=!95Ydtx=QtGA-Tb}zZFb2Rbn(sTG? zQU`V(UW-)*Gw_qAA^HUM3YW$3p*95C22)AXi3{B2%xf5_^N)MFl470oKWtungD%Xl z$JqjBS^8lFFDtswi_=CN+tNc1solk_H)rr}y}+F4aG{^nW?+;so4AxCIR5me;1^vF zUS{z_EY1FmDM1`omG48_CBw1&fgx78^%(xrYm>1V0gtzeBii_ za(Xv0X^aGY{e)pc3(wO=egt-I7vU?Da-1v70t>au@$LC}ytdH;Y%REiw?nStN$mhU z@;ncZ{`RE_%rSH~Fu|x%7idS{Ph9I@Mf*mU;=%MfOd@N!>OmI0S8QOFGabNv zdk~<01q1W%(erc}ubK1#pEpbM>P;3Tb6O8=?unwmOml%T5_*fmclOj%De|tX7hZ7) z@jFUk(VGYKfb|p{o!ue$I--c6D5gdCyJ%t51)+mG1Eb|*Xlc*}WDFPL-0S1Xwy2k6 zoYGb}s2U0M91Bqj&q&9_F38#63{iS6kmtA%a`R)Mrbj_w)@DNL;#H9IWD%t6`Z9eg zlX>&bzr50~_q<81E#2MLFX}s#Pea0O;6mS16qDYDde3wqQrntbzvIIyn>w;mRv~b$ z&=-ssnS=L|Lh{2Q1+1HH((t%F=w~^fK31>BVK$QVg|Zng5vzb<<6=RtVGDHJvw~B7 zS@5Gp7uM`s12y6INY9@HT+;guvLAhg#5PMfQxFA)yQ<*Gro*hp5l;}iV2Iz$fAjm6 z?c-y&{l{xP-ihr$d0_V^lCw&wFuBy8WV1ph*K-s6{^-MwxbPd^zl&oHr>unsKcB$# zk9ANh)j(cniczKBD>UNkCt9%eudw4WN8^?bn#-&trxn70NVkEd^Ds#Is|DqvBrrJr z8afUaK*6hC`nK8vUX*NtGyV0@ol#BfTQ|d)^WBi&G!qS7ui~5Ihxq@(Yw=&`GHh15 z<>b)QOY>xOx$)ocp?UggV&X9b*D|I6pP0xFpQXWySIDr6pD#i6{C2n={vXtN1~W}T zBI>O@L_%84P(9%|Juu#iDb}s$l15a6Zc-NLZYqM+N8W-56UxOFE+9W->VQeGf_FDh z2|V}&INmM+K4Uvza!J3C!+%V-Pd*E_b;h{ntsXz@iV<(SxQVxRC_o>vX;^EY&C~?m z1MqkWHg^xheAN@Imhx|ii|i#G-Yf*~$Re%c1(0wr5gaxRLhb7TkpH@oxQkr4m`HhA zx%C=x^N}KT6F!5KO(tXsT*QqVwvnt&p=9iTm9VnxT77hjHrPFQ14XLC;moFYP-9pH z6V9!Jop;{SmWTH+?b$GXJf`s(64895#S4Ce)lOKn(VbO(KL`OA9ntM~2$2jN1q1hw z!ME0_@OqOhe26y(#jgVUTKfp87S4u=e>1_X-~pt%HHluSX2Gy4S4h&3%b^| zOPH5A!-fOv;GAq1{P$!lDE5Rwq*fpte^CdT85OW9?LI6n9D=hC(qN!sE?jIG3EwtF zgZ!VXxTek=FRrZN?c3k;mYWA@U)y!K*j~k|U7iT%mlO&;=_g>CbpSq#A7JGsMzI>E z>Ch2V39AC0kjavnWXFjcq&CzA#15?nBY~;0XRJPP9k@kx*)KH7D~=ddTp^*;4wE~^ z#n4bN29)2vhv_MfqRgJRF#oV0Y?0p&xAiB1vr8%@OFv^3b2q?;p9#W_Q}Aa#pNE&{ zR^fSJA8ya})7vY)lP|_QS)Cj!@V|Bm{!9G}GP(Jzw7xtTuHxBM7c1ChSK}dwivZ3i zAF?a=!8h|QumCqJSPPW;Q!cDm=C^&kj*vR zqu+Jp!$D6_FAD~jB3DxP+aGqd0JXVY3)4C;K-v0*T*cn6_|swozHzO^Z>=d*CH^hL zE|wLY_1UV^YlciQ-pr<+goz+DHMvf8Izg zNNwV^10{L+S21|C=nX{whp=zzS4QN))2f;dT=M?AXiM@7=#40b56#+e0UiURo(xmP zd%$|d6<`D0V78eFap)gGHl6v->2|*Wi|-@gV1z%!os9$MCS-a|%V1=DFFC)x8((xr z;;}Uk@JzKL{?+usaD#r{z8Cofb4T9c%p-oo^Ql5NKMb#CD1&H@J;dv`VCims4EX4W z_D6LwFtHf-F0Dr8hAo)k{|BEa+hF#a-9+r%MJRK44{=y}S zb0`L;If&D^Mi?e}7z!Tj03Y?Ga7=FqhIh&d9`NVniPt)EN=J-r(Hl!Xl|&JX z&oZQ;b#xpdvYNmHIS9y z9)Y9Ma4_Af1ldk?aI9mg=>5qsh;1o{j8X4N!%;u7ICd+jiq9tsSD!KMH4^pt#S6%m zwL1mhg8?j)DrIIDOyz!TD@MCHUm%XPh9i$ZVC?iPfrYydnjPQ3!SR0~CHWJL-`x*) zEe1%jlQz-3Iu5vxf#le^*Qn+35!74W!TyT-Lbl-vN@fd=_n$ee^k5@=uWO+_%iKvq zelnJA?1OoB*)V?jRq!gy0tX|ZgXB9NCe%J<{(Lqd=bKN!@NZwp)j1o5?$ao+`16Vk zgq&ealhSDOifuyXzK71cu$F9U`BpE}D-Tg;#ROl3G|kviNR1;(aDyuc<4x;PEN29I zTB+b|wuPjbAEcc{o9V_1A1nyILHP)GT(9Cv8mv-5+u0oa?>eDr@==sI{~2-RcN{l5 zjeDNN!p`J;X!^GXeB6hToMZ>`M`tE6vzt%288aQbR=g$c3Iapyf+T07)yO%B8Zas% z2h28~pgRTy=hU8^RO?d}#za(;F9j0xa?lB0HKK`(6rT=NyFPNf%4=9T>Ke1XH44Hz zd|{tR@KxVU#x;q8TW95Q-2Ouo$)QD5^ywZH>>a|reK}Z?SW2rlZpDHlx^(HIFo?*# z24_yTk<*8>MCD&YK%d`D*XiVdq2E{1u`LlM*xQg_JI~;7uW9IW@CwQVIS6wjF*H;) z#>x@i+@?w5Fz#>y?M&>X&B8P7Id?Rf=rEfsxYbD$K6H@x*B{VRM>pXWlaa(B>k^17 z1kki+Ur0EJ;l8@b*jXBi=UpFw)#b;uYFxR%Y=4P{QAN0Amkml+UWP-jLx7VH13%kb zCnx1LVnx@G8n=n$Z_8gMr`n&y2k#^{5<>prfiaER*o4a&3tXXd2>%_A#m{OVkbiMp z@R4mmg?NH?%VrVx?2{O(E`m(a8o|{vjYbF7fw@mS7*y#qx!d!>{K6)}E7)R4(QY)! z*TEm5ulQk0Ldn5Bt*BNlj;(>`iCqJO+XtSa_pj~DsD`WHwx^$5yjw%lRZ_^~Q&MET z%{6jhX(u;t#T}yCI)P}8k-!6Sr?EtKvdGu356?CY;X{GH=jxfyi$6)hvNOV7dQCE} zO}s`@pBa-wTcgQRwMuk#Xrd{hK4eTq6IZ116|=5Qp})7akgtpK(EZg(Y&`p#pKSCM ztJdY>k_Sc@*HMdh-)k^U>M+KY3(Qt$@4C*FlNpq;B$wv>rbbRXgmYCmvq$A2={xg^ z@e(*AIs4@qR~J{DYx|YHeXy7=`JqN8eH>4(92<_~qMfjHk|t(w-LyDu1t?}r0|QTW zx_DLxtyU^!KI^ukRKSS()fV?Lcftw`51k4V20svwAFXuA))6<2O~;Ra?0LyepK*V6 z0-kQ0PmlZ^CosR7aI28Fe55gn?zie-XkZ@I8NGpQDF4fS8Nrap`VWLz;S%EDqfGMd zbi6;G_r6CDP2f>wwl7^FQ30M`6T#9kn^w|; zRL)=$J=c;zwL%3>-7zPe^T?BIQ*fo5?R@Cb&3AB*zA$GlXJ}AGs4$;cK&MphVmjxW z;kRMq@Y0Lh=(#2ir9qzh*)9!ma>gH=ZwVcQp1i0aU!UzU%fshPit z#4%NLDf)+&HVaS`mrcdK+{Eri1^P(f3qK7#LDPDK4)cOBSTl1WwX?{_w-qX=oK{tT zFshZhtNe!w#%lCc1|fPKTLl)cCe<^@BL%HLnD37^fz?}~7w;-?krxQy!}{%9v$ZXG zw^9>FT6MraFFhFX_$$n{=>?K{A^w!Ax_plS$xpyzZn- zt$$vm=XQ(81;ZUUN%Wn5O(@0@EBr)vp8KHgh)&$`B?A|me@DNn5>9Mk5(Z4vpa)X6 z(ttH{>8$i4IMTX=ir+cQ`6N9N1^AVTZpds0GaG?tyVDP{xL62Cy9W9~K2W=tCoz#V zu%GIa(PB$z)+tKje>lSUixu=L-lT8xBWcU2K9SnaRBYnz;dw0+yyl~gr$)UbFAU;o zQw&F6M0^3w1s{)E!9 zIkMD`$lOce-l_c}#myeX#D6(BsZRzegFaSfPbuWw6ugvCGa)c>5O$0m4ud1sliyl7 zuuOD|Cht8*-YS>T?un!6(mmrD6X8y!)3BTlt|-8oY3;bhe6OfoSyOO49j9-CqnMUS zmFRnjM;l?Ed|=`b!iavFKl3V%|7S!G-KeKedXLiw$M(|_Nk5!C;iX7yt_#X$=gL^2S&k&^FwSzO~?Lp4#7f1`V z2F(C%${miNRbRK0YaKQ?>iT6YFZ_!aR~;fxBHL+bnJvBk+5(GTnxUa@DIQVTi0*&y z(NCJi)K&N|NX4Yl8rezo%htCz^5j`AW8yLz*{&FdoM1{D*ECYouamf? zVh8Cn%d6aZusCyBTlQ3uxFig3-&Q7 z|20sF;8`T-<^f0vP=nmxW?&|;Zv6zeowktS+LsayDOVgtb;p8XRI9)OGm|AAKV7NR zq!b+Sr-%4%7kZKJ9qG|2Dp+=FA+8&Iidh+In02=X_sa~T^yt+n`S}3d5}-kMC>T@s z51!1MGxlVdUNy+wQ>w2xzmTM>Y$qKVKxyOvNy;5dtUKLU?(qnA=g=m&^k@PzTx%lc z>4d}j?N1>3_ixC~@`H%_3y^)Uo{2FUq6+3$NzE!3PU5dABQ?c}L{*NZId5x3zn-6F zb{7+>@NYhe-!991>YdKrtZX8AdZV2@_p8(ET{+A_&(l;s+Z@InIaz;JOGJ6|Yq((9 z7>wH#f!`yNaFc;KzQ{?#=?Au9=H*lj2o7XijiW*0@^+Y%f15t;ea2KsRlp&=J5c$| z6K1@d2`PTPv`o7Y%znAUVvRCbXLS>tCcY*=L~FT}S3fy#_q`<9N{{)x)L(RZYc5TR zo6T&vcbfVu+L3HGMU)S$rx|e9nYj;yIh+SI(6qpUQSEr`Y!m*d z%|ct7oxH^KZYb|Ly9{f;1>=dO zJm$*hf?PrZ9JIen8VqKEm@$F>66Y~}c|E+`sfJU1anSh95}- zbU6n#ia#mS8zh?YUwD0)*gj$-dxAQwa3+>tMchTtH{?`-3egE!K<3=pL4V&$php*d zr=I*p+Oy##b90$H{b;_I?ugeQee0b-KP(t79M8g0|L+$Sm!nyaHR>z~RjiZ>LZ&(X9M!L*Q6;{K2=5ECInE8>lo4X1^(>G--m z_Ls<*JS(siTSOw&^JlM;l!x@rgd$q?T8C-i zU(&<&(#*b$R5Ci~63A0iDk0?fl_NvwkAbx~>vlTotS-lHm)msQgciIxRLiUVP2lHe zYLHtcqv+?V%OK}12F0U#VEnT(s7zhK(|YwB$HiFt8jLw#kM z2PxcQOsYE*nfAXHT=2}}ICm5{H9%M4BmNm z8&|IEhW0fs@HImoz{n5E)GNX6>P)g*_>Gf?^5OE6>u_S>4w{!)MN-2il2pq!V13oW zdY_0{|5^fMGD}Epi5<22TZ_^?7Bua!9({hj55&WDK}m5G8Z0@3%6o!f(aaROvuGJ+ z54VNcUK7cK0w+lIDFO+*iy$}O6^F(z!!Wr>evae@-etNE?f?$OUX8(3(l10GYRusE zaVh9$eMwcBp#Bj!tS;(XVepwgB*1i#G5kY@R9$fFTMszKtS1%kr-G8`G$~yHq-3NR zG0*rxf^wxu^ekWMbXW~nykE`vek&)>-*hmhZ91UvNbs=l?x2UZt1ub;5iq)W1FZUH z2+u!>AlktPqTeqU{G|?f;LlZJTsR#AuUx@(ac9xK@F$MmUyd6dJ%^-YBXQCrISv<0 zW3J~dBAz3J@155+oH)&vHqD-e`(`f1+~P(Uxmby)pRuJiF3;%aZGKb$ni1nOk7%#Z zUtM;34NbW{mTrHTOB{EG2>y2AKRD_!Y}dI$$I)pJSU*qTb8Lo4Js%kD<_~YDn?mGk zgl7p~z*}Yq?pf`@?Dw38$CgdSo$G!9ObqA58e&oEho7)ZdkxXed9XBhbN!0Z!hE2! z4rCjGa47x+K9L!QGa7H>wOJpyKG)q`N}UNtWE*3`v}u>neQ@1m?rI>dHtB0px7;<23NaQ*adR6g~JY)LQ2uuFHae43EQp1BKK zR=DEZgu{4a={am#ww5^ocgV%K7(95d8)F=gp!3H~WYM9mv}U*uj=3awehcI1>+qRG znG@z5s&6^Tt`$V2>j+`9r^DMdA<%Es#~N0Ju!;qC?985Z@J)6nYjNu^Jb#jl8s$&H zu3{(je|JW)w-X?&WhvLHe-9TQodwI6$e?LpER2{KOmY-|VV9pWhMsoB-u_2;(KJrz zK*aEx7b(Rh^{BS06m5hV<%=@G9VEDw?yQ}MR+F#L7i;fRE_@9tPEjVo3-9Buz;00+ z>xG5w>%gvX32X55Bb;gLg!wrKA!_*%=w3YuiWluteb(tBt zmKYqjoQ^dKhW+YOxS?kPFT?o?E-_lebp+VrtMOjEbl`M67g&SmPi9d?lL`#)$;VwU z_n>Bh2ldD~OjCEQr4I(XsfL3b%IugxoK82<)z?3uxO_2gf|GT1k1F6mWfe?nj$rB( zMmv4KKU8mg#FbP%%4ebv{$oc}-6Q;(QOpjRVrVk^1YQZtL87XJAKny(j@Acx^>4e; zC`0I_?N>tIpy}9>V@Q{Je&pPikHk}Q7x93=Kg=Jrk(Q6rj>u2#B0maj@$Q~O zxHW*FU(+c{joh(Z)GpE=VTYq*M&X6`fLlA(W6&QPY|E*}v6k)J%SL^A=2j+sxHE-| zn6^T21nl6r6P+~C`6=is6p^;yb7|2LRWy|QNW%kPgZ~E&8uHo{)62{29_KrgzYm0r zcXvEQ7DkEc4FBU@oe%OeQiOBAMHu8NIm6=7w=iy;3-@q~66n~bfD-AW^1|%#bjT%I z{oM!aw#-CrZ6QbTw++)<-{1+S0o>CNhKpXpO<;HW`jDTohcIK6Cx$<7!2HFVgf4pm-rKkw|9h~27rXP0*Ih#S z@OnM;S~wb%zBsWW?MG~G$TfCkV;KDVb`09Of3hl1g5hHCEO>Eh2ZVc1<&qZ-p`Kv_ zBe&*${c*`Il$QBOYx`RntJ;-x*hWb%A3~@wZQ>$lui~midemE02S*hBBM;@D(|;$A z((fVf!8>vTOfnLu-Em2xj;9|CTT zIhpuz9l10q2&G55VROm@DxY74Gs^=oH}pB)a5~KEOQrDg{NS2jc0oSR1QuxO8g;tM)#bmC+D~o%eTw*Ue&Bf|Qy$t)R`F70i{+ zNOJw3J=$6lgM=t5rdOCCy_9-wMv z3Lxtsun=ro;Q9S>$S&Isx&GaxjLs&7@yp1rb4QtaVg`Eli^uin2$z509%E4)bWtFUNz$sAqWHPNW1il!wtq20b2P#fsRs{Ynz6~~TbO-uHJ zqoEovl|7#qyIjFfv3()9Ht%4shAnScm@nKP-(z&rUQ+P!DrByfhHaO>L+phiD0F`U z_Zla_vH#vd#{(INO6Y^|+LhpQCXspU&x4$U2dtMHpi-;uaoPD7An5ZkraP>qZs<%$ zee-EARP}B`wQHRiIdvp17TbgYqh`XMmo22jdICE>$pjkah{4z!O{f*8g(-JTc*b6> z9;YnDw!;(g-<~=g4F85pl>9iSEG;~E&4A22mj*hAx4h~0;y#$!7s7=3%b~z<7a2$^K+An%WY?@1FpO&ho3nRl#W!aroyo$y zyRkx_Dh7Mh^ilC^FtKvHO|&iVv9`}{!^PNQ$RCsi%i;PuhAgeGXN{Ei-VG@P95hupS+3mWX6vk0Q>D0OvZE_D;dh z@@2I63_KN+sRIv2KC{4pM(q`J#?Z_9t+v{4BCUOSl79 ze8-3l?s%pz8pCfyQrErviKq562$mDUI<2daT_b!4jv0XK)Y;%S^)=+@NW&)I=b&EW zBJ9E2VNad~L_Xfh{JMAqCVIJvzG`2Gv)#`i$Tm*cVP&CT-DU34Nf+*s{5u?ZU>r%* z_(@8yZh`bO4AurF(x`K4W5Iq0h_$1S_}l02`S2OdAqfxN9WB)nP!+uw|WR6`fiALmXyrzLB5lj3}Yy7+PwS;hCgt{9QB_ z%iU73?DH-9uwIjTx-B5Jl}Ab5vtcmncsuDAy9QdtzGRZSA{DbN~IVY1Y5W z916HcGVAu@nCCMvPm-c(l|5wf6X<9&d#*KU229!;#l&7Lz)L#UNP=n@G=wFx^5WCr z&ip{(J@K2gUYUm#!>e(s^AFHZnu3)s1-y7~37#76%MbT8L)#2nEVY-RbF~DgZrUl7 zJSB9!X0{ORj1lx&y)C_YbT~bM5@eE^Fo*Q3qy5JHbgJffa`N~ZqMiN{ZMvnAhve}Q|;Lf7@vEGto|^8wJNO;j^=qRGeZZ?+bP1i zm1}WY>Sa7jr=zDuGkKNSjS7=*(g>-|s5*Bo&eImy49->byR!;9c8^AnOmFnMc97ma zl}IEa?$gW1BXC&vQ(DaAQ+x5#w0A&o5!#OC4(sIBZ&MdXktiPThb_X?=^Kgu^VQ%c z>>ApBsp99P5&VK9Bl+nkt#HTL*;KpL+6lE!2;P}SHd$AdjZgl;iuh!9L}4v0^!|j0 zhRq-nSNCuOzt4llivVh&_72H5BXm7|9&Lx;L^-o}^xXqhBwzfg&&+t{LW46|8DPki z$$TUhGS_J6fFIqwM~`w+A;NnjhgtC>O(dPEL@%DMp-W%>z?~LBv{2uf-niI>Grh*( z3eN|;#Zp&(#PLCN_>fM|OK{LtQOK&^*MtLMZqRMH6#66Mq2gE#Iq|207Uo)#SA|!| z*oT1ZH7fSkn^^^S%krAv1=Yt=Ho!#P8uA z!)%P*?}ubx7jAyE1Ovwi{2YnHR8Q#0zLroHJ^vdE%$PwO zi)YgrcNYnBRCkEEBrx^PKZo+WuLKTO3_JRE1MySYOM)Te3& zhr5y>yK3%b>tdX~cslCD+`=m*yLeftS9r5xByC$e3;nJo(!+yuxbE%MG}9!UtQK7Y zN6E|N+!965iYg+jZr-FGwH72JSBKlzoJaNjr!tLgwp44NhfeJirz1C+*ZJ*TO>}S+ zNwX@2+1e{%>op;R)SeAq%hZva{e>w~b5O!-9?md!=A9;4@LPWVBYoy(1chm+D;b^)zYt$4{+nr zA(USx^n_0T!K+h~c@-?AQPu~MEmMUPUEi4t*XQC;XfHV$@etU_>bPlfB|n^8;0+d^ z!e_4(_+kDPys39PHoq1jp`USu(-@k+;s|lh`anu;6d-rSbP%yY!oIBx7Ug|s&fNG* zpv#R*9=?>`S~mo`)51uW;cfbMV>qpScn-~HIiN?83I?P*VAjA=*kw2u{>>4%`u6$U zWO*-WOv@&pod2P7Q#u*YSq-ui50IUG66EyKMpE!yMtI+UMX%~}7^<2^KSYIN#`qeX z)3Xf~GR9z5uM>JrDCcf`^8}YK1K@joJ=AaDA;a|%Bm`%}u;?{Ry6tqLQ2(C#kIW}> z18YP|k^d+o-a{{E&7iOPv}j4|2X4uPR#dQiNQyM_>f6SvlFyzEAgSX64>ks|iUBH= zF}#CiK@9zQ{4I89UdFVh5x7!GnrJ1P3cDo+8&of$i^dIFp0b{Hk1P^;9j{R>SH#r| zE*#ZE7WBV7cTf*BBPO$@;IJD94oVGhcAqtboz9_V`#kBA`{QZT-BBbZa**lzn1zye zf75lYx53El67Bibz*&Fl0Et3R*mXsdj4$*7%_j?(E6fa1pmZPCR)yn9jitEljU+xO z6C8AQV==oViOQ{U#XnxJ=|65Fo_vsxSLW@&YxfwmHLe0jUvuI-RtqPH{{s6OJ(yH6 z7WPkQgeIe6sl z14})7INW_2+Ux9v?9?uS1ABo)jy{CsStlG&5!kjnZE(4f9lp=}gbPPS;=b+2@XUdA zyx9_ea__=eDq$u;t96zMJ%BS_i~SaDz}I{(^;vn)T8mDPz5rD6$>4?lt8 zB3p7;;B&={T|gr}zk%KHIw*Iq#E?KoTI_BCnVi1B);s}m`6FQBlP|FD)<($Cw}ypF zTga>Er*x8Z471;AF1<2eIH!eEVlO)nCSFbedylEqQzIHC{dx~?cc`-3*D_(%@;C56 z86k{638Gvr!mbzx>yuol^*I?*DSHW)Ng7~w)PE$hG@m^7k|zAW2vUFEiS`~Ei|S93 zaXW5@-?>r z3jL0Q=EU@hD=~R|osMV+y2@=84M^x`7VJF*(|?sg;e$@7alHj^;-?C}^(U;v)j;@u zRSEnBmrBN_JTP4>un6Z_v+5Ib;jfMlt3LB9JUO+UEYY$dk0;*4qf4K|y+txmF5IKV zQflBUZG{(-!Wl2Y5OxOKB8ro1z%18>yy~_k7cEV|h-;-%R?;+kUp9TRIFy*Lolng> zl<9{rGIUtyMlSop8pc_08yu5TBaQ8qpmZUGWSd=uIlZxPr#k@NeE19NxczW3-k7?1 z{({dsH^G1G6S%*Rz)6cw?5Ht2Nzu>8q;0Y*>30t%zi}B|e5ex+{d7hLV4>z|6ZKqf zM3PmF!Lj=o^Y7MTD6v`urZJnzt=4DcqrRjtKQUr%yBi8z-xK6+mXoM_u06GG|3Go< zF|PcPkKkTdgQ-HMUHj@!^zmAP!v#molG1N*a`#2J@u?Zaf9&OA^!|{?_uGi^HBIi~ z-b?iRu?3| zx1XrJg91cMy9?3B49S@2$D;MDi1D)1BqkBR2_rv)M9m$drw&(h6$_42Zv0;QMqVA$ z1#fYLL;+Lp=*BJB5P_<3I%r(gLCUJ+Xs6E`Vv<@$>S`025jS2j&HZ_}BCwtxnLWgh z5SVGL6MV4r-VptBUQ3r80LaudI~8VE{%$=BblnL|Hzl{Y;uZjBelU{^h3od+^^6^QX3<1l*$oev~DGU z%LVSb-Fg({Ib@9SU8Xz68wV%7#03YNa7JqvlQ(Sx3|Jf!Sk2>bV(d14Wsm{hJ@SoL z(;khjbDM?i`o7G_7`bXD(Y(xrMB%-_Ef z$*onTMmoU7KE2I!rL{OsS#p~cGzyL!*E!VVY9b@I{VCmg(ips#iKCaYDH`hAV?o(5 z?s0JpNL!2||KDFfOg(^ZgT9=Ks}h)eeaPDHNJ8zBb-dQNWb_es6U^*djAdS9&iG1! ziMS0fbE^gJK@1Mm9>Lr1zJU*!MRQ6j$>UCaP>g&^51k(ki{C!4(=43<`kTMO$X$22 zXTx`q&iRj+ywB^%`{j;Y%eGCZ9lL=$@L^+psHq<8{hGxE`x~LrpK++`mctG9B$D*7 zaYDZ-6|>~Wpp^as_-~s(%%5GvD*Eh)a99e8nZ8_?lLn-e{llcE#<Ewo35&5W7Nw@+Y z)JhCM>s%7n8 zTaFEkvEV`8wS<-PHzWH?zmeU`TBzdYd!**+MDj${iHMCZrEcq*NRGxF5H9{qyU{uF z&u}#8jq`^1svwxX`Ym{^|BMcPI@H5{2$QO;=;0&EWUsP0Rjn|_dw#_*s98t8wY7nr z!5&upg1}kc7R%Zh8na%XJmHV=40yXgh|N#*Wiu>-S=GhGFm(GND_W5ZFHP4#!6!pV zJ$;u|dikEzuiHrS$aSW?kc$4>zJL^#ib(UvLGo;V02g~@FF0yVAkqsILAo)QeBC}9 z#?RGb8WzkKeap1N_G76uCo7K^m)HuXpZAiuJ3aNUo1Y-hZ)8=4PWD9eV$>^saH) zZ=jI~-TrpcB8Z2+6Vne2M+oOK&ve;-aX)Gk4lMlg84LLHsXD+i+yb#ng z!-z|@2UgB7!GdZ{`d_#Q&Fs+T8i^~O@odAY-s$k!ZWAtlo`U<%R*^FXCETj_PE3P_ z1k}qff%rZ{nD_A#d>@KuhgBYd-9rJaa$5+=7%9x+Iv&6Z*9`cnp3Az$^b6kFAEfek zCrB(ANL%&&kNZkXRga+`Z1aL2SDy@3In-xZKhx)ap8oFO#dmBu~q zBgbX8W7P>Iw0Y6xB)1f>urvm*Ncal-`e9h&6U%F_l*9XwhE}C^xX|STX5|X5u`x?w zxxBCo@Dt8qQ+7g)(Pzl3y$Ta$C&9&sqoL%6F}$8_2bE<`1QGu(!YhuM`GaB$YgrA!g!rSlB;Eg>-^J=5hcnj-gyl3Q1@3L;f z$WiB@c-CX$|M)(u_)G%6q#gqL^)fWOx`NKi1k!X+jiyTYlIKNXBuKf27Oit7BdmXu z-;UDc>(X1~afbux8>$7nc|NeYJDT)%ya$~mNyv9!PjhtFLCAxY`oV+dIBR1o#-90x zt4D2t;EP^FTjd|`uHA)ICdK^NE9ZE9yv|RYk&j)zjUfH)A*5-n=iWW(BOR`~tbFV= zR0^nONBN(Hx2KoG>c{=$uj?G)zpGCwOhZXb)d4c?X&CYPxQ8wrzkn{R-^h$Sy^(7Z zx1h@d(&}^48e!s_FA%MpM7kZq$;(R*;L{>;y#2zN9H@9md*>yik^fk%X_<$wRbz0R zN-S@&nd2oRA7k~_k$4O)@tTKU@uoWE{QLvYkWtJ)gBPx(l$r~S#$x!%yntB0Civ*> z1_e@O!t;rsqG13gw>FSxqC%K-N`}-`EFpnYW{`!)H0T5WBf|N?hBA+Yj;?e%GYCCo z|J~7W(&{BBsr+IFPKQH?)M@ZgGJp?xdm-;c9SvIVgd@MFBR77WNd8nbR__(VPLtV8 zOT>8ez2Gl&QkK)g?qqCk3a1(#XBpWikz8xOKRA!;hm-LOS>3PptosRhR%EFLFU?u< z@}MsS-f<+WKCT7v(a)Kc#}jGwcu~ET%P}tZvm+UQ{x=yn$&cKZy2A`kx?Oj^x(niT zFEP7S{9vQh3aCt31q%en&aaM4@ZYnPc*pK064z~Uh5tkpi`j^`gZ*RvPcz z^26L6j`VApBy+d4g}j#65q5#fw3#0=fd*mC|QZ#}pb zV=Eg#UTGZ`z8*%6x))*;d?WFH&OsM{3_7N2unrEPtU|UHRQJ@=dm{oki{>m^HL;Pl zKfH#M&RcT1@s-Ss=+%s#k~Om_x5??NM>#E=XFxrb&k&PK2g%>@!oF%_K2`g7G`}+ zLvx|CP!YNtTi;&B&gxJ2M1KLwexC!fCq~f6|6by8yVsc37D+=tSO|HSV6J$HCoan5 z=~0gc#?LH|`Yeb8^)O@_|DL4RR^3O(^r?GJ&O#oM~b+jr;3ZH#X?jfQBpk^)%l)_`;>WeS z@S^Lo{Os0Be(A>O;w~n zP}_yeK+(_;hW#<3zZDi^YQ-LEVZxE%#d%Cgqb7ZGZWUJjNaY>cd-3VcbG&l*DPCWu z4;>BN(4j7#cGhX)9(2W7_lCpIe`4g`$|7>^q^|HCiG_u=@_6EYDlZoA#HVQ8;5QCD z=lx7Ic$0%yc(Gd*ym8hCe(ZnidFg*ac2*3YC{bd|Xh^&4O) z?!rxY`4?sU7UTIpzwun~W8$4uMPv%Eqhb84VC%Cs$Klt zr@MKVoLGMPibQ<1eK@unSo3lpqwwvceYjlyE3OQBfQt6HG-F*GJuAPnzR%H&c#Yjo z-A=#9RRgTRR7~V0dp+@Fry7>`4+^{5Z#4CQH-;bFL5~q#ESY%xZk zJ-T>fQ~jPjguFi!+GgnxSxs3>VLnkM+x_xFim0^=*!sD`{sAh0lQjU zY&;5!-{oOm>1LrTzkz!-YXgmXUW4fh>o9E*gR1U}&}H8Pyxw^iXG%uYWwrl=*X|*J zO_6Y5>TsA|u#x1Cx&S-cyW!+X4_@6?=#+=4;P8qjylSq@tA07cs|!tGVFE8yhSA>aKR0tu&A0$3p`3+-F}R0UmOOF{){cxM`6&} z-LymRDYY(qN2^ONIEk%#2TxLVC!U+b;F}<0JXMi|5f@u=VeDB4s@)shl zVF&25Lq&9odMK%$e-8@{mhq`QL&UVPnGZi@!M8Mj}7_{4hCONP>?`OC@bd{mDF zzX%oa^5SUw@1>Uw7YbEy%DPVG+;>k z35?CZMdL<2f&#y5a^iH=%lX<&A$?AU;orgcw{};z)XB1M2iXyWitKxIsS4m46 z+5?H|n-&_{_6U`cl~Gnynk3xMITy()8lsZ4Ck;&v`h9-?f$Q_Q_jAs9y7z6>-`by6ZgeD5pVjn;t)-I2Xt35^Cv<|l@c2s1asw2g;qO>%nw&=kIfz}2t-@_) z2-aS$$EPvZF!t~QF6vty?h?E|D#x{8=7i%=mAD+epJ?+NPwS)N(*s<3W;ZCNTtyOg z|I(5fSmHegFCJKi8vZ_ERg9h3rkkXWIyg_Cdt^UtS&~EH0DikTRd?5Z% zOr_#y2Fz2LgD*l)$dTx+lv$lm)7m}l#Ch9=+}t7zjrtG2T$_TOf#*o}W!M|hFILef}Vj9Cu*KqqVwq`NQSjb{3Q$C^Rnj`_sh9@NKiSH%41q`M$?2aF5+ zE_{|O^lE#H7tWpI5(?G9bMXK-W-jMy^8@O?=0c%Z7PAY@VBKwz z(BE*V+(C57*54n!XbvESk8Da~nZ@O%LCmG#_PUhTyb>O`xOS zjPKcK{vZ>2%QkJ|f@ex&J=4OVkR_~o|2uZN_B7ks^Rryx>IGV)Tt|1hzEarRf%w^D zDdejP)8kp^uG#mzWGJ=%=kETEsy83^lWI*nNUc?ZMbRCK3JJ~7*l5JLy>tn zlm$*h6E86=4Etf%cU%dNWu@b(?UoQFqmJh`jzO;^7jf?%2H`PNF)ZDk@6_0U4N-?N z^|~KUmW>w6sT{!$so`M%%0uY!*!KiKr1i;lRAF$0x3Pl0V1pz;bVb{yssRfgmBrCzuuX*nA1%Hnni zf6Eky0F>#r1^q9E=w=>bqGf%svvZdAszkx zlwBO(#VTbAA?(6Gh$x**N7q`Bw{k7vxv?OyF!Awpfz22gg$p|Ga;k#fYg)b^1Gg&L zeU|jV!JKuF!>`0iirX+rH-W3zP=P5`d1$%Z9v2=F!{knL2>!JJ9NeGrKXwi0wn;f* z!m$j#(VxfqU6JVE>WcayQ=suxBm>EG)K=6HUy}^xX8h}7=S+sOJr=cW*&L1)HPnD! zsUE5AyKiR_U`F#jCh~=u>v4V60Dkd#6P!}F0M2`wz^VZkxcSdq_(?XUa76DX#_V{C zjr6L#vjGFE2nrtNWz1mewbEv6_&eqaBq4ZV{lq3|1~;@&+Yil zEkCdmU=*U0kSL+EJvPBMP;E|6Bdj!8y z_6%O}y(;)-tVP512f-++M(EN7e55*qKFn3+&+dB8tm7KkVC_g;>d+;Ts`w~UJY5XO zm6g!7;V-Z|94Y8jeT{ooP+QjEcg}`=|3E(kkt@Lu31MxfNruEFz3ci9dm#zOJHSOMEda4G@?Gv0uP%59^2WtQMMD}}RY-C{TUAlN-zc8`>f&!uE*)2Y%1#%~LX~?_+$3w4K;aU^k@n*|ET5P<&I!9Q$kHs)d{R%1>$-r_@pY+wMEU zpVzQbe=r_fxg7&m{T3K=Pk}dAL!;7WJg)tdFSFhWDQbQ!^W+g`iw~KNwl~bJ+=&He z*OAJ=V-)w<4>T9FVP5thigueS(G44l5ALoK&rvyq)8*PBc32Evnk1$@nb87E))C6y zy|LR=J`43d%h2iPM9kW^9b1~bp!(HBPU>R<=1?&1>GMJUO*F=izl=4uJ5cP~#3R1| zERU|jW7T8e@PrRAg>K`O>$5>!c^}id{R)1J9m2}{Ph;V^Q~0x@cQCuvh74QH*rp2) z+3E#?&XQz_rK+u@JSvO3xO={kzqbkV$11qwV;bKl-N`II?B(L8NP)qyJjldu zur|)e)f&^dTe5fg&DMwDI*Wqb8U+a6BM-9zR3T|<42=HV$o&}R$bI_07;mN5@EO+* ziF>xhVs=pvVN^|`GwfZ(Q`|rD`%KSrGvOd)oPLVdA=>=Y4<%S% zAmM3uFzi@Sk7GvfhN8Zid{s`Rz$PEZ{o9&>vD2ncnYJe#kDEpDcdm#z_g4oYckeW3B_- z^k{-z2c5C}jTMA`d}bHvxD3*BZ}G-gwqffKU%WXv06*V3$IGtIg0}mwLDOv?D0hzs zxi112{!TAxZOR9m2s38$>I>A~mZf`r7Ig1)7B$qR(2>$ys+t!;$B)L)%eA*D>Ou%< zZ8?a!PP1urPz{#loFTQ9El?I03s<84@rx@o`CLwc4?Q6b%Fo&%EX)wntS{mGg)g|% z{cK=X|Ft;nwk&K|>J9!G6EOU$IyZE0mN?Yt9YoID3@R-Nb~}f-;kn9onDtQxmlUms z4zr1TSnWnObej~($_ab*)!9Oqk^@O+$Kx1L1`Pg}Y=(W@ju8-952myRFY!u(9nny4=z_uM)xsFM- zpwfEBZtmwyoS=FZ-nY!=f1aPsf1Xed1$%`I-^&-=+eO!~qP76fy&aELwWl$HkA(h# zU*Ny&b)4r~Sw1e;0=6s<pGz6VEB1#O}3FAZ?M zLGs4)ssBwc^sHS)_P_e@Z&*BZSzZ@BF3(}A>L0Y&wi_>QJ&R`7V@Z*Fjckq~WTv#R zer>hzHqVW#x|agywhR?_wQHB(Uy;Y%2_J}q@7%_X{@-ETrUhUWZ^G|dDYA`jc!!bm zzhih^8_fN-k84||!2LctN=)h}VXKTGH?)6US%~r++~#8gu1}pPe9n2?t>Z7uMb%hz zz8XyX{=;(ROfJ3iI(JXr1J|go#*-&1INy6=c*E`yZgshjp~LDhGkp^tTPnqCpT5K; zHW_e@+hWju^9I=Dr^xXh4>9vU6X6-Pa_@rPqVm6H+*-Ut@R_N?)`Qbwmna#}ny105 zW%-;_FXOH(a>iFI4j@CG^rv4!-TAhB+bLOQ^U#}8j;F$-Q^*!tjz+8R&hXZF9(No| zV40*DJx4WT*GVz%TyqWeXfJ-N2<5U;wLw}l43pv>L3m;@bQr2|r5ZilpW!39XP1V< z-JE1`_p1SrhsUA1CuGV_=h+zfOUL%`$u4o?6$ei1w|GkX#QAomKE2|GNt2*nJ^@Tq zw!of_!>F=+G3xNwKws_!jhFk))aUXN*^yP4s}%`9M_ZD*!hL4=V>Zl9wuP!k2gDLb zBhrx?0n=Rr@XBZxEW=-7_q%s&m9E``KwnedFGCLsyOQ8}T@mEnlEWC&7f|qR4BS=x z$@hEn2?ib;gK3SyFevZ=K6!o_{!{!8!9%{`3a?36w);NcyG9;mdH~lJDA*MkRl=a# zB1GS*IBw7b-p;F!PA9C944q)W)JJ5qz<9(vcdx+1<586K_9o1K8v_xyDljsv52Sqh z@%gWAWBShjQ2aU&rKMN#A1Xcx@5LjKlKhfinOh2@Zd`!+kRZNNcuYec5I4vF5;*q1 zh^LPJ;d^H}!VZ5qh@JcwU-=4LMeA{V^ATU+J8p<7#yQ;OjCA}^-3$i3)oh@L7>)O? z0-J{+ls~c);`Rr!44LKWq zc>9SaP-}N(%F9hNAaS&C-#GmW=1%{BO~F(6Y{Ac-_tzW!#Ao@}f9kLy=q&V4_Q!G) zeOwxJhi~2N37qc~P8?N@8`C~vK<6>6(lDWZ;a<=+a5YQWy^L)cw_c>qZ-$dD7uiA6 zO3M7UUt*@-Ut;niT%r)_LqAgsN$1u*IGF4T=XY6%AH3^^X-U`lr%uDU^s9QX&N2cs zmtTh?jmB`|>|+R#dI%SNpM!^q&^hC{7&QCZA*FI5oC=tT6^Bi^^dswSze=9rG0_d~ zhwe#GKKyj! z$B};&$@gT6|K~n~l&3p>h#YQMou>}Jcj0c;p6|lqX0GLgA$;~v(h3xN5 zn19F?=FiX-ZXq4G%itiG?P$SERY9=f^%J{$ww2&;$%-F>URXmKcpIDW@F_z+w|@eu zK43Wg>p4%m&g|Kb2DLA}&Z~G+eCq~W7<;)x{;B44ksFV`3S!32Lce_K3_Y)46nwikZ#p48foAvG3s`Ng{8EQgd_c$D3 zCC%d}Pp%;52G9&!j&}wyxYdKR7Fuw(e|v$?k7O*J(+U}?0)PL=YVn(Ez^g7>iOX6- z=vL(#n$w$2I&z;_%E}4M(`|aL9BNroBqT%@R3oR-ge|tp1C1>Jj&|=@Oj2{1;9p*|Wii4SB~~iF}}8HJl!l z3H8r=$}2KoL+?~+R(7eF-H*M%oDvqpoqmZxVxb@I#6a=G%d6qitO{6WK8w=VJ(ifB z>XvMBQ>Q1PvxS^Lqj%50(2wmP(aLP3yMOyrS$i>+1pXy1dO2xo$zr%o1sV9u@_C2m z;P301eEI21FvsXRh6m0Qay5QXtg;yw4*7_>Gv|r#S*h}R>mD=tC(pT%g^5r-_88|o zbv<0Vk;C@>oXLFCwD{vH|E_zs_-n3Ok80haT5GI zz2>9Q{AmO^=_%2x6B#r})K2@;Oz@;`C$ZQD(pb04ZjjAxl&K%XWZwYDhmGJ9)y@ju z!@uH}Nv|>Wj2y1{mdA&m{{k^iJt#UB#>YDE0E=Sf^4R9Z{KukD)C{ zA0Segv|J?9eu(Y#^AS1yD`MUHEi7%PA7p%6CQ%tKlBf=rWm*@P!O23Q@4iX|SF;C^ zvc(#nHV=cR!_MRQtl>27VF31ZPk>Oe^}vU&0QPg2``6w^4I!xAk(d6|C$m-lXQQH z{Edc+wD+$R*^HefGQ#;H&n-{bjZYuB4uejj=<$@8=ZB}`MNBsK8Gm#%u`jwMe8Rl_ zv}pYkIC3fyOB?>6%Dt(i`JX0P&e=;FRLUtybp)Msw5O>FS5Pd|g(X>s@O11WELmlT zD=pjb%H(f&IbbMW9XpZl^>W~3CK>QHp9jHO{}PtE<|f}aFBy|I?-Usa)v@|p=S8NL zlSE_Fe~1Q)e~Xsa-(|VJ!1HfprcMlXwmmWc;fbNEa+WDM#?vE|A{p2z1Ixf(<%>^ zZUOK-{wlP^R!MPJ}y*j+ZPbUCvMHODeh z7ghz=GE?y$?p2Z=h3?iT3a_Sue|JdYx{$P{3wZ~%S=4{UK0?l6RWw4G^Wzg)Yh+;Y^in#YHD-G)&8;cQV;0t@Zig~@5%cIPh`z^K}V zG-IfS#3gYgNxoD`)Hl~i7QD|TiNYSLbGT2uzZqXJsgiSvXrqEJIu!O|HjA&lBxG1J zMC%8eh#d1iiezSWiIg_I=lPm{%v)0hgVW>Kxs9i2$0%z&%{gI>V+p@@-9$F(wjs^x zT1!*n0?B5r6OIXg1J}jxI4iF!&~|bvbJ%!^)l4m9ig8x3KCDp%;Uv)@;Xj9lDNrRORu`}R}d z-^bzCKcDerL;xMWtiyr_@4~Ii1^x8k5>{9{jt$@OA4_=LE`E?^jUk&1F-ao;e@0nD z+5mYbJ!72MZHp;$PiH#Gg#e)oiKHZgaxRVF~^p07JDU`C4V{3W<9yWEqz&!E2A2Dxnd7Kc6sqqOJKb62gQrZxMj29C5@B#u1Nd)m#$iVOsFeEw|NGO;M*6)6@A_kGckOz% zL|~+A^u;l5il^Q4BB73xmC-nX`L%Z67#6Bz%@*9?K(>7_DHt!}lpWJZf7}?6qKN_X zN!^Fvyv|4*&b*)dnrZe#3^d)WMw946lgMB;D3)t z&QX}>u1?v2PAS?cWF% zb!-}_kB#CTUk(wn4{}WLRvMc-Ln!`z)yXn1=CG458kzRduOgYTeIgmHr_6cU7@BPu zP7$>OD3CuRahl1~sTJOmz1r~-iwrA??szka?1^<$eKe3p#iY zf?k&rW-8kNad8{D`hfE|eVsgrE+mUD6fR+^$)mtVMTUiHer1P-t1z!Yf?w8ay3nsF z#R5S1?)bl9k$3XgG5g1&Nt5M7CQ~@kv@d<)ZHGj5tl$RvJxGzvDO^F#nja`u;V4zi zTp-Cf*e+2H94*-qww9hecrWq#)WpCX`HGMo*tS+bCo{p9;a@GUA(vwl1N^4df`8*@Ri<`c~FSKvl0 zixfI1iXd1ejbEr(&08J*!;PvB7MPMEW^?ufoAoCM&3C>LIB-^QsPr-aAm{&mB(*Gk z&tE<)#SdnVR$+Y)=ZU1e3x&@I>}FiFQAg5^ayVCYogCBht7%L1g2%3}-Ig$<+s^ zi{B18Yx=In%1oo8O7U(RK|O=yvR&Qj~>FR&plv1DW_SR zjRBo1Eg`OP1<5t72TkXGY=OJ*u8LiQYaiYfvXN5ov2q(k_J-q_-9PzJ{e=$0rSU?? z&I+!M{&36RT8dj{4#nSJRH;{-jkk8l(7qe;5?SvA;pXs65^-}8d1-mkF_R36IChoZ zb@xg1b}Zu`1xB&tld5d#ogbop8M^H026fRe_wTIUSYYAzt!69b`ZDp*eDfO{J5QGHI2a1Wb5da6u>#0d&Sd*ycEW?m zi)8y+;FIoaq~ixnsP}#+UAr<78-$LFzAJhXy?NgyD?BtQ)VP7i)YlY!XdJy*yMcaw z{f4K;IEls_c`mXjT*3!UeM1IcJ^6Rzce4>^e1#4DGdQOeaetyJ*?|Ll@$%7XDpTD} z!wwG@pUlXFwsHEm4sz-zb!68LZVOV0Y2A_Q%m2B)92Qdm7= zR?;?nz4I>x#%t4M#a&c%DS|!>Go|4fx2A~ZHptxa9#SsvCtk9V{@-hmJt;wC zvayju)X!5_l(C>o_zJ!D<0;1O4Aq{}|H@2^B*b5m%*tui5#cb*w3uAsQmyKGa8Ju}jE15Z&t9hNTT z+WMK`p~04PS+$CW@314!xHFJ#+QOfg`4113CqTr6$q*eUbljN5u!Q;z@a<}U=BOdd zc?4f$OB^EkN4-VhUVD2}P^DuCJcG&7Uj z2sThg3z`d)p?k&`SlOb*28EU|`{W_QTo8=%!S@88&0>Y()Ho3$$zA8ln-dTtu=A z8yxM7@;?kH$1Z_f26>SG36xGYu{2r6|Ki6r}tKTPD3nu|X5mVA0Jiw!)(3_6Z6?#M22(1!}p)xXbi zmlxuYTZ6a~|5g|pSIpMFEC9PQTS4QVP6sCZpz=jF+~P@xsc?EPm{u9ED_zrA!p%Uo zb=fgC_x1zkT`&<6`eI;pqc3wEQNT>H#Bi@_0NWQ;!zl>+MuR~nfBkM2)C50aV^Y?N z{e?Mh(eKmj$$SYo%wNwY>MmhHzeX|>`5YD=5XfeK^*2flXf|?ZFQ0PiAb9vY z1848SjBe~^+guj&tuEo%Fi(WvpWa5Ut%n^c?`Bg}i(3m1BNRU!i!JxZlhhSGR;cE|8u=yc<2+4x-l4OBJKo*;9!u--gp<(N7@V{}4>6;WyDUX_l>F!meC_kNs1usH5xez#R zUEsC zwyIEBR8O8WjIdbkBz)_9#~auE@KNdEFvnjAig4y=%&C7ROw-^%^` zYxio{|J?(JE}%|3Z=;cM9R4h6C+@W~>dbwQ8#r^Q_v3ZB~@ss-| zVXbdHwOo8p$DYQ~38N46<%|IrFnX1E>+Dx-kHEuuG89R_b|dZ2$)p9c61?ytkfL4B zQfjFTEqXqi3)ic|iwS>8_MeNuz;we#_Z>(L9k_M%5q|FdUuZc~1qVGR{)C?@xG$8( zur0RyN%2vVcbO!xDR%QU>*R6pdIjt`*^VEV58+F51YT6)Fm|HJg=7prP>jGFcq8nL z6@|_ea%V2K__eVm<<%nnk;d%No|o9}C@a3u9ZO3D4L<9$u)CX&riIh3=y+&fpl@Pm9H=!9JkXZopmL zcoSdC8DZr*SKeh}Gg*DuOKWcRQPR2Ds9CRpcXI1+Q2ZOp)wQIQyDKHC)$b&Og4-$F zJdr6%nTtjpp2MuNn#9{bFOjHVv}DwMp^L`(F$E8fq_`dh`jX{9#s5{(<~gS*Tq>6G zW-LK{jptYrx|mDVyDsF0lwj4IWEjW^UJ%*sXcq5;1!h^2A@B1jcj+;_CHGyDA~ja> zI&ZFomD*Cej;_S0T}-WC%<M04lsz^>zqw-| z?vN4RKK3Vsaynev!>>&9`&C%KDFhaU*fG7=fB8Hb)IS|`V4gT{U36kb_+?WT` z;OMbVG@iB|O(q%uk7MkcLP$IQzoF$=Fz zY;$uYJ6teM*d;EDM*EKvflWH|TkOV;M}$Gm3riZ8?o0<_9+F(lQrckl6T@trA>rR# z=vC2WHlo37OPw`;>k|*RXWW8AHtL{U6$a0a*mC8b29W%^Sp28Y2dp=waJ*?YriX~3 zAxdDTcQoOE)pw!i`bltf%7P?kO~IwInMR4nLSy+Tm?)#bEYB!2xx*uv=B7hT?U>+& z_Fg5LlKYVPUech|{Sx>ML((WuxTmK47INFPT{3M|nZ#hfJN>PkKq`HX=x08LzxQ2> zRMP@T-);+eb#9`_FPCZ7=#wP7vsOH;CI}MjH^3g%r6?oQNrTKe&NW~+x(losPsJBt zWp$i6UiigU+N>5Q&(T4}PE{=UV9pkch+WRz$=uc_bJ5mvqG_^|xo3;F(9bW;xZ$P? zO zlh51C+y@%ljTkkEjefkE9pVLMfm0zns&|P+?o(jxN0af0jWk8r_K2PHhR~^AKV14p zV0OJb4bKhcW^aSH^J19V~1A;Z|t zhIQ0C&R$|NwL-GoHHU)8lO*M?6xWwY&&SQ7v8#&dp|J&(zI{jGGN(vk;Ww-n^n>QP zfFYXSK^=OaX`LrXs+8c(gM2nC@G`{7RkC5Oj$FsEai|gDf^FySvoL`lv20BkD>4;& zW@s+E=NHXB=M;)?A2YQ?;JaV-fNL%@@kp-Vd4ITH;P5(0 zrulSJcCi9sYZ9H#t0lJ=of4_S;S{!~o1(&%X!8?&!7Kfmq~3>dt9pXq$ejT~)==0J zD>C^nbJsD=57E$1DVcNIdKw<>i6^NUi?M&=#~af>k+vS;XD=20ca0L>epw4zGiqT+>L^?H zJX@}4<{=#JWedeaMvGs5O+$=0&5tv`&ZQ@I@zJ}6^67p`+<_gj%y^-g`E=RREP(^7 zeC8~h`{o1;v-~Jho47;BhbiDkxl)|Fq6bbrd`-t79UIHWlCeq&_7wgj=XJwrPKYCF zFDk^UBf_ou)k;dRuB6rFBHSD}6tB?%tWlc4P}HnNWOqGj30v#UD2JA;Xuz!mO>C+LlzPTyh6m?=(=-UPpG7x>>K_1NDFK zjm6n?v-8&j*oNzK*wN?JjPZr&G2s+Dp&!E=mSwZ6jrGi@%pKfkBr}KlsrYb_4n}@U z!pb)rDNJTQxjnGPz)NGqljFMV#66pF=%>B5Cta^$<%KCj-bbmTbSNmh{r`Yxkc0fkFF{~T6Y3XyDB4*eQn5|Oqs`YH|}OjR_|e7UJPO1 z2eiSj?^UeCw2O6jIk1#wXFR^W2z{pmn^)h?Zp|6bCc1gD-JAb0^FsyPt<%@>fUFE{ z_^nSmF~#`1(FHp{CXu{;EAB}4v$Ov=jGH=bFgWj-hB9x*!GnW|e9pQ$JKgtx(Qlm< zjt=Y-IBx1(;P0bQ*3*kG+)Zh=#Xid3IgbkNmUHNkD3TUBy*kPwMRK3svQ=tE+{s;P zZ0dkonouEdQ(NW4&q5y3q*J;i-#U_sQ&ib0`7!WgxE7bzuZ|zvv4Xp~d~^Uq&U_wW#My!Qbw3!Wp3*$hU>3a^LJ zO}vF^1s=V0obR&e29L()KzF2VEBC77`CF0P&B3WOy}%so@AZ-XjA>-@x0v!fgx`BL zDXO!%MmxGJ1)aEn&+ac~Lv2M&=GhuZRkp(?GFmMASOc?t=SR;Uc+wB4|FC0v6*IaL z$*SrLDX*Q!HTmHb-M@|$=DJgkbRR9Q&ZCnbc9U8|5RAM$rTklP08L!8nm<_XhD&y| zfO@YH^m|x>u`;P}^o==&7xto4#Wei&LJjA7E+f-jt4L{FCGEaGo+_?Yk)@giojy}U z$KS=$xaq_ApprE?l8X{q@wYR89Ykd5K1=2cG!c zMHjQ}XuFII`EBka+u>tSC2u_2k-eTuhZoWipIz*tVgV%UtP{Ju8_q(NOK9$sRrF}0 z7d@AsNkz5Nlo2(T7Hkw)dj>N2_P#!?8Fdz;7hlJv)u~wbeGRBhD8ThTck#xJgIuN5 zdtO;Xnrr;Li{w&6FnL7@*(qcQ{!$Nm+9^w%XaH4Bb)(B6@9Eo;5<2dEj5hoc_~jOB z=|J00(kN)>zHXNne>~etaldWh`IRy3oT4{N{pZXUhQETE@;PKP#Z)45VjKNeBu#Ni zp%h(yjV45NU>6sQKelC1lzqHiwX`or2&e^-djM32G@*p)aJ;-Xu9@zDA3i^(A&Zt{ z+I%;>F=!0N&F#afH}k2T^B`R(A!B;VMbf{{n6f86l1Obgr3)K&P)G9#iWP2$^^HX^ z(BdOK9NjFDGrNOH-$t{!`_19zut)G|_cY;MbQOOrsl~s^FDb&?g)(#ne~YXN$z1jZ zMftzPs>E2Nz6o1hLP@41236K8!Itr!_|$qWcYR(OI8SxQhVN-)uGd0qo2Jt^e<{IR z9VmF6O6k!lY1(`xQ6ir`Q8H3Ih^|#c;EP|{cBKV2U^)E&y!?0=hG!+S4b6HK{cG{po7<^%15<^J&#Q;$@p)+&g$(kcNx4O|E!~KA)EuOQ zm}oq7NmU}hyoz?O&!iFR!%x~Q;uDYR%yrTBd^-Dr#XU$SJSE-j8n+Hk; zl%K#wX3NO(PY30cT9R@9ad^ikiL81ilY4z6P$>Px+iddOgnJSrLSJpbgwB zmp!0aYIOtL<+1I#2lZ_r1W-^<*yiXg06H39P z!fouRfpS>)(~IQ0*cXKY9_fYsqJc2U1y> zTs(XBYX&}=F_GyeR+FpzABo~Ug4tJNMRMkbEL3buVasQryR9c1y&;2L-jd3c&U!HE z)t>Or=>?Ooy$O;rmr$O~zzkPmE;+Q3l;)K1=e+FEYu*4bu9^ZPpTETppGSP&Or!y$ z@^MMi7;&mp3vB$XNAsH+=tk5<3S0P{b#45_uC*Dn?C%%YR{zUvQ8FhQsG2WQeE&ch-Y@>gfNK4a?Nj{%px4)ib;pZ#Z z%a!`9N>&9__RV9XPGs{57t;9`wfk_zoiliNX&rj2Z^yFZTk+V|1NbL!Dkb_0Oubua zpfJCK3~NHkW8Mr()QY6YAtTtsX@Lxl8zI7cHYf~Nj48&6~cREF3y5Y%B1_HfVQW=SCS6rO4aK@^YlFG_aTT(TRxMfYc4)-T#kF{72!~`0X7#B zxJD#knaU+vD4NfPP{NvJYdUW(K zABUpsv z80Hvzf{j}Eg86w*=jt7WT{r(5fA{Y{yq8{&7pMP%BlkAL&u{5WJMcF%%o)TjP5BCT zXT$N1T{z=bZDV@Xxy-fhklp)v8Pxq`E7k8%pshg$)cb8Lw|B)Jw*PG%TQbpvId&Mc zo8Q&h#>1T~reqDJWjjH`@7FAYSu(crc+);eYe+x$_19WFA2 zLQe&-6S&+-Iya#`KM`E-gfW@a5o~PNdAK#@2829Gfp0ql*l~NFt;p4YAI@Ev5Y!KU zBno|)7N3|x>JZ%6jS^W6Q^~;X@g&Y{kQjx^Q{=xI{4>IyoW@;ZGPdfh!)2McTNudJ zwf~h27|!9UU4?9s@=4K9$tBkO;u>o#>lEqSImmiFFSBPd8=xsN0yp!t zICiulAGb>Ii-v1**T=_0M;U;<+Dv#;z8;QUna2*VO<>AGr+UE6wWKt|8GU-@lXUrH zrgyWIBo?Za(;-iP&ejQf-%si+ETL}hEu{>eji;iwv5Tf5qRDSIk!e3S@DX+LY4by& zU0lSH0&CcyG!N$IF3(czT3KhjAA7tcn4P|NO3?p<*y7|@Y(ss2HZF7#c)j?A;k&JP z-fc1zX1{03+S{1)f-HEVUcmIOhOHeXX4D;a3FC58`^V`<(H{2Nsl4R z9&(Qz49b# z>Y~=QUnL&wy+l6XDx6+d%_W6nT?pH4juh+am1}nY_QH5@V?c{ozl32)l&~b z$UARAPdmd_b<1*P*E4X@u}!3UxS0RfkV$)H?MKHYs+23R)3WtbB}yJ|$h7Yi`RTP{ zvSc27Y|-aVGEZu+Mi6aBRVB3&?X%X{I?JE0 zo%9A?K9+|6QFPw%Tz!8WS6P`^DI+Bzw2*nv`=&jlQc{Ri+A5XyFf)=BiezU-Br@)K zUxiA8O4BHOBPkMXn5M5A{%^XO11*gi4qjOBfPPg!hhfjfF@4(r zD(N3a(dU`GN@N(`ENB<@!1?6MMKg9}%1roo@d5PMEGLhJ^Tzn%H+b8!43^YK!c?!9 z;K6y3xn>&RwV)JSYMh8~&#=G&JU}D&Dv_X?B1Wr8;7T+vqpi>WF|EykDDupg=1v%f z37r>7k^MIk<>p1!zn9@^#%7@A;+c$}zbX9|5`_vQCu6Yjd^%JkcusvMGQ-0nBx3w> zJZ$p=v)_*e0|zg5A@h~JShJBeN*)WhPX)vNXRa`M+YgA|q6q3|wt$V{G1^{{K)D2O zZb!!g7@gNmy{+6(P1spTv`f$>eXXQ+Su`=PP~ffZ#q$==;_=KXGhVS#iC?;H=8EVe@5`VITth+LabnkR0a?R7}vhB0DvIq5qdvKHnPriqA*`Svf5ne07+7x|%$K77#nhiMj5*Pf5FI7`|VCG4Hf zTo8CcJIH2U;KytDV$#b&jGO<8pE|~mpZqkQmsmRziywD^hC~Q+ZNvuJuwsGG%e1TX zW6zLnm-@-QuKPqJ-;K!13Qo9|NzCCKIU4_II=TMBsIu}}C3$*6jc5i&k!gX=T#ft{ zx}r3VO71KG1$vA8Hv3EN{~aKg655#6Gc%aw$Ggc&a)w!0lvCONZznBI4#TKCbCmh; z4po<};mXhKz*q@QnsUGo*Dv}*b8d&B&*9H_edjN#*A^-K&fd?gF>po08n4 z516>S3Uq7GVJ>k^AUAD}7U@wtZ_RFLCFxn=WPDf;onP>tj4QrFrfCJyhT8kYBH$9~ z(o-b<@hY5kr#C0A{)`?jFeVr7O=lMURVI(8>eKhXt7-I+40N_#D9kopg!^|NCS`_T z(d=~kN%RB0r2@Nq+dleuatD61xF+OJJjnSELvZ-OM)Fes2e2#mkk+^r+^JvtxEPNo zoJ+)F;y3#=9kFZ=l_g3v>s=Ff4=xeEU>gw06td4D{YXm)O_dUuvRg-T zyZ#1O^ciibls>TwydB!f%sUJjeO{LSczqJZ9NdM^pbw~jOP)GSnvRRskH_m9Ci2#b zYCKzckT;Gy!mEw>i`>^)v~wIa60XAQ@BtLJ-D9Ped6A8Sw@Jkq8)z$$0juy2uuasKa1!0*aK0z$NGK)O z+osVeXZMpIH%^n4;%7+cgT<9qzqS&wz!T@qNQ1YJ(&$jSl4aIn4|93p| zrH6c&avELrs_Y@!p&s4X7oi#!k!?11&1) zJe50aLmAIcLUv$cD|bO65+>MMF?Z8vQ4=>edRC+l)mKMa|4P%Q%b*h`JG#Nl@<3+c z*IVdV^A`g*3I45tIH9|A1h0np; zlWFxvdx)TNkClXe92jm5;%trE$eR)+#&=h*br|LoqpNMqoq3rs-Sz7k(UUInz%KXK(- zkCD6Wi)iHGt=!zu-?ZuGO1duj9{to|MlUHzpvCP5OfH!~TUw)Vw23L|8*RY{Wv4|v%DVk_1*Hr;?i8%ZUG( zA;vKvoq3XI1jnn|z_Q~LReAHB+}QGu?DjZAp7hw#eIK={MVTYjIpj@4X7-Zgx=Uo4 z9YOKN(e&Xfb802sObd2;q2!4cw4JsbwT{d{%d4~TL_{|3GnmP%nVrJy=H0mYX*BxV zZs1A|)x+d41JJHeVW*7uWtG3!KyNg{U@;Hrf!|5>q<&JhSztEqpN`|fmrgU=4ohZy z1V1MvyRJu)`@-8%?!*9DJpK{4uC9%UTVmxv!Z+f-wNc0~IFRftRpx=#EOPet1vL0& zf;0N{xSw8|iQ42*cwnqE2DvAoM)Xgd&&Ja9yGD3rTNFB|P6a*t4kpJVl>|!4kbn6u zm=lw1xM8$f5CkzhZ<+Sp!~=pa?3f69@V-64a57%-Cc?> z<&HLaSkFOhk^(y_@fa!jBEh}s`3%#mA5!lSIa2ICkxOrBCCR~Gtg=S_qiJO_l&P|# zM^*MPjbnU?`1fe0=5Y+!+uIO|Qnu#pJn2U}Ws`MWgq^9BG#Sgf@rydg-dqvpX5DOtHE!TUqMG1PtbsBQt_Nc*`}>zRMsH#ZPhpC+mbeG z<)BzPSbLr9oO_VIIB=2kEwU%G>24x#cYuCqlpvCABXQQR?Uhwkqp6?C11fec8Fp94 zL(ZQY|M&l5{+kvo?A%P-3s*2bK__t3r+5%`N`;e7W)O7f9?X^f3zzp!WW^4AVAa$= zl9vHBIBD0Z%3awa_%>aKCT71PN7FjWAJ5v18mD83)zuoBt(ZcqC&ba_BZS(v7;>j- zv@5$MAJTxN^vWM5NpwiYockxz$qfZ1(Ia&`xQJ2i^l34*p0_fiQY+sC&i`A_TBVCI z2cKL-n_H`~?5Hq{?OVW)p00%-1t!C9!weew!JIsE&4bNB;t*(b9_}RWhqxRGIDAzS z%yxdK7f}tZs&-L@F-y@pdKe8JzQoDS71aOxMY>&d8ugK1N~0gEFy~MIrRwItnYNbo z%-8FcI6h38jQ1Y{GT(QSB&|_oRc$W`>+9w+p6QUpo2fABP&yotItkY{oFrBi&1hFK zAER&Fgi$5ELZ^|v5!EZWRR79p8<+C z7eS*q2#(owgUO4_G+u2E4UVJSzncfC*<>4T|7b+%b*s4U9TVt-Ha)uPwHQ72%7|W9 zn^V!nk0HjE&uQ>DJs5Su2@DRdhr{VEMB?XvB;V)= zS&h43bG3=)CTw7wZym+an?!{yEKAl5?xnXsd}VaC4{}joJ4lkWJ*5Y8gn9fU?&|yt zWLcv&$@ZtJ>m`oR+C{BabyQ0*aPqX+Qwi~a9)0yLmo>8VhM4Hxcxv7U zL{!HJ*PFQB))qq@U1(5*C+heWgWrl_2$D~-`Y>A*L>z3%{`m_9&en zyJ9(}_XFeax`pNhvK$FHN~@3yQdrh4PXw<~b{J%Yhj($xP~77PVSR zKU%Nzn&rE3X7^|uvWr2RO`Dm{hXKT2N1rLKP$0e&su_o6`>lT5t0a@WK5*&+B0H*d zJGt)aMWuRFXu0J%&OCGv^U0u=Hncd<(YA(Uqv}zRh*%(S=#P=1R#~d2CTjgD@-8mC zITx?xJU~*r10@Y6;~Coxyy@bDXnyVt6nKQdcg3$AsbF7TvMyq0(WIX<7ADngG#)?=jWQvU*Z~cvt(G(KuaGTC;o?bN#F~ z6}{(17cQJh#XE$Ze4*f+5wf{+wAK)xn*q$OzadOmStL_^HHif1&qcY})m%51RS~Pc znI_Bs#%r!6IPZug9h?1#o}RRr=`-7la!119bfP}ZcaJG8Jm?;>3<;XuQm>!NARcD!>rf%qKs z1IL?-;pPhfNx3wtZZ(dMYUoGT4ioO-cHvI8+?7sB{6+Uk%%#Wf%hUU(W9alqO%nBa z0WoaPq2%RsazB3ylTFuA*C$@w^Q%hqN`F3%`cP;+o8h>W#9q2eayl*zYeAV6rc7kq z12Ri+1C==+g8nWO$b8>Gw`H6me>&WSIhD{K{MyD0ElBehE>9`BmxfEAvnkxE?s=jmN+GcIw_DTys_uHMcsI7#) zc5h*V{i;a1ek51bRmKb_htQY8The7uIXN~kmljUeN3qV;sQzmjx#CgG+#G7dsTq{W z+lm5NhZwZEAB_KM!WG|SQhti5ta+u1RvUyqQqvEtfAFW(EQDVb#8hasyyZ$Z|Wvah0bEotYljCc+GvX0+Ox+}6u;fx@b^B?mE2_@9 zdKA)(mXPXmbHh`j<~n9is^1G6s)$Y`}da}UNn|tZ0 z!PzIb(d_?ZXu>aPG`RPhmRa_ZW+^XnK`#-_R==fQQZDE(mxI!`14B~9MjUO zi%SoB;oMjs&h_hAIQw%eX{kyCdA}ss9p*~jB|M?anw^oZ<)ZSLtLk`ccqc+Wx@;&SW9V0Q2)16nf(W&;hft@c5Lo?vX`^v)kNO& zS~~s$>;4N~-l2eMdkiX@#Ai|JTS^wf<2F0VD9()V*4b8j&~rmYPuT$#W;Ss72ROzIXopazWdqJCNitGTkvN9dJN zqtQ<}2Zb76?;prdWrz61Mj(z%Q`%;^V$u<&S){>&oq6)9%yI6VeTnz6&ly)928-Y&0xSxC65+6k$i65-z3N z@w)9>JbrS3ml%7V4;1^yFLXJ{=e>Kt`$&A@Wh~~?V1ZX4sZh+WAV#eH8+BIASCh5f z?!c~3%VL?A)vO^JvqsBOA@;%_aAb}WwsI~^XpEt&N^H3R!*VjcuU~K#D#66RrjTuU z9P(O~Vcs_fke_o1B9E)VH@|o&z7PSMGaF&@6e9@ipG1D8uHk}j0Nqzpg)730kUJ_k zSxwV;hb1k%UWGp&ml4gIK9$CK_We+|!ISmwT*RusC}!=hce0*RO02D0J3Av_A-g0_ zkDcK4iPhy)*-=-oz`2iM;Mx9~=%)5Ef3B@0AA2T~{AbqWi0^ySU$PM5FF%8zGXvlU zjpRbsXGnCdhowu~$ooaUB!vG9adM}@%WE_%w$~df)vm$7_OXyFdxzIO9mz}S&*U{H zpX43(a{Ss$1V{KDf(0V8SdDGR*xPGd*yO#PY-kR!i*jeP+PS5y+IC-d+>1t5!@v|O zs$Rmagl;&#^BRoS_l33ZmrzlYF`%xtip*Ef0EPA55WXY;e7Q?d9lsN1y&PbYb24Ch zSC+6-*ac}Kad7P8ed8Jkqwz|L;UV~?h{vy901_N$ zvJR_aSVPh2Y~OQfHo#h(wGqu=kK!lr?;FET&e+6ioHGXRYD4l_*iUaRYNG2Woh9d; zN6@^D?}+T2W?GaTP3DvalPa~Hpwsi4*z*bys8tB=A2>#S^Z+?CBzW9*%zzEA++lUo z6s}>s8@)Y$Da~3c#>>zDj#-a8S)o-A@j10HGw3Nha$gRs*EYa9lxV`Ai$1K@vug70 z!&}xRVjJuBua&&m?F|{D&ypiAcSHB#k3v^=9pmQrlZsD0MrQ`Jaf2O-^xh_AI%&-d z5awT=~qun{J3K! zKKb?u*On^s(;{Mc(fd}c^yU)w#25~I7R`ghW`fgDOofGf3xVY!ONtj*@nbi?sC0YY z4tHvFsgvDe%+D9xW!2e?A>$|9eQwg-8+>Ty)o_}iEk&O{+l$(v?Q}zTG?`w|Lo5S^ z>D6QXv}?W%nOSy<-1(ga>;1z>sB;_|<^Q7=&Q(yO(#Ty7cgN0_ZTRJK4od2mqt)&i zybSvtqgLBOk@yc5k}FBN;PJiS6$<4;!vEFCbhtVG3@OtI#g`+L(egnHf#YIy;7S)h zJg}8lW25+y=Y$TCSplX!K7gJ|lBgoG5hv_2rgL4_(82KcL@LjQiE)2T>^>F|nJt&C zhlX;5IlCiRe|}D7%tA|PWfj`@pzXnpapT*LJP8jmsjhF1`9}-CbA|iUHaxY7a*@X4v48Palo7Yb`Io2Q4J$2XsfI1a_8Ow|uJ=UZ%40ey zVFN6AUPsg{I$@`F8Xo*9!Q{xF!JhuxsB%sLqb97!@i9dLGp-$%mR{xebyVUYyNXxY z;*S=0ui?#-QCM>AJ-S{zOWQVNq3nJS=1-p@lQX}E%>QwoHo82xEQO`)O*THxN%rT=*M1zc=vFO zjaJ5m0l6T1^FKN_zSD}o{~CKPeZrjoZe#a^Hf+p~;aF5f;PDvko{h%G zzU8R)`xrWvdRgbTcQ93}s!6hsC-HcaNuRwlppK?L>7DyLy>n}r_;&@M>tcPvKMkN& z6>@ZV;c7IUW)5aDhDj4c(SNx*CQTOhNLCZkvf(df zEQ^@UBhS+3K5yyy1IMXPrVRbE?<=?Q?i=Ry?$5+dYAN}zSO=bHin9hzGgz5*<6iTVi2*He@i$a0>i9O8pM8R8);} z7Y@B6*(0n-bf+w-H_>2@ohh^)Xnc>(@q1uqGY3(od!VAto!ry)fPMeH!h`r3PTuAr z(E2)t$7=CnnLBvhW2@+*V+ukaDv7&0@E8+Y6d~tJ3S8E)r6}|L9*U1uVSVR_**~H%sJN zeXu*R3|+lv;Ax_Q2Q69Vzkw#|Gsl(A9-nCaw=sz*9nM3$BkSo2QzybYIgmw+D#^a# z#_jo+g*m=e{8;fHmFDNm(B;u8^30v*!Z(c}qduf#;?xk(bS>ApZ-_S9Q+n3auwhCi{)Q0R)@Q^aJ?Z0!D$hITVH z(&6M@)IIQpIy!u3ZhFOX%g4$Pow`3XW%Fsli=#p}x~FrmY6V~UFCUn2MMrQY2;COT zSgiW+(7Gmn2hN}AFTB%qK+#!}j;zeWr{Ue?ysjaKIwrWgMw=h8;#j(9T<1A89B4_WaVq13QxQ_iJ;xIVA=wD%EBABUyj5sfk)XaIhuPrP8R*9T|rj` zYpPlJmr*ZX!~CrhTqP0P>2TpQTCXiQ?7jb#pA*SafE2i`l_L;4~cL5f>y3KU{pvxM(d%S=*58MjpPA$L> z8NFof=o(n_xR+kBSwqEMB$BYW*)V*^mMX_TMX?WNIA#7wBDyvO&AL|7hkPG>(v-tp z(m70&0P~5kjBnW({3Cc&=F_kBA-RPCru;U7EZCHUx4nue=tPLEZr_+iH1$0@2 z0sYvK%EegrL-61@^y$6=r{E3AOK+lW8gUHG{zc}cxHIcSjxh~cI?VG}F)E|FhuG)r zrCH+#xR;rZILqNKs>b}|{xeG_U#8llap5}LIp;dY*PP@>Ja0hd*LNUV^qY0y(FIUk zu!sbntbxA37lNm`2reuT!DXfc{FvZCUS+K)Itle#*4EP?t(|Tn3mZ@wDA-7=eZ2Cx_Ast zd~lg@d7F(gMK@sC-0s z6y}INQy$S(N%5p{@g)*pdZJP;As)x3c2hs^o8(l>GO}-k9}OLOoHopHpe1YEP`&6s znxVIa`m!Q4;rMrS{E&hMxvzPd!g^j>CJ-}X0xCxp6r;ZOd)WA{2iDG53o`}=S=qm0 zpjY3GK^2y~e4i5iaYN`>{5S~H4HrPLl3 z9TIy*>s6<5Z?0G{ZZ{ue5)KZVQM{ZRef zY#J=3uj@N=4D^Z;WKk zM=t%Y5^j?aT-+lsp?%PCx-Qj>Hm=!1OJA*^mjcs>oy%fc7~za&b69FfbZGt9PMl%j zjJ1nO@j6$Iw|Ac=qEhF1wbm=NR&yJwF{=btayH2l_e8^Np?fJUC0V~A^l#|geFVMaVr$RP@U>v6}baJ%?Z zOq?!?tA7qr!`(mW`ya+ArY|_Nm*wCdXL$@)o`;q{BWPr{Jg6HFk|PD-xFcX2jxuxN z)y=x-9r^+5KKG%<9d8_aT$f8*vI!T&zQWX-t?~esD9=_}N&V=!5xB=b_ai4yG8@f^tLx(_WZNZ(Qr9uR^sj=F3A&o%#!73NPb1 zzKVwKdV%Zyw9tQ^+flry8J7Q z%)(;FeDs#OhDooc^UK4#ctK3SPZD}vO?O#3LV77bW^ErlwTgi;%gh-x-;Rx+&!X(C zF5D&TcMi;J0cLtQ7@B+snfcer&!(#+qrH!MJ_|*+N89my=Qrg4s-g1b2As41I<8OB zrcZaBz+=r51)f;|{k=hqoSJizo}N5|h&j4gD^K4^4k*Uc5`%m^f76m$T55rNsVVO? z_Bmb(9f4~j7g4uA*J;Aak64#&K%ERlfQ2D;zOz3(a9lyN$B#x&VQ*R6H4k%K3dpv4 zEl8F06FS*etrx+=ygIsX+>0jjn$X{4G9IU)D8Fkr{iQ33<7eMP z^B^-EKip2F6E(@RO)1>|vJGTP^)t@zb2;tr3`VuR>RhABOt7BY0-0C8qO4~ertaE{ zmS4`0zt=YM3WC$}=!mzpJ%25$wdo=GaBLqdR{xy%xVNLc$Zb%)xQTnjOLGx#`eBMx zE)WO7WutkTzOVJb!{-UNQ^@P=3@bp{Q-0K-=@QMcldcHckxR3Cj!>BUn#B8D!IkIa z>4DpON%kK_aC4gkI}T0+mHnfEHRu4H+(yC0zCmzj6hhz}buKg83e>x+h|ba;Xr6SO zmH0~`F1QjJ-(|8RwRl`NQ{YuMbmJDL?nq%GxcJzneCbyMFQz+gk

+|Mt{|~vxAjrRDvs`=28E^#W;7gJZ9u2W4_M`6sh2-it%LP^)H4zIer9`)UFWu$j@A) zXfn~8B)m_?XCSd$E4YI^NYu;^ps{8#SQYhCkNRCj zis+zHJv}QE!mJAMA}vNMLE6&=jB~olv zVMwbC)V`dITz8rFKG&q-MLD$paW78L&!8h?fm<82 zo80;4j)fjt@bR|-Y%NmY3)2?ikf8w|wc;0F)SSlu&^G7K3<~FmL_9`Y=`m}+1ksVU zw`ihGAD!&_iGR^}dSR6=&5c@3zuso))VxBvB+~|WZ+wMm z`l~Q1qaH(}y70S#1xA{Ur?m?enXaS3bXT+)`i$Cyd*`0Su(nd+-Wf#Os^`H<-~AAg z_5e!iJh%y*71=6N2-kFm$dgI-z;DbKI!wir6#_|J=P}fpeVse{Aeoj+cB1d$K*Y8;ICM*i*W7js zb1Jvs^f`fe#qT0DDx1hp*j2_`UsuA?QfZRM6tS|cwJ;stx<>;^dov1*n^rIxD=)(Gj@hu+vIl0J za3tXY#dLDqc;>W=CH2t@qxHtc^nCO?l$=blKVAv1OPu1RoEG7!;MGjGTq~9&)L`w( z08CL|htKW8c!i0VG1w%K%1%tTUflGD(?}hlrIR*6$Xo5o32#TE=AazIl`n(eZ~wBY zYLp%G=r^k=Fhg3@&A@s7N!Vnk0!cy!@XW;(#PUQiI4@cOLHW9*O6Zu$R~%-lW*nwf z!8aJu1`jHkdkNQ1`AG^EKE}X)RlG5{3^Q)`R^(|JlcKY`>D!YVaqXz}DF5*bZ>&(q zJI%A_50?h=(zfIHaT#U!FZncve^IBWM4eDsOq4!b_eYqeT0+8(cX01-F1&ZFfHwDo z@OzO9u=BLZy`P$Jz_ea zVuDwyQPJxrL%{InYmT+HVL{s>NGEzVTQfM68I{4 z1ux!rgxAUa#E+~u!$Ce3TldQ29qAV+Cm93L$FgB_)-~A0ABQ79Hb9I@B3RmQCe)}N za+4!L+Vv1rCESJlZLh)DLmh0Ax4|~Y4pJ7SNsfPiOLkxv?KI6Ij?3)8`sEYr2E$Jf zxV#3kE}nxfXJ_dBI~DS-nStW2+0eMK8ua?Zg?*4I23eHggoH@??^883c)E@Fe~rU} zuyH8FoN3^tQ(Us`O|(m$3rpfofSG$G`6J}ZQa79>pWF(l{TWm8Xo3c{lc|I;E6>5M zF9WP>Y9A9S^#U|~jH#qsmUYVKe?+K$F~RMt=$W^Rse@H4iJ4gkj!_%ngrhgSODkl@ zz3pVjIW@7PhTgMQO|`6@L>=@MNU)5_3-C-hg%gTAP-=f0`u}}`WhXXZg3oG{m&$~x zVi`2nZxLz_3(ULI_F!~!6Wo{Y1&`^Pq`_$v%zs!+i&gfKwnGKP(fJOX|KtpH;fIA; zc^Az~si*B$uEeZ-6Mgq-Gk4@%J`G9UK`tMzCcmZ>!z7Dyq{BR4*r~k~c!dM3PTyHp z{8l@=@W=&Lzoi>mFAlKVXY7Z2upe4n6`^*A1u59sz`XH1fx+4d^jZ6846(>ViJ$>e zJJ1ecyI!&ji^N%NjAA}4t>NyaJ;Eb*=i%h1Ls-|u3B2n-D)&v9>yu>ZmG*P=OW!GU zbTGipw+86JH9P67FMl|$UV;0$-j;|Ff4HD2!AJX>pVb%Y_c zPT$YG6pN>UmyZj~+rvcnRWr!VC-lx@Jt7*N0k?}MvO4iX9>{9}G{)^<7pwNLhHp~X zvEt1Fe?*U!zoN}ak+j4h<9KX_%Y}Zao-4@2{>P6~FgV z(`seLtHhU%UfB(1Vf#34gAUHrze-LsGJ>eX4K8m8CBIAJ=vAjdQoa8JRGI})pRGK% z&wUE??0*LSEnUQ_*ASwn7Xq3_!?oMXp=70-;Cb|dkMJ*vj@d2DMA}=(X|*Bd{U2j$%g&}r*GCcsW<3H~ zkI5Fz_*mV8d)eHKUb368JZcF)Gqx0czuv^-C{g48rDxa#OFx(D2R<+^=1a zQI8W*y|R~TMGKy3u|yhJewyo-`AVWI))R-r8I{@c7ieu*Hfr3kz;4kbe)J_1evCo_ zKkC(NRGPJ!-}H2dpZI4PKYs0e?w-$M{CALLN?Mk{$1~Gl&&2@PwapC1#eD`My#Oy} z??>L`JO*S0O6+t7w%hC5?HjS znd|U2ru(oHXH8771|3l{#N}7Mm#w2q!yeJzRx4b0JeVI-u$fbpg?N-cKDpb7u`jM{%0Uc>3#R1t#jNw64Q52w-Rd6#*|^rO7#dUGstJ24fy;LMX8z(#6r*$sX0v8j}P}Yi0skSCF zXL3aMyKwKzze-Cw<>*(FLcHs!z)zUEkl%b^5APBl!ACrG<8wPq_`9}~`1#{0ztVgQ zZ{YJ5AB;mBzR#ksff7^LfUK?Dq5%H8%l!?@({PRf_U;R(exo!(M z@~2nuC62`Zw(Ie>v0wNRo8$S(Ul#BiP7Psr(KW2hZA4!g6--fWz)gxgD(3CMXA<*p zb?Om_chG`eKgO~$4?Q7A*hSq*&LgEal*z&dRk~F12P|}}h9!!|%#9xv!VFs$+&6w9 zjfyU0ir5(v^8G2XTXGFYR$b%VpPF0OT%Jfys*hp58@Dq*T@r|GmJapP`G%~Wz$|h4 zjM-y(G=A=bn@9?l4ll=s4KDP@0U6l2{yTR^`UXvK74pTNxyY@Mpxr+D#D2*(K*wBk z%kzS81wU|E5D1BlTJZisI30FZ28HU~^r^@JczOCC#ODW+%$w8U-J(5kJAW+A*jWia z&H~$Vo}6{Sij^c(R-NhOkC9Y2FIxHb6LIQ2$i2F&K_jll(~P|Dbi2qL&@Xf)b+`AB z=s&KYbT<)XwhJB)>umUu7S4&8Wiu8t18~x3bsVc(j6H$bRPCt(<_MnpLqYE#)L4>6 zr)5&5X}<&yo(mn2eGl_iwPVETWXL>U0B=I&;o#`Ctn@5-c$5?bW40uMW}+QhM4hGQ zceN9(#%m-bdJmlv+d=ocRgjH><2XdSglukm4~nCHk-rO;!-VGFaA47Es1L1RW%aXI z&GEhLRDFAPj(jOOZ?4PzDL4UF|7oMa)hZ#^sfs<;vGksp1D>iX;~wmvi61`;{L#83 zSnU}EOGX`lW7?-s`mVO%4%3AB=On>mS~I*#e+?PimqCiwuriB(vO(s}M41&-Sk(|Mqx+T1k??hY*3{t)kE{VhOxaQDFB@H)qA{ z>fueK4b_ua2>xTIGoR)xz#}V9Vp;n(UNTymSAFmf-!`7a<(vHZQ9tI>=!F?@IVXi# zSig!a+>l&(=%pgP+|>c@0@r%UvU{xbyuVN}G7HMeib>DPjVK~jN)q0zByoQtD`!OS zC$USGlLsN^xW2IaOe;SICJtB-Y}N&vh!MiwPT+VwQ-SRJo7s&6VeDS-ajdH9Nf_E{ zKz$9ifWJ;Y-l$LH=Q@1kEjJC~;cyw6(qB(Aj+x@c$3^%})}Jbg|KtX`Meyvik5-TV z%Hfp_X|Qy7EsXtB4Rh4rvGVT&AzGTD>cUwaf9F_5kWoJO;SWbn|BGXy)U=s3kM&4e zfIZi7YawaPQxbRzg1qq2Ae=5~XANG7vJ=8eS=oa^#;G=)4d1zd-7`%G&Yb!Uqj&U^ z4|5LVYfDSsNd7MlY6@9QuQ8}}DT7N+xj}7OlZDQTDl$O~dGbvWOQ+P(`n>Ttq3i_A z*sX}3rnU5s$^*FW_y&&bUk55>mas$GjFYA+Q^gB>%?B4@O~Pn$OTLq2 zu9!<2@G3cA{TBkScf#KacldF`ogJs%!EXH9#kz2stYNVtC@4LE&Bjw{$?!`k@_kLe zxk)g-A@k_Kv_1@NF2u9D1vkXqb&%rfNTePb0rO@qD#lsRiPuiTkt!Q5pjMe)4Kd~9 z_=!*@DrDdz?ZJGwLzpN2M=FZfkPBz+nca<-i0XlN#8ImVR=n2Z)T8EsZT~s) z8nu;39~6hmRVzW}b0{nCagZJBSjW!lTmhN4rqJY9x*+RkLGPd3X62H#0Np!dFnN6$ z26-#c$N$;$;vLEOn%zXqO9RRLISZgnm`R|@SUfCnWAr}V!5ts&l1tl%D@}XKN$$!n z#w+3>8pmc*!^6|ar-RqQE>DlSTyvAG_#8{xK$CpGevl+Mk6^wurBd7N9rUDY2xnfd z06!LIvZBK;Se3i)SlNfcVCiQ|L}g;}qsmrt$UcC|Tq>fq<%M|bybZ45<{f)M>j+h_;^gR?UtqmwS2yZk^2d}rrS!uFHB{g7o#e5ioXdZ$XkM*P zG9HOhMr0u@J)Q(wlWsCo%g&Q@nF}D|^osoHyUDTsy=dSki8kgR!tdARZ!d<-#x zvgS_^_xLO`R$zKf+kOVQlcoH)g=et!*D$@}I18IyZSeQfy}VSfE&bgoiLyr?;@Z@9 zC}PXOuCt7llD2{aK1OipffvNAzKzKdJYIjBh#fPUNzy_)4C#)hySpWbLZOzBNf`x8 z|9t~<^G)P`44wC1O@AE63uzDyX&R9V4I`=U`Mj@PNLCpkvPbreDD9G{RJ2r*q#-SJ z&-;ClZzYM4BqK`5DrA)LJ^#S{;oiqR=ks~LU$5u0?zbEN?Ya$L>zv5$*M&3vl>)ba z(Q(P)72?c&x-$R8sSjIoBZei6F{hxXJ?P$qt8~m}5T0y4F4$Zxq}JC46j0zpRbB^# zK?#My{xVj;`yI#t>xea8FT9|1idPGa-ZZ*e!iPsOx;k1-ba z@js(BVnVYs#ikCT496jYhU}6sq|}4{y_!M4h6U2ZmkTKDxg*}#>MlgsItVIPn<#V6 zMNIov!cKRkN(amyFJ1h~A6hP^v*zQI#dqCIcF^?~jZ{d1A$^W9-qIXmR7YUH7Ik*U zc_k|DItK#^62W}13;V27gRUE__$Oy4p-a$s-tFlUnY8>L`@H8o>)SM(U;IQKgKL{< z$FPGmX+Rx4z1&F06b;DT-9T6%?hS$$#tL#v=F+u>e)Qku+mx|srZD2M4vqEh39@PP zrBjoaNS6n0f`Xotq^7;sGg_+;S;@w*srfG~R~^geSZ<~BkdHGYVvegr zh0E!4ic9cPU_sFw3pV#-U&}V}i4tJn>Neo>byZkca}QLm`|`R0b{O(!0crgqQZNwj zQE;1Xja*8l-l8wv_%IgFI6$Ry9EHJq)F|(JKGiPHqqfQmbgtfr?Q+(YYVOvOZkrt< zb$7Wf9Xb7zbW&U~G-{N?%}TLXw&oRhJhbO@_FH1?0gfb{D`?&}3p}Mg8Xe@aS>Tu< z7<%$Q{_=u}EN~)84n*6q0G|zbyeg3U=RE;ir}jn9=WaCe=S|WR?+uYwr)h2TL<)=D zO|^02EGU2%1_oZD6DfvtUG)y-L_MTlBBL$k@D#GBc}X|Etzh+6drGHi@06NXxk`6t ztc7I%uaFd21PuoIaOi727(D2W8a~hHz)e+>d^0BFWjD!0M;q_lRHlBaz1W#7Z=QcU z30H0lXYVGq!@wy)IK#@9Q+@uAITakjz_A;#VC5Ma{=Ac9%4aEZy(LM;`_i)ZIaHrr zBbfGd7PN=Wqz`(7sq|0|y_SBYsgCJ?YPJY z8V)6D_K?0l9){Hfu`geau(gTa_;yA*=4_gWmKI_j&uRf1`ay;7?6knU2cBZRLjWF{ z6vPhcEEJLlkTAvaBt5Xog+#EI20u}P-G7_OOjDqE_Y54mpoA6=NkG%lB7bd@6<4*W zFDo)?k}ddX#k41{x zK_8gko>bgla8z{Sg;W39C2Rz*j=wsbXzT(dOx~!Q1- z8H<%?xzjCiQ};UVU)wU&>wbWxCK~+g*B8aCpEK*2-jmyV7P(LAsqEpR4qTz2FZ<6? zfh(N&Mf8oP(u{~gvi27_quJ4tGe5%lr3>xxit!M19IcEeV-v8dy_Et+^dSAh4*b#@ zj30_N@llOpzkERyPOKispRRd^&W^fbpUa%Ho<9aO_GUq3_!>x9?hpRmYuUL7Jtn;C z4+d{7C4WV~>F2UizWm-YrX*>`@VFF~DV8xd-z<>%H7wu`bp=R{{hPr5ksrXPf0xIq z_;l`6SsWN>TY#<7C^mhUiuml=$PUMEf|Ghl*y~s|trqh#b?>z()xrf&v^k*K{7{%0Tl;gzxSEeRjOYMbau`9XZTi)YCpFo=ep&=xd$Sfd08Gr%T(F*&?xXYCC-V)9RS6PVxOzwA)oT3Tinl8@>@r_v7JT9 zxGCTXa=#BTH(D#nD4dPU#=Fy$r2|P%J(w?+5omOuW~aX&<^6>$NDn{3g7v@A#pp~j z5!rYzoM%E~R5^RlHwpzuC1GJ}wqUjPpI~0Ro6}2bX1sSY1ZmEKIocWU;Zqqre&Y!D z#XkPyGYwLeO^4vrmcG#Mv;wSOei&RoxG~R{di>OR4lKEJGE+9(*cL|1B_|wTiTK~8Q*m1 zus|{2H)4D?o>cA)gPAwe*KWj`TLUp^!4#aGF@^ha*T{BMOawXKUCP4y4WRz9w`h7{ z1{L0$jAbVfCy)I>7wkvCfu1H%eqa%-ck=_&hiVY=F_; z1!kr9Tg=#3{0hvtg*)+#6FY zW>R@kh9D>I3$!b|!94F4?Xc>PQ?_@Lo#H4ks~SRT-A9=98#hu6un;{Ltu)JS7v~ec zi~r?O#hrCq$Tf#{vM3Ez91hW#l5vZaWkc|Fh#w{f{KTvcxAD-j*X;TEv3yd!Gep+> zgh4fhyt(ytcK-BTz9Q3>d?xe4Fp$HffG4D?JwPVEGL~K2Y6kritk7H4lgbOU1;d2tT5A$eKWhs@eUrckJWw(D3mBynf?fNMUy8MTx*-I&ScRsmx$Wzabd^UCG zPAqnLL!uy!OHu2@0Im@fvO2teGzWI_Dbl`M4@22%Gf1o}f&z^ys4$rT-`;3I z(SZljiCm6!dWouZROcr)R^;$a6gziYbLEM$Ur2iOtOfVA?vlkXd_Ygg#?=A)(67~& z=B%HOk=`YwZ4*K-Ec9sG`(Jc2ycXZLx#8GcdrUL!#8>=lw=0I8@`5Ql!lhh7fz)79Psrc13?2_JfI1&Fm^J%4OBm}f)ouGH z9p+XA=UxV}@QclOqAs0MPd8HA+sEu+w<5S$9%tPb{3)>ZHaLWvutKhe=Io`C6BnmZ z#JVz?x#0z!op_6^pTv=Fh!5|w=oJlIt40gGkw4+rQQ9zAz>)UvNjb7FvsT{rF-9~;>y&I zl=E7iuF7Aenx1LYnJy1Kj;{stlogm;l}mnc^YN60C;r!dR%C`plKShBR!+WWrhz;*0Vz|8rhS8`*zRT%;8Mz zJocp51$fkdy;NycFjQ8zK$BlKqy+YdWd1pgz9{}KvJ}#mG*jR~anAhaCf#UVEV7~w zl5FNmVYEb@RE0Un(n4uA%28U|eQF$UPj`-lQs;qBlr}Dd{_1Zb=2<|)QHRMT8I$b~ zMOo^$jbxXygl3BKj8fy}*mzcx$+e)fi zl?pkR-@>h(Je=z(1)sOyB<~^~kXNw@zpA8&UwU&p=>;SSCRT?j=vWUy?v0kvOJ^}1 zRBY!~UKt`fg8Pay-is9f#92^0I9#w?zk*Vx##4N|$OwL>foG2xpwAT(QZ&9u>zoW| zVwfJ;2R##6>#dTK6irM$7|RNCTUp5XU{Da3ke+T+LA4?jCd?Oi<-P{6UF(Td-G)i0 z1!hYJ=!iSKi02UBT85W@{71@Xz6o-^*Qn%SJN+6QKm&L0q0C_cyl&+@{;hH`OzUq) z{X9ksYIZ(!`QI18pn4Z2Y1YuPVP9!V=>u8Z(P6A2djUWAis%sRD#E(?Zlv6xO$vMd zW42A&ETqlQGu@0Ru`_mmFpn=aKm=>~sZ z$4Jec9!q<6_l92g{pov0w4f68f$Ha4(mn$(tX$W`=0CS!zpR$9*qJSGcGzC#n=eOk zFDJ9_;*6|E(Qk;18j1DdUGQm zJFtRVG(}{$zPc?sh?5}4|BWQ5YYh0r{f6ArZji7q0zyCjhVZNEAe$`#N#X#~bf^Po z%S7zcEqd&rT`(*56f}%y)A1SS*cFv|5a4-3s>mA^Di-6RVQ;;r>Vs zmjXQ4kR}~;2Oz#)WIH;Y7hQCgkbY(&m%Zc>8O{Gn>;E;9L1!@jHv5I!?V9j|*oW$| zu$A4#docI!6gzj3372885zJo?haR_%VAuKEw68Lk^cU}!$v%jAdGnD>ad#HPelMi7 zTai?3|Bk|cWQiU;6ILFh0tG|ILQt|CE4?#_dc3OU&Yc*J!&Ck zqI($@B*eq~(nPrHQv=g{ym0CEaL%Va72S$Tu=M3bW_9H;U$p8LGhGqFD#W~IcDFIC znQ6pkI=X@5gQwi0nJF0F*-dMYAExU=tpr0hpH3e;OjAz%sQ!uWIJKyTGe z;FAYKdP_URcOHlDchjIst1r_lP2$=w-{58Ml%U_()lfLOKbY#e!F4e!^Wbq5=rk6= zs}rJwasF`F*kcdwEdD8p{WO~lH{Oy3Ui*We)^&@W_-L+I&V6uvUd9v}rC>C3IM^Kc z1)CJj*d8vGrmjCv?>1i(hVOP2MtkSci>6jGym>~Fu6KZjJr>>LvTYLmn%;2ch&x=0 zzYA~R3H-b{9Q;z(vFv5ppl4bOV<%X{pwrJ~85th9Wt1g^%bkV&K5wAMKZ5>$6X95C z0`&PaPLjR+G#|8ev6v@}m%SLPh-I(FGL3USVEbAj!LurS~Z zMyZiU-e@{*!30sDEVvXBU71itS1r!*u61H2d(&&$Q{&CQm~sa8iD!is=YPVdA}c-CFE>3^TY^SNSt8Is2%X=NzcCd~8`?7}Z z_W+6J&Bxdr9?xt0c`+AP73SyR%0>=&1Lm4Ourc^COnm4}de4tBCA%Foav{(oPDbZU z9RyX!%k;5li=Yx~kBb_%v-xRt?Cy#|w9Dos9zNM$WG7vK?WKlrtp1_Mr0t@oHh*bC z@>Rafygx{zf56d`buew!NVeqTCER1W6>FQPu~d=a6q2+WjN{gV{Fg);kUf@{pLz>F zs}7R6>YCwnRb$j|yTeXZnZkx7efD2eA*<>$g60i0p{3*0DPx(f(0lndir+Agu8nXd zYoq=2aJUmC-WW-@ZBFz0*F)&b*1L3IPbVepx28E-cBnuqbiV5p-BW1Co|j&bV?{Eb z(WFWtB}+k8-3u3{rjy#_YLOWr?lo4vWG5~5FncUwv3urY^vG-$+t-yjm+Ve+It;cf2UX2@I%T>a&s^R?QOknxnblglC48h;LL;$Ha(H=C zI3;N;A7|2D*l^AEo0|l$uA<&qDlky zCeXaDyCAgEjWAojP;)?d8_+fbhOBA}%tj{eN;9?82#v4NV zff4NC>6>`vac{Kf>flT+>#&KRLPeJLTI_S-8>wxpC#B9p>=In5M{y9pxn!}}?Wv{M zy-I|2dE{r}K|iYF>2K;y5|(bj6AMnF6iKJz8-yxA1bJ8bRPwJh3X6~1*_OW8pw^rVk7;h;7Oxo3`_ zHw^@*XbWhqmu6+(lC3B%~}h$kWc( zVJP=>RujI>d0Lv%Z!8%mf5GR!>}b`iKV-eOjTTHCN@4p%-mKYtycw6mP0!C_)|J6f z^Q;VZj_W6~m8VDt=-a{h&ICAm`wa}(cNxrv+`*9ck2GmP4chkn2+>2W;7CS2=I)JT z6}`IHIs-*;ohr^k|HXpCgoDsSA(qv4D6*61#$&npQ_g!`2mfhmQOV;AS+aoIKo+(7 z6S`NtA+6~tq&Q?9zBVzY)gPB(g7N_Bvs#ZV9vV}>ON;qwN;7F_;w7}`Gn*B*y=9*p zqRI09DvHpsq7oMaN-An1Te&>S<_6OMdPIHtt;ZwWbCG?LK>bQYpO@zzc0x{^m&%E* z)ea8y(mP<{@in~Kx_vCz!Hl)s+Q;chtg-Hmzij*a9!yhD#uoK^Bhxl4CgqNW)cavN z?Nm91!J{Y9m?ke8x=w8A$5i1z)8EKFa>DJZzuB6B6)-5ef_lC=&rj*_2km+)0tvGf6+B6Wv(|y$^xD5fkJXGw)pEHHse7U zKXpS9o9ZeL+Cx@wnR%f+|NAOiEq8$p6D83AE`%JEb~#72W!Jlk##TL zKe}uvBgvO8ydFmWQ!J?O&Iqh`-$h${j>dHF-RK{+A5&^3;Kbenw<-N4|4zA>|L%An zG&wnD@mJ(To*9n;SLE@P|5A)<7(=-W4~ki^Ytm&iGo{1NKBY{z33S#?n{q6^Qe2rF z%?jv&6>*<%i`{RoBCLe}xWS$CeSC_G{x-2Ryrh*Eu3O@ zDo2+3I-D74rBbAklyYq9>DwTCI+wPLLT}DwPt&KcAN|##Qr`-$^*s#NXDf+}R$Vw; z(+n>QG9abw796w`GphGjvg5;dgZ#)->@@R$KK+znqPx{c%5)c33nO>U z5%kCA3S-LdlQduxmG^5F@0}(*QH@?X9#!H(Uz?ZO?J?}%%z>}4k# zj)KC!U{pNK!?Jy=B|5d9EI#4^Soe94MK?2v8@?Y~2NZGdqhn!(VG8@Arv*XsIS{s^ z0lZeF!_E{n7W%4-^W3o;io@qap5iIia&Ib)H~ou^-p?sUyrbtv?WOp8*@DrOF@nLs zTNE&BC*_2$B+uiOpt@u`s3&e^+}&Hu(zOOA#N@(~mk*%Ww+GakZh{4Y2O&*wC~UoW z8q`k=gSlQBU@4vt5~rDQ7oHWf6Dbz#w79#seBi+@zA=Dt{Vy_~v~O@-c?YYzLa1qG z!HW9Mktz&~gfibRaApk$_0NML$aorzdl16!Ty!5(SLx7Pk9oqppSFU1zkcL9aWD1Q z%TsvSRGNuBS^L{Gl7BNF)B;DqsaZ$a`?oIaDW?oAk9?%^zv|$Mwkgy%t%6OQ2KQ>% z4VE`?g6O4lV<`<&x#GMLsQYpk`{r^5d=@W-8TV~ivV#JgeqYSOmcC<)2AzPC7fui* zy5lOZ=u3Mp@PtGiT~w)crn*&sC};dxe1G&Fh7Gt&H#hXAn$j53`~6#FX*?0ny(JiE zSws^DH{gH%Pe5&~K3w<^A-U5#M6%F*Ak;@3geOr;A>OnOB9eZvfBhU-;-wOB>;m@X zb)((34VQ3Ycpd0e>2pK0{&Kq%jG(tdKQK5K2TMab*{v!A$PD@^HIQzS+HN@{oiMCG zYGnM4v~(t8fY}FHHgW=nj;TkhRQX>YnHU%1uLBv4FQ>2U>6<%NB7mSr*YdQ zNglG&cg8ZNuGt3~rX}XB?n`xN_`@q! z1+qZ{!67|Zs+3g%o%?d3rQ|5^Q6bP6r@_fgm$2X3Gue%|H(kfzf-{1XN{>-n8(^SC$sNp0ptBvL0huGPKsIoU14#sx5ybT z47P!cxq~3P_6uu0sLI_s_X{6Ap2`-A=aB)Gm1tnRgMCcs$^LXC!K&RKBso8U2A6E6 zLE2~VbNxni+dGxzoEpYgFEWyO)PH7;`YTvT;2>0WdCpf)I*exLx8UyM=493}itHb` z)9HpI)U(%RO8e%3X||%n+QOY({XB+?-f7ORsdtg##7ZexBZZe@`zx z?$KcPRvN$1j${2c@o6-NB_8Z1n@SZ*wo|9t@a=;AOh+MLfs-(_Pac`yIUwz2Rs{9F z_hE~;i_SC(LXV5vY0g}I+G=rud`>))RNt1PzS2H?{`gR;^o%4=#c`yWEsqD^+LO|m zUnF-&q?uJEN^TzCf@@~1#gsGExWs8X8UCF>b9Eib&@P&+x18kfZujBCtJd=~b|mv3 ziXTd~xG(IejwcMWh@g3Uu24F*)78J(!nE>sVaF3yVRqnh7HxSKPL7-l&IRwpo!A>X zGPwyy-&w{w7Otdyq0?aHdy#46)d0JOG_#F8i)l~~15Rsag!mmSMmN4%)?_rDpVZyR z4bw9ri=qA)HE|6_&8{WW^2cO|? zi&ZqX(TbT@>SFQ{FDzF$LYAtLGKuCgWwWp_I)sjUG*H-pzxeE3Bb}+AP8;S7fO?sdbhBDNc(Hyc1Uz~N$MTDTiSO7` zR}lU!cgFKZhED9RcQfyE zSX~w`=f*0`N=VZN9B8OpkDH&XQp>3Abit&IFHZQz z;=XIq(%;dP_iZ8@-c-wL#JR!*gTe5k;hWU>ik2*Q@-a!W$8UHR`5$PztYi}~eid_{Pb5N5cRh@+?jqkdYkFpr zPl?n2!6~y$Xk73GN}PJi$S@Hc@0!E9Nw>fu;~^~`@tgR?hlGAV9N?yk3xv+u%r>WW zs1$sbGsWs$^Tpf+r>+EwcXQ)~e0YkSh!_%)0!-_R?*n!dI*!Iwn z+23r&MAbmj4sOSoQJ=}pIUk!#8nJJ`NbbhaKR9Z74SqUO%KV(dC}s!HPSJxTm%ANC zo$JLs9Cb-`oC!RAX9-WdbXolxA1Z687936}3X>DVXu-%1>9oghz{v0fmg-!^j1z7k z2Qx|0{}#Ee&p(#NYTQZb(#rji^w4o;nIQtHm?WJkfu0Di%(T zZ{?p(=n{IaJ|rx5bQIiF)TpL+BU`ZRKh_Y}m-<#dA&@&rB}uCHQO-9D-};VMxw8)A>P^0kC$9Oh>4s)E{9tEA=2G?GY^0EJnkp2e_ry zVt(dLsW5%kQp$4pLV+9IX_C%ain0kJ=XC+(qcWS0J+36BlF|I@WFBp%DD?B|g%yWW$-mu=eq8hx zQ_3kc&gKN=^>?GmFXq#*E7|P)zyXx>RguEy#A9Eb;k2@N04el1$NmQHXW#UV*q<%^ z2u%0krn6sgv6qaWF?Tv!-$(2KdGEr=(L8=B8&4X?gE7Xl8c$@pkm}N{tR>(V3wZ57 z_IZ7nv2F>w))7&pT1c65%_;8$i8EV4H)?K5B++AQ6*hSV! zHE4QwG_{K}yp*~x6nbG-bYntfDZK7Pi81^|p%*Gkq zrZ)K?L8pX2%~Kc zseS%r!N75XU{L>5{AIHSuzJ(9!hAeV>o5#^r6V_#uRd9A*+~X!;JKz z_&WnKG4{L{9Q)qQE(gzLEi;Sl@;_^_$8v_yJL(1tA5@NYXZKKf%ucwVWCWW{Wh`!B zI{0|bB8ZU3DE(bHR_s1_rt@NV^aEJ#@Z}qu=TVGtD%oAmq)+@;!O-ESpqu_v7?GMN z7$~5iY*<6rT@KR`F?(bA?KPcA%n;`eWwgOMoXXQRX;A%f{@Xr1RG!|GdhI*J((aw) z+vN{&w-1`}!zMFWb7mW}Gpd8lu7f$}x+um>pJPlpvY&&7gVC;4@cE$FB_F+v(U?o% z;#3F+O@DorDEn<@1qvqCcgV*S=+h)Cf>kr z5e1AJ6bWIzQl?(*#U2C}Lh#O6utT#KOllQ5_Tv3r6@E$DA~hl6qaMpNnF%Sn{ovg! z3E&k0qIdpc(cD>BzU~Cgc~wT`W5xg{O2I=w$SXgw9#%YUw&cRtQaM>ByxcDEW^ooV4R&jKr zD;L6sJF`den`rCg@1)q z{{!7^Ui`%Uc3|_>os03mOg&ylvyC60!lF)dzS+45!cWbE(p6(1f6*Ga{B8e-B?@^RCyMA)C!HmI=-6FXDy$rZ1^iOls_jMLp5JJ~U1RhuFW|lGT-b9c0neM-Y-njTp0~5XiK~aR67@Nf4Pja6 zSCmHn!Wv0}oGrlD4(c)b_SuJe;!6j{9X z+rNSCzN0X8_$c0Yp$d0*rxod;A{X-Q0#|9jnI$=|hNS+H><5OX=iuN6(D#kGMjj1d_$$0xYJ^7IR&UcyQij_GC!##Jz2?JQ}Z-p!DB z`xxAfTEQ9@xG;WLmZWm)1}^wwPmDd1gYFK%Vtf-{{kvJ@>QCfzB$3Emqw)Tl zPmOlU78~#E4G2w%hl|- zm=C(=avf)M3Y^OP1zhx`gS5C(6EDR|$-LAzY+|@U< zqHYRJK9Eg+OQvy+LJ&*pzQUg|SE74w-;%H47923?CQGHWZ0yDvtlBvXZJi3)!~RBW zrAkl9uA7de%SlOb3`hQd2?8j~??e+di!lrN@qfeWXn|J)`G@`!mK7YK z=Z>7P^qm5IcheTE!>VXd>?8{CETs2;e^9{*KME~!pwFu`$*Ob@jbH0SVbv0`yVM8o z4*rGD9=*rByiGEN?9I6LsW0igtHjlyB>A+ao5jueTAGt>gMaQWVCJ1mNdDJp=F{g0 z`0i+C#mP-jwmla1dF%q~8POzG1$b-2UX*wFIl4@-!S@E$ct#RP_3`EOVDDvN)})() z!oUd1;(H5n{$c#Og_VM}hoN9r>qgaUK8jhzZFE)a;vIMFNf+~dvCE=?OxES0&$Uk5 zGka5HwW^-Dc)FLY?@A-yclSN4kPDYY%`hnq8clfArhtEu{){i!HC+c4qEA1|KVDQs`MV zL8Vw*&>c2QP=7m5FxsI>x8|Rx+PjNsV=E(%CV_Ms7w~tM^~9SS97t(&C-vTX9tYHH zlRZl4FM0ztkk_@5iLQ22yg)tXc(U_B=p+TPL!-Q_X!ixJ*0;<9Wgc3b~X|vmX_bbig4LUO%UG$;0^eKx>?CC-!v}Eb+{klpNA+DynC))zG2c3|!wJv^3N!9v^8r7|AqGMCuQ{u;Lih0nB z%!+)mrs+4A!)c;#n7Ya=Zlt zzTQHgvf0#jf1ic6e#-p6ygXiKv=l!SU#7wLD==cC9?jZNj$g7mDavji)rH-mfDmoEUGzfe zmw$m?jqgL(w0F?QhZm{gWVJ9-y_s(o`x6IyKV!nBWcD`aCiH*^(G%C7w~jOu_mPJ5 z#{VAQZP)=z4Nijmy)4XlrOEmgKaxGpUQ7YsoAK-T(^O)=pN*`&f$haTx#h2<+*^lm zT=!)&3#iWGLTvVuyy$ZMm+ub zI=3jlRdV^d1zv^QG;UlVewbl`w+0_26th>?bJozMSZ$H1pF{;kPlcY2!-XB~#{@Ia z3ZYm1ETKSQEuE{>bTlSz6M8uMy$YFe}}@yIs32=$!)fTpML3XuV?)(aNDq}6)9V^f0Twjd(V|1{l zyB5>O%aywL0!CAd6>eVljTyUUO%K0v&l}xo;fp@%kTvqJDy*E(CRp&S2NHGg|L+33s z`rU)C^m3u4i#q7or_&;*;xo>l`xXXfT#+ivWkAco!%~y^@8JH6tr&E;jlVKHhz=k9 zDyWHh_cz9^#EUa_rDsc^PAw9uEIXt_)N-T?M)B}3ss{+G84Lay0V-OLz%go%=+I`Y zSmb41QSHrAwv53e12SZ3H>RNeRCkthu!n50(MWu7U^E|c_%q)3UMpI>D#W~-AsQ^m z74uG`Q7dXWL_Q3M6*YH6_E-S4wKzzv&YqDD+W3o2u~QV_HoKSRv_+X zmj5agdltF)bfg+`yI=Fc84eQve;TNsyMw0}xp*!u8BCVUgEf<`fSh{#n4=wmuCX!rwBkF~y*I$Wt;M)<<8v7EN9?{eRkC&8^_iFW8!Y;#2fa04vaCT` zu&z7_nwsZ9P5d|L96JHOY&<}=rLE8}TA%aoOk~^jvq)7o874PcLYBdCW#DObB= z)cl(ioIYPxexi{qcc^f&%d=U+bCCx&C=AkUUcdy;LYDpKKhD=Poz)fe;kug+VpgU; zMl@S+Gs=^&tYR1Hs(!$QE3@!|gA_Dc`=YJ)UCgev0lj`F*|~x|mXDs?jVm6qS=*k% z?rddf?j*kdv^*|oF9%7s-f-aeDzU55n+BTCK$Q_OzV5i4)^xz6QD?gEZbF9#M zsGH0-ISC`H596};#~EB&1Lo(~b7@1jg4xo|IP`NMav!#!OIevjPR9$?Eu>u2uwR_b zQ5#&>R>ya=f5x-#cQGrEC3IN9f<~RG<%PN}62o5y*~$lhn04L@jLDggA&Z@)D!C5u zaMLAF7-7hU=f{Ha!Z=D%JVc{Xb4j}~hIQ@;7W(Vd3NAt2Bm}-gn;&`jH(wqG&Kb`+ zh`Y$N7dH57_$PJ;By7d``Rs%FTGqLADDT`^gR5fAFsdS!`6~Uv^XpDx@!?CjL4G)m z8hlA)yg$Z^*YAllsWljc%{Y3HAD*)f0jbk4uz6pIw+;1Sn%E1OSSAmSlF49tQw6*J zr|7%`a(dq|uAyO+mWC9iL`z2NJ6QP=evxg zl=Yg}@6bRNnf6zRu&)vxy%>fzUkb2IZ7)gu1$OmVIQJYw1j9G3EJIgUxEPU-cDsfM ziNPCj(>JxUqi$ZPE4z+g-Yb&!#`C0iwt+TSsF%5xPewVPH*Ea!B&;?2hsB34Q2fgb zP>Jy%%hQ3>Ru(W}_xI62^az;qKx%(7ss1^4!DGea8kCJ8>iyg-^mh zdoqRFmiO_P!&}^9O|)U)DDtUx#B7I)cx8)q*)8!N?O(Ky9+e$Ah$TgQ65U)YYM zm4q+sm*G<_YoX|HZrSJgE!?YBk?1)i3-xBjNG#yXls!9#0)5`&kw1A5^tcNy4?YFy zD^qCpq+9q=X$I>EIgINS>Y?xB$vAA(7EF!21j|?|Xm@`MP0D>?pnEgs4rriMonAD2 zuM&<5xLejeK8s~)H{u8lhVjei3U~WxGIp&7&sXf1nWW3gBwJ-!)y}?b{q=n6=D)J+ z*um52+o1{CcYX*r=bDtM+1{niV+P{x+DDkXc^<7#QkD7uDZo87$8mvsL3R3G+3k5& z5If5djxCP?^HsCKS<{uXPP|Pa3kpCl^Eh~n4Q7wi3~+Ikuf+ZDNt@n8;>iCxSxBHV zmp`E`QHvfkG2!++zLN-%<%)h4j7K|+YaN4fU(?zuRGChWf9ByX~uR% zZNqxif1IV(SRv#@CWNg|X2&c4V5-7aPB&u?2$f%9W%^LOELTM%CvIlPFKWWO(hJZq zvk7+RjprIV{*kxN4f47>4W8#X!wBZewjH&lp%SCzO5H8)`@o;vC4)=s)#{h*MqUv+ zg&l6g;6&9JcK56^OJB8}O}W;>E{tSg(WsB!ca=CH>?BL> zO2$*ljEf$5i+StK!LR<1fRAx zyc-*VD!)FF`9oWfwhcgBJBwGd4}&hDjyDc`2|-?~!DHziO6v84RFB`JVNb>hK3DW1 zNS-lvMh&8u-h!%D1z6yjEqS;^jCvJ{HlIRR+S0FBe0~X+X>7u653OKhCf<{s3zp_Y zVe2vU%Lvl8n1|MW8KiWSr!9##6thdey_!-J9Ug)S3JOc?OfNbSta9TFV{zZ6d34Y-De>rqN~1W#}6cNF%Z@v4hQ{ zAYz>lL);&o1M!rNA!rJH)B;R(Pkyj_n@rB)J0i zk=p1>$j-<}%w?l_-3$J_^2#*W>-!y&+(T);ax?v05`c-5dt=M7BhdGZ5g1KMWzGMM zW+yt%q089&sOWw{%CNi8c5NNXe+%SZeN8Lt%rIejlcwM$IVl(Y@Bmsh^ro6qtt#QDB+HDCcpfd2JF~H`3kpb zTFXkBSb2|qFdPF-%ML*2`kkmVzXN8>FM=GYYjMg};-Vy<;c66Rv`*p|e2plj?fYfS z@S7{-yx0hT7PYf7?kpyHK4%Y>yGyL`r|f7zt<)Ds?!bC)?yCE1QfWySiW)OG*MyTo z*6-u^Z%ik%RW-+91qTos=VQyyIy$#4fIPZY(JuJ_GE4HH$8%Na^-cvLTJ<$)M9%`r zmkkwrOrW;U9o}TkVwm?}6g<%5V96m3cvcw9Oa5@Ezb@r(%CWrZrSGh%G@A)dE1|UD z4+L%07UFEZY0=%!H2dWhG#{wIg$zhx^N+=0kNBmeU!sBkWe8-M{~dd8?7*Mu%Y`Dp z8#rjm2vX9`rv>}s@ynyfw0>d$%^6)lrPCBC`|NQ1;Qs|oLroyMDG!_tZ$rc?6<+K3 zT2@$@2Gfs!gPa>bc~>+q#6n z{ogluGgAY8N@@acYy1LajblI%Ysj$R9W%Xej7LW|ipm$3g5K8vT$X*2x*wZ>kAkZ) zW#~|LP1gbMggM}oC6#EG_nREsvM^pplpX37LHa}PqN?#{%-Jf6vkrwpcRmRLBJS|U%dX1!EkT!MBYf@H*}af@;(Lrype7sKkV`s z$j-}yT}Jan`QZ-WH8~F_-7b+;hI!KX2m3``?I?&GHIxQ@x4}@oSQ_`wify~vmu1y7 z<8QU~WFH+y?(dG1V^}c8i6^Poq#0=9e}JtR6^~A9l&PELXnb&O3X48q4bgXA!QfGi zOuXA4{PT8#_VtZ0x8gMD^-YC*`(aS??*n|ae*{f8N_Z{FCHlrO1ODs-h$!t3YAdtw zwSo%_E4U0U4%?wW9AR3&t7+Y^&unhHB5A62$HXD7*q{ZR;m*%oY0^ZzbAKHjE2zPk z9^G-~(?+&?rzg8qvRv{^EG9MSo;|ncv9fPb+9dbNj2*1a0*ga0z~!9}ulC_6gm*4v zzjYg6=lxW`!gaj8+8EXz;4Em&{VlT(%ZJ(pU&y3K354p7XOF0scd^ny4-bE0 z<+C(6S9T3X{!_%CxQYgP%x7v%-clcS64*T(PnKHc?DN#aa8u^VJ9O^k&D*@8b$u7U zj_{>EbxYXuAPrK8l*bWEORzjspLX=IE&K31R~R`Xofg~d!c`rn_~+$27AKc0)TBNo z`DX^gLMacjocD(5`8u)xhFE~c+@AfW2wib2)H@kh{HJ(`QCF6^2V(@AwqjD;$S73d~-jY zf1kj`FZTz(XrV0hnH_!`6(xK+z?18l*_ib-faAuh%Np+MqYByP*;!+g)CM));e*D;M=W8cpudc>S4}g!Uqet z0-|Zy=YOPXE&1?Xn9z=0!?4GGN7|IzlWsM1;MtfJbokCNv8VZZ+VXb+eZO*3)XVo0 zdo0>5G4Eo;spqzcasIiIUt9r)<`hCvQ#RIhPRFlXI;HQG0_$7<8y{)}aDfJeOnWh- zo^QQDHD{fWC+p4qH?+NM!BQ`L`e6q~m-k_#jMYi;w+*^j%$L1i_7k)B&B3Qn58~dg z!BqFoh1}4YZXY{|XNLJu(w0(+l2@j8e+{Vp$xu->LLHm?KBBsYzGAO?!Q!y2Y?@wj zSPZ|lNSt&;MKth3M)mzA&hjD9(%Z>A)Z_%}h=FCTVeCx&B3WUA40~%sDBJkbF}=XE%x7ExuGdX)LuJEff)VHay{{E_*bLmw4$jakqLplPNS}vC=E@^6pDnn%AiN3KNR!e;sE@{fY|}%c<%0 zS1Oa3EZt5p>L^}9O&TY~A;0A4^6tr^WtS%%5H&>g37O*PAuC0#)Hc!e*CQ$8^I*3o zdCCQ(mE7&a9SX$Ej1*?Zf3SB-OSoZM~ zlW(oUoQXs`@->9kH!&1-_6Xf~0s45;UsUQ@A*xix(=Q`sQ89Oq2*F9BYS~(GM8$pj z`n-zX%`B&~$&2Z5cqUy5iU5ky^x+8JxR6yb+$jEnq54Q#|^BJD(2h#@#V)g zv|+TAP0#6ct-_OXM@sps+deuYb&D?Nd=Z^~Jm;=&{*O+#y`l%7B}a|D z2dCw-pQVJ&2q=A7;mffnIGnyYS8&_gd}1gKd9tmd_&g(Dx{JXTw_|Xs#0G40L6>z2r#! z|J>wtk@RX{OIiByF<6;0g#OdGLG$}bI~YN0=S`Jw>jI1c!>dkoH9$QfHY7T9R}5t6c|hlhvfVwjRWp zy@%P}JebX2!@@3<;V)sOY-XCA@bJ1n=aapdG}3)>@7RUR$1oG4BvybhbRi{fmw3Z( z)G)7m7cJY6NV{LyCNWZj=1`Ruf69y@e!}sGfP29uAdAf2{8)Wo#g$qr4atKs@T%b+M z>##N0UG{4GMj|pWcwwi*rr8hV0Ww)uiB@utIwqV zRdof`C2#0B1~Zp;<^ljiTglCbX&f!%bQMoxQQbN&9L z(x9m~$!=r-vly(+Y?f+~#kiw%(masu&&`CXA3IrnSpqm{-u46;JB7v*Qe5YrNekUb3Jp~40wNS3XCazAlwNX3mVJq!NU0}4L)os zeb&S9z`GN4wKkEay_$f|spD*_$YIk7_rZ!*pOhQgkzsjQb$FlKx)$6dF2kjCB@ z+$)W(Fte92Y{{{MWx1nanCe7{TfP*OO|v0kUI?o)7O>l=t#rhrl--_j1Xde#fY0F@ zu;cYX7xVTH{*wOD$rN7QK&My# zBCFV^)GK`?o8gekcHT>)^eJ1JLjNqb%BoJ5m|=(Y#wKX;;TNjB^JX>^R4~~`a*VGx z2j#glfrVGIo0(PkLg6FZZN7pX+LwyqQrGf(NilSPlnHmI>9T+EA)sgSgzuJq4Ekj0e(`^VqE{6~z26SFf?#s9XYuqoeEF=LW5y8HY<(`r+e zy4jQQRSR+V^$6T@{|5EE@(Q2JJlIyJkvMr#IX6VxgJpzYgzf_lK)1WI@%6UnFiYVg zSoqBplAFySw^VZ7>2yjAH6MC2@;iMJOPCPVTgcg|#ak7Gz%}WNG;#Sg_Mp2t&c5>- z`%K=#=Dyct^*Z-(PswX$IdmuJB&kcgo26`JVSldFr9bESyIxT284UVq?^&4k4Q#KR zO@>aBXk5Py!WE@)IP_Lu}x|YO?t`{j#g^}TSJIT43BLsbG0Y}3ME?dK!h1xuT zo3V4)=Fq>i=)Y@h#l$|eEK&xkJ=}TK?v3zhtOJCtS`U$Ft7uP}l&i#eQ|_?^;MA4} zMq!7^|Ge~Yz7xwHntd<(q48Egc}2nT_cW%ikL)YED{EdpPj++&zDPPEF)pYc(d4tQ+RfR=oL^ke3Chq)k2}VA@46!>Wpt;Ur zTK!I9o35*Z>6y=9-@BP0E*^zBqcda+OZU*C^enO4vaPJlOdDPtULn1UZ)4Qa4Z^Vo zTZyNf3K3zkv?-?spI-qFRsj@~50`bJ0-mpGAmheV8dt}mP%xN{?zIv9 zy$jH)JVtU(8PdSBdFM%X)I0Nj}J!sU=#VNab;ln0r-rTzpC;j+AavgVR zM(+S=eLg6_kHniORbJv0f<}i-#w(9a*|8VE<@r z65Q~{hf+4yYPMjaCwYy)AEL+UvtFUS8LimNYyAns2jh>z`KCiKY1a{;*x{+f)`pG@PLTZiH<>(#=MH*NU3#vj*POPsY|OJpv)vxKfIh3t*~8CJ6K0PE%y#dVDx zDx90Djy3Q1aA`KBVAeSu(wptrk99S?(T)LdPvReR#n0l6*B#-F=01cUFGI-w;b>8F z;~bg_A*4K6nH6p0VU*i^*}bQq$tK{5^d7iN{)#&({o!qre{X|%LW$vV~N= z9Z2((4qn#pV4ECU*y>4*c)VZArKAhRLiC8?vXZWQm|_>o-1e2R*crd^#^VwA&M87R zU1JF<`CfoCej&Vayff7G9RM?ao+76OQ)z;=GdL{00ZQwt@!2j_iASy~nixx645PWU zRa2e}PCL;kwGgqFUx?Tzt2f=>mQ4p@Ka%?CDfqWb;%IGO&%DaqgdJn1;Jd=7+?j!j z>~O_Z-1+A{wmT}|%;uS>YWomiH|VCk_lig{uREjYU?Re^ekWKBdd_euV%xBLH2`ASRi_ z(r#5J+I?s-sS`@cg2k*SrVq2H& zWtlAzWhJ#i z^=$P~N4C)CC_wwQGOa%g*o}cLTvU`EnHPn!^TpEhP2wU?QrODEt%h;;?-;P<*RQdh zpqG+gPl;Ck?L|(Sdi1a4hS<%uMs$u^O;4{b64e8KQ^u;H^i;8uoF>V*gEwxF$Mb!7 z@%VHyd3}TqrCL!yb#>WpuZL)P_+IJFHFw#F8i}2@X~Rc}AA}ryZ1! zq(k`S$1Gw?9k=OBwy>Z#vg!?!_};E@ymyU^AElqcTQqEB-x8uA8)vyo}<(6F0RMQ|Oe*SQ=`|DHy*X_3Bl4Y3>R9Tl|s~Cf=96 zW6f+~U_P6oF^yfU?uL8v?=ZRe2C}&S1#h1!6B@tSS@An)`&9gDy-}!w(*p213)#ACU9l0zYZaZpa!P=RA2v5hdR?6RxivCEIJ!pH-G0 z$APhBENp$*wIqoeK~Lb;wYFo__O#?bJ*8IXLyC;D#(ishfi9w=*=E$e(p2HlJ$ z*H3vu?(}ygLo;d5Kb&-A5t6s!p->!cF1>zCKvCTguGN>bH+xgSdfq`{%){#}?ye>z zZz_}cVcXeUL5uId-<%Jze-9O+0i^u>2IJ4qg0$sEzOe$_HM zeLsmZliDcJuMaz76pB%y#@O(qniVPk1Kt^gxLkQ5yPMq7Y|B17INpymdd~9o=cxmn(H+-dT)x<dxolFlg2glYMxG>FoaxF zFOceb84b$~#y@A5;w7(5LfuaXjC|C^yn0Lm!^(NE@Mbf~)?5d1NjU57t&JK3*u+nIOZIZ*uQOt%vD z(x=^SSnxg^dZ|9=jZQ~FOKA*iDer{Xi|g6F-(K|m(-ZpV@dojs)Y-6Gf*KzV2@NqO zm^x)A$=e>Jkf+aSH~3S+z3UWH?}owp9(bT47_(}EX?6P=yyhrFyR{}EjNa z@R(Pdqrj&eHHP}pxiD|WX9f;m>3drerS!4FTTcEoNA)r34{pZ!w{BtMmP3@OC`ZrL zH&NWwFl^0#M8iuDWA6{kg`;Z4#QN9>yQ*_!dmR>I-;MvIzQ!CDckCa|OLfL=T2buL zURwyydIxb58>?S2g3=K;nz-)*c6!{TOV-(RCd5YU*=S2SJ^v$@hsyALo(vwc>-^Tg z6>y4D;rsh)i2Bk+&e~UKu#P-;O|}BRSepsBv59=e3*@Kxf-08h(3R|B+8$v>;NgVS ztp?5dYonaX7`#!egB2g7GtpINmh=7z1fIJ9s{1|h>WiBsn^Fm@R8~Nc(I`x5za|`t z>SXKdZKx|*q`!t;VD$PKdL)*RV~=9tT-0j_AH0v3r10J4}oSL{{Q zLv->!jEzIz!*sus>=4!Bh~P_rv8Aw9>S`Z0xCo~mbfC4Z96p5&BBz4cka5KeO;+@y z=134rbolhZo|(xBMJPjq5Y@J%rM#yPq+r)%;^UB>fw5OP*+*=|l@lUQ(QWf7&~yKYGt{m3rmLq7<9rtrmH5J+zd~ z-BAadzuF4DSN?*aKUQ4m;}YCcH%oAFG=`O;437Uk03-Jp!f9Ji2r!j)!ptwzM284` zIYPQWRCg!c#Y(iszmfj`yU+2BqM?UoVDLH*?q%gG6eV{-?`&@_C_+;(ux#gkaQC?Q zM@`as#Gd?HFH*hB0Nh^|NHte~;%Ord3LIrXy3u|tL3uZ88qkXdnvQ{S>@qA$n9khd zZ@~8C{&djSLt;Dm;k2@3NS`%B)*y7TbdP$8GqD0Dd%UA5VJ49ofyW(Lrpc|c&irj~ESwkZ z{u+v7%1LC0?os}9V|xDEiAGd9;_#GNFk-E;^UKyhAoo>?yA*!hdBJE6eCO(qUp}v+ zeL)%I)e?yltg=X_^o!6Ga#*N8&`HKC#^Sx;A{vphk8PT4%^5hHz+=+PvHRaS6nB0x zH`qB9FFSAL{YA&qbeYFEMY>=CWO*1lim6Frms(5j(oqpoMidi#Y6otv&U*q~M--)cX)9O?6=v zYF)yC$~yM(Oc3au+e44Lis?q5T-xJ#!r9}=U`Q~};&Sf$&;X@w^g6UJ6~wO=b-Zk- zL-Nu4{uf9cTjz;AEFa?NJwI^jqE>J_smd9Z2b1QCrm~RSXko?Pdc3WBnN4)mlcgu= zGXFwlq5Y(fEUrQe`*IF!_Dp+Wjb|;^N&9!xoF>w#-D)g7>=-*bJ-(wfzUX?=zdVEeVD3a(^iDlCLN)45DAZE>rTx z*W$|V4&o5nPrk}Vkm7P5p7-YA=H?3&zHb)k#01Ii_MAvQ88cDg?NV%CZAY0ScH=pF ze_H?gC@nv^8gu6cV7L;GE#g_^V_wUOPZySjj#XiPbxSa5x#YZd4`PcGrFP?ETP}G_ z3A;SS9Gh0}B+FTP6y$P?#?L&%u1%|<0Jpzn7k`t)yC5oEY^3{{GpVOdt*ExkS5z60 z%Ifc*lzn^llGn9%;s-rB1?z?%!ln}^xbCA4V_J$Ecjo*~Y!m{?B=xw^Jk}E%C+wH_ zgMdfPrqbR;zsO~}9SiU8B{b)B(hy&LtQvj?SNyvo(_0mbwl8;-x~s&?^5Yr%YhO%? z5;MK%q&c@WI)-b!)Wr_=DikiCwa1DVj22xfp$BV@P}-h1N^iZyaTrGnCobrfk`L%MFiq(M7gl9tR}_;cFuc`^HZHjv-2q%5dNCgE!%*0wem16>!H-AsDVOT zFS5?oftsy;j@_<`-o``J~t~FRMi_5#a6uVKf&c$h;=VGCQpnjCtuaVo7|f>%2^Myvn1DPzwdBw;Aq(U;0N6MIzeLRzXST(4$-fjK+iz(9v$&z zY5QNoICFI=kN1c5j-@R0`8X)Lco_QFEQe8&!+y^3rJy+3h{b;Or)66mXr3^Xx-YI} zpI4d6$^%0{NB1EmCf}um2+6m6%N(M82T+Cn4RPU3YZ|f~MDqnRanh<&KxM;VlIa%S zMgA8bP%(vfny{I-9+$%py>bw~4wdIsizmaEeuto8Z!frQWCW#~{y?R9&X-aKq|pvPCDDLu1+Dy*ApDyHuVXJz zUBn(SHg1u)$;*U()>)G2<}GxsUp>?Q7A(2VzVMav4{dAJqiW$Y8~MV#m4(Y|fWS+`=|>=24c%rr!I(Tn5L(cokE4=5q+# zB*wY&RW0C)kH}1od(nVZVTYd?-SGz;{#3Q`QB0YHHe;JO&`os36=1~1NnU~*u4t`ukh>Uv$83P(& zU}6ZE+$n%Dy_JRNAY<0BehBMav9dI3p(+0R%5biiw0Ef1$TFqpy*D=*EY29fHv1oN zaQSUnjYT1izWB!3z5hm*V5Ne80uBhV%2{y4eVXhX{1w&O<`MHpinFMqQw;;?o1BxV zx6wt^Yu2OI$3+yQ6<_*wt1|WT>tatlBEZl$1R}ClLDZq?;QYK^%I9{mt-XeTO6EM- zX3Ie6wdD@GwB#(PSbQy+7jzLRk6aseLY&8bKCtL)K}8?X@s)oH~%7jd11|UMXUtP<2T^AlwIUoMo{_esZt)@4#w*4qJsTXk=uP$9O#)% zp%oc)`Nw7o(=n$3R^RaSJYCtnJTvfYR)IcguUY-1ckHe17q;8YjMepQ1-+yttnJAY zP*Fb25*I#aC6$?2vB8J>HR)hP(`7Vje~Q);BUG)!9oD?7V#(%bS;IpsmQeGYd*2o! z@ez06x$T`4weu7W)%U`s@7H2=vBas|agyQ=NZ(5zJ&{}OE)EV|NX-|gN_|uvs(oZg z*GJtViQJ8w;{=db{LMJ#j00YF=Z;s;0}H1Yprf4xgYWurw;wy;4j)T4`&>3V^6naY zyP-kcdEqwA>gr2oKjb8y=Rm&S-F$wh{&_ynJA)^w`?BSp&bTI&OmV{Tk+V=mM?x3=j78%+Z%{kj8*}asK<)1KOuF5%54mqxPOUQxnKcG< z{8oTMA}35Z;lbQaoMZ|`?FhQb81?#9S;Nn0=GAl(BHp(P+vhe>V7D=Ja#|LZx2~s~ zZTqQv^;&#ien)zqJ;YyoeW{1n7m24a4)av@F+aCF?myLo+}*}PZkEyrFnw>wrdEf- zus+9G`c}rS|91~9Pvvpvk~Xk(mG4aZJl z&Ti%nq3T5?&5Axvni0WtaMA*@D$=1FbQ>`>hX2wd0&Jr&*9*!Np;#N3O_dFfTWID9=TF1^O4 zbZz8T*es=VuRQ8-U5e@40lc+iGab)=O7lA~`jM3U>uO!Uy*#C1{jz#s(+?U(c3K? zYrlMAu{RFE{wqqd)cG3RR?kCV7w!W8ZO?}I*#$8E(iN!Mmd{Vfe!=&$%4CJ=_gMD1 zc;Qw4AzIa`Mpp6n!8Lvb%Iueux#oNU9-NT;m*cTB$^&CdCbQO;Z7feE3~Qc_1v$sR z?3`5WtDO6s68RKLdbWi7(X9(Enj}J4?f`H+ZpkY5Za~X&?K z_gAzTA6tH=$ap8F9M%kNbN=wIPNR9VvyRYonemRt9pO`FFW9wh3%eKe7KZx{W!I;V zm)v~!Aq+%Rx!w)WK8xZ^Z&%WY?UAHE_X8W(;VvwRxh%YBaK_%#n%VQxNVYoD84D*! z_xu00V#kw9(61y4ieHbD?w~m^?bv8p%Q+)(_56X6fqg*n=O^%!{5ZZ;PD3V2ypC04 zDWlMeHZ+`q+42D>W(2TpBWxi5i8Zf$wtydZa}hK?D~6d%M!=)DdN`zO5_U{m3YV7k zgasbb^Vv`y|8z{E3B4*<_qIZ`)sMvGpJuUp_o9UMoyH8`*RtHgH12cwPyFLHl=eEE zgkEdb@^Ubr*Iug+h8I1^)bTuf6ETOm>mLXA2stun{YER-RpHAexl(2qFDhAo!au3e za4_dJc+V{2Y6czvoP7n7ZpgF8s~BAE`xy-R7ErTX!BUoKmoy~L!lmEt;`opk;8To~Um1n;DM zL(xN@3-~pKmUK^Kohk{i|EVPib{w12I-Rml&!#i0IIQ=ZPk0n*?z&54rt9JSs#KPLC;s)7F(WF-|GSg=zoh;pg6Q=AE4Q3lsTAypQ+Q^37Z->x>FK%?s^RsB8 zZG)c&{-Ya%%EbwpCZe&`6LEre25nsIPCc6p+` zsXLuF%@X%k52VDXHRz==oGwW$hP;JY_}to^y7$c{lihKo;l2xF67EsYwOV*TOY)`M zn}beLhvJ;xWjatG4II}8WB!ykqP}YowcaVBv?;;ZCv!R}-{xWZz8?^t_5>QUBf#9| z6iZj0Oy#EaqN2Uz7+o-1)EM1D2WB2+bsMHh-JTTQTCWVIeoI7UBR`#bJo&nft5R5thJ#GWbbJcF>_{iIpHnDk zbPheLbrIzzh0%kXYee0e2C7%Q&GbeL0VS6*?08!T6(`Ejcl&B^K63!RRlJ4hxh*s{ zyo|CvO(?myFO?XD(b@6VO=$p(6LOoYSTR?|jlj&daNxj6HK zl-nk5q^>NYoTT~SP(7XR(a-=>V$wh#3#dZN4*ylmrwsv;pP_C&Sx-7gUk*4^9ovLscN8jGj)e zM*7^ZduuV{&nY+?c9B?tL)_2DE!TUL;B@Eyy zmm3IC4K0HH?n>e8B#9v&(!i#99!D#kGRPnLsO;3#H865BgUa;%aP0VT(iF#_xojr) z>O?(gjky6HQEsr(^CoZhYcqSX=KyH8pJ4`poA{nCJ@`Q@XYd;~x9~mV`|v}Dy5pO= zRJK-oF2q!{!d_2Jx;31M!*BgZLvB2zD-lwT+B%JjK0c@80;wGY4^dC-j4rFN2Si>@6e}sY?d!JTBvcC5B|Z1->qoS zHlAd4mdxp2A8F@kCnW5z=fd3{f{n^?T>JJOB#l&LnU-71=B_^vRr-VB_QNDzslFZ> zb)sOder%in{DWZ=YoitlPQ%yN?dZ{IhYTJl6^E5fHga*=1*^KS-*YVR+S5z6R zz+Qem31tUwusLymaF@!bvdQ-~;n;UAC>}T%v?nz1hG(|&^YeSdr5(j&^qUhEd^<&_ z>r6~A*dwm!@}P^etMTeTDW`e&1U}d-G<@GI;j~3bNK!*PI4tuZ`_6#`kT>?89`*WE1!C9e#A~# ztfkpCEu#Fs+03E&I_JOaC|oIbfVAY(?BRNSUa8*?i2tw!ZclN7+4vs)2c8zSEN+N< z7JLvVe9ff`XBM%rx0_g>V3V>lON&@Rdj#a{o5L#lTF9(jMG9}R!F#5anA*Gt&+Xqt zuECFJX_ps`82wH7bMg+UO5Hcx=nEK^URic%*L+e)y-opr3WPoXW9YmeYI@^1-d>_z zQrc9MjHJ5f`5Z|@SrH8*WS5K(GFmjGiAvGXkXe+{JvbUaEhjJ$%6&h4OO2aYoX2Vaoi%YvN;?gexL_Jl=<|)6URbCb}*v z@Yu$Y`1wi+?=rp^2NWA1=c74(@wq`y%0}QTe_Jx3V+PMgz9C)ZE9w0+S`;75he;ow zp_UAXyN0%)+)!1VdU-sm>|cXwjVUD3B!T1|>8IlQjX3_}Fr4@}0-3cDxIE%B)i-z} z6Bd2GfX25|u{gCF zN9mmct4BdF;a3Ow!*-CX=iXwPk~18883u3fj;4L8fsnn;9yX-cQu(g&^!|ZAXytpH z79Q}Tzu#5U?<$slqO!3pzz94QX zW7Q;90{uLbbX`p)w%Sf0wPGQRag3)AhSakLZ;e??M;Gu3SWZTHJfkkPuW3j5E&9p4 zlKz;fMZ5ay==2;ZdZqa;P2ZhEYc1DN6(I!BKIIlF>-f>z1*Wj8aUFVX_)e-011r1Y z4oux#O!^Pzq5H2ueyFrQR=Ri6OOJ=sPrr`9@WErW=(|5$zM2e)xhc>@^;xec3_I?y z4m&<_F2q!Fuu*>}*>oiz?xlZ*ywr8DW9E4xSOrOc*a~jm#D%cW#g=wye;_5xe8g#n zH|V3+cZgg`vO~`O0D5}57j5h^LKO#&yZ&0Ao=^xP$5WbU+L8|tqrQoi^V*8imU1L> zr~&4g34T7uMl4n@q75gTY3q|=Ao@F&rg{i{T`IVmUVJ29ByX_tp7+@W5_YVj#XG|4 z&xhLii4bzO82*&I!G^{9@bdjnPe$M}ZE7Zl>$2ft@hZq&H^ABTSc@+wT0-@+L3WYkJvMAv3+q++nDq<@VO<6Opv?1H zGRZ+1u6cNXW~(LK66UWu{S0ZIE*9xnk0Xt9rjmQxh40w*!$j)ya88>%rDh|a68o_& zG}lvw7LC+!{4Ux;%UN~$WZod1o1qKSHf$mDeohpt8SSA%d`G}R9gbdIkw`D8E@JxJ zgUB!Gqm0gqesc52N|a@8KzgBY7B4JiJ--Vb<*b{mv*Aa$d$tKGz@%-ZPpURCq|wt@%YVKl+f$KRski>JI|9>S5T(6#9E_ z4t>(KoHljc#~E|y;FNF|*!?aQ)jI9a=7G?cdnJkEp5%#|HZ{O3tA`MA*8yg~ZNQlH zg}8LVHJB=;0dlTdux52Fd2fjqa0EV~-HE%Ng{{y^ZA7?*Hh@^DDTu+B?e}Zmx<+w{} zF9|n@hO2fuos?hBh1xdb$T)gy&|}${qAgco+HlXae_B;0c^u6OE&`*@F5fO=A9gBdLED zO1jeiasd_bjM(-M-KzhJH0#tbl`q#5*>_X8-UF&+oNNLCcb0y44nfu1gG_N*6-lw~ zgfQu2f>*s0#yJy=z9}U(3V>YxQ7=>h~7j*@+U zx=5YHO1kgcXUC-WFEq+69W~as6WJ~5jBH>dXWlwen58BPpYaWrS_!?Q+Tp~c`Vy31 zYlFaeFIb&f17BO-LD8QH@FZ?I9_+>bkPrH?<$0vJO4rK+}@g(N1u}`%9h+e zNf}UoUMTiGuESj2Q%y3vp1|}9JCK)^72kQO#oX&%#Z2jlpjD?#$^0vIWc=by%xKX{ z%9;9$;?F7aKMKux@jz9rbaoN{dD_%kJ!O_xS?vhXijN?4ks(~N7ztIPA28wcF=A0x zLXJA6kw;+0S-nf4v4(y0#6))*zj8Y{t@fTawjZF*6B(+$=ppDwtp};dY=~Ji9x}Ie z!NKX-P<^vOn3wcJ(VjJ+2gAYW$sN?y*v~}yHQ|WIx3Ql8g(W-6FvZ%2mvoQAbBV8b zTi(QyF}_}s;ZYzMEE5sY`Nl<8TTjS)LE(ScZc<_E9)ErZwD(T^2dukq5q z!?0|QGLzYnfd~Brp04`}+F3ZCq#k}ti(QQAjr}d;#FjW}Y4(}v>p#Q2d3Bz)x(_hI zW1P$Vx`5l&Blu#2guLw1B|t_B-A|YU2W3@ZZ%`(DsdxteJ_2z{QCIw@33RtG2Zrp5I&o@ACGtLkzoFg|HBTWn01;U4O|c zsOz(ew>8-z!Qn8~r-M`vI}K?z23WY^BDR~3$0Wn4c_hv{>{Z- zoJHa*&oO@$oZ+-p5IicahcC+|S&j3n;d<9t=!qh%asO7<*!(Q(IJ%3SP%{mF&3y`e zs@~8Pn#yixj9B^McOkt`8~w^Q(NN|+y?SIS+K!3DkE$VjRlt2dwMmhm5@v>nTqa|% zO*`(W6_Lp{LF7|aGVE~?x(TmKP~*)HbTgbrQ<4Yi?a&eQ%!2PE=I}gXKlLzJo@Gdk z#XJZM83Si0$phQc3OzzM&Pn<%6sHEk&hrOY{VFSV>9%R?zU#lCP{IwW3?<;lum4yl z`w6U#Llv~U&WA%1Qgrm(tLR%$$V+@WOruBX^Zv~{`3PQ%AE&hdOPz0szAf4VCw)?d zEU^;#P@4qwiw2otSqLs;eL2R~h%tR$&&BzsF)Q90GkSB|$?AihV|*V1vc-EokN zGfc#su8ElRRD{L-HuOS6GfqqWgMZ&I=0PhAH?Nxryl5Ufge*eSMS0}xB6%<`%7O-? zD)?~eG#Q!VO#}1BapC_2HjTWMc==J`&aYL@Z98TU9#QLn4W-0xycU?Ih#~r!H$-Ob zAy;h5;WSjT<0l$H-@5^U!+C`r9jyUp!m^?1LnU=PGZ9a}+)ew>eZw*p6BGqrL5)r$ zyqzm_&aPj>A2Rc?DB6iEotiF|Iatn4?`K*0^TN#dZaeeAup9QeZD7@ddfVAgD)I8?Exd`v_=Wywj`xtxjAFh^|iQ9WBncWQ>7KYg2y^LghQ!jy=I4fde?@mtUTxB)< z<}z=GrLnTMuUXmi&Csvr4wEb9!S9R;IN9<=$iY9S6D&7?UHVfXJ6;pb!Y?3mRPeiY zdXoKPD?zI9w`gB|H+j|~qF$xiBD&%@Gp%_P^Pc^NBZCFEZ|Py?`0*lUFWZiWlkd^I zFe`jo_LJV4mBGvFycAfQmh|>vPkb9*$LrWvC>EdPhLK+Dlf=>i99;7Svc1j&|9zr%;ohRx#mD- zyVhiGictuYbZ;*+^p7qPlmxUP@H6Vl>u~Ma7PL-yi>lKB_q3kCZJas2PLU)= zw$0eMxgAlhj92Z@#MfV*(v$BIik5tW`+HKL)o?TGvM-X|sQ!m_+ieN&61$*Kd5|lZ zH5*LS%2{jJ22n$=vnowbpm3m@oL)GRsZ$Rm4mO2ED$5bn1)k^b%h$p1-6Tf7Xe2F@ z*+>iS7@?%k6KmJd#jc4K>lC+(fXH{kTY<6IqCy`II)pd~#IY&xA+Y1hsStm3b&tS#e*1{zw0X2Bxu5vnQ-wQj z)?i@o0JT~kpsVC+aSI|p&voLLHC0;pq8Z$Qj z<0Zz$;{&q=7-fEv#$Q>6RU6Fl$!vYxzRM03-wPbBl#Tbe&Nfw_l|hvsFK%=F(g&50dA#rg-8FPjfot$_Q9wVnIIqx=D6llf<=---PaK zym%*1NK)W6y3~;cW4GnJtcnMwmmcTMbQtVW>!qK*xiR$FTsqs|nP_ELl87u-=4_8F zsA-gN4a@`@Z3^N{X=nO7YBW9Z!50asM;)}ssv0X^O5qwmLg@^-otVC;AeCf zcBCb5+mM(G%!fZ-_@Tg^eq0>K6>swfyI)P{)U3r1>3o16l9cgF&J21`cQPK_>Bo%u zk%hbS+^}NlExfE_gSnmxyl$@}=06wu62*OJH#vly^ZCTpX?IhDOQ$%iJF?>Ahu<@4 z+vd~Qq`74B;rZx&M;n(N7{oIl-eODePhRJ5EpL9m3_r{exFOQ#h5X2KOb$EFD;rjxdZJ!(Vk9@YW*>-t0>!MjzTtGd2~_ zYl~(v+9x{D?Wi^Ps&Wd>t=2;!sLq@8g`sB4Lz*ZNN($2LVA~!WNc$yEo$cpf`jR=Q zylDe{5wHc%FA+Ezd;de5sP%NypufPO&?e<|LI>+@6;YS6Kt0F1MAF=Wd3ZOB_VpzQ zeS{W_ljrdC^e_17mnsUTDikI@;$xm|qgMHN>c~@>eetg=Gb1h?_$YBU2UlQra zHKQ=>`)5=h{Rz*H2*jI=_CIlE z%@<7iX-cD~eI~Ih9YH>-jRxKx!zJ{m(|&6sk=N`eOl!;@q85^j8?q=aE)2nIi9uMR zF;rmicwqLKx2QJ$J-*WR^H7PDt}T1L0kgkh*anoa^Wyg}ISXXY!BmhMVZZs?{V;%ZQ8# z-$xxk_b}Ndl?qxE#0#xYSfdyu6n6fF3(YT(@-BQ&e;SU=~edtc?wfIXsx{^^x(CiqmUW}J! zJjK|Gaa`@_DtNHdif%aLK`dpJ;bPSYh+Mp#yC8E1OwvA(77s=6n`#1C$JT=Et|UgW zvzl&im`c{{)um5H2n^9oC+1A7T+P49st7}co#X3M7&xL5eR3|L>zqkAb(1vM*#5Nb*;rIjZA8#eEp=5dQALh3+)v&IaME z1xR&EBFQTxnH`xwA%2AtxjJ(ynbv1YdgIeUdxkIheboY@zIDU82eV1Cqu~D7lu6E= z%Ok52cpBw*g?i4ip*c6iC}HJ{v*Nq4s&6Fycw`P&^xlLVFx^fH_U{4XZ`tTtO2|UR zNwj60A~~h1LeCpj;py~BtPVN>T?N|kd1F3_SW-ew?Z#nSyznj;eFtEU_*$oL%tq(KL<1bzCLl80~8U{cQ>k_j%TJVO(eRTw-F>5GbIP7>c~ z2WgK?H)y7g0+sqk(6{u5e&3;xH1OPUo|ZReY&wo9hBH9^teDie36>Wu zro7cRhWB?Dlt>Cvvng;?s-f0Pm*T-JL5@IE;-SiZx@PMjpm6? z@7a#?pV#3Ln+rJ1Es_jTui&=K75eBGZAqwXq&Q07l)NW`-$KP#;PmVw?{7RLKgzAR z`sz9%CshJNWvyWOq%!hyV=nzyAvlKK$?>iWG-$*n!H=f@3l+XiA>%b~h>LH2!FuQK z+^O>~A;3`^&eom+8M|f@vVS{d-kS@mLxsEA>>zsU)@(+33**?+HSRn6z(q~o+(mH;#*y=TS+XG?rO+8tB2E54l$rs?@Vj_8kqn^Z_JV0j@mqLDiMW)D5Mpcw* z_ElssOP}`7JVt9LhGP7JdGv{#Fgr0b;Fvv2sN}**nt0(J4%;#X$F^O;@y=p$Rmz)2 zCoyD~wHfNHNv9rbW)Kx|D%q5Cg!n$9uscuzjEyWv+~;D_yLl`0X{yt>z!JD0(+b_$ z4ya}4#PqIT&6)&0WR=D(625cCF-ze#COgUCt@}SwBJ&q5X!=E4Vm#?f*BWkv-*5VU z<4}x-6__wsPOFN0(9|P9bavh!nqw#85>~d-_;y>`?&C_Y6{OJHB3ZiT%L1x!t(c@q zw$O-}ao{na#GPojBa_;vLyAln;gw1VF6@QUgTg*H^dWUoOM#>DEYzK?XB8#4vML|8 zz)P>tnugi#c<@~ZKKxzD8>1_}5d2Lj5^{wnqi}}dFtl7ahTiH~iL+9oFg9i_#)$vm z%F!M|?%@#qn^Y&dY%)NL1(wZ)ov}i{MUEakWkiEV+tQ#WmL4(QjB8T%6D6A_R>{y4 zR+`QgZ~Wy3gPuK{-CZxZ+_0H7=pSGm3$3BfRgYEpua`B|JHk4WWzb=>1 z)O=9Gk3XY}k2{0#cg%Er6<3a1-$*ez_lz;#*FxY)nFzbvD15U;8+Xs!i1`xx2ufp4LvrUfcvuQ#g`_qF)hNNSDP`dOu?^1ZY+|S8?&5M|>o8wzgg(yh z=-@CFS2XOVr%d`t#8?9|Z-WlKnL7nmw$ zbtKQ;9Q=<hXgc;He)L#@Sxyxw=W`MLZ+^q1ccWUFu}F`+7!2 zYc(9}>f&VU&)~mOb!vC15r*_2%2cEzd){q*sSopvRead9JaP2oLweyE|Rrk+7< zSvT@vpbd>b-b6*A)4W!AM?1BB98UA5v?G2O+GjXYUaFYPa9+yn+mO#(%Gpjul>_AU zg~KHA&n#lPLz>)?mH;wc8I*>3lJxxu4>T>w%8$1oV_g-fb~r-9`zTaD(}y!^nsBMR zF1_qA2M;Z<#PwMrqT9R+ltB{-4tIomH)P?g$6Rm-c80(|H8n+k&g7NCI4)gjFNSB= z(R1rcQR-b3XGEm9BA815z5R^(dcJhl#FJEZ+!#*pu{J$@br1a{*~y%{IhTA&l7Z2- zpJ3j!`y^q73U{!#gM9Uz51TvBlM<7KL{i8rFpe*A#uYs@@|uVwYc|F;S7X}UZy5Wh z2Gd9G13%gY**>o!OSK5}_Me18A^W_%LJQ>gcayKZjbxfK;Ou5I`Y+m&9{ZLij`;J5 zQ!*H&lh$OQ-u)mh?EPs`%=&y1Bk_hTc@V-&7GLAH*U#aPd}-lNPtoUN`l9gcStm@; zlgE^wF1V}uG#dGPquF^`GW|#jgIlGji|$0a%*l_2x7?=ZIpLZ=`~x{;r$?Q3gmPNz zG^yeChlIQEoJ4o2knjgH$=dCLSKaj;S@Y}$sQZLaAGdWhv%~=`8z5sS$xECkZ|D3r|V(nSMAVT$86w z8HH|mjBZ{%min50AujGCh_ujsFzS9y7gt^;=Wc}%`)(7uHh%{(+#k45hI7#&lMApv{1+9EBS z{wNBc|JRDH&BxL3dl0|ObrJeE7a;#f6+dq&!FMBe;Xz@?E?SXK%hKkGlIDA($4*;% z=TQ*z?~N8sl*%RT!5y@;P>op5ZDNWQ57D?S8%fUfl_c`PQW7(NkQBDHkTub^?+0`rM~n=%NCM!Qs9}l5u!Y! zh2}LU;f&ON`ikqJ6%`s}(}XkJj$=|(|5F{gUJ=7Rz1#+p{=Vdvu=AMob{mcpI5Fm) zlR-UsE66mqK(O3uYE1(vKZ1kO;Tp_+ohoR!NMTk|6+Awy3w}=lPLHr-H*t2jCE)~) zUD?a(md~V%`d)#0j3)$Zm4LKiFM8a4Lh}5p>8Q1dKvLXU?!nsY$G{@kcgxr8FBmBZLiZImO1l?`6Z5+p+;{Kf5W|gcS|!W}`0f ztZ796t6fwAI$@9C=>}!;%)OpG_qtBZ1YcF+_@l7jER;N~&qwKPm8cR%XhB~#S^Yu8 z^t$QNvA4`fPTxDKYjA)$WxW!PSeL`8sYPHhWCvv3T}xGV3pu`7nXpDr2Qt&+Vez>w z(6KRvg(W%c;iZq^=fMni>n9y}_Tw|V$}f@4JQ~X;+Wun?NH1qk6pv(cZp>!qKeT22 zm-w(lzGXq*&oHpT0x)4@gxpyNsZpa3+MjN}*i_Yzt}K z)8MN53*h_5!OZ7tx&4tLHFqsFn8OxE%)2RHnfluyG(mY4)08PiuNE-$#wiNRDksCS znUXfouI?iLej*56ioB(BnrDuex$QCjQOl{j3srH<@1EzPz4~Q`6=* zPCvtM++)cfI{1m-pR^e7czg2!bxOR-xcj_pI~<8uhez-e&wb!Y&mw;IAt!#BjSBB|RF3!j zEX^;m(Bm_Er{UiT4*ZU%^1P_*0Pnvz16`ZUP_1Mq^ZG{;*{}Q-jwAb#FQ z&+)2x^>ZO}X~G6lvTGyJ{aR1z(t=_A`UGNE7)0)*NOGukhYof7CXP?2q<6y}@pAUw zyfj~l-egFTZUA&ua!3;g@qQxMK1tE>m7`#y(X51^2z6b7X+IJ7+B@ z2tJR_){RuD;w~MPQ^743I_;Cc7lBRpT_X2NljMv0NWAk8uKwsyWLCG~wSaJ}2;YZ% zp)xuHmj7dXt-6VCI&aPEw9Upb zs^$FQk==a6kx~3aA-nR29>8;lDgA!Yfrxkafc@w&$dgzFMUSR|nK*-a_g<4uxR*eR z7Y2!QS1bnmWx+5exDl8YmgIGr6pWVWCKB&PFpSB4dOOFHUis~U&qKAaSLPZo75f3J z8gDSCgjtc%>^@=HB`TUQ9}N`MgMi_~Y8_;9otCQP8ab{qg&$kZOib=oRhq6x&q=x4ZMG&m&tls zNS$Td!T-?_!ufi#8)vDoiX%or>a|s*vppDNj(tMo>!Dbq^Mkif3*xh+pYmRDhw-Y3 zzy|5+5I+t2LZVn>^3YuiGAsbw;oWGSk{6Yl}M;5VG-9pnRwQ*nDB}uWvM}gh5 z7DilACtinAMAdByq+^HzWGZYYxyx%wmueK6G-&e^U!6x&iC=iRzz;Y2-$pH~Q%HLW zRIa(sh6(K9RHHmN`tk%}Lp<@0PA@6nZi}~a8u%&Chx7L8(Y)M)bUZd}fVNE)cv6UoH;K)Mvfo8{XTXF55qM(-Edi~9~#g73OV|} zfIVpfnG4OxrF9+DXMsJLw4soM`kIM9CyXLFM^wl@_5C<{#kxZVg^v@ZAl>xuE@qN7(h%0ojXfIK@7N_P(zq@AZzu>WT5Be(72A zd_pF*Sv!`@33d>7FMLVT9?c=A{kMRQbtSoYx{Q3Uwg8pf2AI)jPtsp5A^)W0iM7Ev zc;aw|F$|hR7rgeud+jff^VP%H=U(VB_bxRxxQnZHpM`=e?pU@f5eh=GK{itpTf!a- zto&GX`8t+Y-sHg#Yt6&|BBQW%n&8%cpNG-TRosI}7iuQ*CTn$&OZVPDXXoppZk!8s z60%`Q7cA+;)$Zh0#tCp4-3rW{Y%(crwYadoo!Pa}3r(%{$g2rGV6>%?RVc88{XzPm zzt#f}NxtPJnoF^u_ZfM)WjrS4NI1^(`-+$5x$#odml3HBTWbBzA0v1fTzqvVHu%lw z$TckPjW}oFJ~?6InZV13!Xp z!eeG*(BCH;U0Qqi;id)rkd}YEy16nxR#O*WI+(+YQ=6H={JGpUE)p{z=VH>7BPc6B zfU`Rda9Z_e)Ou6SeD)rK@IeC&{MTZbk04`rX3}+9`!uvrR1z&?HeViVTPV%k9F~*E8)e9gS1TMt%h%(Z4AIOi3tu*{l z4NeR(MycT^P?YWN?2`K8*QUL#G_c zM&>~+UOTUi4f(ccx3(1R^+YsoUnMtgcR^G)SJ#pG;;v5_TBHLT5fr2E8LW zn)bN?oVS~h-1IImTO9=%wf}KmtwEgP&s2Ws=zg5JqZHTemE;|c*73vFuIATYmqlmS zY*u|l6P4q0XtvPn50Lpp-URI?_b1(8IxLsqhMtS${WSxey=p0Jcs7ODnRMX_x(~BA z?#JR~9T;@JhWh(2;zDmP=eiI6W73}>kl2&Y$>AIBWMiTg_eA0ny?ZvE^o_Yg zax!0lgQ?)_vOUk$a~808mmWZ4D9W7kMavIUXs(GCzi(JQZ{yL#pZ$|BIJDDn=bU%E zvDy@-H$)wVpLc<4is8`NBOyx3-wskUUlQ-ckt8t;VAf?_GQ2F4?s#zm^@^^di~>)q zJJ;Zd!_qyh*Pk@F5VRDB=nSHA+I(#JZ#wTZ&5}3%Rf@lcXz{*o-gwSa zm=)}NcBRRWBLx#>;NX_ylOx~PdNV%{P7}$E$q|Q@yd+9t8@LzdS;YBuUQM#9F0)#>owApniV~jG zvC2dKaR-%TK+JbT;emJ1zDk}QwPhFF9rG3Ei8J`azXcES@ZVTHtraD|-ePs>LYjA| z1h#xGBPX)&vJq4ZUd)>TDSs)4lHRg1he0!b`8{OSY;tDrFND|vLsLag<~hOr(QipHA9n9_tx^lRd0`uS!QStptW5-FAB z?Uk+cT2CY?+LB2$qeqa#izib3%uI@7S1^got;pkHYShAW9op}!p|T=dR&uikE9zYe z-E$_hUPk%Q>wSlfuFog;FMUF1n?yKevx(N`%w;WY4zf{guY|l$JYLFv3~kyyuzyY* ztK%sN5#x%XNO=Azl-kk}dnV&#*;wjwbsM#ln}9k~w~);F%0zqfD^B%+Ha!l(HAb~R zX~>XVawz_&Xo6=0d7C(rX3n%BBiB_x+^0&^K0KSIW|z_E^bFRaO_zM1mJhN=*R%Rl zJ6WwyF7T`~8+`vurc)e*yOoCuJbpM9a(+6pA(g?b?v-04bLCZ-qNqu0E;T{vynbTP zK2n@NaVEO1@1WvIpO^`2c5vx|$@H?F5$fEuq~jWcybHXp{lb|=pYdPg zVt$rJ86GNI#mhT<7Tg655)Q~MsYnoVP(VBpTEr9_H+<- zvMpqwqK@&p_XXq94Dcec;>vYCh+fX`BWpB7!rj>m&zf%FZSI8fL#tMyrP6dR|1FC# z%li23S>yRJt^4udUrlrxFHfXrzJ%Mpwa^uI0KVNlN5l`R=)kXwZsGU-uzU|#*PNoYrK{-2m5m_rQI1SaiK5l(({RFiKSpxSO{To% z5I57sn~Br*rSG3N(KF}95Uu^Te9+HJ#Qt9yL=MKV@zYA_nq|AO@{t^#%;@E1wsaE} z*afTa^?;4iA#s)`l4mESxqq(;nadRd6LHQx(e;3>q|7{s*uD2BM>i!iS%3DDVtZ$j zWUvUk2@z$^q_pyj_{qX&FwP`S z+_~otsg2ysv~4zqORJ2D|7I^3<+7Ik7WJY0?7(F&F2wCrUuiGSwCB`?~? z%-t2q{TiH3N1FDF$=Ytt+R%aFw*Mf9eqDowWhId6{~ofX1^-yB2=Z(Ph-!Tg)IJY| zYil=xM`kSqw(TYPLeEv(aRHOHE)6F?pN8e?0qC-M0LLg#!IZm~FlpmvOt3BzT#k?E zz;<1F`sg?&SVN6$Fk3}8b>0xp@~iZ_W*RwUIEOk+{X!>P@g{~`j=)%6#AqzHCpYyP zh>CR-*TFBKU83it`uT5i>){Mob@2wdyj$RXd_M(^law&MxQSdhd;xjcLT6KIHXH48 zf{j-$g&(GckRI?CHBK=6aD$Qj*erSUeXtp`77b$Gbvry$Z-9-)^HHl~2rU=<*^8>V znhwKvR8rT8=560W>oRX~k);pFq1qv&?!PvMbbciX!X%)@)RRW5`OuwdaW%!sza70! zXfR&+yXo1yH1ab2AsCq_(4WilY21zqB6i+`?3&dmUnC74#+xBMX%s8#xD;}3%7ff5 z2P~U?p4T(^#!IKR@xx4BW69aKnD}ZKmPJ&dx%E)`-Qy2h-?)WV^Ec9S&V%&6SC)9j zhZ)Qt2_bWweup-PXwe&Q&(Y&HSILf#7NQ30b#%oGO;XhoN5YE+sANYMSupknojPLz ztyyr0JilvB-0yE;{%!FfZ6${w?9WTaC85#r_;Ev4WzGOQGhhqsSIdS)1ruTA?;F@| zY=pNWb9nX|*Zx3T3W5c`MEi zQzV1mIPRZt1}WTagVX67@p1QM+~cIoRS~98{)t`ro1iUbp5?fEl%v~oAEDEEYMU*TF)?gm2 zh-WfEi(a_bN@qRTK%KW{QoDRjsyd;8MlIMzrL4Yjj#~=p(&8gjZrBv2^7IFC)>$1j zF5RIm{W-9ywT@n_spcXQJZSZyDA7QMBpPk^!3yzptmw1GmIF?tX|W4uar+YYVlV~u z&pzTF=qIDvN}dEv8byP3tj+cUBf$%gN=0Mn9c#MT zIGQ_?;>pEvM$A&#Go*CcQLZ@6jwo0q(5;KjxE;kuY5s$9vLx}Ukdtkq%41u}GSA;6 z^Op^GrLKtHlzYQ?f8(f@zBBD=JB9_jw$a1ad+D4$CG>r|k<5HlNk%?;z{EXyg|qJ$ zlJ474n7Z~ToH8sYWvh?le}}@*)^aAqOqourW)9F>uPt!-_H@h;?r#eYCDxpaRfP3x z21v{0V6a&|i7Ktsq+0fooZ69p%q`WYSMIj`M>XuEXjYR7$#eK1UcA4NOe%Ot&hP1= zV-sX4cVLb9gv>Y8d7eUFce?hzP}5*#}_fRqlJ;PP$3%*}8& z%=r{X?Noy3ahV$++Bl!)I{ctDvU|wyk`i&>?vpsmd9yIH*o=`U1=c`hF1Kx04AV2A zjoZuKphw5`qw32ubng~hYN&OdUR01Flh5wva{oI=bnDNG%Y^+|{vA)MGxa3xJHC8~wITX}J)O=-GEM&G!mP*;b|eLY+&Le# zVsmK33V)P+9EjD%t)dCH8_Bh*ZzwzC3s=9}oBmfG&fHH)Bl{G)sk?I_OsKA*W8Tf8 zTC-f}qlyP4=IlMD&u$+*6!Vs=-n@?L`hTUl<9#TtJ}ip(dXf9DLW0yCT}|@VIddZv z-{Ryu=1k}Ltu$~T9A+G)B=V@hOY6P{pX>yWd&fEIJJ|__@eK%0+88@+CiW@R;Ncq_ zHeH#I&qkjI&zq9Cy!it7`wUeD`=VZA&CcxLHGvnt!AxR~L}oX@uE7ofViFQ6y<?5jL%3VKD7X6z)pLr+sXrK{qeYd7fvy)>F7wh>+ZGM&aAR;R=EdUExd zgQ7=o`$l>9MueHq=$)}@7@XdYN3Na1B`e1A5~?!%uAPF{NNpCKmZe6oeqYP@>lwqW zaxXI4-W7+wSwq%UO#pLyQ+j|nunEbVgU(Dr67VlSx?{mr0tQWdm# z`ADW!qMcOu`7*0KZi@f<_0yL9Bj_HTN8q6LmD}f3Okye~px2-`CcO7#nwp=&%vv%1 zQRRw$S>+feI*t{;fmaTy!|dg^dFf4Myf(837Z{8IO{D^K&^bzUduz$VVXk;ac$Q2L zIY5uf)G;G_izt>U;pniDXgsxx?lG4Y-jjb*M)fsK+nhmWn!KaxzMbOc2Yys}=oG5x z_JzEjSWUXdw3F5qd#*fpxI&_G=F;6m2I%sgy7c0uCtSS1l`*rthYKTo>GiF}_)&5g zPTLcLiBD7z8#*xfm<6gr7v33s&0Le8LO;MKs4?EDa19&H2Si)1AeJ)Kx6c9MBj=J7*2jox@LX6QxWzjiocVCxa)r zg+BM0TqRj>jg$eqY&X(idm8@d=)425`rkKh@5m~Yk+Nq*$$8(0G&GdZjzXd>?fO*6 z%x;jCkd%z1%=5mFib_kORHPxLp^~JfzUTM*%iqT1InQ-p*XuQU++@%$S0@cyUlI3% z`-P2tJ;^1C2$kgp^95GZY>Z)?SQi={UD3`>%Ez*+g3?5l1~xYG2+~%+nPka zg%XGFKJ>J3biNgMXT+jKgb2Aaqne1G zszzlQ9n@QGfiF-W^HCorX|_V?5j$r84QKLCqzB8tOvjTKd|-B>Iyo$MmUE5q0m;$>Pn_`PmLxF*+< zWK6OjIgnKwMM3RADnxwpa3_+hzpg_ptab^zR~s53?9>khY^J3)o@n*p z7!A8?Lq4!BQExqR!$P9HNF1pfSq3vQ$uo^@J2XGlg`JIJ2w+m=nSOIOt1@I!ogjMQr zgvRX##IJNEG;*In&d&!{c+R4Ma)ES~Zzai9D#j!ebs8+(wVZcdMT;-~G(}=B{d-oG zW^U7P|m;fL60-(ZXGa(8=DKBt>f0O^baF>t@bnp8mT?>_c7Y$zdZpQ0YwuBxkZZ z&Ckiv!v8q4;K!)vc$^Mp$l>ILx9HFI22898!g<|magPB{8%p=mHDhwPkM~A8ejf9j zcC8a3_RjsZ{rdtsPjNbmkL=|h_F6G-lUg~uC?#P7wvHTK{E2z{XDr<;TZ4J^wP7|0nuR2^^tpIIaL*YzwyH>V9$l+U}^gnRNIj_ zjqbsqhlMyT^Dai)kNu;^ z&xH_exJ2uF$CAYv$#fH3VR|j?Ih_Tz)FXBgAsZXe$EgwXg)Lk{cO}*aO(8Zg%hDQ44c&PUb>a7sx7H51$b0=-OXO1=H7Z=ib!R1s$;WjmjjHX&8 z(kMOrn#gQQqxU_N=&0&FWc`I;$GIxUXpV~rMk*b^x_5$WGJgsB_3gpDF>L}9QIbC1 zwh}WUO6ihc9JzQ%6P(Z1gVFo>+z8*bV3ZyW1rseX@IpT^HT*;6s`bdj?|V7!`a_(k zQib+rQ|j&*s?qIHlc}5aQ|`dT_q4H4n;tyUK>zNMq6u8{)R@!>j$sEGPE+(B717%$ zH>%+DdEt0g<|nqb72{2%CJg;5g7addaLNWJ^sY~%2c2cn^xy=H z%GSfr+lvJbk?n`lyAm3t9cln26;auKVX9N0;4gAgf00rXIQX=u_qzb7xf()7ABX zo3p(H^*;LG(b83z^R^rNs**8z)(O1WQh}L07x5G;2h(>1KzR3bJay<1@jER~eRm7| zJ-I!Mb-*1atY|;&+#W*vPvz3DVM|ccOoY4iIbXON%>|vIY6zR{L3*zC6T{ys%=)oD zFnXyrJu4PVHvhJ*>o!_I7n~gHD5I}QuYEpEHck3KV`Hson{ozosN9?y57kgHq1T`I z_A>pDz7K0PeqflJB$fu>!Gcu+AIUA0e)G?ux78M7&9iGf>ypK*FYw0&_pXD2@&E*P zTEO-}XS})QEbVgurK#DWIXVPRpzP8r|usl>*FGl_v9D3 zQ0qoD-i6b3nLgaRum32wXBkJIjc2-o>rw8&d}c$H7*V^-khMRQnF@7bZ^OFN4w+7Y z6SfO$wpC%K(RIB3*a|00JV4w1k5N3hf?wBg8NWQs=NJAAr$4ev;J=7InDZ@z=>G}A z^7rBR_Khl@wJ9Q*8w^Qgw9uRJQG(k7N43%J0R*qMfP?i}T(97mi!9wmUd)=yPYOEL3+2PoW^{%Mths@ zXf^r?cIt1ZCVtC#^|vqZ$-C8jRNEqcr)MU=MJPxa%s#_y8znH{Kgg4&-h;HXW;Fh) zPQo!>hrpqG4^zMRAB+!jf*V`DLrRG<7}ac_+M(@C`JGkV-+YfLcNTu7 z+oJB0EWI#pfusj@K2%CCocT^WB9;>KEh)5h&vD0n>lBI3!a6Ri>oC`RnWabEx7Be6 zFW{BW3~rV1<7EngAJw~+Pc&S@i@fOOO;uV^_rqCMJE4_bHavpO_i|uo*>TW(-5l&! zEQPBxT43n27sea+}~Lke4O>6918iD@%xSNg&Z`f5T0mmRfh@(njW3ejA;cn@7s*gSk}t2RG)t|kVbp?lklw?R6=9 zUh|W`Sn*ziJMe?gIJ~GEh!Zjl;oIDL)^1N9o4RwDO%=~&7dFVTo7ZH)!_j|1@lgla zlD)X@nMfp~-y=&aYj#o<$zHOlEtt!>zm_ZC-%1Z%6&TCrMRZDSF`cOTne6`M$p6@8 zS8qOdL;bv8uj_5box)ak9d^bYIkv3tCL2%wu?z0ZXJuA&v0{0NFweDwo82} z>7fT=_cMu`ZV-Lz`WeGDzT>@UQ(otG53hA+6kgjkmY3Kvju$bX%um{?!q4$f;r*)b z@iE1X{L0XG{FvBzyxqPb-hPtMQOKOZD>iM#mh2^1I6@QE0-d>z%%zU$w@c^&7cVL! z5l{X+C}MX1Eg-J_L!k5hEID?092uTGhinqu;!bXBK&tON%!!JJ=JAi9vG+0feYp?5 z-8X5*&aJp}e*y0Ayvd8IM`M{v92AnlE*EDZf}= z@^vO}HuQwIv%koXv~$P)t~0#Cic!4e@8uY|^d3eCH>1_&>#6f_Ao-cS8I-cZVc+SG z5WW92XzZQ=KC9M~T<2FcFB|WGakd5N9`J_)2B}cde*xSV3eRNoB_Y_OmrE{AqEgI! z`qpX#eR^aS9SR9TBkLw!IXzi^$1NnRKawd_o`R=TrEuU!9bU81!d7KP ze5!a1J2Lb!=|?-)wC+8TyX8fjw~H~BAp%2c{TO2KC=5cLB!To|Czxci3g#q;L)PN@7Qs3g5yzl)yqxPsOK_oT&VHa~SjKYq1+iQn#aG5NJ(+_=_< zWKCuoM1L@X#_rkhX53i8B|U+BQLl$Kt1_q^ih|C26;SzLHstnBf;F!;Lg@0$Y?UkTD@QC43PLeM9U4+?n0cIZ`275Ipc&8RFICphu=f&?> z-8q4`ICmCjjTjGVvlh}NOBUjnzg_s}L^vOGSdv$8Il)UF6-Sf1qd}{~0XF}A4qjsO zSS^eFtizs{aHD!Rv}Hen_ZLROmD=lY=fHf(*kuX@JM^GJz905DE0S@!_s9mJ*Q7E2 zr{IprsvA&R0kggfUBEA%DA5FD@rx*u_16Uo%kRPt$p_GRJp?}6*uue(<9K+~KAh5d z7E>iu(4<(%BDGK8xN#9Q)c!dRQ4xNG{Q`b`0f7}+TVRJ*jd0%|4>yd9n( zUiz50Xs(6vb0*-%v0H?`ggtK~^9fs>eh_I}EhwDS2DNMEvs?2ou^OqSa7*Y*D zkEd;r?*9~O`AOh^?-_(nHwV?YBv5N`0F%^e=B2|R`P)95v|OzqSH0@N?x7zI(0u}n z=8XW0PjQ?~-y87udI7)3A7v-?)v-on!=cHf8HP2V!XA}W$1l!Spla$1%VOf0eew&y zdfrYnQPjnrqHXxd^d9OA37?;P=YY0K6)UxkvXc+Tu#zgC5O<*q;<~Q_t$z$lGG;;4 zwfjKy`yhMo1DI2O2xh4nGbg)DNx@JN>6>Z+>M;hS?sG6KTjxY-ZrYK8hhwSer)=_I ze?H_dn-B3zYQaDF0q_@OSOvRk*5hF;9Gw>pdzU_7N6yQHt|^njIH3kygN~E1`TF>E z^Aq~kWEf{vj&c+ttUNkx%l*gBBZsn@v$ zrPi#3q7$nV*v$Iea%P=VXRxDBwnD1*in_kwSjhYA4I4z>IfiX5Kv#(#n!ITVrygvC zp}9{8wSUd^^j;Jcp$Y=yy%o0IxDNkSG@|Z5ePN&EgN4WL;OMjt5SeKKx({?f*1a7} zk9L#1oMx`xqla9)c9{HV>xJnnUFlSz*K9Wb6X*CL6%F4$0VVEgZO&3R>fbVzBzOq@ zNYO?()*r>HhMi$aaWbgoXV5O8=Ux!D9wt|n5M%dTrY0tW)POY}syu)*(&Vu0`8UpY zcsU-vd>hUE*VB%Q#f(p9HD>ISKnS$M;+z?%20OS7vPa3qA$g{@Xe7y0ISIOsGf4BT zO5&RRnpE?vV6;LfLYX2nE#fSBJW9xLU!NwiZxhI+xermSY%BL@)-BLz$pD|RE71Kj zqSpCM8nwws+tfb?JoF#SJr?rw{xrOv>8sK&%)+86CLY4&oO8FTLnM%Sn_7WYr&&D2>&^CaQyo!5E)wvz7_&^ z>x3M%sCxCro3&%d;O_lv{ zc=Z?TYm~(&H@4%I^MCM2s0fFH<@9J&6}eb_g~Z;GAv!h{B+=oXl6ulFGcQhW-XYuEVjR}$FUWm7{a;o%0ixdCcFSw zC0?i2r(berg_UGV{6k0Wy+6pI7mFFamBlpW$~dCGa}oV>`UP`WVLzmvT*GxHx-cFo z7s01}0_;0>S>Sn2#H|4pcw^~werjq7CY8)WpZ7=b=a(Ri-KIoky7#aK>(V)G%ZuFn z3%;Dk;18U%M2uH&v*Z;HcjLq9pLo&k%>vWJ7QN?WVMN<`oM?U$v8)?2W~bs3Z5IFA z!O;^vA5l%XA0Hb?CU>^Zt9zHwL;TZDJBIpir$r*pQ|(7fkrcBE)XF`L3lFFOU-ugh ze=#DL^4oE~*JOIsAPwDhzTu7%4%4?C;b(y?t`attpDaCC-2oG5E6>I7xleiV!e?mj zlS$B32oq4qa8aI_Y-k-Y~bc>dQ^qo(THn>Xo z`}i$5e66W_g3w2t@fIaQ%sK0(Ozu_ZcaW9)O%=ZV!^us?G{iK9-Z^lT9+FO@V;>C@ zO^rv6Y0|cgVdoE2Wm?InUVYfTb2~Vlm`-}`x?^VJOo)8`n3$DYqsmTMdS3ns(Ky&l z8#1qfxA;6f?ZwmV{n0St^C##^Is!dS7IaK$3^PG(7|eVlS*1ct^0HdkDW#twmygdU z{Lc2#al;`!tQ2#?R(H z_85Y0+eC={*2*mL+(;9l9{rs+p^I%gs;4`mp1`{p@!5iWY^Bu8`T=MBa2|~e@+Jq` zI8q}z1#HsHxHDR5jMPYDO6AIhJA(&?XbJgN{eQUE`T{NzSalU95Ad+lRy<_17Sk8Y z;pXRF@aUa4uh`MUd_NM#PWW8|dv5K8LV;g7xUv~e_IN_rWj|EA>`iZ;90Mm;odw0u zGTc#iDcx{7AJz7z;+*CCFlC2ulb!I0ItjgEtD8?8-z?fszt~KnKgp2eNT!~cX3B!H zMF$v#92EW^7Lt9kZTMi}A=+;D3CH9FVkx7EIX^pbrrc62_Rq!E+@pB)eG4|n&By#H z>D1VMHXN3&Wv4V8pz~gOk%4!MAtJLLbdK}FKK2XEXRL6xOEqjz^hA3-%Dq{sB6MFa zpy~ZoG%GNpIj2uk1<%`z=*=@UO;eT0Y1E??R+~}gR1DD#kR!6KZ|Jl8c`#EtKQomv9JoiUGHbBmJK=(KZ()(+NDS}lA;aS$;X|ty zth7nO3DayySHc50IWwBWMp0b7Lj(O&&R~nOBw6v`85XpU!n&J(ak%UNzWDr^e&047 z=Z9=S!!$AK?cGg>&L8JqJ4rHU=QR+!Hd{NLbYfGjipLiNL!wUY&)K(UtVxrQ^7$w42s4iBKW}tW-reR9tR?(ZO#Z>)Ixv zbLfmyw{D8iQ9Ca=c-?IIkwK0^ZL#ie0ZC;-*)c!rt>Eij7F*KAgBuO9e)V zdB}1Mnst`CZ1$v6d|xr2PXy7nNtfL*6tI*tb`S~to#;w9bQQ*&brbQ4P)slg(Rw7Ws9LIeN6C`eKh^R zBQ8DGpNKEcpsVwWsodL*Tt#jl7zsN8ziB40xuTIdIW7^x%?t2+P$-NO4FF5xi;6#V z$*KKsP>x%~Q1dWwx1NQi3JYm_&sR|%;@=rdyS+{-DC%hfJ6Qu&Q&_v}s zG9e1I#6ca8$coUTW6#n4UzJ?k?l&~nR*d`LCPt^;*2SnBPiUg?JTzAONRKWlXQF3Z zr>!wOnmbS7uCG@>iA`d3*6TjB>#YID)gPc<_)PCU86nfwr1sR0#l+r2aDY8KiKVfZ z@$UR)JUs3PW*`1W`-EM)Y^NcdsZ@fmn=7DfQ7D++)df%8-Q?}cJet}*lRQt;atx(! zm?uJ~g72F^vkOgV+64>LFuTcUHJ72bybaxY_YQY-@H|t}6i3CCN5L|?5ioA<5UCH2 zBzc`bnQYf<=yBW`?>VdhH6tn3{z?y&9Ulv4P7T6kmrPA&FX;2R8@LtX z_j#E`!e0KHrr?8E2E}F+237UhG0v}H_;@gwDolkVPiDZ+Y1`n2Wf*9E`%5QG3aJ~i zoxr^_FQLyb4bT}MjmW&UEu7$>Lhr6PIzR6|j&n^S@4l6h*VXx;td-2M4YcmY%RDkL z7=hvMe6abq@GRdmnS9v3oH|e6LQWnvc(OKQkHX($`Y_Y`D?IyALQbzKBWvw`3qAHQuFFD>bUaGt z;`WZB0V&0Foo7A0ylE2`zBGpUGdYJ|I6MZ&yR1WTDRtBzkqryajU@5=#R)aI!$tO+ zkid(z`1 zFZfa4uw#2vSlcDDScR#1@KId{iguPknl}T7`qvZw;T5X9jiqX9W9iiQh^n7L(O6NR z5w#HRr2Bg5%S12gcCZ`yQ`Wv6LyH$o!r0sGxX4-ovxh~Pf1BOtw^6NVGwCLZMs_ z84Xr_wsj>bWpK>PgxPsFAJ#qFM7r2}T)@myGDcnxLcd=m9l?gMe0mZTG3KyG*c524 z*+`mw4uR5wQKUd?Gnd$tNYuXks(U|b3BOa@jSf%vkG1K3Mi-u5!;kQpLW(aau=90o z*z$ySHsS4c_K?g`)@IfbaCN%KwP!R_t2L{M$QW%heVhS})Ql%#HXStFYXgbX6D9e5 z&JNT1B$yPbjpS2FB}vPx11WD=PPP3fRS9jT$xerGdY2WhZ9Rh8N-gMn{WNYRGqHV! zFFxt7$92osV}7t_UD=CPvZn7iD`xLchJ@##C1KxK>02#Ozo!wB4Ss-Cv@ch~E78f7 zZ&6aFukKU#aT@Y@kSx|Uh4E2$Kw?f5Xl@iLh61=dIYlVL`oB!YBBM}dQO zANeW}d{ahFhLL11xvA&xkWe8WhFAq+G*R;iOHa`Xzh;D*Cy?VkfqqX+Zuqt^uuCET+0#Fg( zzzknc^xXo- zOhY5mm8wn3r#W#iRcncV^b|a{B!})9%)sdWLpYaj#-&?|@$~(CjC8Hz#m3yl5YcqZ zpIqUn7_5RCZ&cu6Sv0F%br2Y0wY@2~cuoH>%rY%OrSz#x z@Y7s~d$NdrHfhHtb7o-J`avk0_KSW=N}#b1V&F#Z1bAJu1@au)Nz1E0)Ozm)ay*p- z?dQ9M9MA-gxm&}60uzul*CJ77yJ1vMAZTd~fy>fR*gtt9=uZUFuWuqe;}3w#;geAC zGZh?U7Lk8Ow&dBiK5}{6UQVv%1HG91j#p3-HdX1TSvjK+_*Gbnfy*x86wR-^NUs;L z^A3>5HoI}zrPq)-*-3DLaP-=6CS;wkh3qpr@bv0bI8JtvsITp0AhL#YKs|VWXcyFF zjfPA|dl;7+P`9^V9L)Ly3Hw(_*Q}brYF!H?j52`G)_7QMwF@RYiNMZ4p>Ov}9%i2_ z!bMAGlCPJW=@k1U+;S)!OWA+8^vVJlyGk0i?_bCS_?CnE?=XOot6Be(ZDf#^lGdGh zxNL0#8kg*5D#C2x)`GuqXnr!Q>8}O($*$zc1#{tUZ9rOr{h&tWBD|ez28Z7*CNJh* z0}JCW(#s~3NkP4^p~?>u7EOf=nGmSlwTU#Z$e_`u$1>Up(e%P#8Hxu4qGiE1va@rs zuq$-LjNMMSqumC5uX1qnXE~g9(Lu$Rk3c(V5LWC@W*0jQzz&z&RAt#WbcmV_>Z1fU z?UGqAg=9^91)_0?vX!*L5aiF?anX3&3>Hd#UB!pD&5*#UK`m+3Km5%f^C z=0*2}zL4R<+9f}=JHl_-?EsM;tNTMz61J7Zz2h0cR=>mHgK3C0*}^aF?|b1 zlP_tLSh4B5V83lL#OG$PdUT(vhe>+TnVpBKkni*prcN^?>+<@s& z2rg&cL4B*h^eqwL(x?3;e(}|$AnFp%KXa0fEp8?0f%0HBS{x+2PSBVWPf;PMoMHrn z0riD+SDcVR35>VY*CXJxWe{ia#u_f(xCSC;6iKJRN(mkD97kwA;;=@@nXk#hN0E9~ zbj~1@tiKIechVur*aVa;9}*!RBI(2(L^)g1;w`+l@6978uGB!0R18F})Mwtzx29ad zW*U9X2I^%*K|T8?mtl$Y(1Wd@TRRb^shEO+vlDYEEg3@d=7GV82+mga9W1{5gKSW} zPv&Z@W(~eLu_BT!;JN4=J9Z->&!5}|YyJ+ZhX+wID+%Q0cED2KHb|+pf>6&e$m&}M zi(_;g2h`O-^i2Xi+MEI^fg{O17umWlJt^VNa}y+Mx04?`EYL?;7H6$pL6#gj&5A9T zt1XUxOFk8{AbY}*mFEA#rL}IT@G2bIjpd;?deFqW+ZiC6mMeuLhWyr{j zhGJ1!Rr)8=DIo++5gYry(aTNomBFuS51z6>R?Q=W8@ zZd-MDV(!O|-%}5Gd554UnFKC(KR{v2UkKNffMW9)NZa?C3>97idj%zW$nPL^ES?Us zp0h}iGZi>%HK1JS3cCj6Nx?)nx-40P`O@+hVuU^Z*PTD0XJrSJ;vVQTD`2(%tYbxe zGvGsj6#To&K)FAQdIiTZ=UOWzSypK0R!Pzg!&sUAMhLJMXT^TSf_B*}R<_xgH6{1i z5uay6cIR_Q>lB5~+hd^RaU7Hx`~j!i?WBLJ6LB^k&(y5dn3_vnL3%|qrzO9OnKaWK zM5rX#HoXLWUmeC*)dYgW{2;Qak+r?J3T}k;v64|kaA5-n$)X8REt1wZm6qTDXeRPOX8E>ZC`T;M&6{Mbf3UcaE`|3)#5V_FQhNj0ahD|78ET85wd0gLzCD|*8N2}#$9#+yZ9UMpR=^!#|nU*B_5cN zl7Tnk*OH%eUcp<*rEqJxIr^?QBX)NC*pX|Wg1?6hKvg&s^C^J#iZuwgVKbsO#fAIt zBW1)icG3YUMWP|}j31xx1(oa{FzS~M7gFyF`d>c~Ik_^Bi}j;dy5`b?xv4bf@GI!a z$zYdeKZn0oCG3KeD_D`CW32D=a&~0rTvp}SZ#da|7v?6PBtI-`n6OPk{yr&z-_h`b zH|su#sn(M*W=HTV9~S| zT+PNo=jnTO zOzwM&_iv3x#YLZB*$YunIP`_iP&Xzy-;bh$>n-lzpFcPyzz5AVQi;^RE*k5)5cL=4 z(Tj<*$Z^|B@^hzU?Y~u>BwHnr&e;>qnHR>BthL3o=<0T^K2?DZ-w7r0Pd-B2)JN#r zxf#>$Av^ln7uHO799(T&$r`Tf1>=i~u(vFMzV2F!%n!i>Wbqf(pJt%9VJ0s!h46|3 zuhFnC90srLA?#fx*q;*zsrMYwVL*h+*2K{jml~<*g+@AH>_F8wenkyc&$=^j4w6>h zl5?>wCt>LV>zWNF_wXl(R*XAxl~)iPf^KXJ1`Rl|5+@$A zQakM6M!714>S>~A<^(dmqLQAOlf=wUEabimxdb0qMPClCr&oKdxU_i;=)B)UN~GR{ zsM`oG^MpTLdq)OTdlWJEzxB91A`zP`yYR`nW*mH?g@;cbgK?{0!@B(Ku-E@1dGHQc zk*x&WUv;r=LO&wc>^S^!JB+q{wo}X4Yk0onIDV?$##_$Lr4I%7d3t6m9A4o@N;a93 z$lYJ);qWowJkU-`x11DSvoq+O7e;9FmP5nOH>oS$1LmFd z+Z2K_;WIIIbTmFamyQuHLoluVHLrN!H7Q;iMsD7GOIDnyh8#N`c;{^j_51qSaiV_k zUf&P+W;eKcGlU(nR1Ge!Sx4u{o8U5?k^G$gh1jrrKcj8)A4qgvL8-iJ^pVO()LXoV zllrxY`v2@e-Kj;~kp4-54V}kbpXA1cX_|pn&9W8t>fTG-Iv0e56e zAlKX!esvo_QO|9nzL#Lob|rq|jR?G5Btf@k22#DXyRoHrKPJ8QA=Nkj(Z{;AOv*QZ zw79N?%Vg%_jP83R$Rv_GXM6}O#SB4FD~KGwZ9&>4^w4j*3N{qf;8@4!7;?}I-TowF zOJ6!~?5xAj3m(twY|F=oZj~6#vt%&BnH^K$OU`*K!#&-tBxafc80NUZ+SK3VyWbMr zp*)_Xvnp)FZIG3ab2jW@MK z2NiYtgNde*t7m}YeG&3wVjhf~6H0DpAEW1mUCdD*drWjajLn$_yj)f&uDilx%=dBp z%wK6ddu19gt6{p1D*D-nC{8GLVl0F z42n085H2y?QRukg^i@e%_sIk=>)q!S=Ns^21TN91lPuO1OQNRIGfZA)$B|6vGvV*{su`$IsK2FQOK zs#~*n3m$L#f`JAz(9m@hri>P$Pdx(g$gT!tUiX1Wh5{VdF+=ZL_Owhl58dbf#_wj22hk3S6mRI`mmzRiY z#c=tX_+Nkwjz40?j8FR_?kTip8dHy%%3 zL;uA0VA7apc(>#~+#(u=XV*K^$rF~N?uaCw5f9|&4Bh38BJGyZ zdz|PNzO(l6;>6|}}gqN;y z{4jQqG+Y@&HvHE?THPpFblnd!V&224)DTGb$$)zS(?QB&fUd@j5v7(cj))3zD0BhtLcw+~Ng(U%l{&EtLafy@jDbNxcI{_0`)rzPBlw7uwi zMjAF{3cbKTD@eztGqftKl}_*dO)HYm(u<%#JYNXRdizk4ecO(>_BMm@62XVF;wuS- zg|zWj1I#X}A~BBw!6juaOwLr`ROYpF*G9QP!srmhIS|MrCHkvUMaXBOG+k&i)JT+qM38?EQ8 z=bo=wO!sb%;10>WC+Dj5iQJ6Dpncs=Kng2X?IHqnN z4CMQv^Ahodnx(LR$zZoh$(g+0g94LR6+;uRj%n1y9+4CXA&<0WRh^GY9g z@bZdR@jzQ1y}oY-op)^x38xYC!$o7FH2W$lk5|EC)6BTfKGC>7*9GlM4KeOwF{)1a zjHWwpk}K!W6Fup0a$mlM?1^~;Q}2b7whxh1^4d3=^VXNdjMv8(wfA{tEkAx4?~1wG zejzzg1Rs7Du>M;g!y&UKh}1TQ)EXIbe}M+sT4cb@DXk=9WRx=}yzMRGvRRW&^z=HWvgJlsI@ zXGwDHf``3@{q z#n&eATbV$!R_?}><;pnpH5N;@JJIHwh4e`NY}(@Yhgl|fj!K@jr74>i)2~vi>5IQ_ zsda}JWlC0&8{#4KzTgIaB|*sKGjDOq+A7?Tp@oLZZlv&EC;8bS@abdH!0~r51m3?1 z!Rzv1g3%k;TKx*u~bPr%RHKj2tkIjeAAjTPJek~ORnw#eD% zp-nsmr1XUS-0pr%+>W^Gt29ou+`%2Sw4pgkGL9_q11`(H@9lM%@>zQ8E2vLP)ejUYMyBl#oT()NAS zh8!CsxKyG->hE@Q0fWCG&Ef%hwJHRz*_N}?@7BO?!%rZ#Y67j#i=}>}Tya$#Aqo#o zX^wFYzFBbyH_IQzy|cE^!AYM{sneNC4_!lzZKb$-UL1~;jze<^!t`u)Ba?JKlBl7P zbj|8;>Zwr9RgDl87)_nrvyrDc)w3}q+_eDoG*ifx&WT-L$GCPHSvxF6TOoKGB8?=m8?BBU^B2n@a(k?bqi zNz3vIkoGxC(to|Dw^ggD$fs*`>7HuPXm~)rX&xr^GP6N;Z#u0kP{YYVx;THrh`LF# zzadxZAG!X+5MK7nLUPY(b{c5IhV02SZoJ^{(=6pjt!l<=QK{&r+=0_~S>Vlm16VWJ z7B7WcqO3v=SDOA1S7m6?-Ct+a%@?|W6avzU* zNHaTwx8dYbU9@J}Y+@EPU)ZNDty&&63MSu|Ag8vSCvU&}AqSuTM?~{BI=br13*8!Mr15c5z($fMX{QAL$|{(C=r9d!(}MPP1t|1vhvTs+P_g|r zbeSR)%xk93QP;6>Er)FxziH9&-NbnGHBbmzPBz_6CA>roP71C6!LrZ=WcVkcKw!)mS{PMlv0P5K*1SaAq1-nW1p^R~mx zHFg*S)^Ph`EQmDyB|~jG=#$uj1uq}tv2R*9$y*lm#&37D-*w?IdZ3i6|}zxW(^( zuEMKtd4nCL0a#)w^sWZ#pwWK92TYCIL^@+`^dWCU`timR#MH ziCWIzKq=!0X6^LD%bl~4k8Z)8)rY{_*GtF?W9XECgXHL~Q6zlHMAC54khGc)3hs+f zO!@6ZCM&oaLIwZaQE73~DW~gb>7xTnN2Gy*`ghvB@h8)GL|OQpGK|khUEw9xB;)Mf zJUXEI3iQlw5wXat#?Z%Nba15)iQGY$%zHLO{MUu}dEKJ`?U7b`s<#8v$>8D7hCXO(VP`=#8HjA!OH7IOf)eEgPk< za_Kp3+pkDO~T;vRq(h+GVZ)s^LN<^%xE{YA3%e$p~W z4V1FWq@h7iYTlMz5x7U2A!bG)*|ujNS?kN=vDWE0N}-9GJ-Y>A&5q>GjzD_&%RVlZ z*^CLZhDpTqUvx}zE{1$=#5XNA_#*Qh*3hfG=|n$XZr=!OY0bfK&m7X1QwMWhd%;%u zGgbVui&{@xU3c-QmvE=eBo&o`R92^&Je6()s|Sv-W;hr!{1(FS*8OO*fW!S6lh9V| z5K0M-rD<0-IsOqg<}W2Ipuc$*E4Anx)c)H{$|qlhksC7UGUZoPDQ6m*DOcl~py>w{!r2q~L;j0Cuv zkiGZ*qv7M69dE7=rsf;uINR;7X~G2s(mtRMi#t3ZYbnLhJ!dfLb5Y&r$>;Htm?k=A zkD!vZ73fl?4@y)1$IzLFQ`Lo0JY)_bN>oS)%@OX|`<4*VD3xfYLP<&^B|~PCdCJ%% zQRX=BdlHEfX_BdwqIps&mG3+M-RHUg+~?lA&))l8YyDOLwY$jcvYECxYh4g~<+wW3 zFKU3dPtU^=t9{&Z-J`HvvKU@1e1mrn%;1)48*FZ{ z%6Y!G8afDy|QLE5Ud5`vnQ>6vRn)CuG`3A=xA zD`GA7P@bz7={MvuW<_RGM9zL8E@$qmr>fR2c6`n)qtCs2G2o=cI@_{PV!F zHvr{23b3zgGL{aWz%nUw%*(Aq!GkTZAaXoOE@>c!U+O`tY7*U0>rK*Dslf^F#e$*! z0=Tq#GP%6yHO`tQ0`cdIz+kT>PCjr1WrO_K8aox7Y#d1~W-qT9_`96Oa|7t#a2Jm$ z`(X^cW}?>v)p)Ey(+@b)AXx#8n!szs7h-m>Oa0i zTuc*%W+MnryyLMGL2}bG?H2EGgGu=QmE6yP&Pd}xoF^*=~t)+#2 z=V=0eZVZi$pxh4r%;2`2v|s6B;zyr?ovkXMm?F+j&50ys4^?3Les%bnt;aSsS(A-{ z>&WLh-B@4p13#}#!|E44w3bsr-ZTp@&Qvf?4JM3Ep%(g2e205G(r9k}QJT3cj|APH zM~l`=kyCTJ=n-P1A>>9v$3cUeQtKAzW+HJ{vqVBENC zJ~t(8FP=4eMSq=Bq6%wg;`!ebsHdJLUf}uYf8WcYS;IwY`EUu}-C!(`&Avm%D@Or0 zyaJ*Yh{C;B#<1Xg0gc*LPuF>zr`PLFQEiDkwCk1^-MUbnesXP~Clb=>y_072+}bTP zB(8^s?K?`RZI@$hh52OWt&o~AvvpzW{#AnajyY_aRR*jvA0iLxjhSB$d=S4{V14pa z^sf0!u4cVK_aj-Dxz!kdsm(!;%~epLWkr+r3}eRcHgx1Q_@hs^;EvMeSY?ECljjZm z$@`9+DDuq6DeM&vcU8(%!)50<1qlMG#JNRt-BKm#xGn(xEg{B7FrKNt8 zNXGhHxKeO}T{92z3@HB7fv;)xZW^>AL&Y4uYT!p>Mzv5g>v^!Z z?I^R$sEWAx?j&1zeJsm=67#OAvDhT88cEM>3YCK3ZQ?4;@ z2O3E$pACqs-befLReG29bjb=lx$UBT#n$DT6w#C(za(Ld%7nNQfL~hY396kJwbVuB#O-;Y( zodzkP>dX^D(VYJ%-b$v|S2j`Ij22E$A=%-^FgpvV|gtP4= z==&l8Ju9b3`eSs-O$iqwvAYknBuvPS!Esch_A^!bBtq1mJ!0Kn$&tpyOHdxTA4>B@ z$eC}6Y%#WzlOGr00;i3zGW0UWrSveywo{n*-5r=`{tcs=vqAakcU&-?XNKRKLS8qx zVv9V>CXbFF+?sWC#4Qh&TYD1?zdb_wxtdn{<%3xO^n5BD>ue@2uDj9U zum=n|h$8mCp?U^=X!zbrg;JOvk;u zB_zBrTH3&iMk$@Awl7`_#pfB&2TtQ?n&oXeq~|1DknxEoMP#xL>-S-@OFlMS+5m@V zn4`P3Ieg=N=AobY8Sv2|oE+gprtTghkq@;{e-EIFxjz}x_=B7=8U;nKno+?d808OU zp_Z#OuCG|c_Im6iHWdWbS6b58fHySJP@LMS2MR~KKA?l$1$6k*Lp~=nla{Zq7aAUE zriCxVpu~KOQ0;9BjcmINGhJ)Q!6sW8o78{~US}|LaShtVH(?L&nM~^L!<0UXhrP4; zuFFFhJJt@}S2;nF)N&@?F9Y>dCg6$#X@Y*u$)uUt328gs;YpMq3f867uwK?^zO@lb zKIjwt9m!`0dZ?$l3%#(dj4GBC3N-^y@P1ugx^;UO&HC_z?z2mxr+>ubi?pZg(5fX& z(T(rs-#)hEwqO2OaJCAoef7}S@)IT)?m%~UPrTC+&S~N*96YOup(o~H`J4k7laq-y z^QN*Xmq$aEDbK5Um<=zx+~IfoHDXfvotns2LS~8;b8pQ_oKtxP*P6^FoLw8;RM3sW zEm`E(y4|$*<8qqevwq3vGSHY`tu4}n!Zzuxg(_S z{bHIW^q@OjSem-thcZrwsqx-g$}W|oQ67)!1rJ>s?Og<%pO&8d#OpS3zW#!oE~sFN%bwzvcb75eMjGbZ&F8a+ zmY8#FA5<<*1YB4Se@o?{WPKKn`k2U=7^R?Ioh9_d@g3y7Ujz?MZ)37-Bv`G-Zv@JD z79_S;3uivvj0W$Na95}lE^0J@?lE;}ZTSu-Xg5L9=Mxb0ypmbi@P*W*K45MQtR~lV z!>Bkv6WYEz1;ysGc+q7T)r@Q4vhfC}^P7ezhnmn^?gOs*b{9oz@1bJXM7S?i$4Ezc z!s|;5*?{BWaOcesRG5j>BwUXWZX9_iG*X*NwY0C{izPcq`CLa~mH28QSGr!&e)y_sPkOB<8f~LNecE6VdaHX9Mpa zgqlSzD3e0y*OgO%q&x(fWyOA-^RG?J$KDE!uq1VfJt!h|{xoRp+ zR}Z|VXOy;6i+Q2cbKzlj1MJg!XIhrQv_!*mk4Z7jJOlLDh-2chNQSlHt; z0LGr)HVoHR2t{Aclbh*bd@g(=d~tn)sv-yJ z>e-RZU(NHR>_8=BRe2nqU3CImu{U(8_X%=nzaMR$`G(%q*-bBwze8T7C{bz~NACM< zBbSa<^4{p3Re4*NOfPLr?D617o0@hfOVKBaTYy|pJVc} z28^s)j0;a#z&hu8s?GZdkJqJ>D=V(yvfj%mH}5(Yna#nq;&(ZZNC$4E%{aXCco^r+ zmw>s8zmdIJ_h^)AH@z?aRXDz7P^j=enchB=4Hf?>2uQ^orojR%cY6`k$@DW%{>ZZpRT0i zE8NM^=|vFf;>XM}9e@bU8n$`sYcvUZ0}{`SaoOc`Xz;BBH-}pS^BY5?r@)fd+)bk- zt(ruBccLbImQbZR14n&I<|O@B;ROj3ZvGn&F39C2w|u=FCl`MPuWI~9e_0s_kG`KO zoU7<3lr32;T$)rZq>um7lb2m+d1wQB`C&1$M};KqDus$)9(6p3weB3t0DLJnLjA&=GfF+X%!kPoN@ z;d_3MS>s0hrpJIv;RXnOCW3lPRiWPQ5LnC9W5(wm+$H5`6drP5*Y89w0fSz06)Xj{nSs zb)SWjcH#7_Umf$My^}mJ@djHpZ%1|f8qPr^}~rgIZS8YJ%w2=ULXuuL%${_(y=O;biqeo zda+f8DZA*;&Qofced01ct|38kC&q;Rj^N^kz3{$9`Mi<~&}dD=pfGLTNye2ti^ zRS&6|ss(dB#i{x+p2Bg*&rp%vD0ZIgSf;_j2=Z>)g6c$V62(@qC(4T%{1y{L-yUr8nubPL6>nogS9OXpbT!)aHB4A|HhpVp?68~4*AD!;H};a$2mq?uF57e(hSJLr_<@@)EORXQtNpIkd~lq&e=v$4^) zp>x9r@JzVK=)QF)w^H07f6sSzf%OYP^M1$brz@g~sQqr@dA<__yVby_aShH@I)&0& zr*Rzb#j)TsLRK1|gtBWU(+28B)eiGLrOv}pq+L!=Ien+?OBc|4DY|@CqLAXrG!lAY zCCNBg!rFcxhr#Jv_*qK;9^1onQezI|l$?J|X`Cjm519m8eUjkENgpt6eGU4@lo`pc z$zaks1MEUA$%FDl!RUSz#OenV-8&vkY5h0z_Fc&A5B>_Tj%>mWE7T$Ktu=fk>Nv)4 z3`w=ur>S>-(99{rq(J9B)J}M2W)^dRmhhRglD~YPR>LE@;p<`or)%IlDzK?hyzlBl z1vg3|gIX$S&E!o*bP(F52gV@u$l~LtmQM zZU|ug-@hSO`!9j%(b=z*&?EOD4X?UK7s4GNMl`#%S8ymA<5D_6VC5x;WMwRaeSEuEBZBq zUi+9%7vLI{%9w+VR!vlViVp-A>5zMWN0Dvs6KJa32#wY_j7=@-c;^z&g{l$7HEIWW z4^%P6A6|o5;`V6bd;~XS+oDP39aaq1GcCVF;hfB6w%H&Hvci1H$DAP2x~7dxup4A- z0_|(^pC5#_QGX#}XiCk_gd&s;4}~RsXTqmf=G3*AVS-I%h;W@ec~aO$FO;mK!o(Oh z_rG_zaG5qaF#Vq3bHrVoxsk!{$-C+4QLAag_(+@^bR9o-EWxs{vlw|K77LFDV|M!o zTwtDqF{2GoQDiB!)LkXP{G5Ftg~FzpvJm*7g*BYYce?Z}fxW+sn8k4;uq- zw41*cTpq3_O44ea;t^}|C!@9|;Dslr^L`Wd8{NT#xz7Boip8)tYn<4Ti>_ri(fDNx zPMgw$3de`o@DIhHylN3RFRNiaZ9fo+A>Epg7pKYB{aVcF72Z4(vkPLqW6_uiN6~LR zsO+x=A55f4-IVb)ngMBeV0#f#?~FLALcXQyb=q(_egLTzCfQ zv(uIA@4!-~@nsir-L?SM1uSDvDD7fm&UCPYGOtM7j@3wnu{gf97Z=-3gBN=qL*kSq zCQ<7qT1uNi;rvX9fA@k3Wf_v~MW~ti6B2B^k{Xs3Q{`)UD64CSp$7lZ>g6dYk$wk) zKNUiqpMO`XT`=uTnoV1p|Pd*~^ zUi&k}`*#qGP^)R}n?cUFi}AdeojmU*8NPjML(8#xkfoFYNB;?+OH>5KH3ykflW(F( z*Fj2mJ)%wj!XR+Rboyy%JtfQY*d=lEsqgDDoO9zad%c0@tH)Fuk_USO;atiZT+}%huWIX}IU5W?HDb)Bi&uz=)njT}6iG7tKhsf3K;?cGtq_ap`a&xs|=1WJxqvS;B_{k|e2Y5^3=JK$0WtiQ&>PlF+UM z+1{h!WtBM2EPa8AEYIwj(ahSb+K@qsN}9A~JRO+JuScH0rgi8eaZpYm+Bg|^x!$8Q zckCfU2CJag!w(mZ>wy9J94gne0xNILO4F7H!JKe$(0bh%1LzDtsTST@}~} zACXbbCZM?E7jFP~%+v&CFfrrz6VWp{?1!yCz)9~mndr=SQl;CG`4zPwH)cA{kB`7r z3oP*Dr~%Zt-NyJBhLD_uJCr_qNPEBV9gIK!z^tp{(9VDEEH?$>TX%#?>^V)9vn+AL z2UWb-R{$OU2gsxF#WUQ zM!4ASBOEz97n0Oxq3qOgg4`>E5ES7|&VQ+5m73KcuA&DbRL$7tYA0Ne`8X@miS6Hb znW-~;NBHa|OS`tA$P$6Jb57t1IiH`zt3jB zb|nZ39hG<+j#kaf4gtF)tBlyhhp^wwTp+G$M@`VRF0$mEF-p3LL$-ArjvW|8#d)jH z~@M}(sqh1A>k2kkXmCsbKp&%7L;N;z`{+OTFmIU4#L zSG=gke~S-uay`7)exM4kpFN79m5~_stP5Lt-8=8z2&QSA#oR2wQ%8K!UVjGMKeLqg zdpSbt=oT{Xs~0=CC=}@PZJ?$i#&;y~Jgq0b(0I9(?@w_+t6>i`iLZb!H$2z_HhySy z;vz;zd6UeS`pjlyOIjzfR4AQtn=Uc76;A!72!Gm@XthELc{yx>ad{Itl?k`8E6fPH z0$$=Dm$&FraszWcOR;$VbnM$6gavnWaP#v&C^*KT`uw9P8oQiLj$KR6CbYvIMJrO0 zqDA7iFNCv)KC_)wM?HMnZ$CG@n=gEO8N*pAu@u>7Gou?Xco32RqD+g?o~Un|BW zBq=lXNsh!oSV?7W31C*@Ns!u?MxJ!okORCIwRLGewQ~2MYDMO3WW_h^`W^!BWFIkw zPZaR`uLz9GR0c~6V+eXVgjZdhvAk>zH$%OR+s8A>q-rUio1=h*|6JjziVucVOvjq! zSiG~~8!l!Lcko?h8%83arSmc5SM92~+47k%E)Q9|&@D{%#52q%Hyw6;UJl4PmhoME zPl!iA2B}KkLX9OVaEyNy)Yv#c4--zKTXd+(^>OI+dLwFtUq`1KdAK5ZExI|X;WGVM z0&L2}iIvXGbp3I7`ivD8A8g^2J`H2`3w;#fxh6{=xDi!31>l~#qI>=;%$2wbyP^z; z?yIX%|KBhu46P#y;lu2XsyAfs2^o-Ow821rCiCAhzOUxMLAJ+x2He?X03%sdR9bI{ zPLU9;`K~>l--pwgmOnR`8;u{ygQ*s5_STEI4bnKZv><$?wF*a`x*&V{9u_tB(@j17 zs6FBfl^2`PBku^O(G`uG?iXQQwE&B{4zttW^`nWxX$(zs#~74{gswd#bfP_LBfgcS z+q?s*>`lyrNIUcTJ7T1vQjJ9DkJLQ*`Ma7G+XKbzhxneje0HLQB(#i*C)|N7DC;?e zN+3s$9E2{zf zDf+PVr~?!Z@EVw72br|wI4rj*f;$d%@Mtx|Mh-Q@==sO#><>ZArCa&z;zusH=}8nO z)NI1kzBE({5hK4u!(jWxZsC~CFL*}4DQL{q!?d4+^zpYhq_D~sF2a8p^H>z08XUqW z4a)fG+E>)7OoY0x@i=M2T9j6L3YNdWg14Ru*hP2})5qeF!;NDrXUCJ8>P9ALE8orV z=rb6t+64c3d}7a8^)SEh%fWV~3#57dFkE!jAScWuXzZ&sW>?N~m>QJ=FYp+gwkg1w zd;YSTt87SP_zOr6-U5ER)dUMp+y_k)mg)%av3-K~nA#eKFn#O4ZC@E+i zu?T+T>%#f_0}#nGF(!?RVl)FMvL7dH0}-JY$t-jyi;O0~k_>$ae!P;&c{~%cPY%Ms zepAMcu_RZ*3`io=OJe`~LFKy6k*@n>D1@=-_!;SmJ7nvTZ^%W@4oImUx}rc5_o@pBLpWc zWM+I*BKtHy3t|huv8B5P829VIjKu20Ov|q|YDM8pXOuNNChP}9_%v~$@=Y-K4%i#w zX4HqI3)Rpd_tUSIF_Z)RZrR1g1dfo!FAO438di|L1aFN+&KN0OF_~XOAr`YZBi-?Me zVgkS4EXpbr*d`pHho+xl_8;CRxHfh^{1)N+xe8MGbyow|8v9u~@i|I~<*^#FZ>mi% zO2d`&>zU}C&mn5Sf^3xXhV!5KJduGqvva04@!d9&WV6R`{ib)|q;G_qb!KCn<9$vf z;5Sw!y0dqJu7bCmG|gul@VNVLEYirt#C8d8@xHm7^dB!ydU-i^EwD!Tx3vS1Rdm@>fTJzHKv#Mfo|{^WB9fCZmiHVR1h(PcYt69Abq#YX&r49~a*GX% zJuE2Ea02FiHe2v)5#w1OMYP_x5$B-`Xp?<tgxIR0Ce&}98M=INcuq;0`bGR8HL23YN*Z+eq4+Q5#J*1m{Wxt)02 zFcI&ZEJqKiyWo48!s)FGkkO8SPtu_f_D-C=;}pcozR3s4_8w-|V>gs}=z@OSZk*pD z0j~|K$d7z8x*}l#Ra?IiCDq?EzxU}veV7w=uhpXNE9`5|#V>^DYL5O28)WB+C!qer zH1clcP3UbMAOq&S&$`5z8lF9l-b1Z)^vBOw^;i-vrnO>D(Ik{Hm%;Xusd&ol4jzfz z0iU-Qqvor}&?EOAElrOQb%zxs`D`CR@*OrbB?U((6vM`m9y~O9IUf8`i;?C$Lu}<3 zdT!APo@G%&Hkt^~;R$c>{rn3zdx#2^#+{^ZY7NQR#*1|DydElT8llZ^hoM5%oG67@ zgUC^BI^|m*6r3>T=G2sM_Sf!n6TDYqg2yYoQ1urC zbgQpN#Z^`8@Cm!t9RHr%bS8D|uEkfDFlbj;p9T0Pc`m>hD)Cp9K` zqIx>LklH02TbKcVzncqHBrnolZDsJ&wxVkeZorCLsrYtxKUQyg&&g>$;mG<`+|DKu zZfp#G?#Ok-?H9yRwa5`-W~o5E?OV`NFhV8C2+-@=1mEcm z&>ne@Vk2%CzI;C#Z*4-Ai|$ln*C-l$CW{WKpCY?jr{eRSd*OXfdUap_G@;Ic?WiF7 zo$=(^!0=HHyL*3tf%yo2J1fe~11EglI)R_bR&W|e-0_Z7BHqBmXtgkpT3RXMic$Y? zqR&5`@lsIZul$bPCo2i8JquF>xlkql3QS+=gO0*4oK?FM&Mn!E(nmaiTel21^I6U9 zyX&Cm8L!vso@a0E386XT#OXiZxim6=9e&+B9Xi@GnYc~|#`k)1%`1zs^yHFS?B;tt z_B^wJ=%f#rH-0;((9^)&ihGIAzrDoG`CH)EtRfucsepH*(`a2@9n<-CBdoSO0EtoK z;oY=M;{13zyKI#z++4$V_`KN;W<4ulM~*70?kR$h;7u?~DqHZZpax~mL|~cH0Sx)| z0{i)GG*M5Y>~J|x5+j`H_sEjlyw_Tl@{R**;Z`1un7}C$dlS1GJ^N6 z8--)7hJ@4nVuW^Ejf9%9y+X4Iku-k#ZJK;Kn9R7QO#&61K0KPLBp9#~hT64kCIJWot&c??-!2{(g4kBHq)Tf^DusEcNTfe|`k}8~riw(0Fod z*c-Ntw?)%H51chToihozkM3^*aIGH2J8$H<`F3eox!Vkcyl+l2x0-&qaZ5Py{bJz~ z`#fQUc7`y1q+7VsOiZZTF-kb5{w(9#af^C>Qiq%B=W!E%hQ86NNaROL!1i%1IA#`~ z3$>pFiAHasIkXARxhVRA_QIM!4grgwXeZ zyztP?DBRQ3Rn{v>%^?)GN zcoW;|HUzzs&*IUzK0Nk427^jpak58-@paP%PTSc5ANLsG*NKT(!sn%v0~T(3zi7cOE4Hzb*s|-iQEQFf7MZheNfi~w(W@-XSk+OpQcE>7UH@fv5DQkvlz2yXo0jmM*u;rDgMiNy1-mYVuK&K74CL zONLZXoE?X9OA^p1uLcD_hCwi-2^xGy-?;NlL?X%`HLZ=IF@7x*y<^~dp1&zv|Na*K zo7;>T<^LhRT#I?Xb2+u8&yjC>=A`zVz{(Zo+~~1!98EXjStqygoboenp?ny(&?%0S z-9DdFY2erVfpbhiPBIx;xB+MOyv3lS9aMJ~zpp1wVVVErsCeBnQdK%YH$I%r*1W!t z)aELl3E2paU*3UPqqQw6?XIaP&tg; zKN>GTo`m;~9>X7vFSrG!L7Ya?ZBFaZB5wKtbv*a@EjO;s5InE3bm72dn3L#;*S&Aj zC1Ho?*kyld_1|dvSNAj>*q1~z>boGwX9t>ZI|fa8Baks-4z2%caHoGhMqm4jDq5{L zaqa_1>-ffgcQV59ySKr!qbHbSXH4PJRe_*;(R8prJrgyt8s)OmP#DSIjf6hvy=gSw z3wnm_^Z5Uu$`5XG_bBWryoaSeh2*{DQbFgbZ1QcIm(bjNuTZ2wneO0WD_YH39>8W6GoK=;K@b#n7B3aEIa0|}>2{zY9YuJO2Oh>S^J`5|opJN(+#GEic zyd9>%rl{BB3;k(Suxl(;cJ?AKqKk#f{GGqn0|o7)Zqg0&7<{#O5}BNoN5kyK(`B!_ z;LvzIs?a6HZg7)EKTA^>>3IrG{(G=EbvCw&zQdBK&v5IqD)7peM;+HEOvMXj(0nO` z8|$X9*Tby%Ecs_fLT3&xS#=rLlq1HDxrkR>-{P`SpJ0=}0w%duV-w_K<{4}B^pNJ% zzMQB&;+;f)6eS4Nt>fv<+Gj$;{Qb1uU0i4rdl(ix)}jpq+vw^wK6GKV8g=+_h%MT) zmaS~{G;h!H!&ULyVc_F9&fsAbelz-p^}qU1DDI0E?Wb_dwogp8+Ju@NpOsneQXVPb z5pov8^O@Q|&(Ly3JQ^0JV&ImqPRS^D~@S+BB*qI z4-W>`p!~c}{&yduqdtU?)CuFr-Cf5>ugoi4{#pc!mUY7U@Sj+(!E;z@O=*aLW+)0q(5bM(c$~gYyK)&RR7su&51RxB=RpW!M3mZ_}JJOe+bp_%-O#HhFVX4{qnidT+!$g=ZK(p&6qzp5nE+ zE-+$M3(-p@;Fj-W=q+)8pTRCnX2myf)8jkmC(Sl5Id~YJT*!l0?>uXIcclVzGK&Va z%%XX(L}_I2QW|0q#$M|IlC}Papz?b>uFtk*TWl|*{*w*Zn-xQ~+V7Ep+Ib`^HVjT} zi=-E|U+|e@XT1O95I3{m2%n@389dOK9WbI}OH z15wCFl=6BS|J0uBLe@vyFy%fO^lsBQTJEo8CRtQMb3Y}M z%;81!%FG+I!7my$CrZ+dF|`z4j)vz>;&kEPPt?LMgjQJ3B2P-i$8h|`!DB$wfjCotxvPszVo&*)r! zKl1xA85~CC)8(rishjm;Ixw<{K0ody9JgpL4cYXF1}r;43ZFPb&!P83Wn(W~<6bg$ zzZa5K8Nj?d^8^A@he&Ei6>K>XB}nG8-CTxJt zUeYdk*ny_PI+o$o=6J^!`KP!*_iRX2-Jh5*-!bG>5g@NG~(g_ zP1V^-_nv=FQ$<$_CBrI(is~=vKe>hE-zB~`+ix=z*TxBQbgH3Iqyy5jW-=BT4?wTA zj!YP>LM&FiWD-_kyBpOl@kfL<>(p|kKD#YzYIuXQwi z|8G3KzH~QzFLsR1YJ9?te!dgan!ms{s!Q3-dGt}^6z0OuY4k*K2lTHRhRtLztz5E? z)=Z70&$HU7=4=y~p#BDRwMeUn_PIvSj8-Nx9@;eE%|k5AlcGvXXH%0_K6kA1k46kn zBs+~m;Y$4toHX;WAkE?qTv%KIQC|(<&Fa-Ss$H7BSUiSVQC0|hvd*D|d=M#GyB6~N zk1=cW7tuu>(o`}L7+1dtFv!|WxsQ{DQ}^$tH*?nu=g;AJy#8{+H7~Sipcj9q*{Dlg z<#&^tQcE%8M;XfY4P%7L0}`{+k502b%6B2UQnJjA95$ItQ&T+YA@yo{daDeXu)K-) z@KSgyX-rm47$)_7^PTTB(Kj8!3l@7y6L|1TrHI)+s^c$m4e z>>{KW?`8Vm{$;Z6Ehg*6_0mamTIm>3A>F#<2|e|ICuZ;LH6N7JC65EWNP5RGHQreb zT9I4m_yae{qDd=3blV5$)DOat=dbZ}fd>l7b0)}ugQQGJI=2d`yObglvt&XovP>MB1br~W-4_Y?A2o1Lq#7dvz__j{l6I>UbQC&`Fp98V_w z&NoqV4)3e9*p6{dVn{bip<2-k_#|-(WOCYRNU1yR|HjdtM>pvHS*g^p$%MxHE}@3+ z_S4m0lBjxDAG<{TqIuA*m(1O>yP2ZndQe>z3|%KCf?Qc08t}e-m#YWR`Sw{sj`RaT zsIvhCoQ`1<$6qE!|B}hFkxxWrl@DE#Wl6_=d`eAR7cns#W|0YgJZS2L zp8DPxSM(0cT&`AEfd}5zTnYL#266q`cBms{HmdfgneEaq|-@)fi6K z#HG@)cdCdgeNCvYIi%0LgA>OehIZ!$s1K2WLv?Lz+=vpB?IVvOzYpO|>4=&OKO@N0 z&f7KP;GmY8U%=a6A%Yc~W zuJBCJj9eZNCqAn`fy$~*!tHkg)$>!~pSBnqaPbvs2gRn(13XBTBDR$yI}Ny>TRCc=RYW_0R)OCmJ)oX5sl`ZrtcUo_JB7U(0nCocM`e zY?0x6Ay%tVeLEoy()vqGzaA%&()MtE(_NDG^gK0~H9$nuj#DK?8ThdNAJne3gYO<{ zps=@xRjNI#6Ri5+hV!n7;p|jrykNWnD_)4=pELJ3xx;q2w(vDu zRxd@42yUUb?+dtf{1q~`*Ou%C!~Lv5xbHngysv41pCu(?yHAn(3M;6t za}W6{{}i;B{vn%0qS(>xW61Qb0-`8ZO9tA0(!gJtbg!lbI6C|Xs=MTI&KfUR=8hdCy#yOaRN;+uSI8%HMPAxKZ;F|A@ThB+fh_M(?jNJlYwf z>Pv9StYHlGdmA!{M zwXUdXaG$&@ap854F;v=b8ZF+-k~Krc@N8TZ91jj88iUta|AD_4E|-l zB}kVDhudMLMDAlQBjyaO``k)maeWYmPQ0ytV3b^=9aq76%3F}SIbYe*+m=*vACArSASF$!tKTk@1y7AlXb~DepO?g1gxs_x^*Hyap z;5S+{>l)RVDoR%n0hP=Dfx6Fg$sKJ|VxF9hjH(uC^)aJ6exAoUpK>tS)(!2;zoW#P zwKywo5~j5}lbTjXa#!)*{}i2xTTO2lhBb>Mm1rU*zh)7Q`&}m@5ebo5WG-Y%rf5(q zACIQAnk zSu$12hfa5C(M=ymGW@Wdzkgqw)(^S~vMR&Td$ltj|0%(&I4?R9e+v8S>x02@H`u(g z9W#7yQBL&>Iu$C=D0Yv)JHy?{ysrg&P4=_62{!mr`8m^2Sjm5?c4a!r!`U>`N|xSs z1FG`hK-K4R=q>#Q-IjBK@eg3yftMiGiX`jCRj}Eso16JY14f_SCb-MD(Q}V%iT=}X z5`#i@iZePuWITlOO#3m9yb`+e^&71=lSkQo3Z!#W1%ym73{PT^IYkWy+V7)%fAhJ} z#jO}R;uP98zQ;$d8D-`l%2}-29_-S(iE)|4yPN7^23`aCD_JnHc6iAKo7^X$9{(-;TX^67D|-wlkOK%P`cf2;Z9o;e;p8vGd$EH40k!+v!p_m_*=ePx z@I_-j{5aQuR}GwSRn={Z)ox|cdXwPvEl-j=ricv%lfX(QjQ)GsN)vX+AXr3^k`4|D5QJ-YAK_rZGtl9U~`AYdUeJ9Wz(&h3QJ;LGJ!Vk;=2d zq&r~|>*nWS_UZSmb?;y}7!ZsP1`6}ezAQFjLq3^wmI?g7ja=q%eH>hC%jd*) zDARER1Kd?XvHCLZxKPM0M5aJoXdc{OFo<{2HGuR9%V?x~ID~-=yy-U`+*jv7{f4jX zM5;H}A(cuarbtuzgmLt`!HD%7$Ycr9(KPV$O)MU|8**w|MJA(nh}M-4)*2G5Y6BALbQJNVkrDL9u!(%W8I6vw5{_T zttcv>i_=qSt&R_DIyedn+S<`gc)yIu`2fnodFOt5EEGRF58I9PMVeD@gPiOtkyhkk zP#(LCUH-F>sOTFN2Td>5!?3pt_~W6GHLo9+w#joUF# z>9fIWFyHYN;09p66@yu!^bV0+#B5R*1v2?fQc!Uzj})AoVUNQ&xcVy_6<1zl zRzuFSyI%+5{Jyt%^o|dHPgY)kKGIv}`Hsjr<=h_9f` zU5OF{*KraBy-;$OeG7b{Ke(+)z_5|WfIkudhx%@j+!GyY8aG{HJ7Xp#Hd=y<`fzAg zmP!}~9y+@B-ld1V)D@pxa z#v&!3$vSKtbQ%Xxq=y1bv50|8uS7QOzVP`Elt~F0lk%%9*tqq-=`Vr&BesHSiqj<=lo_o0ACS zt;pQd#9@-N6KnW+mMo;w@NWHowDREyI#@|~JLEJ?xRrzX>nripJ}vQCz8-@^XJO{6 zLnI?TA0Mqvp!6fIr0Dw^?v?pN0kY)q+3X z4vzlWf@81SP`r?TsJz=Qaahzyb(&)+_goz0 zRFmDO4iZoPhF8mffJI0LUhp?Y7acv+z33(G9zF|)U7ZJ$>iW2!f-}J3zc{4fAL+Eo zZF

C>biSmjBfrz-v$Qam(0Xcp1|Nk#_&!&(ti4b-7IbD^0LiyoS2hn^S7fZ%*l) z69tz#;jS~GH0O8_EgL_JOD}L_-tRY|aCbOw2&6{t037n7qZ;#NEhHT z?UR=xt@uB5C)$opdYHrBAHL2~Ro23iLyeH;t_%Yg{ov!&9+ahsYUpUeE*dh@o}SF# zDP*fc*wvrsVbb|AkZ&(7l8)93C!|7BYP9?A}2TWq@$t)nu5qsRuT_sAUf}oyI5A)JSvdMChKV3ndT7!lKtU z5Z!6cjH6#;`O-3aRQidp?3azN0v*ZMplzWClKB|vUX9CiVN-Evy03Ze4xVh z8z}l>2i?4;M78m&sKZBiW)jzvLq;O#y9+y$yl&QC`Y`SEct{EHJL!X7p+t3Djzl?J zpMo=fqIB*<_WPu|;2ydt8ra|w9_@i4kIW!q-WM30qX)8l z6!=W~&Gu3Odgi5*qLDq#Yv=JzeKK+1^2o#|2pbDn%h^>Ky!KW*Jojz_ zS{)1I^M%f<&Sz^(s*+=N-4AKPxOjYWAcBL8ej) zRRUYFDD4PZ2=m^As3L6pkdOKSdoiPI2bwu*u~eJq=H>STq#8W8`Ga-xL4k8Ig>#%^!G5(nvdmSt`IN`$xYWj$4S!X|v;)35G``Q` zdqVU0O5+|Dw@KIb=QU;K>{`QXe|myO4LMxbo>KN;$67WzpcK>&6oJV@8y2Kq0l<8q z+EoU^6E8v8oN;jGWG@^oKLP$D9)VnaG7Fx~vt=9Z&|&#Ps!6mZ2ayflpC3-K&_q$v z6X}3)2kr66A`L|?vTbR`pEpjBIc*@ff6CanVJ;?J@573)57uwuk0Sz@ym?=q&A-U&@JfaA%e2zMvxvo6c!y4 z!$Fq_nEt&F)RHbjsB1e6_$%TXjelZXpFY*SI4c?L-YrpiRx2@f(4g+V3Br8)IDIk< zl_;jVk$(JZzVeWzxZ`dWFKZ)*ca99eF>jYMgTq3vFD1>EzwiXl&i2Hhx`8Cq+DvjO zmsmiNDl1AkZ+~n55H@kv5$w?uuoE}ViLd(|ezl z?_&-`O7Z|5ABbd5-iBe8zUcjY2FZgKNqRjca|2Z*=XOXWJC{6?%sz5L@}J6BdS|+s zR2B0uY1j!`ysV1tsNmS<*>Pm7uPk_grtqaB_u?hBI8yJufjO6D(8A*!Nj=`i4mAg& zVc`?jBG-!p&c?G-R}X2p+d>)sCDglDV08AMDddYRCCYs_CBuIfpp@M#{MjDF zwDhutJLypj-r!+*Uy{{y}1IW-}%dUdI8S%cTs+Q3&;oT0MDt(aPY|&c9>Sekqc*E{t5tuafTjGF(@_gJgyN(5@XqHtCrx z?XB8EitA%2X~S0Xe0rGreOo}YT(X4ExE>#{adbFnAU>Y4MST9eKKt@)B8-}~mcQYo zftnA_vnPjAShBqvMlRLE)t%jJNYXdR-1i8YHr^7c)S1KE%q1f0z7Ubl`di@ty;!8P z_bXPhZN*DB2eG-@QWDkoODR#8|;4x_u5zhb@DW>OKtJ_G$e)2Io8F5kxXT#SePQJVY{DS3zvsfVcb7e(?5tKi$rzhtDn8)xr)&ngn~pf78O zNNa8dG`R=EjG9L*=|M0lj`N41;}(mC=JwEzhp%ak`&NX;iv=AzD3WoWDjIq5G<2q%hw%?9_*v%_g?CXM zt$ZN`#U@8+*IX@0e>YVc@7l^0eBMn-f)6$dhm&BupmRzYbldm=scLQ@W0xe}C~^n+ z2foB$-@PpPsGdXiwp>>HEtgMkv=a8&-`MwytI1hTfn3JlL$jWbtZ6WheL2sFpOPz) zn}hWA`9z#B{uCkIqf2T3NcHSM2%6M|JIpFrWAku0Bvna!&M4DLmuV91sx!P}@)QaS z`5=*Z%A$vJ<_Z0gJ>>X3kB(YqP~d^3boJduI=S&4Wq;8{^OerzsJ0&)?xrLE)SW%_ zXk{NZtzoOlmDNv|M|FXRfBn%6GE1++s|~H#Z{#Jm{F^tWb+=N~RdaguCI#k-?wlsLY6P}$^IWpL7$h+&?v+gNRu%3> zJ(M(cAl6;Jhdp&kbg18Kva{Ml8|`P3db1hHii=2Dw}GUTVjSM+s54n}TNr)MA7p#A zK{+QEAD&KS;RiTzOm8C%yApy|YVQ&Ewi@qrRpWDq|40&di!9!5;o}32(fBRZB(v=! z6|B{i4Bq|%&6hPZxeyUu-hUqata|uP$qLD2-E_><>y|jtKB{;!k~Sjj@GC$;sl+HC#os@NN$J5Qhz^2UpCgVXHwr#U8x@8J}7`!&sz2_Z2(vK z(SW^g=)0CG>nR!)AI!e1=98n*QoeH@NrqYOpuNo#VPd%-*(?V%mO0I7lnFlgRmHTn zVF(SnnMmr3f@ov=F}(5|$z(zX|LjyL^uI0weY@GrS2v#(TP3pvs|@+;1zXuodnNWf zHl6-cZ6bKCNV&ToQ=Pgkef!fuUTbw7_+MA~U)Rq<&a^OC#0!qdjhjTew|c==IusI$ z)I`BUwu+>e3-d_9U1*^Am7)btr|hgMe$Df@?C{dJ%u4AP?!Ht&1Jau@%R3)yM#y5Z za*@DLZzFv)7tdbME1uva%T!KSvpn~&EUD}nb4|I*pWG+QR^NEWPL24&S@u5WtF8Sg zAow4hm>VJ)m>5NSbPVX0b`j0ht8s8KF@yRKagfOk#eW^61qRn6$0I_!j%}La$x9MT`L{7k_;l6T_V=#0kzsES zsc+naeX|R&F0qaaQ+&sgw?~1=uuok3mJh7G;VFz9s>$~|lL5vd`4I4^n#T83lmC<` zI{s5kVF6tNgDnzotl9;xOL);J+q-l~@Q|;1C`%nj-6V2zucEuUnMi3+fygFJ9!jP& zc6ISSkY6}}UVi$(Qr~@q#TJ3oKkqE2)%&p-Nh{IR&lS@udT7&@&3IQ<8%Iwo;H@*~ zV9vGCpeecoTA~4<*ZK+MM%1wG15&I`PnLP_(qpG)XL4~X#*j}+Bb%#ShsUIQ9QFt9 zWG27rgbd|8mZ3QqQpa0kZPRWV-Xiy_6>`7?1%kxXQI~b z=M=N$FS={}=EiGuk$>ztRDV2}rH0h;-=-YLmm6fTLFWs4JQQ~B_3|{P(GC0kR$^Tf zeAppPb(Wp87vzKxZ6hOvvhe@Pw9F|BAj;2MKJcW9P z)5=r#(B=DoY<7|omL;t~a{ETAORtce-*sHt{u$dkc(%3gHJ4OBjvcvY#-77sW?EHG zvPs75(z-8btk=h#RiA+$ukXOhnIq`GM|Q09<8c1i>MwBc_+9Q&gBBF*P65{)s%U-t z5w&p95|uDd`Z0erSib)vl6AZwKBG606c^h``hSv0G|s3=Myn@q2kXC5%##x|d2AM& zCG=1gs_tO<#0nuQm#v?34EnZti(GOX`MV}#sQBPA ziPNQFl2zR!>2_+Hu;VX+eBmAIJa??bxOgm`col?aLiRz*oIG+%-Agk(XR*@xY2@I) zgb#1H%=W!E!1l+?;q&1HuKCzebl8AQylEIOu!Y&aJChumS1qQ%g#r(6cOQmX z-$H#=1OCnDbo~BdJh#O;iMz7=GXKP~884kR@v{W&7BF$(tG4B*KMH>)?28g3u9tZ$InjJ37~YnnQ?pAymgc zhL^7jM8?wA_^pvhX61i0D}9B~!PR2*FXPDRd$Z90O~r*X)5&CZK23Pv$Co79@GAc) zVVhbP^S`p3S*&=;homPvRIWXWhd-TWSLZh3vY*dUV%3Hr&Rw7|_!~<0SdrAI1lq5( zmG;=T3TH|oH}BxXz83{S<$iUxx4nq{*|-rth*QM$v>i)rCs0^qEh;zIkV0WLyZ>5V zaLj!tqd~zmu#2b5z4pR8Vj}ZybQN4|X5b`ki`Op);KwrynNw93Cd@VD96xD@f0`Gw zRd4pN*wNcSY4cM~5?0P%=}p5i;WlJuzZb8i49Clw8Z;=RoCXIM6X$o4k~E6Q_`qrE zh#ALHuiX^q`}xrx_Xjk2$SFME-w`8)|NhcV4=MEgaK3tzFMhb*Kumo#cI{jRoOTR0 z)tTezd5fUmfOyzAq>;Jw9EObUcP#8zAA2=m2Fo1P$(5FRapQc~;q4wpe&i2fPI_CG zZJFfG=3X1a%oh_4aQcQiC!)wn;GA!jYozS!lPGPJh#F5nqym{h+B370ToZ3Y^gL&l zwY!asUTU$eIzpDX`4=r)JXj)iVlaj2AE%QJ_XK85Fr}SeKzdc{Scbci!|R|JmiqV> zB)@vf&K>mS2Tsg|r|*Q`-<+dhcVq<1)s5jJS4u$Uo++zHmSQzR|9|*=cN`jABK$rb z@LQ=KDev7%hQf?zd1)=?xe9x!A7WZ7<3*?Aj3fheW|C9x9u|5oPNZ3p&S!0SLW?(? zxmN)=vC=Y!dJFK>;O7 zCZx1LV5B#dQp)XSp$qzy6$WlcRP5v*yNrQ|2QR>r*^3-ptwi#FKY@17TFBNs#cq!~ zi7zylg5t|4@_lum-iJ)2E}15ZkDUUd-NA6iW*&6*e*w8q1aFg(GRTX3#D?>OAb&(K zgnxPht6o`x-ba6C@$fd+c62q{)xC)QO3`r$AF&9}mOGH#<4kPayA9P&w~&3`Cd@}Y zfg6;B-}ZcfsrJwLlF)j%TItF4Bp-lLuY~vWu_FB9dWW2}{or(#p}40zot<`n0nhXA zU{z@>rPfWR0^Ll0xKWuPd&&fmW9t7wBTUlDY!Fz5!9&f1qI=| zD(ZX!g|Pw5?&3cdRpgCZQ_XRdk{L?oT*oU5thj*%)+{WfmDa4Qz(-cP>`Hbk6Przf zv6mOHi9<)i>XHqt;m#ST%4mk!s~-dRW(m3WKEw_0wFP$NbdcvXS;D$~wBOH$-WAEx z0r_Dtea;d%ShG;1JUw3YUqLAJJ^l@qUlc?NBR9jp;ARM2(8Inw&*Eh-3oh}Orpd0 zwR7PUV8t3ANF3BB8fAU~{yI2&x|sj5`X|1sO2MGBb<8Lf znQ_G)-Z)8y=HV~WZBgUTeOkzm8@vsi)^eb-dpjpwniu^T`5x7`v!ly{$R^o}Jy)R$@)`F(F6yiLFoU;JZD3Ke29k_zs8tx=kkW|Rco?H{)7vk?(tIm(Y%<( ze#%1`(+=+a@UtL4A_gva_k&QOLmwa2$z0U*FtaZW%uYGOf&1F%`tb$W3%)fIfv;QX zeuF<08bfvqirGMQJ&dwjMy{GAOhz>h25wvjV`6QXzRWuqb}y4Po_q?n`?o^Wi!(gG zQSc%bUPsmI&zaMzIanqBEjVtL;^p$C{NLt-4hKH;!|)G(*$L|noak8-ie#)i&!jkdFQyOy$ke`X3#=Wf9l_6kxplEHJ*323?5WdC(>Idi^W!Ssv=(I)-LSTMc} z(^XxBIbaRBq`L6ZTq_tAZ-a0%DY#=13kgFW!j4rJ!1DP#@L1~Mpnc*qlWyI~+ok!k zhfj~-Bff^?mEreT(5=nvR73(gPPiaaOY4Gc>%S7sN#2sCbtaPY(GdcRK0u=S z`L<-vt~*pwuS;XJgYfq=7p%Pe64RvTJ2=bLV%E&*4pYzN;S{yQWkEM&S<0-h{CDqa zOgd=79alUG>O*d@$Jx(#{87q|I*;IUrrxpXm|C#jl) z*geLL!g~wQX3}2>-!FJ7)*#FdsSr1bwOP~0b{IHz2}wD5Q0*FB($)D534s&g(P_a| zv$)%#++N?o<%<-E@;y=Su{399Z-;cphfL3$!i(ki_{{gV_KAZZW0drMzIyR=7Lyp_ z@X=n6XYclL3#Yu|uK7%2eg9O@(zid3y)l&x%cXe!-F|WzypFC;-%N-4u3$vqmc=70yC3uxjxfq`*i7z~d!5SLh0o1sBXX{ zO7G<;(@KpkuJvO+_jo?GT$fCus`(3#E6{UP5m=Qo(qNBiL9YcKH%h~Y=hW!zk^y{$ zR2p}luR-ghJK2ufYcOQ1H*-ln2U;%P+?mebBq!EqMV*eU$ov}KGy2FzsotkWi8pBF zyDeDXy$R8JGxyQ3Kk4wx*+WwURBUyo;#-@kV3HONl~RR~^9u2!LLnWuSx5(0m~t`m zOCd(S9~O^&!*ZvWLZR&r8v0~Ddd+&tYYLtgPfIz{+x#2*^rFag^KXYOPj0ZbEC1L5 zo3;Gz(emu!kgcp`f_EyK3kIs#@Qn zU+xj+_OBiH&w7b^JA#=}tUg{)S&8?hc8bM{Ls@!26{ov35o>OCa}T^1;G61Gbf&0P zGT`JWI<`T`uB#7YEuXTj{*g~yCL--wEp*=~l?tCGkb=O` zd_MUI>D}=F@R~wFM~b;Ft4*Yu8V*M-_klq@hjOZS*`qB>AZoZSoqdsr8s7zOSArrv ze40d8qPpl<*<7~f^(9W_=^7TYDF@@f7~-yl&3v8L6_)nhk0nGTv+~?9%;-D8Wcq#N z#@@Kh*Bv>|@>cE=7}EWrtUZ-`bEyIscX`tM1Ciuf`iBAqr;6QSVfVGVm_NAd2cK7} z!`92LvR@X}K#RZR;QKG%p;{@IT?y?Y@!T%<-nt!DAG=H{pB~b5c?Xm!*g}QXKS%{) zNdAg4TTwcNFY&L!Iqm+WGouer6wO2Xrb=$Ee2e(oO+B7>Uxs@c)S0jMSZ=Uc5Ex#4 z4fCHKg`ACLkUe&Tz)||lB8xcw^ZcVMa^Pybm*k9Fo;>5zqpq>T+tza8Jv=va^ixoq zdW822T8rna)7dR?D6JALM%$&$T*-rBta?H`$T&0B>+z2cw)~_V!POY_(vQ~u8Aov* z!Y(RwDS!N+6Rq)<#luJE;_xzi>{lDlLIw>$w)rSdSZ&8gFde?QC2pKQL-NZRxJJn!P{Mw``@ z@#2&>*rjfS7?y^g*ZT71>xRR!OU^9$Nf#MCHzkMAAaaO^r%f*QG;4A$9n@Y;rUicZ zZ}$yai&-4pR_<94(vRKYz3C;8ULqrr4*2={Y!21M_&h9r+&ka9f( z>=xNV_-`+`V&qPX(pzZC8wqfc0S@2Z$x*oF8VWWZFYw{qSx1%?4b2Vc|fy6^y%4-_vT~Dbyztht15%kI5h?ZxTlV0lq@()R*Wq*CK?yemM z9L>RdOEzJlmkr-7e*^zMvP3(*CK~Xxj72V6$RWSNWVm*0TsWk@I)-#ncBk zFxTW9jW02!g&~IIs@{$!U0-OD^i$H{#)!QI7TqX0RqQ$tgttyOvQazx;is(I=W;x-qC_Ths*?M-ZYrCUaE86P(@2Mf9os+mhqQb4Dr&ehl9n_+ z!V7iZV90zYn(V%Sd?v-QlW*(M@@b1iB+L*kRSWrY=Qz6bHjboilqph1m6E;MDC3PW zW__+D`Pv(}#KM6F29BpmlOy>zQp3oxD7UOS%DL>Q$eTNIeGwZMsm#5&-GO%{qs6x$ z1fmlz#J}$Yg#7FuzW=UFvN{k>>TAE!yh@blUOX$Y8M=hNPs)>MTF)i7S68So?I&`c z@@yOT8VqL(eOcRS6qqgKk!D|{rxOLn;8`P-ldd2y&;GP%X{SW~Q8Hb*{)afdm!$8o z4`x{h;ZOM`{0XNTl3!$mA44MfYuBrpV&oV8Q^j5^c)y3kF{vb%vlca1xs%A|E8d;> zM93LDpv@2R1rAv)E!WnjTg~#4{`)schLXNyo@SFo{g^a0cy6Y1ij##bMIIcv-vD1dNG z#oreG%RhXfw{Z>hvA9&C=Gja|kG4w&1!qh8jc=j6?^)>dRUV3_W{KouZBcjLM;7+w zG&SZn!UO?3?{1?;@T`ruHFA5_BCl> zq`wfrbsNPG+>?N7)h6Qjq(8_fHQ*nC^KtF|Bhe>3|tX(mfGh-G*411P9#0d?EUN%X5mQd7hp>dih$G0szI zsM9Mjw7vqPEh6{?!q zWVi~pWc)#alSOhLq)7H#HeOeI$YcftG45S4rWS2vcf&t{-tIX{M=51erKGA-d=Tw8gz7{)}Nv)=lk6W1X+2?%f zl8rPnSeexf{|RikkTtqH9b7|(vzz|Tj%avsH=m{QfphJPQ&I>)Sl@qdj$RIUjr zHYKqCmf&PoUj5$43eC!M3$4vMLIhk!OOPA5Yn>{49ilf<&Hl6 z>5ir!``zeFr3*)im;4<{ zzitpImM24QNdR<}ABE%IZ@}aJ1<};}`S3e0UZl2B2Ermma48NXcJZ|Wzi^;r{6`~7 z7%`5vPTIr9h?PY8(Q6=oO(v75$MW9)X=A)~JNp9ZAm{cPf|c?>YSSH*v24Qi=N)j~ zx81npbv#qDOT)7FuN-Ov_Os}-_2SA8PgqEPG0bo8h0Lh0aN9;7?i;QHmG(pkyoV5# zy94@A4Z@_i!z|NLP<%y;-4cbu=j|`xCg;qgHYd_+!5g6Q(U@?uB1?Yojr`;az~H}V zk*Rl^NG(zqyyUvDGChFX-s#Wo=6r{FKZNet;BdiB?7*5&Hna3J1Ae`h317YRDR=$b zZihpt!sndN<2=$1Fq4WrydS&_qF;@Jg~|h&Rna?0xp|*+P7s*uHwM9Yb&0^A@U@p3 z;RqT9f$+4h1V+!F%1tE|amd-*b*RBzbSn*Y4 zmtPH!GVU{5J;CQKxSrSeDZ%7-(y%P>9m_6S#!AXwIXrXI;tN0iL4{K*dAsY)*t$AH zEM8g7#;RVx%|UC#6_wA~wPRP<$J%>b$)!bbM1BIs?>j=9lULwKm!-65+EJPnWrpG3 z?vlDm4y8}8#&_PMVYQH(yW|>(@s%mUGx|hiwV0r5$V%Atb&u%3BSw&xD;B)1n?!mq zePDR88pPj9f(ywiETC@|#42h-zj4yy?XewfON|$9+1i3jCSJjX#%FPQsy}(`I*sxj zF0@?Nkp?$4W7)ua;-0tTF*qvM{^a3m%Jz^@%kLFfm@mR~hYNJlqLCKTcp4K^M%|;g zQq_n$N^Dl=XE)1Jq>l>ty*>@SL-HXlHHqCXyaiXrTZsk-+<{>CDrC@4<2VK8Ah&=roXuvh1q{&|-+!!5+@;(__UNRT^#`>eC zw6wkL-ZVHJa!52zQYhr}Jz?%MAs_p?0uFlyK+N2W@b>0jnB2b`HrZ@rrPBLYnsKw( zvfkF=*_0nFYX3^XBfA{i-J?MBgc!@_#IY^&qe*t?Yb+H7)0!9BG|?`b5@UVHx?Y=% zyaVX;o(PI<684o-U*LVCS~4HKkj{#~&;|>^nK9!ED|DMe7Inq=DESEKKm7_TZYzkS zq_RZj#VbVf@0yC>>L!uOp6QTQAH*GPoCV7U41)c=p-^V$hoh>yFl2Ht)_a_1vknw; z?t=3))o_r5#*IKGF`t7M2hCxwnJ>9>;njTX&{otx>qag=m(VeRCAx9%Pb!Z%NjA5# zB>gl7(1eIoJi2wMI4SQh>v68(deezEMy zuH2M`x}d2t4_D7H;Ga6_p)*|}lg%nZ4s0;VTwXc5aOX1A-dDowQ)EbMj01RWUn-Igk%;;Q{}t&hi50Q_ zeWIYPmqaST9N7M_f$s&r&{?qx##Xs-nZmx(#i@n-#wDX;xf<^JFM_RlRnA^LclDVZBap3)Iyh+t8ve& z`r~sYS5_oag)Jw(f~HgiI0o3UAMS_n*sfiIFGK?lr~HLNM+=c!Pq9c>cQQ2mldw*q z%VN~?8jkIl2$wbbK_YjZiGKx?$KnOpoZ!eD4LZ?QUz5$4wv4lAYhVi(mGSegT4B`c z3|3HI&JN`#qiEbHmfwshI!aKZ@N_N~M!(Li)G8H@!p6~*TbvmIV!Wn;3g9P?0! z=Gy$HGVM4OW|8j2mgwtYUi5Mn*{H(OHlE~nsqG;}cN@&-4anf4F-;lwkCvw$C+~%8 zh?n-Hnr&MtY^MR~&akDNH?o{dZwy;EVkGt#`mI6sBB-LXBHi{Dk?g*ekTv2u$qq4x z(+4I%j>3H49WtSjw}Th;1`r^p1Cs5tSV87{)-g=X?nw@_q`(Q>eEkj-NBN_ohXt8L zC}C*e5A+y!6t$gn#C;9fq?*5%#@%kjnOkDF_E?}?xpzs zFQ`l7ouvN^W#R{w(8HBSB!iS*ky^|HNUYukS9U#vjO(w zrCQd|s0b&Ek2-wHmV@vs88G^b61x)?!>T{OVHLA}vCTJTl5Tn`^4d-O!Q77|ch4V# zy)SX^c8#Ht>xW_ACS{uRJDNm7|7qG@JIcINO)>dN^!lL!z5R4iGE(G6w&PA=&9>om zxN9*TS+HM#{y0lUU9qDG|J|UF;|gJC^H|%%2&gNLVN3d*A}*^Q=5LaS2H-@HkxYS{ z2}jx6J5O0?yDMxkQeao)(;;NbQdaAm!rRNMqQ^f4jQ{WjH#ew>&wsNZsZT#}P2+Ps zFPPiswn(&^_R-@F(X{ODPiC8>MY8LRsNHCTgq>e3 zu^P5cGPUY19o&%sGX^{p4IcjtuB^NWIzo5nZrWwvGStlA^)gdyEuo9^}dCOXEF-UGHgQIfaKqsW}(}#ZRcE2 zzq}g+K^Xg)`+?iRc5>f8jASS5!uYEL-B^E*23%R=L@M8jzck?te}Szf%iu9EX7wtJ zcl$~KLT^y-#$_t&+C=f~waoveHC??pgboc7vE)}f*#PTE{Nkx7QK&sZnhSTZ>R;j9 zGWd%ZvJFY?k2Mz0T?}75lSImLQ6f`IPt4u?2&9X8Vap|ZR2f&p{&_UR!i@}Eb4G#G zvK81FZi3T1pW&T|UYvQ4!}^&^@ZKkXY!vtfpJpFpTzf1k2uz5#A9~U3VGb9$z6j?V zL{Po%6AE0ghCkolL1`n#3;xV%3O}AKWP)bX0%3p5uO3H-j-}I_)FL|gr$As}_)>tt z<=I6(fJfX!qx5HrYzN%}vpXs*+u=Wa{Zn5!TU;f>(M5b_+YYSJeZaiFFUF4Zd3f^n zc=CFv%pa05N7*7fY>jLeSB`FB)28oWzO5IzBWhKk?r6!TxYy!!*$xVs8%Lw#|2UM( zTC?5P9l_jpJ<*&$_@T0mf?e)ot23kU@F~2Sr#Hq=xlFr1e`7ZC-843GFGVCChMB)c zh(?q!(U`G`te?LxZhiNY1tn#0JJds&=c=8gw>FCmfB0frWFb~0titq#&UB*l=Km-< z^M9(|E)1LJBx49s2}w~D_OrGIjZ#S|rBQu_ibj=WrU;=F5{1YtD(q*SN-|VRWUPcz z8mK5r!@J*q;C#*x=j{DFYu)#C;mXGwnA(LZbm5v}GQ820%Zzkk226WNtjBlqGc=Q% zXg(R%D9Dk}Tc*@3uLBN5Y=gntO>p|?e!9~j7H#fMz*OaS@~>(+_0XlXed$+_nP`h; zeu#tgIacO3(v;Xll+E^?Dz}PI>_~@<C*}8xp;0j7A4jb*^bFf2E_0VbsxN76pmH@QZYmjNoEx| zgUow-D0e@>igiTtJh@^_TatvQFU!+y+Lh$a_*~i@B*sly{F5fF871!qr=#W~af}|_ zf>Cw+-K8Rde{c8DH=j4s4NpMouPg+A+X1oic~JPM4f^9dS(Slj ztfq)2)bVWOao;Jq;U$l5du3p+k}z8DdWf07vIJwEdhxlrS6DEP@DAiKICa_$YE_p( zVTuoD~bc#+thSV0D;_ zTuhuf_oeC_Ca;Pl0?kO={dPNMN+_dfL^t_8kWZKNCqXRd1fvq`;qex!3hzyup+7s1 zDS7oC)H;tahfXzA96l{dWE3Ak!MJts=1K(Fx4x6cIc`I(j&_{%rH2`9-Az)SxX?tU zP_C!amZ$~Fz}mx;bc`%GO;4YA0aulukdm>_Awt*{ntnIHorSv~$)O0c ze@|u>XXJtVcb><~&pzCT*VEs9qhyDHGrjK?Z=F_W%2nNLC&nuzh(X4DviOK5Nz~{8 zucQsIvTqKoOgaQU0p*a@b{}pB#Xz}s0bC3(1N+0Ra9AXi@wi$-A2NT*!duB?vQsD7 zCwPRL&TYe_`qij-cr%_ayaY#P_5o~4L^}V6Iq*g2PQPXhr$Ei%j( zu(Q1$$LPl5k}I>B5FKqe`Eon`a(IaT@;eWIcLhV(gv+e*v@*D5KOS~(-3_Pz6wvZM zJ<GfM{1ZR$<)T=O{H@4m_>}P3AzduFQ79O*DxJ9mqXPX+qgcM}CrAd4wQkl^V<+i8 zXI;eqV{KYp;CquTEeq6!Em%yx{*@+j)7xmcX#k}G1m%M6TDSB~CVnLrV7b?tl;aJa zlgN;`1vOM+S}uL|gax6?)nIa`9V|i@!=(j-psO;OEcLDDe!c1D`RBUmuU{w-wgrLc z>VCZaItW)-)CxSz778Y=d@qRXTq!ts$q|n}@J17vn{=dmF-+;W#mZ}kLF)X~#6M&O zM!1JiXG>-LGHC$WAN(F8u!N%1I@A^ZMnc~HQ)P6E_p_DX;HIXR?^k92%jkQy=B#k(En=4D|#NstVnEmdkU^2(&{#*YDY~R=m z|In1akuE3^OQRN9uu&-2{Y;b*OL==swN?`&-rsObF_%<*XxI1T0r zRN5Ocb&n=?H60gNPJRrUYZpVpqDs~_t`YX@KEkE;CM5FPL2};S50i!BXnK_&jy~Tm zSb54rAb;fsP0Z>iI;VYkk23E?^(cnTukVA;AyMnI4=*I%xk~)%f zU@nK#UK0r^H{ecxWtX12%w9bzMt2t<76`RWfSc9ZsNoe^p0DePm&4ZKV@MV(s}05P z&8qY-o56jON(T3J+OTNsDA=g1gHs)?bpG}tsx%T}eW7_ZXpZZoqMW!@%s?6y${Pj~ zpGG+H`6cZVlrsLc)z(Z)J(;w=mfR;pXjD6h0TUPTysK~wnzITwrWoR<^(P_3VgS}u zy@t5beXKPn!|w8NWoMpyOkQ*rU}P1~t-l$DY`|)&^xsyz{z?);`(?s%_IMA~q$_cP&?mlGSGNX0bkO5r{!V$5f^@AOoUHRxvHN#=^X~ zl_0oj!AkW_=Vyk`z}Mh4Yc-U~I)7|qefGVF>z)5tSFf48-`k%&H{MH&&1>k!+t(@0 zOhg9>;R^U9PJ@acTEA!h!BThjrOA5#LU z{SL52YBEf=T}V=5f>~dg2zE;$VfAcwvT_&Bvr3Zj@Xc6?RxN)AnJ7-z9}*V`ja;fo zIMoi*_Kroxu-UvLVFNv*=1$IE8zFZa=Yx^sOtM_d*1FwwFVQ+YgS>HA0aFzwz^?d2 zBI10L#{At&b2d+==M5G>=!EGo*CY?5lq+G*m=bvRIu(3lav+Y;q)q!qU{dxGo(?wv zkzyAitWS!ZyRn~D^PA6Fd782svO>(I6EY~AWk*|fd?HEHe&W#B!-C09doV$(1o`v= zmV_1vRI zQqggVm3))HZkw?PyOQ*8oGH@vU-7a$!$KDwR8$`%PnQHX04-k zvzPM!_cn5TT{4;e>j0<3IWVr9Jc#@EQYxPM7-jZrfCtab&^KKQx|0k*TH+E+Nxu!M z!s*mFelK=+egW5*uOQW`!*dHFFzx%WKx|97z^QYiK-W`9us$GJ;2GV9KdvWW-jZg_ zn^u6=7Z&1_BW>IgCp}K-o}=}m-IDaoIdwXCX)m+=%Q8~1Wb z1vzc2OkaFEMau+BiB9!iM)@(%#mModnh`nl=@*{!7JmQ^pJCxy^a=W5$zBMU>p`W8 z>hSs{(TW@ESMYadHS10{zT0~@U!eA)U$EWwia;i2&C2T;@t(Vut;i+Kj14Z@ohz`Ro()d8g0xC9~MaQ1(gydQ)+Lr5d&{8L+7Y^T;=DENoTfV*tb)h zrQbqa>b8f3RO5el)!h5CxNY*1$bHYz-6A3W~m*`tdD7cw^8Y=ofg7r z&F=!sZK-(4{T2SK%g1l58|MF$!zJ<$2x}EbbY9;fJ8k{SR}bo+@q(r#!~&F3%X zM$stO-?;sNC(-k?;!>plP^S$K>BVzyII~Ni?CL*CzfYYMp?-0F;jS+izB>J%e^#nE1J z-z5W2JM2K2X~PJ1znOuXE^z4B17f5k&Pn-(V&dU^>@>cQEt2|}R5S|K#b3d2d z{i>q3&55ek6fzm(eW~EWe{@q|8LGJ((PT#*3^4Sj{!&v&`|!DndzpuD=VwJskd~jzUnn?n+-cmp>;tkHtoJX@% zE6@+)g6QRS+sQ6TS6Xnu7K^@iCyqUkETdBl!7OTI^;{%`cA|3AheY8gG6F_uno z+(XvZhLLKY6Vzv63jLc!XxYvvzN@E$YAejSnoG5i!E+F178{Xwj=J=09N*hoJ%?3b zlUYl%%(IN!DaGi+v4k5%gUe2keuH>OK4bBc$+O08cYsS_%Y^i$#e zg~ixMhq2YMj^9Mw#co$Uly`qb&7OUvkDeQl*#^JLUfOBUf--mFf8wdYPiZx#n7kC+ z6qXT4OnAd8bQyuMgDq4=Ca{7u3D#2QEjznMo1OS(HXQS6C4*PrQZunW=I-_Rw06fw zZcD8h$$0dMNKdIDrWa~x;lzF_UZri_KGMN6U;ZN}<%>`$0LhqQTNGARq1!x) z!)H;IA`4;{tw=xSX%YLLx%86dI(oeMCue>uf%f%o;?|c)a2-wq+(Y|wsOYhV@3QSf z$HHBBd9WQRK=!sv|vT4Kc;H z(7HWmDh5p)FPJ1ahL^=2;U8~Nbjm$OVPY9GIJyEZe%u99cUqxLQy~5A*9(qw+!#|W z{$9G~IHd5stIjVyz^-$kOD^S;7mgc9x^Xh;p*JAPCmbeBtpv%>5#(>}b^7M{6DoAx zkFk**<~BdoBo})0!7x0PDTuU%hMgcpK1k91#4mN@5cn5eq=;+{n7Ns9c-=a z7D%*yz_hRD1uiO!ajy40eAt==vy==WRl6N>P8v}g8!M>!J^08`0&X#O_} zU!+Q)&{P^`WP5;=vj7ZA{XmB?g@jq{AYySJOjK8pVc${aL`*hO5a#cM{k-#K(Q#7n zvWpot<@@7~v2cNk!@EtQthlo;E9?|YZtvMg+cwR`S<8a(s_qc}+V+Zb+B%W!N(~gK zm{y|1y^G9WCju+PPteZ4JZFo~QAkWGWGC8Pfcw)6;NA><>+}&9XcCoX6&89zjy**!#Wzr702^Kw}Im7%Ok`xVx1og{dul)=SeE$G55*!RbUY^$CLa;qFc!*VPM z7x_t2wy3~j<`8){Fha&B2$Sj!(#%&6canUc&v+e+B~tBUn9*g`BrbFo(LThp1pm&1 zX%GG3aziW>qbDku%45B_5S|je#rm59!J_L+aqCHCEWII$pI;_m@T#f&`*AXU*HJ?= zgAXtv!<3jjbE85TsW3;QjBv~Uf$Z#jGIPybSld}d-{uoYKDC96I23bbTMWsySIOjs zl_m3i+fph~EkmY1pGyyR989mxz(5q$vTjLYG6 zM=P4ycHpyl{8=`+0f*Y_1ZpO^*s6C5-#nK=e`QaMjnBf*$ye~mKSd_&yD!Q=n~ZDy zn{no=MT93GK$yZA>Xf~R>kqZ4NSx$KF3#(M^(X#Bha zJ@QmGhiDwEVrq`QulTd6m&R^$WHLlPkqg%}NV=#7ERlXeF54V~)Sz@obM(NZLf-HA z&IJpW*JI)4`xw*Y4N(do>A`Vv}6OTQlW%B*CL#4UxbaWVKQBMmElMdD_?j~HSi z%LGqdNx%InC^tIW36kDtQLVR|YL4(3KrvnFS^5xd@QWN`={&Ttp3oPslcx?ARqVnJjXe`#CUnPcV)QMl@(bx;Pd4(TN+rNvX-s(mnjX5M<^%`-K zddy6T-$^bcq=QYtG9qO@7c?gSWPYuA1Ik~|p!uC>K|hxq5-B~6^!Xc*@PL1ye!!W3M#a;FlOx38O%>ekS&KBRbljf{|m!@bPa7O;}+{gLnt2rp;5@ zzE!Ys2C{b1^aJs8zoB5ZOrkx&|*no_{pi z_&Nw=zyAR(#Y3#L)Ht*7Hp0v|ZIIhGn^kCg4w4hi$ZhRO6pWruN6k5$FDlGPB?Zw~$Xape3_X>L}>dZy{oxN>&F zFO=`ONP8vQXx&s3-j{30UD~@GB;KtD{ny4&(VxyNG+s=4ev16o99ZCay~Q zNj}#Gvzr|q;Ndz3KK4&%)kJJr$#)?nclu?p{Bj9O-c6-zB_4v-@ERh$={?hDGXpQ9 zl0eG#0L{_#Ir)4UX5gg3Bi(@yo)@QFLe%4)j8q?++0a{3GAa?VeP+wTeC$#+3^s|+k{m<1nm zcZl8(1U^Uc3e0?8u#*Z zw}b1lp+r;>Tx~SiY3FByq((Q2tjU39@$E3o@3*tl=CC3r_h5_UQ&wrn*}6ZBqR48V zMN!(wgn4Y|nLG_>x$CMxeC<6@ol+_u)>ya`+3C_m4ZDi49+)KONishFLz@H4ypk~Pt;|AoQN84|sF5{mtq zi0ggh=}hk$O#fqy(xaxR__z?S{p0yVQ&yn;h!!2NR;zgHEDDq3(m-Kw0lE16jCG=B z8WEd+1~&hV11t9qICxYEic>8>WQ`A@9^YxCtUgRpRmXV$1k5?!1c%i*&RC$t%G>NU@-(4?gu*NHe6$C;f7*Aq-k7-UCzA_Sv4LevHoZxCW6aAi*YiWj8cZv zXhwo720QEEx^0u_KxGt_dLYfD8a*QNp+`Y~uoi5q-jSZve=uiGBgnrU2X&`8u=`*P zi;VN&UQr{oDQiRY3?{%bM$Qa5lG&o!1q>Kl2g$1*Wj|V-aZI zm4L1-4Yb(gFqGj+R$^TOJWyE9inaT|lx-7fviECreX)|9Q8NWTk?l>Ns)!yZ%*JV`*1k5SslK8k>(vHdsxVO z2nA)+*zxaovkDsetmvQrpk&f3xa8Kr<*$>2Yd;UrCk_+2fl^5n6KJDtjtb4$m&+V| zf0e8ElOXAd7BK&XF_F--AO{ZKB+BA$kh662K+xMfpMH9#HNK)7w24v zc}Zd9vmU$^yUgn3%>kJ`IjHhA3nGS%Y4y2Rv{urbd+bnw+6yX)QoT8GqPA4z+fI-g zXUm;#Qi!5boUx-}fAcfT$F9eEujaKYWPwU)#d& z4&BUpO9#VQFJE}{pDx>rb|5O;OW zcm}@RdlLF{9h~dh3gad3Kxz0euIcCfq>|@&xAr(J?bt+pWtPy73VkqhK?kJd`7&jp z_4M)=B&|4$hWlxtYeorZi_At(7jbUAm;}$%)MUP0s3KWYQqW}L0}g7tSo!z@IHf!ZZn+;K_lB}S`{D<3!D}M9 zQs#tFyQDF9_ZyVe^`UX|RxuY_-r#orOdYJ{{!N5e|C(Tcj?)TKcN-&X|8m7swhdWkEd>hBTTB9AuTRcBneTyWaaGzXtFg2r@TnS zUHN_(DQ3s<08IKxZZVB$ZReQfk<>ADKN`iDW6j23^wJMwMpMer>Rb`=S1CcyuWsba zf+AXO`4*Go%mgdCj^ekMqGVk}Bt$J5=Fbx;Wam3G;-5H$3m1g*%-&MEl8wOCM~7(9 z(u1fyyo2takxadYJUNW`DL0rz?X?t+m zx53>n8@{{p*#uW$z{w>F^b( zW`7ZAFaHncJ6%9oWj+&~B1{6OWk9V8|Nb%m4SFS>G}dc7W3|x>W$GPpdc7R?cwjqa zrqyyUTi=nvm5-^7x(g~P%^^F#{UZs>l3<~;4(<3ENt;O&7VLe2r3)qsZ}bm z+%LrLyo1;tuosVr2MQ)tx8jfW;WRbH1Lc+6uzJ!c7KWw?gzXtjS^5v@)n!;K<4>PP zdL#cR0_~VF+R&klvWctwXbyQ-87gM z-B8hPd$OYL#}@0y@Ezo*oZ|iS7fJAADcH_?!|HDS#W^jm*mwCH4lc~XES+%twPI8t z{^vTq`ujf=`cjS=*0b?fQadhvHG=wfM^SIVTWoRgN4;z7@U}%3tdF|}+dpg|K2giT z_sId0Z*ZCX3zQ*r_7PAIe+bH+YstO}TRLr<0qUDIF~zPeWOU0Crdod)%uzqabXJv- z6fb{rN3Vg{B}Ty+^UvVn`VSnty69fTmjjG$r3xrMfVyog>JnWsuHw1T6_4pn*DI)>VnObCyMXP-lb~s&4Brggpp18IZR|%_HXs2u zRy%>WkHfsm8>H&kEY8>c2AL<8OSbtH&~V|mU@fXj25OMh$_z54K?@l7%6ifrcmbyF z;Gdyk@8GD62%J9f)H?G|0yBThLR`N32)!rlPsI9`qEd1>J_*dmE+tudZOm3$#P7F$ zy^?1&9gCoC(^_!MkAe*wLde#@M)K#^MesN;3%|UNkh7xC$Y>_d7b*!R8CKQYxZpza z=xTf$5?`A&`>uOx;1obU{PHrE>c3v^JT_h{1<`Qgd4Q`vnpyGYXjw)agcLv0`(G3 z24QI%5c=c8s^+-EosD{+)~pQi5W_A?^8%GN7hn$TLho!1%wi7E;&c1CTZb}`^a)jX z416ZWQ@voS`$Mi-QIWhZ_(YT6gcFB8IoPe&Tof#7>EXsXLL&FOw@y z?#|)JU30p>A)A!C=8~VPMqnRZOnNr7gQ?duGRNa1X$@?lTXRzA;kkvBk#M8^f9o(n z1~Ffpf96`$G-UI>s-bmqc9&I4{yM)m-y#JP5>9JlT1P{B*LCD{w^9m z3vPZ$gK}YMa_+P}Ezl~(Xr51SSaBUDyS%}b^R}Rt?;7j%b$^(zBcojNStN~%Dl0;c zYH>0WM%>RePHAiMBeSsr_+`dvOA78K-8H;2$IW%;?9%cmj8}UUSdoAK0oD@ zGxKP6Xge(*{J~Uj*hw#2r!y;e8d1$BPShyA$l7pTDRU^>n_2gL44JsrpJ@+}Lb(mk z>6i&gCAXaaxW63#Ze(Px`pw!5g$o+=k(LD=^?*GDf9}qVm8Z-eDb2`{_SC z($GssmA&{Gnmr9Tl7Uj?2hnNYRP_G$hVlm;`hCkITKma{cFXkA_~-j^?sr3+$-8Bp zC)T3oR83@r25D<8ORF=JD3Y~wgSJ0?Ijx5gm8-B$o@PQ{E{Uh)b_zXLA&rNnKJeT! zGhEI2V8n~1Xf`zzrNSQLrs+3OMRE%YnM9!7wY9i5t{?RVS6k<r`=t z%%^EL8Zh9uFk0HYqiIV6Xv3B76=xzGDhdwz(4^r^dMWo8ifZW6sgG1BeJez}%wM6* z%26Rp2ZRO9kpo5&g0bmoIzGhJC~OhYPfp;p>XJofxHMy%e# z^~~!dRt7VyYtI(Z^#{&Sf36c}X64~=yW^O4d>$T{AcIC)_tECiJCrmCqRlxQaKe`c z+_qYo^gbUYB_dD3exwr^9R?G(iunhinZvnO%dz-NBDYU%a27ja(NJ$jF6q4pm6+?DkXpxS$rt9L6T z`Mdt3T>+ob=k$6^2)%+S-;@PY<#Yt9yX^5_Gw%!!YsH*bi*fAUDs*XmgDLii)6?@A zi`bjw;jRdx8`2EZm)(Fl<-Jt*i8Jc+jFm?DP}<^OM3b)iRwDHdq>d5DNeKm!ttx6rq3F_w};+J!=e_vZw zIrEO)T6a3G=n!n2{(?NW%S54>i)rd{aa_v2!|3=-bbb33)0~>|OLdVzrmzdcR`0_5 zt7qfp%xoP$q#$T%@m2>Q2 z$|{zO=Xl45j2`#tSunj7-B>0fAHegaUK5k1|6p5bC!C4PBJ(cp<@1rJsZM|kZ9aUP ziUbysto|t&x?wMW$Frxu+^cDk>>kXTsg4C7C!u5LAUc0)7061zzya68xLs@jpS^7) zP3aC0Hs}KV3Emtx;}6b$bPh$2>k#F-$7uhTXL04*k#L_WIL%-@6-3mLZTY9jzeZhV z`qMqkbz3u(_iUv*bDEf3XMeJDPdx*TEyTHf2*jdP82YxGo|((fF6K|PPVu`<0^fY4 zsxG(TWfTj#>w7tw*eNJ6DU621b<*iiCSd*r6-*cZD-hWiCvg0a&$)G;6^Mo$MxDeW z-1M@C-m@u(G z{Be-DQ4-qaU6^6Ef|=z#K)&!DlOrEIQC#Q&LhLWBv;Qg(Gnc~p(Px;?drjBRI?w!D z+CU@*PST4Zq7Az5;O|ZU#n6Ho zcj>j6dCa|s`P@N?TDm3X7Prvi3D=Qqz+D~eVuCFgNOE?D(Vs(*RJs*DY(mJgPlL`X z6|Jln9AB4(Y_mHFcsRmUz0%~;qZ`0%Z!uX~a*nGp=w&MV zv}onGIC`XaEscI2kNY;=!?{68TyMf?r8B)@A9_RW>tfiB8L*>GgIV}jgIHG{ zAVL#&&@jkF)x7xv37(~qsakaN%hUNI8*+0Jhx;v2Mi0aU@{R*Tgua&lK9-`}a{vo-qr_uF_jr8oO zDtR2x%Ci8bpqS!m+-@w*j#ZluU1cc{{YwahiyXcFAR=l(@3-v4 z8J@A)H8RA0jijI&<>tX80I}}s*E@38boDE|xS%T_!WmutKO;g<_aFRPz!BImS z1SZ|!A5eoUgML9k&Nr~m8pPdwMf8*W5Hawz6d>EET7R+f>}5=W3}ki$jCtGTxq9)t2PKfeDQ1C}apNxA=c z5ZMxh#xYw_BE60oFdU^py`Et3+5`5A=gU}cD6fFiWG1jY>fyxR)n5P~HyA~fK;eR8DBhSR2@O~Hb z%YQjl=?G=2Cp1A!`ZAc6HVaI~@pIg7)g(B7E?n9k&RdSOa5i^>+w^iN#@9Y3Id!%0 zPZ=8(w5_-;VFoQ6l0*Luhw#*CW2_qEhfR})@c1}ebkTN$Oes4mKCXwZ@Rg>Q zEe26j@(Q=}K|d=a{FK|n`$sN_7=n!PGT0V-2~PFcg8C69Qqd%^E=;zfe>Xotk@KE36E)I;n27o zoWakD+szH}WzKC3=w1N|BAKvfC=r5nvPs8@cse2>2A28SWK(k?$WGaU&4q!4Hm-*7 z+0M{xdWuGGZnC~Ko`H2tH!O+21g5haVXjdLF$tMTBXc==r{*aQmQ^P?XCIT#ofX8a zY&LDV5{MJMHjzcTzoB&PE}R%(M0+b-sQ5=I46Qy6m7yva-ZD!dKimadeq0nNhjw8? z`E~4W`$w&`_QI22kD*U!nANV#;EHaykR#cnw614^^>J;dYY&99d*eCOp^<56oG3Y9(h$h4}WJJIKZOL$-w!`7$LACUz;o ztKPlv!s#+x^@@Zy{C_C;^Kw?V8pxU6bf`MH#>!Y@0-P6`1XIV_!?{B$5FxpQls@pJ zK}UZOa`rE^d2oY9mr5`bB-JP2>dV5Pqf zf^+dEzKe8@ouIKF?!-QVYvKUc)jH55(v>us^LG(>{@GTq1fEZVxyW0cB&@xOd1*A3 zshG|X)yKO@)Y4vHPKv<9sYO)o&ojdO(5D~NeTde9<#78_C1eFZqEUNSlZw|XN!t)V z?>)L74OC*O#b!@DFfRJ$7-IfH$cZc%xP8~9V_kXMG7)NIsKZ5GQ<#6tq5=?I6zsn`j+cbI&MtJSw z>}>DT4~^@{cZm(4cZYx0CLR%}4On260iIE${@0Nv;5;>;!H>%@`smZe-5NMAQA{&#BW6FH%$Tj#<|* z2GSNjffK(M!W@x{CUhq7vUQ@wDop9mj&8UCtTg9uf#dveV zZX9~|6k{q}us>8?pnd<3!1y8~m}yIzCSkUP=T@nXdF zRB+j0XVPVGoqVi21H$?TNQH?XdB3QW9^sOy8{aK8=%^+6)tkVv+6C6_TF1(m%RqxA z3j-JqM|K55Mp+?vmyJ|3Ngc(QRK%bomKa!n8sDm2M6>tq_)2IpPLglMu7e+O^_Rz( z=64SpWO%=(el6DB?ZB&HI&`=dI>cnCjS2c7=;&@r0G z&gFeUIiIf5k`4>pu&9_CnmA*2F2QFj^YQx@IlRK!(s1`idPQ)PDsFBoKlk5zTDi$h zppm%{AA0QlzmJaT6_TLQpDj_>-5B{{4 zOL`1_F({8Bo~6tMl{29CNeYrr%qsuv7YIB4}L% ze=0Z%l@c}Yl>pj7q_@cnj`dA~xxp`Jmsu$}17}GJf2Urs$`P$?i)pHpD*0%4k4d;+ z1s2QXh_l8h@lVhMMUnGh{HX^*t|&nG=9T;oaU03>dt31?_a40*YKSG@p5wQsG%V@6 zO3&|$p^G=)!Lh@u@W4PfvabA&!&er2reDL%u=6-uvIFHF+S6-MA+T&L&pH{~M-1=& zV{C6qkW&d)VE=hndZjd&9K1e`ocXhx%C-licEbl|eh*9KiuCD8vut`?*AFC|eaKl? zFV63x8lAQFIK4AYf+_zbN{UmzkRO$`*0cYlaS2IxY5ON7l*_)0MdFtQif8Ae{gWSj z-v2Fb+cgzm2n}LOwJAnq??rZJCcT^PMAOd~&@X!NsHGN4vv-!#Pk%PjQoJW!Bus3Ci^=|LWBI%9SlW40 zpK)J&fl7XPL5^<8Co(+~_?-K8KA+x#8i)BFgW5If?{%I_>N?5&c=C;Qw*_#ozSr@5 z6GPmzshW$M_6r$T4b+bBMDO#95q=HR+->Ik`CvN6bLaTp>IFRV<0`KH9Eo=nu3(&r zB_`}k!~BYFbRV_ARp%@)s6!Yp7G1;AH{)?qL?(vV?W2j;p3^V6S{VM}9eq+_%G}uW z02K--EspxiUA^>?Mk&AMDq2VAI{g@8bMh=*q(7F1ef~~%Dn`(2tLJfLrkzyst}afk zJBiEQDWLD>-?VT4N;LT#hQ>T^QF6Kyjc_kPl^L&5e!&RNUtEQDC!~3npqox6&by(AJ2(Bq?Xe$GBtMnD9Pr=*nP@sHxtKm^?L>{<*Nocu z6z_|#1%c$!;H~Qe)e?)jqJ6FHcg(P$NL}Z#Ck$QQFC|=%3 zZ>-uz7Jt?yp+4y}`qxU*-!lM*O((!LxB<_fY=rbIMF^SqAKdY&g1=?OaMqol#kqZg zd0I)}@5pz}VM2U`OgsR>jMT9oPH}5;{Awn)@yrL*_R&9T50dw5~3L9NlylaQ6&!#dQE2>G4<}I9pa5RL9m_*@+P6g2EU5z z;k7)!%@)Q=nlsv6l|=d8ABbP00d0Y=LHf`w5|uI@O7BR(_8(F3Pa=smOs!%i1HIu! zrZ`y?n*zt<*Wt_m-r;G#E}YkR5|ujmU4K+pxt+2y*e^FEdACc+{cA(O22UWjszj+@ zACK6lFUNb?5-m}O zwBz=8ODbKk3Ou(Lg5S(vS9=pC5j6VF2qVtZ&>V4z5zS%1oC97nnLZXawU#CHl zrlPcmRGMfnG7=F*MnlR-CA;N0*YzlE4J9RMDI^IcP4zp!|NO`6@p{g=@9X+}-fvf$ z9rA^q#Mjdc_eIi;@qeTilfFs=T?3^5-^=!_B|>Vdg3QA<9Su&^lJ_I7Tb`W);r49 z5Ly1cYDl_IiP|b1DO>rk^YKz8C|%bCbEaN^UuVC8+cN92ea#Kjd$S5&`?ZH^!`^e% z(P3bjP(c}L;ZhlCe~KHL$tpeL*|8^Gw8rm^bl}SisQGpvRtL`#@0VU&O5qHg5crr4 zT9b#rhBdLE7b1V58icIHpShm-CHPuK$_+5Mg3aGLgcFbckfUWPnW|l+croYvGGsc9 zoxX!U_)Mkh;-R!p(U`S=bOXJ^m+0`}=QR07s&u-=7pZzp2ZcGx)BDYF>_(3~q!rY% z%i{(}74L=8w6+wNvh}*4IA%MZzAyG^$DP9$#iP)7(lN4jR=|p;Gi;^AfRw)FOYRIj z%?@r}0IKt*<9JtV*7sO7u6^I;k~<e?<1GI*7!gp11lo>ZcYO(2|bkLu_QWJSgifi2^)$K1(%ps1sub#xh>n4)f z5H~tJRgqGD&li0q{iwgl)reWyj?)UCQ=k1RwA$(=g^v7-oh@HTCNYVIZ|Tp*9nD~M z8J+Ca=G}OK3u7aDTQjHbF?e#WHJy|*pxn>Rba>-7D(?M|vX3^>Q?&vra8ROm5f!wR z3!vFE>v4vxIR)?BO(|+A(tdg&)EbyiyK2U;DWmh)zbE?Qxm$FdDK4bKJE}B)uN-ZX zyDrRWdO@=b6)9`?G{LBRs4#yG(#aD)sqYpEEi`_Pr>aazHU@E6=2;w(p(*m1e{hiw zLzso72Nw|Ui6*p%E)K9In4}j~aU9h2OG3?I@rMTT=@NI!My)Eq)GRJKv*|x2;?P(2# z-*AFniOsmOMa-@FZlH-lS!5;tUlJbeciCJx0kbDBVuiW=Sb9n`XP(u;;-eby)oUQR zphgNAq(O$IyinGAImz6QCdcR>l+rMj_Sq+qbK?;#cg~|-4F|;cL$y?P{RvXPJQeJU zr<3}!ndo_Z6qj4*fUVtnaKcJP_*P^NKBs$N@9`}z>f?Xojj&oKv+*WnT~lJ~j>XVP zjWwjbsG3fldQI~aN6{?3Zn9W!jW!!B#qa7vOwL^>dIFAe>XJTK>AOzsY{YWIRQ6;0 z+GNS1yOvnr+$!<4`-GAWLn&zEPFzzKi1UAmyRd+LSn9cw-c1jr@nJ6HR25Dwem>M9 zb)_c#zhoPuB1o*Jg0E4j5V6KY#Mqsr-tC{+@e6Y7bYOp)(|rT0SLo1*oKniqs-W@1 zb;)Y#Int2)W_|Mhu!y9y!tqsEc>MEyJmRyMnYa9MF%;jIc026wg!%}^RbONKQ&+M0 zfLI)Vq*~maRf;Ln0@^dR*9V%l-Ovq-?@`nWBW2Lr=VQ8D(?gn zwHJtc(O_nM=nqWj?ZEo_?!n!h*e#lQ1l(rb#rmE$T$nYDirkXuz@$9xle;TB^M+wy ztsL}Oa2_VAO&324H`0;|hUlSkSR^-r*|}bzBM0hQIuM0#M{{hUiObLv?`*QQv zBE8k#f<@jzV6*{6e~8E_mb?Ik10UIx10;EJvm5s=w`cPXF9a12Z>HU&CMZYDLGwSs z+%DGwHp|3IkbY=q8X?!wgI2OrmrUS^pbxc~OW;)63&>mXkv)=+h8S)%(=6M>K94D* z)z#rNUbO_SoJpbTOP9FT&rSFt^D|BixQ_q2j?=!K$7!|ea<1vW4}vI$1>M^`3pVnC z&05EpWc_oD_Ta&NRv43cGMXKd*9X(uXl{a$9UkvG$IhQ4R&eTqaJ#{c1q9`DLHB+O z_iw7O+d==~_W*yE9Z?CJGG0S*hb|ly8K6;KpI}DgWFg^ev&b~QLT)+zsc>fw-q|*m zZB8%}xUE0XXn{GTifpdoZy&-w<>kC$xEZ7duaovmkH8yZZt>=^J8zGr+wDMcH{U1`f{8e7u-UYkaA6IW~^HN{b9(e|>?!RQy-oIyO z$|vITCl_JiDHDhZEh7O#GuLsr9r*+=2r$d(Z zuDywg>GRp8o?43d;sw)=i=CMAT3TeRA)RXMM#)*3baAg0q|ZKzBgALo`6(tW^FMD$ zRz1N|bT)I}IuS-hPh#mMS*&+-GiN`-7ZqY2v+EypS*}bdr+e0n$!!h}u< z-G2@528j&TzsDfI=s8^eEpo!Y8bV2BHgD{_ir1d}k{>M`g_f8qh)U?9a_J>z+Burm z+1{s1Y6b*pL&)gvYjQkw8MFI+C;fn0rp9 zkny@s`)9R5m{T1@2Bbmsw{Y0P44Bc$DNVGs2$v97a^N&SjD zzN_sEHk0k(`G(p2f0~+5V;Dr=hv(A^rLpAddybxMk)!L=J*oNhA-cZK9ENtyz?dEpMmpveU&U;tMMw-k12$U!%hMJ^^G94+JF;3$1>${^~}_4 z440*}pUwCo$4)j~g0Q?N5a~D*JSG-{((+cIsqT>Pu$)bK`2<$y{&P`Yd0Y~!kRcp= zF$x!LSufc$#DTl8HJA>J+Amd8dO&+aj!3=iCQJL-h;yiU*T{ZPIdHlT?AHrLD6|;I z8)@Y5A>TuJerp9kP393l+UBOn=1qi$d1mmX-x65%dlq7+I6HZCki|B~GqZVaxUH>` zdB}ILD0wOD;r2kUs-NH_w;L+v?18`Y+Ia2GOOUzp9Gq4EjD;=Pl)a~oYgo5e_^llS z{ZpRPhW$R|F*bvqR{zHG7q{r_p|f;mKwp~fvx@IqsScmG1CY@c$eZc(v^H8~=EcpEy?|-+j#1GX z7nD zT*_WOPl8v)3wVtouXydN6L^)RVXVhEmZEQO$9YNpNp*@8|GO3^)yj^Rs$ZBR{~sG|4z) z7k9nl4&&E6V*WBeSs)E#iRtIqI=iRrRjesymQ}L9hX%1ZiUV0t#5EVI8~tHWiiG`M z(+v0ezW^aEf>+mm3mz@eE>;vo;}%=9SqZZRQk+S7Z7Za@K856wHbSarrN+vwuCsYJ z*9wcd$GkHJ@bkW|;3Lg>@p;sTkJ{qQkDIk0zC7N@KB-2-zLw7ro7Y6!@BAU%3wz5{ zRwt19{d#O$e%>WEErIo3Ka#cAmkB?1cd(ibqlMU(`Iz9gTsSj77zX!q29G|nF#7W} zDBWKJ%VQcLbB7{Vx1tWeY`Vu%c0QD9Tj;kI-beWI~DcLJ9?0=SOp zyvr9ye$?;5{3($WsW4;!Ki%{*sMUUEr`)|T=E5VExY-&MimE9lLy4Y7BxBufBg|V~ zgB70>QMHe&2-_RX9XWT93(-G}@{deij(?ekeX`$T0FU48J4)ZcSe`&%T8~L}#~q`JX?HJ4eBKZ{h zDfVrxe%VP?NkT0*ztf{}GSXh9?Q~?yOl)t7WPbb{EbUAsm(&F2D%%hD#oxtW*~#Q_ z`43)`w&6#W(KNQ9l17jJBa~h6q@l&@a74cq^uDr~UjMnwW{$~bmZ`ybJaH)~om~RU zBc`)C+XHyMXAtjt@eQ>i8K~QwQDm35S#tfCF;-T)tq!)jK zwnX>f>KWqSm3)@Qi*sKa*@t9oBhWY@pS*oa$#{-8GzH&*wC#ssYt2@O|D475zxa&b zm?7c&&!5SwtB1fIGy{*1-voG-LPGg>=@^lJ(5Y-s2RnDr`6=1bA)*6eMZhH5lsSPy zW&2X0gB7hTm`C2pt=MO>zHlKoLK1$ef$6zBVD{p%-0QL)e462m1w#^D&O(OE=<(Ze z{ct~$ahQOyy^BBzjU;2&oF;9ZQLOpQ1bFgZpCvBVfu`G`I|aj{P11s?Y>(&B{fSLyZouNcgG0o-tvX^C9I=cHfi*4>o;nO zv!{^FmUv~~av^Kh5w>LEdG>x<9NYBn2+o)7EqvE}fN>cm!u>xXLSptqE@`F*x3qXT z7XDF%o%_uAUgnt)k)#Q8rkoTrM=N*}jbz?ISsv~WbK$kiUcoiXEfD4-;l({Ei{4a? zX9nw2e#Q^l)-O=nf6-Mc9;Ym|9jGL=h`%5;)YGE+jG6Rrc0FC5=1uq2TWQi(F?V1V zfhI;rTrNM@jKLo@a2wUJ@s($|!`3so=hKuhWkH#w=gJQ5*SW_qDt0!^*?JqeA}!z^ z?SfCa&)^*I%MU&Kg`b`FlizbYnm^syz`OpJ!EY}tX=Tc43pR~|r zXn${z9kP!-5pGKwYI2!m!5LOCwLzF1JqOPx7zp_a#Vq~Gey;HjVSZ5@Mp#Z^xl2qj z;`n8jp8uX%n6JiRD@C^F&2QYL(Zg`Kdx1nUQ56j{k4k!$#xcLuM=@_?6~5RjP@m&J zgnxVAqi+5I7O&LJF7h&L!kiW0{EKHpAdS=>irL7(KHU9R$0X;jJ^*?1QkK;JHw$lw zWaW##V@-Q7SsBg3sAH#b?eWJLHS7tVJy8bXw5(7>zsYo)2McUl!Y zyW*J(PVvB6xgTuqlJ$)C>}U2l7udD0BVouIM|N$NA-nubTzD>$y4=%4P+z~4yS>y? z^641E;M05A?fF_PX?_i3A5U|Ir&e(;R_!jeD+Mn4n;GUROu>MKW^6#edAMqnFB;~w zWB7v(;iJBIFS_R|d34qVFE-_1=8743?9^Tqt^yjcBvM@_Lxc02$gJI&j>~!AwI{!3zr$1DMNT1JqUy!B15?aPO@fELhe~vZBI8 zJl84X(EfzJ^3yp)AK@fT@3=&pcQ)d! zwoy1iFH+20M`MlceXKeB5hjQ^@HMrI*uQ>9B3U&%d;$vB341Gm!T{dp9!uYk()S5oqnW^COmb^%`hCfDf6DBR8F#=LYQMcIoK z>UoM?^go3COLnov8>86}*F9wYCL7o9-HAs{uX63r?eUK6KT=J5Dx|novJ{L!pU!Rg zp=Pe|r1lWk0it(qGb7vj*3uCU;3Tly+q;W%? z%QF)l_XEUTP9ID1|7S%jyc6*J@NV=xdYy`7)9J<4Lo_R5FK;LH;eHm?cc3%b1IJ z{20{-RO7pWYcXP@rnqme7C8mk| z0-jG=hE~TPVdvLctT4JR2`GvH!y7rw%Kt5!+$pl2tDJ;n+t)5iW@+50-5>GonLlja zpbfY-(w|NGs!2oMXkx#OZ7gPx5*U25<8?8jX9iE#MD`?q&WB$=^TuwLEX>Uoqjw% zlRg*LRsCWIbep)yp4V*U%_ZEN(|K%Pvo;>yxeN^zGM(PE=5fEat6|>t6Cy{phg26^ zl7X*@@N0q+d)VC}xj+0WXZ@;-o%m!&{e~|Gox9E0-^|-(%_tWvP`%1cXt^$Ql*T}q zVKUou=_L2@Q#ZSr>rTlhw~&Fz^qQ)8oqH9&j6qT^SCKFQ3=FTKdZ`h~_qv8FTSM5x zE+tkRJ__vDY-6+S&jB;jfx&Tm@ac#MEb5*p6l#9Q^#=8vk?(9a`jP`XaYG9u^yi3f zth0<8)XB9^?+1hWjmOq=bI7!M03N=WBnSs?vU^J{Sh{_qi}RB}JX)gw+9HGf`WhS2 z;pfdt=iH$GR5HM9(qs1YP7ef3Tm@NUSF-R?0!?VD;$8&k($Hbvv^o7RjEL-FXBK=1 zd4ut&JzooY|2r?_Ia`tThHx4)nirlf(8ZHo)#6#H4l9FPg_xAX_;uU{mxju0beP{n z8jbhy_*Eno-EiS@Z;rWbNx?uhSA1^ufaNcJ1)BHOgxd~}S@-qrq`mbHNi&S7&hQ&u znb1Sw;WwHCz8D3g^s!j3cD~IXQZF!LBI-S@^M7cFom_ zJu?$IiQyujJoFac9)Fdr^cIl$>>B*Cp#zh4EMZghZSl&sD&gxlk$G@>1)DhCgVYO_ zQSO}_;YuloKCc!-|KJYlTeBbL9&LcVz4qYwcv<1-Abqyt(hSm&Sq=l9w6ll7YlU}R zImDKqW=CpAFrT|+Fg<)L=G*Bp*S`n2%z3AT+PI%=ebsFC(%lGVf1k+K_Up^Kii=p0 z@mGu-6;FE0v@mJ(fB3jf!ll|bVD;oW|nM|t9Bbc;&GaLJJ7RW#FfHLzeU@?s8j{L$E4z1 zy;YL)ZWYX^s5i$gE9Y`vI$`^Smz?Icm)N0VgTIZ9F)5E1I#$dSVmlSV#^(wQ*51g* z7)Fx%ICs{5@gar3n!{{P&7$6ot`H|5jHSEEBr*C$TyT*d+`Xwy9vTx^?yo;!?a~f9 zAw!aN-ztwljiMzXSQ+DWa+Mp0s9~ zIjy=A%}&)?k&KrIPN-L-u?5!T6Yvww6nn7ATLQ`j4`IraBAhtx033+`$@ZTcskaDR z2>s>;eqzVs+AT|VV$EE3;-@ToD0wFCz4vh{gI8gi`U<>fbObNGbYeqgMhay=yo56| zPclcZ?I;;?feBy6qM6nXm(xo}!+)P2FvYb&;yF8)u6!DgclG{~YHbjOF0&IZ-iTxq z%&Wm>*?&02_&BNkcar+P*@s&F!|>`vEITGY4~%;Wl2u7Ikf=ABqV*!Nf}4($V_)FX zM}z3j%7Iks!_j8vS>%1e5Cb+o=DLq7W5U%EVOElZa5iWh+f;Reg|%Da(Tx_ke=Nt% zwTfa^;c-Ity$i4;_AOYxGzN#?4sgJD3VT%>2WOAOu;#U=>00+e;kj!Lt3Lc!IBDp} zRtyW1q^j(qlo)deax>)ur>?->9^asO$1a*Y$&fwwd`X&dm9Q~-5G%NQf=;DKD82h7 zU9^j*UW4c2_1^Lr_(&PSZxyZ>5G^zbA6QOEAj%eXu|+9Zx{^*RV(1Y#(z*TQ)JHTOaWm(2a3C1vagyqVX`C+RvPtS^&>@EzrP0z zS{*@W<1BFa*#K6b4dG$kE;h;OEd+!OKzMQio9~-*o(FueOvaq`dvb_PR5fD#Jqxkm zaTPAm2*#%6GsvpVfMg63*ut_KENRLGZoA$am#4Q!;DJ|u?5MVdprB=lmd-=q$j#+& z@OKtCU6}XbnUDuc9@FIio!5tw%@ac)XoLV9u+*nq|55Y|=AD|@Wwdv*K3pK%kJ zb;MHm4B$ZfmmlCY)e$wNThYB=YKN*J|nk|&fxk`s$l!)_2Gwi%No6W1=Ld);P&>T*U zGB;eHVQs)rH#P7{fdIi!5lvp`@#qM+`cR_y1zi=XeDOncAWcp%OBsa z?x28o!Q?f25m>EU0SfhA!t;AxV0GjPWzC3!!I^W&a%myUvKWes=C_iw;~J7T4xnuB zN;0&$#hMp(u(T(!6ev-bnzRpRPuz8B!}Sh!Kxk)G9xBW~J6p0xVTZ8jLJRAMx7k3g z40c%a2W!#t5%x`ghtr#jSl^`EWu*mPU|VYq`s#1NzV1Bx`tvB;7v@e$;_=EYJCXEm zUZmuCE|@Cv$cALNvK-%Uw4l8&Ynrne#;pGi%Bz2p#smWxzPO!9w!UXumZfpWJ6_{Y zR}-?;c|;GFKBR}nUqLtYHn~Z5fzioEw!3*C`>^Sz*k4Fzfp2zzV}vHV@j{37Uo@OO zin@Y_pFZIVBYK4V7HfQZYzSz4{>bjQ9A|dDbwD}#H+y`i21i-6uqdS~G_Ylg%aeh= zEQvn_dXp^Jo*Cz1XORFZv;RQaraVeovxNoT9t(Nvnqk_iqu@Aq7;sMVLQUQP3}3X3 zl$4)vCrbntdi5&mY(31<2Kd1)UqA4?sWq+DBqWdFX zR&g_wojnyIJReuUu9dIm+*FL1>WKnja^53Bc=H|9H-CkKTlEkmJB6!EdPDhj8sIrT zL9mWfA&+Gb$o#G@E0Fug{j1yxYeY_}{6=K)JMN%a^+4d=QrKkDK^k%?yul-71GA!TmZ`cA$G$#yn0?PT>@fX} zmWu6cu9F(qcWf={`c()gr{}?fsC$rO{Rn4N%96#@e;At_?fh!%A@onai;1oKS&7ai zm)cdc(dj}hUO$%0e(4XvC|!;U*f~f4@u5C`=D5Mj7i*%Y zQ@_n^G~;1U*~K=I({eZWveHGx}wo%t_11YXJE%*#L% zZyZ@n*1fCoZg2>>=jij&xH492$;iSr9 zBv~MrV#bop=d$`Y^@6!+7pC1c#iQkC@tVV8$*4;svAnH{1>e)cp~gS3!?OS%;92&~ z?53c*UKdl8w+L_gW#Pn~S21tIJ5*3^7S4{YWZL@zXt+}=J7?Zr7Ct4&^;p!g{a~8I!@%c8gS3MBAgR9SCMH*4rxi$@uZ*lIeOTE%+~$b zKSY@`)c=a!N_|Q0(?FW%Cu=qXOojwRhBpeeS7VZSIKJwuDs##!0U%;qwBlFsqBwRgznGMxW!oxp5 zq4Ew@td|{xllms&{Fy`W&zWp|zA6p5hepEd=a%DSs9Y+RS%J$l8lp?(stWzXEbBHpDlpJZM3>0(D6|=(yP! zT&Zq?r`tsS-`jbdqDLCexhZn&S3hUr-`zN?12QbMG@RXZN&xi*J}|SSHw$vf12ywv zmf8H9eefaBTDzTv3gW#l?lSf5*iPm_s(2`*PI&ikDKqWkjfSTtkn)8~Wt$w0u`cHr z^`CWsX>7Qy_jYP|Z6&O_e zljOIzfz}FrP`$ED=#c%weQawHo&>4W^yECYvfG2DwEYG*=My;WlRdlN5Q-Vkbh%?j zrmWA8&$v6_qR{or26vsBg~|;}n4RRY#LGDbCxy7<+o>Of7e_sW%N~YU_VOw&JrKZs zl2;N8m(0UOZyHH$a3Q%nOroi&Zzx{BgH)ZoNiOp)xyNbY@0m+z@{BHg>gh&fhnI7| zT6%<%sW<3>{T%9e`+{Ou2UFM#8FA0SQLp3_;o!h!)c+nVg-$Ud&O-#Eds@n1OKwW}GR-AE!oflGt{vyN_J^p?_SYYq78+I)gQt zIJh_@g`vB>InAB28~1+QK#S`{9_W@t3KYK4FtHQizBvwG3M4dnT_T0w-zc@OIVE`7 zkubL}H*=#0#az>+-K&At?h-v>w%;U){As-LBUUikD}#ZAuO%>Vi zT^KrY0B0wi&*c_8#L~;z7!+^`FS`sDLazj~G|MyG*gZ2j!&)=8!66f4&DK%SYj=9~ z?l8G)&Ln&P8hnrtkNJnBbpLK|YO3#`*ga)*>Uss8XxWPuRU%u#`kmk?tHMq#9>4{D z%w?N?sS5RLON5$*>oI!DP4xQHM~Gg06>mzBZ=cI6_; zxwiA{NQWmI?eV$%g-TZWmf%_Co=bGgRs8j(JFWYW!}5DLEaxq0hxR5mB#*Z5JAf}2 z8KJdu50;$pWBIF1*wn_oLfe^1EM>_K%n+91yg^|MDxF;lKc5!D##C^MwiDRhxUKB6 z+6HuAAHrHSqELH~RlxyXaa z@PePu9`S?w>GLvTMzw8yD4o#10x#xu!iT^NaO~3yv%M$ry~AAiA)+hrUz{bpzPS+8 zdtU_^(}QL={9=W^sV zg*V0}4-Zy}JWRqcy%P;nM_f3i)djsnnCQA1e420hAmc!Ze^1N%C zCNF<(6d%899`CF*n}t}d;kp*L^Ny9bAvr*iyQpy-wr?E5_jgR;b%*~4%`F))$kvVd z4Ee>zYi|Ixgrh89dQjlY&*9pSjm%x|7no1Xfx#kw=k?-Xms7`-KsKwt@a4M>HpN%r z{r;(xFHwhRCS3=i{3~4LKeI?%WrJG^Xl!1Kj4hQIH(dP|hb>Y)c709dUVW$n0 zS(I`b*p9gkeQx~{pHVv8r}tfK;f!W5a@J#GZ$=5cnuUw|<19`?c>$T0UnG?{dD3?5 zBkrMI;FCR{C_H_;@QRyAdNVgkHTM0aXX~%S2+`%6Sf)sqf7L*&kr_YolM=fhoX?Mb zJBHt^a2t;NYvWa~zG2!=$KW`>p`h^o4m_{<39Xh&ykYeTh>6vK#XB!^?RMiqHY}BC z+Z(b=b>ewhX$E^&FS7KzqQSOu8|c2h&m^vw(8PHeXh#ht`IYiCef%;UvU57#f8L1- zqYE(gbzeAYxq#-ZTEY(66=UYf>vY)938yE8kcsUocy)RkTpB!(#WcKzI}^vlr&~U} z?SvZmY@5zoT=Gr zwxXLj@wltV?^*zcC(lZL=F8x|F$-YWin-uvJsPbiHc{fkBh+-{HXUvz+Ig4z~SO2}L$-E)Ebvr<@t{9H0^6PZEX16lf$Z)9^V3o}oogTu*X5I^WF9JE>h zjXyuaC(j?y{C6P?O>kw?Z-j$;^B8uyY#L7e^@Al@__2SRCo=oL`mE@sEr#EE3B7ju zz>vacT=A+EV7T}tS9v}d7Bqc<(MzXErW6gL%;on;e_##WoOBr^;|>U4l@G!Og$b;6 z&suiu)nWMV7zz4k`@<2fhfuUTh|SjfNecH%#qLcZ?0RoYedY~?4||96dUt<7S1Bj@ zGNQn`>pA=UI8wM&(1liI7DB&QZJ46*jx?>^=(J4%1EpLI)J zHfP@f>&ovCx-AnPY>wyN-k1vW!W)<;E(WDodpIl&6-6FNOtk=q zd#w;>lO_uKh^q@lz-qDyPei%@pX?7QfMG?b2>sZLWL4Ux|I z>Lpb@d>Hyv{U@E6y+zEt?34}*^^%V7HkBH_)sVUjOAtF5@wDpkJ39Guz0fau0}b)t zfcG=Ixw^E6q$9L}hQcZ6)AudwoNyK9@-M*CG!`=R>fypTdkCJE24>Hm!B+n!=(BYh zZkmXgtyM^SZYXonA4UM*=*WUE?gw>88%Q;oAo(?4PRs>GOEtNxQmxdb(uCYhsjl-Q z{HJCrog8#k>M;4YRCD}1>8xZ`sd>VA>4;;8=yIZ%;TQ8F$DelLsO+hvyh9N!mJh-& zw+!&;#e&4s1=DRDBF-oTOx1KGpM72qmWr@4072aKRx+R2aj?R-#3YaKe*0fZ5{C$FS%%#`#cVV{Fy=IS8 z%e|Gf4Q9~VX@1zbcAunBRgprxV=3)+2sOSuO^4KC$nWiETI#ZdhK?Rhy8Q|y=9tW_NK4GQbU$L^-$$+0?(7i%d(l}}w%>392>|H9H zEmY?f=i5N!$TKW5y&eYphEiFGK-2yDN*6U*Nh8%dsp7Q{&C%Y>Wt0KU*dB)4qAb|X zLyLuF->aC{%|NW*uS%&tJ#>8M7TTffN3&~dsJ~i2ny~2+MvJW3BS|_ebIT+4^M^BA z^XNG{*ZmEYjApaWH)FWk@q)1G=0VaNCAxO@x0BNpMV2V<#-!ix@g`|9yv6)|@KJo< z3QPV$Tc|T!^_sxUr{=k&4{MPsDRcCA-AL@Uu7e%+--VibCTx$&Th8;*Vo>Rs0bZ+D zgSz>Ayx5qAmfJ_rLiyM03zn+@6oolmQUrouy0@?jGJ*p$m-9bN|GgQix|rH+N4Exwztu%`73YXwH^}WI=P&d%jB4k z*xI&SxMhEpMO7N$F}I5_?R5{NoxH;<&lQfaH6wux9 z9!mY4p%$HD`GNlb*&HsiqA1OI zr2St3*%z!63aieujQr0`QFRtd-t@+ilk6$z{wLvQACb2UlQ3s?6ZuG|LCgt%_Go1V zGk|aO$|H)xD)&;N@(`*Oxg#BB|8VfV;i6yYK9T{jroLZ=wYm*~_XfpyU7l+OCAv+<>KV)Ryg-C~M~8{+Zn2yYs8YCgO5crls2J_yUTM7N1aKZsiKLNKTd z!LBL&$bINoy1nfd%~5xuan~EEebHN~zN;%O6y`ba51bB1W#-U-WqTO2NP*=;ufqNR z7_58n1TutP7`EvG$?mg8+b4UPmMrA{C2a(+K`+p7-&O38=&@cW zK1t^(l}Vj6U(zl4WzrG-I;Cb$NILmU6FoiOfLqR&N=H5ynZL>|uzy_`Bs^&co9!Oh zcfc#!{B1C;KV2d$>qunp4)@{CpYR}s-76^3wwpBV7FsFj@S+Rye!k(3qpx82o_AS}hPA zHd&Nlq2Q7rnasW(8Vzy}k!5(8($?LsSnpDa=)H-%WG$6UU-{d=VC14~B?U!(qRg0IGkVz`90F7}2*={LThJ!KcEvGoo z*Ps;$pq7yf*K6cKu}FfGm7h>b)Mlx3aTwK!*@C=P8|hY>HuWu@PeXDi(T?ArNN!Ld z)>MASSA+Km3CD7AjMY@UxGGhg>osDx`Em5x_7f8qB5Qaf$K*_Bu%}-w;ZW^kxYha* zb_ZRDgzRA~sr@yS_}+o&u4B;pZ#TSrXUvQ$m9(%GLmW(Ce>v0K4PshXGz(p>9%EVb$*mMdxI)`nRK7z!F1`uNQ zSmZ1mp~?3*ON~Yypl9P2FegIhd`eVqQFmUfNue`QeqeN#GHHkN|UUZ)Sn_O$5b z12U0OrBTkCY2%%5Sa{R|!cRxBbDbO5@5&HZ?C}n)*Tk_id;Wt_#ecXHR))gcb13q@AzH9}3{lGlN{E=I@-O z2RimboK*B(oD#h+zw6+ib1l43JVuWyEnOON#!y+tP%_GhrZT@D!gnu%`}dtN&Mt5c3P#fK>v39Jh+?dOzvWJ z9}+Vv=xl^kv+!pi|61lv8}qep+Q+hZm!tQTU8Cl%VP^wV!!U+VA#k>5`!; z_sM zbt{|S^K=%!%c+r9U9A93YX?cb&p(9{+cE4;do*Y#XA0+PqUcOP1f8jxf%^BN*y(xR zLe#Dl_9bi(^s(}Ei8E~B?oQv&8rF3)z2>i+pUQe}#e!@2z|IQ)ysZX#+f1%SLmzsJ z{FW^ZH}PzQIVGojq61B;QWK{F`txF}boB3O(h>h%r16>W>E7lORO@j@lD43bSF}~) zb(1!VjI-p(}Xh08|MEOAHU0}OEnjdvflz6h#6rb+et+K~lJh%0v7tXUa}&GD*ivo_ zH-FzoEHv9B*|s~2tqpj=hRwH>+Prs_ayc(3~5VbyVos% ze!h-E=vOax_w@y~;en=5y2Y;SgPuHT=$Vqu#kUmQ`IaVEa6XVtAh#UKewR z4Q1Pii7@MX=-5e{7CO-O+oMpeS)3`3ct9P`BysWIC*sWBaWv`FLR`IHoUZg8PjAfB zCQdhHn7${TOozrOP@SYjN_|Se@{1YytGSo)ND5{;{dSV?E9TNgYLCf|bycW$X96V0 zr9$+IpTv6JG%jy237v~IQOm~yOmq`T-_8qM%j;lzbKoSv+>U*$<+SG;zHIUiQ z^Jq`mdB|Dl4FMl~U_;Gw5F7o8xm`D!?mZiC`QKm(_aNv!%AYo{jQiontd`P+7YWAqC+onl8??6k>DVXw>li3BM435*mAD<*7xB>LIg6YlQ?VaISS zefK~W>syEL*v2{<*#8`EjgW*ZBbIQJHKIXrWE$cT~Gb>59lKaz==84rtt@QtWo}LE=4F z+--$jBc;IpxP!o7{Q=9ay@7c~0^fadJPGLRVLt6VA~;8s=)?pO8d`7(59qgGa`se| zShpL_ZWW`!w%=gh#W*xR(t=5&4+|~@e_qEgnpdba!LO`7*wj6R%*$oatK-FvR<3}6 zhL6xDD-Q7ob78r72TY&woj&RPK-Z})rE$~KISFeka{7@w{V}H-l&()Fdzz0hUBg#1r@8M=K@h3=b&PR7}@>)7qx6LCbkweMPP$ws2HM}a-u!DI zWBvsuNDKXW=WLiGx0TG*T+J09tR!pmzmlq70d!ZgIte+NP7b#|BRLkg$l}_uR<+ry zsP5v`-bl!zjadw=S22Vw{y#_hJcJsoWhLj> z2u`;3nE0d$Eav3VufxlEN#9nCYOCf7k2#@rcobwk8_#VR9zgA%&6xTxfe{;LOZ4pi zBUjx^7@z;Xg2=gjoV# zu?vlR--Qb^gQ@hn-rDPn-AJg$L~7U?NRO;sj6Fix9ARicN>l{rtLHk>RVVc4s=Pri zZxuL)Nn+Hq2RJ&M=UVoP2*14*!3cTyX2b7MKjLW@HqkeS!D@G_Wp!!Gx%vSSocH9bIF&iS!{;_Ns#% zz2Z&wxtt;@?0xcF-I^Qiy@y=?vyc?U3)#IXD~R6gWKvr7ioQI4i$49wlb_jQbo!uq?(N%cKwKa_;U8KZ0vpuqbOSYTI8I zvMtYQQfcRxH~qpNH#qQTGv%A z!XyO8V(uL3x1|Zj9^FY2k_I8>KO^SjEMKrX_KW7regi!vN%HW?ao8;Vo$>tjfOP)R zfY>LKQR4Gge$w1K*x8bUN`-r2X-YQPB611DbT+{LS?6Gt<31+;$a->0KNYOv6KTsg z2`sb8p&@^)@pJW4d@q%TrK~3z_|%Q+5mIR4G>_DW%2Fj02Xb23n6!1S0d0*QGQOaX z1O+wIb!8_=+mstHYQ$AqTBt}Cu3APLpB^FkSu;qIj3r?||Af)44ckRW zn)5+E&+mlhVlZ!2FH`(GLxvd%BT%cw_9%IYklD=u-QWXEgr!7Y-zQ}C zG|Ag)H8K!91Tm7UF^0cR&I>cZI|&BB%if@wa=9qaT}EAJIfyU!Cmpp&qJKUnCpLS4 zT2f1GdXF~ec#uaOgQ;}hi7L`)Jj^+kYLRQNWx!D{7m|yrm|N=SN&3uEFb>=ZOV38o za7LT#UV4?86~2KSIUwARHw_~zUL(G9gQE@U@M*O*MP`62jq z)xyT8ZLmUCk8HDf3-bS6hQJfTu7FgomC5^OaOM1SC>;uer!zOfcfA^-`FR%VSyQHU zRtF^ZhY<&hn{=y_;7v5W3Tf@0;P*zTQBIs;E?rs2#2Ko?7=3wSAGw~iy}4Wa>qP?{f0H4Jf6|D$KN96?4Ui%!CJIC8cf zc8Z@NzX#UA(&#o=u(}zRPL80T<`j_8tIOddsepEwCdfT;mq}e}NE?=TptO1+`SWET z>PzRMN9t8n;LV6d&I(Xe+DH(=cPR6l#3^(0NhSXGVB_5W^le`GV3j8-r_m`J(09>hby&Fxz5eu5;$I=h!!0^%BiedO26MOMEBW~ z(7^s6UA3YKBa#neVcvEOk5{1Ewr)cs&p13du@loAUyx4O1CYrFf!l&=xLrO6qUNX6 z9-f`aN^TfnO}_48m(N+vF3H&d?`4G?u;_netgH{G9?YZH#OeI#fB*Q=$6JIu;&L2( zD~&GM?{I-I>yeN4t@XJtMux`9F;zMYN-iix$CC{xUK@!zJKAwn-VklRu?Xc_^-<;Q zBYM551dBEkbn5V;#`FE?x7^#P{7T3Y<-CR^9k!sJcN^|GU52K9A?G;ML-L!(!8dnt z_&TwVUG1yM>J_)MN|B|kNyadomK_U)o0Q1VmbG|;itzeQEAZ@eUwjjkgpF$~>1whE z#SR`t=?!ygwss~Hv5xt;aC|I|4Jbz!-Dh+j&ZeDT8c|c+0PQ;|jaqGl%5(Cu{9XzM zGd397(TO*rx-eT*3*C&X=>5qe;NmqGa$O0mh2xv&k}-mdscH6Jew^Pj{9%6|hs;cPMLWBzFZ(}ocG5Ovx^e;c zXt@&U4KJ&C6*q-C9zR3l-R9Dc!qLo0?-?|*I1;^W{OQ${2sAi4fORIVxc|I6wv?a2 zH%lb2v$!9VEw|JEa<7ogQO@AxG{pJzJ|bTW*TSJ-V6{F~!^h3v*^P^5vkQmq;eE?$ zcAV{RD6h|fcB>!6=WjSKo>z^zY3Aq|cpC5QJHadD$I!>a!?h~ED@j7mFHlx~OW&Hi zqL#0a=lImYWkq|Etw(#UP92C#%-Ej0=+zP3P_wmn$0wiZj&die<>ccWCt zISjgxPU}UoX~+3xc(l2X-Wm6n>U*t%*oN!y-%u7e^Qktp$UP_C!vE6B(^FuHO9RO| z^c!~B#F9f_dE7PqIP<~x8SyO}g+WqLn7mdCRkDVtM)Mb#p7ffStVyJkWwKGVelb1E z-Q}KUEa9{YZ_^M1aU5^-7G0ZsaDjU}9;#SDcQ?<#mvs-Y;eaPEnb?HNV%jA0YY^S} zvlj~ze$c9E4Ji6I1n0Y!k}PQnI@~X4tB%%MEm*FF`<#yAT7i!e`6>jPFKt1yy_sMn zr$spr6D~|YwK`dHGLxvV51k&aLBrVw!W>nf^nFNUD(1gKr)&@IT7)6@ZOKx4R}`sZ zY$2_h--j}h(=aOf9&_es5Pjct8$~Pz>5olvI4eCHzkj%e>3>TZ1=TqE&Nc~mb*qC@ z))t&_P!?yLcSWO>jkvDQ>pFSA-**aq^&xsRptfS_`-_b-xN+#J(IYz%i6dB0}DEk_>?}F zokNG0y<|p@>!ryZy|tFAtLW0Sc-#r&c%!uunET!c-F|6+Ui>1=c<;iw2YthJbD}Y1 zQX0MpIE04>4$|UxU(i6mi5C+-GuK1&>3(B-P|Z%Fo=RqrIOq*K9{qw*x$j8kVH@y! zc$d!ZleKyjcM?pBs>%3K9XL&XD^8iRiEVr>6{>RNxHy_@>zffJ=Leu*9A zY7{~0jy7P+g|n+qg*(*l3~DoP!pwFpdd2BD@lOkc5xx=lGx#~pobUr}SO3L54M(uG z9cN6w*c5XuVSOu_sUI7Xlc5vI4R9X7H zUe5g%-(QosuLq1ag}|I?o+LeR$g=U+I=YQJO;25)$qiI(CCx%_R3}6kiUOnIMw1PA z6doX(`!3U!uU!UZ4z9X~m8Ek&{gGSBDK+ASJ{8(R)YN_); zrGz6byN`lHoFgrser3X5;^k4=XDX#LK)Hq=RA$~oR=DgvcRf$tNvo#l+#ui9|dN->D7 zd=K~keJ4My_d`aD3`Cn(LiWw;a9FMqeCF4Zv8OX3X1mDg5*-?wDVW@sJD_&s>*S>m^UqjxYxge7quK5S_Y+ibvbHljtL3%QXk=AzY0Qvs+>l9Mx7eu0l zlfeG2F<7qI4?C{r!RWg}4{X+a@_h3k=d^7R^P_>%NdE|0b9ab5I=32>S8cS48|Z+O zo8_QYPYJG>zk{dG5bke%4E6nop}$8KDw5uVoRhJts2(MB167?jLtp@(&qmkS5PWT4v=oreO82I+^Zua2W6PN$=}Jdg+Iu1rxOrz zPzElt(vWm4mqazWa9OBG)}-zOuRTxTe(E`RzUU7)RSjU=?Jyh@Xhv3S=9vMrOl-8a z#MWtH{D^ru{LFuQ@b!p7S~M(&ddX9GiJv2wcG+3v<-mBd(rBBIsa+4@M%FN*?K;e< zG$3BY+3LWlspP%YDXv~(A(L11ms~cQNi5c>keF*}++oKzpf*F86Y4jEsB;}e|JeZ{ z7yQZh#0-$~v7@IIj6fsSn%upp3Q@CJs=W7WZGVps9=|*t=UX<=-hmLDK34cn7O7%) zpck*9@)<|QZ4Hka&>w*WrQ4lc*{EaAp0uGnDCA)v|K}euPy?Ul=Z}|=^yPcl_s^v z8*#OxIF}+i8&`)Ad|0q+6>0Ps~vr;(OYX6T08+HmIl)Jo<=AP98dmT)uCc9eYjt0 zX*Bie23lg0NgKqk5lbCPhJrKJ8m;z`uCZ|Na9*-s-~2BfnAA&4f2#6&Z^e zR|OW~YSz&DHG4p-pPhPbKRjA+1|q*_(-(_R6T|cA;L~gf+I7nz|Dzw+8Ysj5buF}U zf*B3tRk_&IYv|OnUg)`*kyWQyfvtC&X%v+vzjHKT`Wq*3cRNB3dLD)qBOb$wDP|xS zLTPm-(q7LETJ8zdl;iL6X zE@X`=E*nAPBYSvRP{Ddmzru!!ZG)4|d8}0D940+39j5eu!O5;((7pTsgzH@c^-JlH zRI19W>GzW34jH(&>I%O6a0l6lQrf57O0QTw1^Ga0tJki${DX^!ykWrd3)vW#Pww`Xklz>$!z)5rm6Pk?LBA$D zO6oF%wH2|cE&lApc}s*nwujh1YZ-1YyNe4fe&PpnTPzIWdC|=ic|Wh!WJTi-dey%b zb|xr;noT?=AJn0rPc~vnR|THGcYt0HnOFPMau$jDVTv+WZIR|pLG%4;xYDti{uFH_ zH;g0c<2`;vbaE*${~ZBO7e|hsT`&X_58+Whb{zT75xgYbfxOF2oKD~uTju*K^+&AZ=iYbEW@-y64KkP1mO6emMzs z9>@Ltv=^UV5$-@;!)P;Tj1xr0qV5bCgpYQ3_3$&?RJV=J7D=}HzHU8;9rh=-Zoy>B z2*DwI_5$}tOO$4gy$Uk%GswM>k~HnKKJ#`u83j z-g5&-8U~PKeLwN#buC_eT`ZnX+s2MaYlAoTXGvN2CAcVm4Q9ArXJ$+6fC6DAt{%P% zPgiE(T1}pge4ao*FWgD{ry8NdZ4p{3GLrrlnCz#-6FHTMl*FuGM*`-QGFP5`K#i>v zXy4&fZjFI4ok34w@>pk{Il31D1t(achbKv0<%7r8s({uXC3eIoDVF`YfHh6EK+l?2 z^r~?vH8dXs8eykcagSkE&iXOjd8POHX-jr`>qCdEIsHy0UE*I^o?E`t#(08Z!>;RqK z97}eS-^8HCfj9(DCDB6{sFj|uo08gv*c!)+eNf@2Jqh7ge&%^Y^KM?@d>TI{r;;qW z+=|)KUU>Y{NN)X;Fmz?~;3gl!>l#%>x2qFb@Wkwv9dj{B>1sAaU*^$v!Mfa&oRR@1*_-J zz(q9|Ft%wTiO3ZsddGiZ(46D^_bL5BmMdH0R|yojb8?%br02S0zt6S60Hk+X#Ei1+2C1!iW` zhTGKIBmiFTE zGjVFTJcfA)&aoa7nAGr${NYq^l!Y;h&bv%fQxDMgUu*G5-wE85ITq6cf@x2THHzk) zp?2f0quxo1N>;9D7Iclu9}lF_2|^a^Whq&5QjR>_^MKp6Yz0j`>W6+A_NaSG0nf-u zUt8#K9`v&B0ppT_i3-1IgJvfrwHw3H6*oa=pDKvh*w9LU4V*FiKdy9|C@EW8h_aKFYWqOWrv?wKr!Io?lr@jiL{-d@a0jts)H z)@%3?cYS&7dnb7Phep_ux`^K|n~L=_H$rBZIGj1J2^seyz~#OYDI6C@E*Ao9@c9Cs ziI?$-?k_IO?F&?DwZksA1#mWC4@66GbnZbVnkwY!=d9==N85LBpT*5-7H?Er%??ui zaX)GE(`(#}3)Rd52R{;DHy+Kk2eDID$e+zL!>=X>c%6=WmWdM}^%#?Iv#H zrw`8MC+wTcPi9B)$-nntZHpRw-SZk+w^qZh>VwSW(A%)JqlY^nf0hPGuZ8d%x_E4i z9W8kB9F99_f^ruN=pIXPmK_)?8ubVx6@Fzg+$4ga@iwmNL2Az zI;)Ohx&j`c;7Q9~PtR2YB^kTw~n@Kk6f2X?T4>|kIJ#=`> z2Trf;5^h|rk3;V5ywc?b{KSir7#8uIghxqYNRBD5^X?}<$5@@8wPXjj{|e&0(g|<6 zvy_)H4rQcuJCG+T*!U-&)aAwF{VFZY5TDL#e0YTHfD3v~od-9ntl`vt9+Dc*!@kxv zP?T^V%p~gJz^JQmY0P|(-7IhferVRZ-U#9Lr)84i;bYv5QQfFfx}4dYzLzk(a|`a^E-`i9p1h5G?y70mer{t6+_lz8q}>Kk_C!$An?5d5=#<}l4V2#j4-$uYY{a8z0X9`(dP_WY-i zD`5+2_qt);iB!VhSV`YX56~)a8&r9CjN5(vCaGM|MLu$=p!Z-l`DpVMLz4^Pxn2e4 zyBFX@k?EMSG!BDIPf%wed+&GY7+8ByO#CIxFiJ-7;^aOrCu+s3FSN%6t5UJ?Z#pNk z-=CcS6+v$7%!g~59n`GRALJ(@5fSqB9fi9E*ZgTXyLk(|-ue~dw?2cYH(_wZa}7+z z_oQc975yXZT7PQ)PAi%;NVD`7SXvVUj^dD4EZc z`$b+zHnL;x*~1y-A=YF14A4H7k1u;G@UfFNZyG3t{_mb}&8ynrT^mPYQ$!#%bUsR@ zOVh-x7`Rn(ob>uwf>^OXO>BNZX1h8=;{h|6D>oDJ{p`SFMg|--9wFpDj?wA2uGB_3 z<qH2YoC8Z##(}$@H5jHHAZBAua6^wOxPl{RnWB%M z85r+Cx`%&&lA;S-xv(8Zd;jG^i_GDenifv7y$Nxz(&3d?DqDW>7Q5u{5JWU9vf-|Y z?6fbztW1?9tTvEjJuaL9i3V#{(fkrLJ3oU=^;kG_U4=e5I2-H>1rOV_e;95*4YMOh z!;;1F0>itVSQoc*-90X}a`kbNy>C8a(<1beHrH{BAMsS-*GwvTt(##R+L+YBhhREW z0}>Y=h6yhoLdms*tWvWxJ8jo`R=sQkyX5>bcG}mEY-VH-8(=a6RtMLyir$3PO{if- zIU%z@*O{FWV$Pa%v+S5w;f!-OV@Ho!4X;gd;e2}v#HrPwq04$Y);^csU2urW)j36) zv{uk=S8FCI;UYb4I~nDhjp%*n2RKDs0u4oTaLo65TCZ+_lV-nR+A9-4%1i>BrhNu? zp)aQvS_YN6GVI!4``AfGIacx7X?DfA-E3^VI#j3<%)kGTRayUs9sAv!9e=%=oxAn} zJhgoQW5Ub?9_|#@Wy?g?@NFGQxS)=ac5(1%)-8NoUrB~Ntx?lx1b58f12|O}lIa=i zXsFCXT5gnr*4xGC{e*Lv((6IbzqZ1i(V6u0n)BSrY1y26hB@`wp+cU{cID1r-A3+u zZD7Uctz_f;53pNYcd&`FIjr)+N_Oq6EzIfV6R~`Kro&{3sy)5&xfPe*)h5XQPn*w?03{>lC({S@KPLhI)D#v89I7_~G4QNk zs8;Vx;$kdw@Mg9dCRAHP#J4>-&UXk+7HHuf@`8(cU&wS+TxXRZHNw{eF)&(x9=$EM zmXvNk0sLkOGI_Bcnkv7;&=)gs)1?y_`uUN-FbKwgS!Z$m1rL;EE~D4?k?12hO~1sN z)u!|r({Ilw(~FkxVPx`E60p0C+jY2_Sk7#R>eomrB+irkaaZAKfFmS)6NRk%1N7>r zXntg!8FVcITzJY3we0`m6@EIeKSS`7+CRs|r)Lq7@_dL~na_%h;#jsQ62gZ>p?U&K zFZNtS^SA%d#(p2}u5v~zvkkOA&w++%YS2v8DBR*#NcRbukcHcqGNFsjxx_;X%vE%y zIl6zjANj{`|m<~_CKm*HKMb(wiemb93-S!QR^u2_X|5}LQ+W>Bi zt{zQ$SA-=>$1%=H4ZW&!=|^=@9Q|0EOE(i|UXcB?LEu1DJYP(cUS38i>GwFPc@@p< zccR%HdZA3LB&A9;gDi#tT6#eqj$`rht!%kBx#dts2r?V8zsTej{?C`^XBP<@7uxc@sTX@&UzOt6R7~1oM9$n-d|b}Kau9DEESlIB33yaW9Xd3Hs-}`V{+4ODJWbU z&(W6MD5u~-nl9eA+AY*dQinc+r`|g_H~Iox{Cotgex{R4y{T|Uu^$rG9)<+DK6oq< z4dR(L1al;4it8pa>q8dE+)V+;_XIrc3&A_b8RmY;B)=BkBdabh0@cEUk8W5;>Io%^A!0IaARjN0C!ZljY{6{=r#Y2f1;J zs~D#P8&GV{GSc#N7gTgUghZu_!0*`y5A2S@fN3Dq)XW00JNvC7lNLdE&|45+|C<=! zE~euj`=YY9Bq;p1n=~1(1viodI}SF1kF=dt@%qQ)Pgxrn7O8QEJ_~2WoK6t^od|Xk zKEUj_1&XQ~+=G4@=CMx*n(muRpC&|dse%W_{_0i~`y$Cawj!kM`$1r0uc6G%3~Umg zz-2hS0K-2|;Ney=Xcf(eL*I_FqE>y3ezM?wT4Rf+W^Q2as4l>YdomcOp~fiNOQEix z3*i*aIg2gw^w9$uIFWc1svkDM`RAX({&h3xnyG?nOg6CfMG*fq0%9k{K}=H+q*kWD zqI+(nJ98$Ee|mxz%$Z9@3UjKu5?yZkuP4mk$~1KKJ4-irmGRTojl~xu*AVI53n4C! zvJ+?+jeuv=`ke)k5q8ur;Rdg?`4_J;W)zm0^rGs=zgW{HMYLVcQq#f+lBV{T>$=ng z+A}}EsjC~|e9dn-r@RsFPFxO?-bV;~Wy3JV)0&)?-USieH{r>Ov2gR)W{A2FT6?d& zgvbw?({W}CY5AJ*v>?)$=w!+>XX5wWW`1ekFywr#`{%94*+r(S*dPX>o(zm#D=*eR90BhKL1Z zVN?1-UZVXbuWWsYSKIg$zdEg;A9BKI(ZO3N<*0^Z&R#(`z4wqoc>=$MW4}eUO2VcE3PIlWQ6#o|>*R6xx9Ok#JdhbwP)A!kMH$YM>w;TzC~d;C1r4XHwv80I}mffwq)7N!?^Xa;xl#_M1Rh+pQxw z@_J!G@l_K0T#0GCZOeW4;K+yz4VY!IgqPpykJQ8i%eAHP_N@T=ncAV-oN#*bMwL}j zUM89D|Cg1$+KVlkSNomtD9 zKBn_l2%dqJ{^+T!hrew0#2Osk)K@0J!uyei0csmIydrsR&KZYQ8 zRoK<`kyTz=22X#zWT#BI0fi5HSdFKzgnhRhx*#>D)>o*#>+N3AN^ULg%uK+O3ze~c zSvX!`Lh$Ih06fIHp{N5(8ulFnmziy#ICD9dR$KvM>Epqf7hKinyl74SVKj5PL+dh^ zFt-b$Fkk-y78NhY#0#r<+0rmxwA+p!Ya)*qcg5n>K~W5Pdz^;sX@a}#URd6(1{d{( z9g3_o@Wt>qtMlJsc6pBm89Pl~xYy^CGOs0&D)pQGY|z7+F=kjH+}X3t%J7X>3A$+r zIjeGaYJW=8YF|MedEEVtYL1yiKFqBLo#N58y`Oq$=$k{F`iCztb3zx$Yw6+kiSqE*@qji&nz2*m;ch zKoHpF>O$ov7jUy5WM#Ad5x!?GnX5Jx4x}ySf}>fS+N*+FXD4DB%)uy&+cZm-()jzU zVN9ej!)e)1E`Mqv10s4ba^gi2x$?KUq0~*Xd8Zh&$S|1vs_vmn#jDX7HsG3J5z2kD zM8!|Z^x?cNjEqgjy2lY%Ruzs+T{3+%UZ`v23Q+3CCn$KSOZSDQ;S}G0q-R?%;EN7g z>fa0*noY1IXe`<8@D%o#ETb9irp%@XiMZ@#Hs;OBMZc~eC}1&Q-m zRyGOK{5r9(;T-ygox-*yN&Kjflc>~(Xn`{s28mw{VS2GL=$VF-12ayOl6^67Wd1Dj zp{$Lo38=R8uOFcAjGBl=o20;CJBOL~hcVjb8_K$TqPxP*5N-dzO#PXi+`w81(i5X6 zE+9vlf*g zoxTlyo(u?cP6;geK7c1=bn(m~19~c9lhu{Sy&ySQ#La6F`ts&CxF1tvYcfSYfb^C) z7$dd@r~JMRV!!-BYiSo35qpD{9g8Bv-G@ox(u=efw&Cn@Q=HWwPd(zh@t*Mf^(k)r zsBuZWTEZ=)xAUpRN`I2*b&JmF3jrM$Eg_?43VJhgVNTa~aGISXco^5vj@(|l*~|+~ z3-qwhG8%U{3a;Hg8S1w`g=|cWCY7RMwX4?eWs){tpptd6tldd|Yd(C6F`zMNLO*+AF}?ez5u0z1z>b!9Uc_ZOhHdP|O!*~r zyd^{H%#&ct_%7HY-auCdW--@I{D_jyE{KXV71(em!L>FJO014TNX1SXl>7%D2+YIy zhYh&FWILUvtVez9Dy&ZLTTD()`pccQoI(Hf4$#~8tYC6WFex9>qL=-HAbD#A>?vDM z-Zu)fa@ATm-F5;xEXv^6TYoeqCd_AN3$n5^5X@5c(#P#Tc#&fxP{}S;$eDMex5`AU z$v8z%%wuth?-l4Pa|F4nUJ(18Mw&WPq4u{M6g(3A*Z(~yR>{@e4_Pa;bj>C!vW#I> zhJ)aRvEV9_ZqYx~fo}byK%cDNOaHTa&Rj6@B(ZiciTK6>h!rv$p^^7V)9s0b2Ht~< z6AnX5NhL(eR|+}aO(3`E3XIHr2GUA5;mxQ)H0Qb@ZU0en^Gt)lEfmF;-WJ>;utiU= zu7?@B)yVzpI|MfDClWP(4xIer3h}=#K;6w&@~2U7@786|5EpF{;md-}goR*ts+mN4 zY0}Tri%_HgE4S;bB)zxUk0gH_M_NsSxO>x|z;w}0s8+EMY6@Yi>i1%}S-u@QU=YOj z<GLw^xB=H~Rz zWPJBIyzP6CdTjVX8h*sU(f1Bq`W`WK&KDfp`m&H@*bAac?@7gHGl6M%ifD~7pg)V; zXzZCf)Eu)PM1yk4$4rjv`7%uG#>PQ#LKVpg*M<}?6S$LU0PS%m;4gNBT*}%22MPkn z(P~lLCb-EQF8sk&t-e%mpC7kvO985@Zp5&ae{su}6Xe1fIh4^&qTa7J;$hj9xNC|H zzV#V}GhcL(zv;Flbc;Ik=HV1z<3*t2dlQIVY-j3yUJ}2h{bZYLxNv8cqt~Z-(8o`A z5XIm-p#1zA(cjulM~xK$&GdUPm9n7zdktJX9s=j?Ym@6xck&J zs2&xDzF$%V=XNUda^WI8o*|A`pSED*wdM4Stuv&**@A_V7x6-~Io4@d;!x-ew5i`j zYI-J+L2Gev+Ef5Lcf||YlTz?{Aj8S6`9d4-T;+bby`U0~o=jIoApOs;nfV(VOa%8U zT{e9J{U|#`<_F9rYrMtCnEs=%(Jc~8&n+Y;j(EfT^nSSQ=nTEbw8_K;@}Sio3JDvG z=-Z5|^!Tv6N zNIu>Mx1^N8d&ygJ@@*7N5RoR!4%X9&RmC8;s)AlH@F1kPn%13{1noV0$abYjSd$(G zCc}ylx?_>R*Y$=^2ZG_s#*t7MvjZ*+xI@KYB#3uf$=Jj2WZ>B|{ z-~qY#)tMQR51r-e&^zA>`W*{d`PDbT zsxFbNZIPi_yPPn4rUHKK--nr7Yw>O6CLGglj3%B_q5QQjir01E>qlXzTXldnmHxvj z%lm;&`93=5xCvhWVTU4$LrmcncQP>X1o;&=8S2ArL1Owj)GRfluF1cM>)6Y5+d>^u z9`c%dS5`v9x98TF)|yZn+=xr>?7|T#>xli)YhW?O1I|>cLcH@g$lO^&G~(vs*1g-2 zuMfm47s@cMRTD*$mf*r4GqIZAf!pG2P-4F!gpUlzNtFj+|ASfV)WF@W+quy&dvgWd zm;8@*v?b8TChMtRTc)t1x`p-}`bd7p=`lyFPf@eEkF73`QljguKT`7G88w$lU|LE7 zXlDL?E>G5776bZ9pESGgUkVSTHrI0 zlx+@Z~;U+SE-N{SFmmOdRVQFcDA>Z;(l?^pZFAKE)g;?8)|Tq zVF7u*?<$zpnxTnkK0UGV7=HkZF1?bWBCiUMYg7XH(Y*wbg%W^(`P-f^!FQoKiB^TrFZsGEx_m1xbN z$4B{~Y-A{}_(zAX$jl~wpOxwO;2Mm>rD4f@S@NlLSu6?S3 zVy_KB!NeAHtdh{!FbzBpufmv}u6UyPW35q7D26Tve%!a?9CxgWfWQ-KS#uq_l}|y^ zskx-anb3b!nhr&@)0Im%Q1j>kZt!{uwMh{eRV!aHFAvToK@D{@`Be~kku-#Bx4*-w z86#-utq$Dyk>V`H&2+%Hgr4`EfMaBq(UU57Yx`O}aAbl||H}L0suL18dh309eV!%V zq41h!U*1Fidw2vN4~io#`ho73Rq@BFRd{2aHFsT6hitC<0GB-EShtXEV0$YQjZQAX zs)u01zxws*n?5uR-b?!~$Kz_b2lzjV&ch$8_l@IL2$^N1G-RYgXz-l- zIwcwHjEbTy4JCzdTZG8W2vJrvj3|`n+}CMXl~9_ZrG!dp7wLC?|H1RTo^h`GzCNG# zJ2YCzZq)4~`?@!fo)P)<_%C}*eG^9BF{iOg;{yhUd*A_yV$AzE0%MoR5!G;aI2m$` z2~OnMAssW}Tj2oAoazA{FD5fweI-f#rpg(Rm7u(;5Or@3M}^BnaidBwDlUD2BNY}< znYqIGkJG?OUHN#R&yU8d??*WMmfkOUO5b>X;nuw^rNP&Gsh@NZE_ED_PW82D88Vx) zC#;BmuAAW2o=(g@%G2!io5}p3KBE4qkoGt>;P&-4IL+P>d@@={!`6%Bm9UF>n%==E zIqSlawpfT8c@zRt!eB?0JIJoABy+BK(QM`O7+QG@6D%_^V`&>^228}rmkyZoMd(l3 zO{3*6&f?Pf{!E-sfN1ow2TbQ^ClWmQuHXQ;MHkbZsPTR%`b9|5kk~`STXPI)zY+|m z8fU|X04Y+d`j4BOo=aulY$H`3p=fmW9jb0+P@_jo$tYKDO5tu&@iiOHdQOM5GGXrS zI}C5q>WG)gahR6!(Z+P_b^1lu1BZJq!)t0=5k2-`a=DOPE~7Q~ z2btW@2e^Bcr)ic?KW%HNr!Q1WAaLePwo>iIhB^2mgQ*H3BrF_#eB)><75s;(74soXy>_u{+zHA zL-dE^l2Q2>9(D$+clzw|B=}P>a=R_VbQAh?(|pCFf2E{g?GZ1@GAa1 z$956N z53`NKA}L{}lhnY#XeXM|bAk)anJq3}E?fZE1k(MvU+L=1bb4u)H_hr>D7edJqki!> z%rI2NiA(Cx-|G|UzA}SkhRmf4_;51tD<5Zy3rYBnU3f@t55~WdrPH34(c)FtIWwUv zn7Ma6o}MaAgP+}n2whFs&cESq9d4m#W!tE#@oHK;GKCiAyW?)_R?Hb1B4mJya67D| zKU@wHsYyw6`204`S@2_C*>afc`aO@P|DHk5yX_z+_GOX>k2A=N?7KoPwgeRe0>r%! zwt$4!Z1PO4jo27j(~QZJsclUt9h3cwK9tbI=JC-qyu=EooBbjgZ?#F(Y~j}|s<^p@IDYo0q8~%S@{$c%nimV!{{K<`z2>w*a}QM+RSODPiR4Yk z2)e~m9!IVHk4)~#CJ(%CVjT0CANI(Y+*a-flhMP`HBcFn?I)5GSKM(D>3}fbg&?uH zpR50Pp840=gv&?0X4Kv+6&&2rp!v&zSguK-AI%R7490h;CJv)_9e;xK>M5kFFh&&e zsgUfr87g{iGlnLd`%NRuddOr62gY|EWejdqam`+%V9%+YWQ+F-F82W^D$hckSbI|F zvt;1fA8QcO`!Of84s(l4dAV!bM6xZJ5IidkE(-U5FMnJiHOc?b-ld7$-{XeYrC-q6 z42moxJRr_r1RrAn$GU3p&SRS z?rDS_xp(0V8JpJ$%j)`=hOc5`X(_N&oP~4ySv4V%kxCRz-(l~Ie|Yn$8aBVVh4sTU zcq4UT2HW$9qL~jmC%ED1f_mYD-+~!I-#ZGwvBNla#)5#J#LD_vz4Oh)$ml zLrs6=VhFh;6PP1KUwGxiNAm5%;NKy=pupbwnN~>{M!PG zLcZnL4}IGDbO$}UKv!(sca=Lc&4hmBeQ4u_OzwQyC89Lo3M%aqWX7@0Lag~A2B|lY zKNe4Dw2=FkF}nr1f2&zgi-BLWz1fK?Zm}LSRawuc60Ak3A}pF`iRVMgP$y;zE?qAV z%kU-miotrz2 zn=qGp8Up!r!It|m)j@n(*@@;m&mkHSd7{snJ!Hp+XY{c5Ih(|jUQDv83j~NXxD&ef z$@l&5!9>b{bv@P2F2^=@L9PmjC%$H7%O0|mA9C=h!H6{J*MeNkBf4K>8Z=tWgI80A zLWuKocs_MFWIaCtVg3K;vkDdX-nN<5HI{{6eFJdzcp^NvnFg2ICPB>A1X1MYB}~)K z(L`p>c4nVt5~ttgPQu63aas!!s6;7CPmR%{9pY)+`@t`aou3(rFHj{lHjk-Q;RX05 zxrsGC^A*BVBB5dFd+_f$1N?$CA#0-mwH{Ac{Z%jE_48x!FzOJiyk`>&3qHV0XG{2T zQJVG1@MMSUx`8-ilHkzJU=Ejkyk#DoN_q#Ao{kajBIUR@pOr{^ z=1;mj(S=d;^rG_@ub`$v2Wi!dqud#RXTGvIgOhV+VEDo{;OX`Q7XHqM<6dh79+NbQ z-cd(BdkDR@+)l`iI|KQ1X25ARBRE|A2a=zML85y)yWY!_9r4kMH9T;Q9p@6u3ga0& zW!o}fo{eFhTH4^PF@e)o^WfTtrR?Z?mw|Ymg77=1$#L!FG%C}OE?uQXroNj?XB&>C z_YD8h4Tpv7fwv{Gtl?<<@RhXV!FU`NPy?xd`vIC}k*|GM$+PM=BsirSZ#h~BJDW7- zqQhQtYTHdBH#iifGwaD`E8#t>--M%MzOthR{{QU%0T1i*;8?yJ)H{?winkg>QU$2@ zw1zugLUzhg2A*E1fz~rYa3u9T=r`;F1*tmD&{>CWG+ah<(+ogT)r8(Y8csW;?C48; zPfG>Agp}1vB1#y+Mb8}q5k!-8mKKxWGC8CsY9jeP+ytkcJHw3I-iwi%4Vdur3hKf- zUdpM6<_#*sPQP~Hj@TJa#sMtkpp6?y+L zZ&o*eT7evsa;k;6CD{m^8%sNaUP*-U+#5Yo=x6Yw~x| z2JZcXb#%j&Ly+-DpF}=e0tyZj!SeDmt|LW8EIA0YCO zT@RAhXhaTXMADDYLCLxe)MNf8x@31Zjd?hYzF5>t#-!@d6@C`X;FPzV_u1cca)biN zHC%+;t%cAk;{~fu3+FnMn>gM-3x-`#N3+^x82{P;3y$n1c0(GOalsXkV9lYwWjQhP z3t*JX14)JQLRgw8OWNockl*Ree5onJOE+#p*v++YaFQi09x5XI`H^UPEP{(q38hC4 zdZ3oTZ>$`@n2xboz!iIZ7q?U{qZi(Hka?n~;%_^R7_)RjsvgWGvt-lB;lCwtqHz_R zQ7(X-n1e!=u$D;Oa6THa9oH z{X2p1(A%DTW9mt7yc+~;+XdJ6--X2aW7W3^aV|6W#Mq@d*6a~nVz-TR4q*muJ0jo z_smGNXCe*1$kWdYKhWlgPhzuE<7s`y6c}F+10He9p&)u2L^-v?r43`?zF8||hWr7) z(Q{aJcmuV8KUp3988ETk9W=UsgVo!7?#iJ%7%P0vhj**twNERt`>P~+)PEs=8;j{4 zFBX@*c!$yB2lr@w1hMhX*l`l7I20e zSe0#VaOaGIxaO=2x!o-=N#1=$HCIKvw89Uce-FhDT0$KbJ%q4Po6z~@2<*+?${S7j zz?&aFjSmGL#o(4`ygS4N^|Zr@j;b-G!&``ds4;oC^#Ye3{*qI8v03!7sFjAaJVQm> zR_5WrN}S$xh1*%b9Tb!^K|NX#PHW92{xb69O43cL^XxB;KenIiXyc$~;0rL(wJ?X* zM}u#YRE-rnvJPdu*5ViV`sa3j_$f6s7W}Ce?~Ul7mMlie4)8Wkt-Sy7zt~;ThB^Fi zUN`*$;xj86wlo9g7+xkfG!D|E*Y}Yfe=0~$PrDeEs>mgQ6SF-@juuVZOoC^m1U)(Q zCa7ge}-|-N{#8J{3|r{H3)Y%(bS}58+UKZJm$mqM>s^Xf^ORRfZSak zNX_OBqwYcuRZ9z1Bu*xr&w}**#(StzmK)8ob|bfU-4M$dtUEc zJ(l{5Fs4O|mpE^R-JMp*H=FW`Q%3Vcil5-A7hiZ8=}@eq#pwMqk-ij|3wCYFP`Z0Q zY{>gU!_Mu8k({act8Ng3uBd}{B(qKhD%yQd}ukS@+$Dbt3 z{rm(ULmpl?L|*<#23}z1VO&frX20Kq2bx#o`oBsdkCp8ZyXp!YZ=DVqjr+-f=VdZ{ zyArdsO_$8Nv5Ur9?4}h$-sNWg4dDx-|QK7GfO z<_x?l_)7JZy-{~f2_}AhfH7mnqRy8Ipj)B`S)~VI@Ae5Wp*W5_hWk_|?AO((m!?d- z-f@!OB2BW=0!fUuAu;xGCu^B4^v83Bs>8e*nKiJ8#>Y6)gzf*KU(5hMY>6{g_8K7# z+=91GhVaIbSy)=x&3m_=;wRqO&dYq?B=p71(KcR+{`;UqkJh#0(YvqFn`mHSZZ{lp<~v`8o4vPP2AR7$*Tc0cY6h}LSo&9Bq8!SZV^7TCA-N=O-y(X+)&tZ0?bTq8i$i-cM7SNI3Ww^TKB9N%6<_?U0N4ADn zK-{I5usdictoQ!`Go#OtUN2X2SIHiv=NOWg0w><6?hq5=_MJ=`I+?zA%Mia>5`kk! z`(nCoHCA0oz;3%#JnvDB4+<%->XC}=+y0`-#+l-t!KtKqQX4IobLLtiUx3Hl5lrmq zX(Vs<3^0jMrlse;QFY~)v^wwyJLLHS_&u%+B9FBawS^mDa%U~BS5XlqjNby1-oIe^ z@&bqq*Mbq+$3XuF(BZY4=x9QQte+4&=hs+h;nt<_qjB#+WL6{VO~%HX~T2H#9t z!H?}r;Kx<_U~hvgFFCAC@CkF|2wMXhhcfZ*eG${*9>g5ke~aX}ig}6ZY_6<#9;d(9 z9oL?}ijQCKqK&evVPV}ulDgdp)~9GQ`EM^UN4|U@GbId&d&Ws}M(DVgOsRu~H!gF< z?i`mh*Nc=tSw;5!3Z%J_%f$g!JdIc+Ng5On6Sg)&m>ne1SXUZj-WUucq2BAunL#yUT$uWpoRhqb%OaCNMqtW}3)_xeb2iw- zem8&&;k`rzX>-HO4QNK&A~Y!0XZ&W@fXc1WuxiLHFcw!s{+SbG@y6}6o}EXxy&F%K zi9wT$u6Lg9DMp`NUh)48V@yvlO*w+6V-(4pd zE_go`r8lFb^iw$bgFuB_6Nw+;kA@=}NSK}*eRE+m=w0iBlL=2j;lMmleW=5#>VAfw zp>6Q!kuQ}`J%Gkd)$~I`hB((y8iv-HK@{T(**jH;vhxaVQ}SM7To6wz{@GG_Idd*+ z`)HbYCzSDBvx|v7G@fXmF5+IyzJq3`Dsiqw0#18hj&bOV{M{t1xHy}B-Yet2p}eyq{D8yTjzEz?8q5$piXNX1*kswUG*RaTo$!I7AzU}jE*?qL zcg*KfzXZ@1KDtc%XiL<(rb;g!`GqFFE_gC=H%1NqpM6?|x*O%mgKfuQiHscmy5bbs zFnTx`51gZk&)kUnL>KDmc?lMdUJB1myx}1PK+jzfct- zSvhd?R(hh(+DuyJ@E((X7UAfMy=Z^&F^YPgpv=2WaylX&zNAFpkiZgjTj!3)ZXLlq zGgEAz>W$YTH`65V+h{7en%SKq_^h;Mz={3lIOZ(JJ>Hi={lg1zy4NX8^}T`CdB!MR zhlFca74GRBh*#-csv2fbKegD1Gv0^W@Y@g3J*H_y-Xf8U+q-}^t52aJsjKO0t5;~V zRs)w6f5q({QZ)R;MU*}=i!O{*Cl4ntfYI(Hu>F1!n5=gsH+Du?t}1YX1jg6Z{ez+u+h}f8^DPoMR+}q$ z63x|mOediqr05T8Q=#*}kPeTzAg~sKQFq@FfoqkEI!`R=v&jolP1Hp%R27nJCtK#~ zV@c3?@mid_e>f%{_(E>ky&{48?eX6FF}y5#VNdyZlznVM?d*r5>5e$T*KiXccT*QvOe?^{L+xnOxkGp~PX|?w|G<-b&f>UbGIZM`5fNuEwmCg> z4;)&P28v-7)N_U(CQsr7nqUpSoD+if!ELy-D3Ce)b~99XMKU)F7tuJ?a_WBZC5@=_ zK@~Y&j5WKBS9IzyyjU4`F0)6?rDe1y;TEb&xzmtypK+AWG=8-?mKG{xLydAc`f7R@J3nAGzJGjptM2BH#~OJYeF7d8uigm**Z5{eS%qETvC z4yOI<#W;&Uyq?t&v4m^{YaT<`o%wmN#eFwZ-Y<{yModDvU96C1PrH0)79Jj#R(2unT-d6$Z_RlEv#Rwbg?@ z-68lr+r#MtVK=OPWG*Iu96+@~;XYruBONw>DO{MVNUA;6$kT;7y!?)ryvnpE*e`Tm z>eipc*tEUZc!}<-7uH$D;dRIOU)zhX!$CXwSIu( ze?KL$<*Ibon>V5j=No9uJdSHs{7kIP1Rq~dJ1K80q?3&{itVqbq1N}`qS?Kl>F?Bc zICY5vP8EEH73q%X(^5yXcRb=X^m1_W$X;}In}gm$cK0%+IJfOMuN86(lWs@zx?Z}N zZE_uZ*?sV5%2C?fT1J0d5&CnRO?l~UY54x$AYRIxi_7(d9=?4am5o=W{G`)hwEq#e zR$~jT-mr##%|6Zz-F=r>?$9P*1C2rDFkx10kms&k)Sx3q%@FfB&B8CS0{xGdF)@XA zIpr~LQ8MTy4g>}B20_pG#m-6mGHp{{X|@i(E!v28&acN=PyZvM&HrL#y%cDESx0KW zkAtQ}in)<2-LT+2eP;a#1z!O!%eqNl`^M4JGfdIpj{@^#s~i1ldXCr)y$n0{l#)|+ zU&yb81*E4toJ@5+BARBYO!(Fs##U3EeB8Q(YKPR*+x!gNo^TEoXB0Cj!Lta{mO)$g zrt*3_fAX#o$dCV=%>?j<-|?H>G);p> z{QL)|F}5`5xQVdCc);}@9VB0i)-tL5XKv>FCFJbvHZti(3snEB7w(2+z}H8UXtXWo zrkIXoMx5>ix#R;R(mRjre4WTOe;Yuv`z|#1*-UCYZ#max@_@b%>7f(;%fze7Exep( z0RH=U6kisM<&|n;c*SQC_^v_@tK?RZrBE3xvH4^(a} z1P9^1BlcniIese_!c!kZTFN(YFVloWZzsT!rZXV^`cmi^o+NKixWFWpC5%(hZqe23 z2qsoGo=6N@f=-qznf+%Qij;<;#j=s;e%*lhUpZDx7=dX4^Z20)>zc|1nAQcdiu2~uwR&uk{0&Ebo*Om-Sbs+%fA-pWaD&3y=jN2 zX3Q2ce(QBo<^7UgX2D`h`v`tOcxm@PwW#V&Ue=EfBi=Cxpo^1F2S1!2BuDc#enr zB90xBwFGis4S}YWUNGs_5*Rgo1gQES;i@)&rT5H+aVvB6xbnN5G-&!fvgTL>Et8&y zcA0;0j)@UI$~}bnTQ}nFN7)#ba2f6A*5XX3SXiYt5dQ&!mZeG})7oXd1C31l?J3g?8Wi(y;ZWYAnMf}`cm zps}P0tR7{<+M{cjGbj8=_=a8*J@gd`h~3Kku92n>mPAsE&%4peRvjbCrs7)JH>mfi z7F!R;;kV_FFjXT2!?te2c^8_{^zIDE6FSRu>N$)qY{Qa@6kPKpm3WEzP;Fl&JzI2* zRh_+sMg;ld-Dk^5X;w1G-ms+N%F9G{^d)ZFg>mFUX(=q0p9E`W+=PU6M&xhHJg}bP zNs4E+lRl>}AeFs|xvG15?XN$iarwN>r^H`;q zfQ!Cd!M*No0@gwG1B4pOTr&kO!(N*On=QHAj$nK&Pk-P2!7Bhp$tx?k8`P2|JldprVx5-5eC=%R5~6 zlW`cYQcvGcjez50DoJfk2hf);0!PW3v(}!>eEeYrve%u_d}1wTp5Fs^Tve&qOcE1P z^l)bTR#f#^0rde+#Ovq<@u#5WATy>4jDGhLpS$%Sdt?xjcgRDG{!g-t%Oh$XX(Z9r zl)F8_nV$YUft>7aBkLwS#*m{WxMJ%=tTs-gPs)QR??TboQUl%F|IpR_@4>+OF)w=;_LR{>VO{5nPd zuC$;hiMH0b(e53`=n@QK9G-yA(&Y8eUPQSvAk4oT1 zJXp+Xx}AYh1HI5It3GzNOF?favy^-XP%H5tAD`cGix;$a7(3jLx`UCC=s6p>4Qh{fV>)U^D_)uUm7bb1@m1ARBhh!J~GRzhFcbL7y>?WXkk z&JdVBr3X#Z`@u!Zi8dct3*>S=X_3Ffg#Gp<;q%wy-kvqMFnEAo?4OJE9%{I=ArLzJ ze$!;ZvAE<>7dH0)VGKJq(f8T&Fjw_C9yq#{%YMw#Jl}Y#BkH3G34!Pk&vPzoJ~7X_ zWa(vttK7KNHsW{jA>>urHBEDfnSzje|SpIpO3xX>^EWGI@1Jg34vj zr-23o;&IwJO!c;XB=7A@aAheV-K@dniCi$St55KpzQrT0I7r$=^8 zAU7_bz;Sj<=*6T|lC+@;T&|XbeXJp?y;+LY`s&Eew~&CyA=(1>?G~hac7wCVa@y?@(9J`V zNs4bLnK1ndF<|pa*NrbUU~(^wQfoj{>)-hQ9FGykbGM3|==L!yX{ogil}kH{kB%Lo z#s2+5r_lsclKQbNQyZSje1k(T>glEfc3^)%kC~#Z2p}~;s&gB8r}#j8 z7VU=d6a8TA*A^7lM_0Jr zJ&K%K(nhaz6`}IC+nC<98(l;DxKD4_a%I9>YT_phs}5+o7|45$A2X5@qtA(9z2l(B2VlG?sSexK8&?Hz&tq;vwQ*IH@d90{c`CsJ-(Ls-Ns*^@Yw={SQ_@dOk6|tpJ*brogL$4PgD@2$-j@10%yNU@n#>&%M{va~*$Zrli1vw~oiq z*4x~YMS@2l`U*Wa@;u$~+>i`+Y$dYO$B{(`my?B;?r@b=tH|`HOX#sNHMIIq4^6`) zw7c9#eXn|g-Tt4@s^kLoA$~w{3M+NPo7Fg6%i1K}ziSs5^>+7FN0OfmiU8JB&lGl4tjYDqSMlMeL@y>eMHf#<} zFL&Y=&Cfu^&KBBq?;Rbze1P1KyF$&EuVL;9&xDc|3(onzJE?NDg4!Y>XEJ^ow6=2a zdb>1ariVbt=m1vr_fU4mfCcXHc??-k5}9_BDXhfRX~bc^6Zi}BwNsb_8}`B$c&A3_ zSv3tJw8lYEq$9Izv(U?rzt6~Qlq1&$1lMdxzbNgj3G@E%15}8U!1*JM=$Z;on$r6X zo&Lzv(1G)`dc`SjdjX}+_H$u|*B00^_C7UR`JD88-U!c@%EGg57gnP=559f40RLoO zfsb1_ygKj--kKHAX+;TW_EYG5S(Kv=BlNHj$P%sMI(DeEIqUf~9@dq(!_^W~s8@dm znJrFm{$C!fsdItQ1B>a*62Y%1q)!e!QKxA?_EVqiKj`>-E%U6WnLfI@2%XHwp`?cr zJ@@%0^EyriN)lmTya_QBm9*I4zeA*{Ap0Yo>N2yCp4 zFuFgMSoaJ-oWmtN;x?Wi8ZZ?qU7BIxXB6%_hq3Bj~!K%LgViHDOC=VqySwv+!q9y!XNV<`OX)yEC6zlpjlTZVPOs~qCj*`4{<>9i>I@y7E4)Oi5INNi>vVM#|c6tJt09yfNgH2iuCHtrqFrS4Bm#hUA+E!2kA28^ds z8DHq~a91);vWn;qoF&WOyV5sbzLAjO<|rk1gZ4ZhWZn*+AbL7?1`WDVK#mSNlA^Z& zVPCyq;;Su;;ov8xP4Ne$=s7^-!D!NKy#q=oe}HP&D!B4N@Q*M9B=CC~uj13f+e+2* zE1(*4E0xJtxo0S~+k*>hjHOvO|4@mFtpr=7VAQUcw9I~hSSb$WEN194$$QR_5e;gh z)u+@%9oCjK{or4Ex@aU774Kq`;vC$yz>AXCwUAum%tF z;Bu!wNt24<%T-#4;^6%42a(hU`+A5&1x{DNv zw%I3EJW#Y-uovg*E$kjoMs9cUQCsR|Nk^@q0R4ySHQHxZK-5m~g{OLR)6oSZ(Z zKqOcH1m}G-V8(zBOtLj6KgI$iId(za+X7HM?+rCEGua6v1xx3}W1y}1nw0Ep0qysX z8Pf^BA>r&V1nESg<2(e)1|Q>@=W9Un?Q`^qJBr%Fg>x|Dg@zaVagxe?5XtmG&Ute< zXQK|&ck6L~^lp%s^1?YMDg-Bd))uqBO6iuh%gFxmPe_AgKIuEHA?mM~OA2QVP-eL{ zEtIaH`%aaEvc3$NH`osO_t(JO^LJUrTQ=;n1)E_*?H1PH@ogykqz}m0dZq8hqw#XCs^B*i#RS z*lNj0!ND*VHb%UKzrz#RF@D>?FY+PG5$?(C228-{ua%IyE~8t27zt+$U0m!smD3ei znQyPxau+|Q(&E|S#Pq$0d3eo@-YFc)?AraEMz*=ppgDFl;-EgdH6?-C8dY}G_8F|y z;W2RAH<=`J<^oq^8(MCQ14)7L8vbN0t3ox{MY2oTf+O?UX&Oh^5dDMfb`L`~`f8xa zydVU!vw~TXWfeQNcMsHero+(%6$~%1rB`Lw0y!GaMFdZzw?9sxOlB8%aTITp+pEWg z?y_Uvz5E~^$p=))uQH}TB^w!AAr~9$E)8@0H^QmKv0&EdOu(>-)fF;M2a;3C>7{ib z);&en>aBz+fg@Q<#}}k-NFHkxB+agKF=vzJ?q z44asr&e~xes~xZeGIk3c2i_R|D%z0Nk6XFl3%AfCs*g}s<%RfndMruK|0Bx1uf&{h z*$z_YCR3GHacFQ%hOB-!01+uN5Ef((saq-`UhV~HS}}}(*9Z*XXG?1CJHSKnE3#OB zB#rbfCk0RMa4iCBRLVdCx*z^yW#nzaLEVKmjW`64y7sV3{pF$I&LC_4Jcu1}Hk#GA zYzHq24Pm$ZLfFMBKzC(4tX;Yrv~LfRU~v=IeRnK5d%=+zb+e!D?q5;au)u<>3BE?B zk8PnLqZC2D=>;5^Vgkovhrw4-0jnjs5$-xnB|W2JaYp$xp+ zf5CFtkR<_ntNw%MF;{VtrUyDjucMb1Si$a$BXDQcebTw~B&*Y=3NGiYVGTPQQX2qH zU-X3Kr*=ceX=%vYIRFx89L1gKa%6~P8R4}~ka_Y9-SSn9F~56?)VWqN)mOd{QII`Z zdrS#zh8!W6YlcB5YXY+ljb?{*CBo1NA(-5~6R!q2(el-kvGrg9Rty;7dxc64XHTWE zrv9*J-%@@^MJ*0KI*Mx^E7Ra7*9n*DN*d?ggi&gHxDQW8z@l~YAoRKxoYn{YHVJvdi8B0T+d5u;PY*wL&QN|r zS2Q-n<=`*#i&S;Bz{G5HAX;@X+@8I$)YUwVauUv9o->LhPp_sAWNF%m-4div9CB0OCgg*SVA4jP3)LhSoW{+A#H|D(& z9evZtnQ8yz&P{v944XR3rfIS^s`<&F{`*}xT)LEA_K8A^SGq8D(OOV7)n%S7`USyD z#k9_Q65+0o;>YDYK3DJIl_pH&M?OA*{wLpHXa942+3ABH1z+iZ zD+NZG_z%78)CtFS=3tnk0gYT8Lfc9V#UTc-=xc)`%-7OIVyVm=V*G6hYPmVn4ziXU zGT2Ke8@R$$zK-cq4yA8`rqlB(Db#iY5__w!+^;!bA;H}St{9fEl1~o8BlbCJn;(Mn z0XO-X(YN>^*20c$StEXrYQ^K@D}>kglvfO==x^@AZ(a0_w_5%gZ@AAO-S7M9%6+z& z>)Q%*l+9@7_&{7Bx{pV<_T#$KUrUM%`=; zG1sjku`3xIb8iQ2dS%S~kbeggqCSGNkiFh`zaDg^T!l1;kI=})>;(#wDjciTcUldY)lG;?BRvYOaC>}Pt-J%}`?O|P`7 zVCsyq&_su!p!ac*kP2Y#u{BJLxJ9h!EIBXnl8Kr|uZ9n4uLOp)=EMZ6P;g9sqICLmT7dXkUm^-IJ zNO^`ab0=>AmA)HuwRxRf-m&N6bGr|ao#pw~-^VRr{)E3lc7GpkUf6|ky)Gz{y9DEh zJV5v9>ZqyK!S&tlVRcFZA@cfscD?@u7!th>veG5s_l*D;`#cD==d9#17hlJ9O=hA= zho{kq7CGKbagdj>h(Jf7ryQOx!;hQy1Mhny9x8CfqC+eOPrXBP#!Jxu<_YJGHb-i8 zERMLC?V>{yoasN?7E!zQTl)CUQ##hdlzzM$OP^hPgbuOS@T9Q`2HtsG)nea`zpK7r z!%Aypb;4k#QMKS|-^waUE*AbHQ@K}r4M>lo12AtTY3cqr^a$)B0zl>+;L^hW(9c^0$${@Qsr{Z_7{m>B2AV0X$P%gIlOE zk)M@=4moBh`E(X4#h0PN#|PY-(;ZiC=EsB5WsW(_{GND9e2wR-A*3H&FeSOZxUU2*FT6?-1HCo=DG2P;v*Qhd=l6ER)@HKuj6HZ9|k$s zHnb~ziZ`N!j(yT7^!bsD7h-HMUScKEybRvc^P!eq+i5~^Ej7zh zL-=CC;*7Az-~3%*sX20II|FD8*-TxA2|n`O3o3nP-lkzg=h7SR zcwBg<0+a4P$Kdb5s2cT@AC`QL*F31g>wYoB-sc>yEu*;R+$?@s#t_Wa9?Pp+#q%<& zYw_FID1J<8Ef&e$1Lm%RVb+6D!p;l~# zCG>RXR9aX&j#27R#xY(oX!Es>Vj-%i{$ zwvgVkyomEMW$@C`tGv!z1KwQt{XGorgJFNCRpK*LV(*BL;Z+eBd z*x`rrYewULhldiyo0s@erjN1R?B#Xu0LRkQzbHR z*P&(f!H+Qf{&*DLHQ0fQQ!43SKSkcPGn98+7{!IHTf|}1Ta4@!qxa}d{Ji#H>hz=w z%|gOaW6lA=7f{Pj_z}qjm9G`}-g}^_$X?(m4ui2Jb$I%xGJRL&!>tO5r%RIjP&@oF zUKVoCktQ32u6i@M<^7Z~Y44`8`(Dx#%`go8^B+~s-H73m{-{#Eg%QvEAlfRpYlT8P zYIwco7D@{};=FPAz4JC^|GB{Lb<)JmGiB)gYzeYz>Md${yO1C8s)Wjp4Cah<^%x&X z6)Gp}z)g}i(Y(Hc=-dzjby4;(%RZ)R)_+N4$)y-BBi0tjjr+|d=(nJ1`w?u;iN<%0cGtY&=rYU`7?D}4E+jch0 zmej*bQV}Stw-rXb&xhSKUR<Rp#Oe$lVs*YXVpgC(4NmNby2NhWI3f)f zR1A;}hC9e9RT*+@^icY8)(c`H}vZ9+;ZsYFN-sr=jU&ilb9Uw+H{X3ja!eP5R$L`YY|(87Iy zS>I@lmnC&H5d*D&sW>h&n;)@WgqONkNkjEFV)VmebS^bxvaEbbxZ__CA9b4MY6ySd zchir%9Vz*=ha2JflUi}v+=R`(^j~cX9ZIWXuJ%yc;i}K{3v6ERmOL<2oQ{eAWunZd zF8YF5Ok*c}XLT>dvTOh5!UOZy?6g8>_@=%Kx_;ZRBKsPsPR3snC~?!>G@^V`#-1)AZ>J$(!ikI6+L5n>&n*Zljaspu|Qjj$e?t3u;T`7|N2o~CApQufnL z%T)Wd==C#|yEJ4PE zwWfJK?p*(CfqCD#A6$}^iG-{hjoqt`V!|x`Qu#JwqxFjW?fHy&y^A6{ziQHn?i;zW zb>Vcjkmb5M^*Ph&+zD!n=90}RSGnRKd(s@U1vh{7CdYOT;=Uj;a%JXG;^DRi?)L8m7nlUH=KET8BeIRayP#(#S~LT{PUoZm#h>rIxAkH zxr8cx+T4VOEix!}evoTA*GjV6w$Rjf0-rTwHK{-UjTA2!MYA#$=y8LU1jnu=RZkBP z&OU_MTAIx<#(v=c=RT~8{DL2him>$CF#ec79hb}(p@|zrP-J@!4qnJBd!Ml$44eJn zdier45Y`X4Ladk(6iZ@^n^H?VEC#k*lk@y4A!_*6OqBiKA2utDkab2kdZ~ zRub*d(Wg^`nuKTmKYB?@V4V%TrVF1AP&Y4wvKF%x`onq^dgb(DNXm8$-PTHX{uqJo zwKZ6}y%S3_XW-m;A@6bY6V%R}#Z8IMqL=nQM8Ri9|GIpl;rSEFdUyB<9t+{z>$4}E zwJL&3)Axgl@n+IKHV-z0`+?N{0Z`YxV_shRfjHg0PbZYzAvI}wu`p4g(KOf|9e>)%3^3{nrN_ht! zYmcWRH+Z2_KsGKaZNenyiCB8AW?wa7Rb{Z?6I2+uq zErBbyzCe7xCTtn41;&b-z+U_;xg5@uW9tvlB{IrPr@#Xnd4DT?zQv6;r$%G8|1gH` zF~ePQ_Bb|@!TYOK=y2{5df9joE3gk;=XT=x?pUHLxgBH`WVz`^I>LI{4WqjI@ULqk z&w82RlatOkOZov?ocCmvhv%^735!|D++pZkR>g{~O<*0?Y++@e|AT@ZCSdc-6cS8) zKvKGayb5{)x|Vuy@+;C~$L4SwU9*YvhvoE<-x<^lv81_ewV3uz=sH>n9+-qKjQMt( zrgTiFWQG$>9f;xOqMzd&vm5A~ql258+|YzKLHXCg+}qJdF(){Rm($+C&q)?KfgWPK z^0m1zp9y6{Z2n`TSER5L<)z@&33KR{j$`M{*ubh~K7pr?ltA950t8YU7xB}J1ZeGs znf{*S)1-Dz??e=x+cAS{II)c0Y78X#iw_dtmZeyF)fLa&m&DaR1*n)FseiTI>PpO*=f=g0n@hWSDtS*Gv|T^1w9 zPHGKeXWbYBKV*0qb~I*{kA7g)b7a{WQ!lfloo_?**2{1+cQLtW>I17DE``bA-{8;! z4d$+wJ(K;PEsVRV#>hV{C6({>NbZ<*RK5BFt~vgJZojyJ4(C57b(WRP;{OEJso8#1 ztJlTbhiXu`JkVgpXiO=#!seCgL^@3!TVIOuY94x+R~-qyxiav4%?O&Gp9v$1V_m2JiVnsLcHcAz3@%%;9-VT%R>Ooao0*bTuUgu3n&U<|?Q!>b7KmRKQ4& z7!b6?F#hoj&ip?+7^x>p3b--2F>i?8I(m_GZMsjadXliXJQ5w=R^qduPxxV^FO%{r zfhP5D!f;{>_re4o#^w(gQPqtTZYk4Vn;meJ7P2e0v8a&8lRZD;Ai6%6;P$7aCqxY{ zEL}}5Ec!{>ZjJ)&U@aQCSQ0esV+6KDvc+#3U$VE)0>+I{C$1vd<6AnPdyIbt$cxUWr?7@75S{~D569YBgKMlyj;sie$KpX`;&V9sTkz>1`Mz&=?;{@cHjT@GD`lMF*2U2OhDeE!3rKw<^kog}`Em9|__5FgOS>d6AaH|_ zx!6u>uTBBy^OyM%*_nbjWE$q)x`D!x5l-FFPXBg<2~JDOJ9S>?XAAxJuZI1&DxRk& zymZmvxiQV`xyfa!x`by>Wzt8f#pJKDD!vct;B_^Q@g{o}czvU8UO~jmR*shfXQA`_^Sq{|As-yC$(tvR<5fo;!>e_9xJzO(+Q|9S zvswZp>(m%-$?ys8pJ_2T7u|!9BeOvI?=UzVS;Bn#RS&vu#-LjB13o{9rk4y^2=ILg z#}pSaj&|KJ!?K6UoYcdY|HboDqQ9d?zZ1!f-%6La{e_&`0@{{2o0sp9!pf7!FsZnn zKe+xLf8t~Szq;=pemnITzbBXTN}@Wv^>8@9a7qjIpD4jC?#s|-MF#!0QMy)GWB|6QVL;VfprgT*op?@yRlj!^uJ~@I z|1O6UsirziJKaL>nC%jLBfB7d#%4&m>xJ67hw*yzTC%(r}GtUE+ z&$wZ-l@T5I?+f|GF9#)iS?a0jg|61iup=Z3{e#P~f97i5X0`&pRk?<#sq=ZJshwXz zvv{KonfOEW9qw6LiK<2(sIjyi*Xm!Qw>Gpv10 z^dav;Hp0#YH`oQ9!BFz+DH~v#!LA>!XIYCYtXRoXXo~b^bte7g@--OBm*}BY=p=k@ zJqtbbRxt0QKXC`WPs4`Vd|_{S29A2C6WtBlQKDoy=H;j2v)r5fh{d%$>3_ki{Md(W z=K2DIxd^pyFG4q|Su`(5lFsT6q!9^rq=9|~zm*GN@;fCMzs{diy{ipNUk`y%T_?ou z2!@<5XGxjZahPn<2c0G%khUNIrX0Ub>fTHO`*RnGMNAU8p}L2=9sh$C1-D}Bu1YjZ zUWQXYUnGma6=I;$On7$P4H_rxftJ^`cr$hzW~*l41-N|BhNe2%!j$pzQDd1B-R2ib3^#pchL_gDval(rO^WEI5`@h{C;z;B z63M?1N~)HqkmtAUaD_61Cjb54{rM4Md(w&WaiNDHbpVCgfdyZ;jAkBq38ErhoZ-Mc z@?n$&%r4&x?)?dHN@OaOrXLpg=38OailsRA>M2It`6DX6*^6cSYjFB}ajdPHhUEhZ zu;KcE;Ea)n|Eg=5rMiVeZlH+%r*fJVy;}qU3#H+P;!gqnI1?=Tt-+}LE}H&ksr$AO zq*-?>s{J}bqkEiTYk(~T`dKh1O%0gtmRU@ZvL=bnzCxl*C(}@`SIm2t7V@w50?1VP zf?HDnoL@J^^68X-xu;pshMcKu2Ll}` zaAeyA4~`KeZ{^A7TtC7lG?J6a6=Y3#D=l*EBbf=EwCLFm0z1x1qKCFYFBt#0~2Yk>Tb5@}Jvr9G?+E-pzOk#s35s!Pp5@+~x)K zy0IL8G-mJ;`XRiNge2}9wqg1L&&hU}6V*TmB)uWR-xnHIA7MvG7DA1a2s@dJfvBC)__%T@y2(Xi z-mD!|uaI(N^A$29=`I?Mnn@FTuM=A7g$K$*$|~3okSDtzs;ZM&nX4h7{iBpT^K_)f zJ35IRJ4nwdbIj}DIrLfTNqRS^Oz;P-C5Nuv043KO~y_%U~jttteUq$>fdi<=r>EsXB}W>e>A}TpU`$(0gSn=o$Ep4A zW@)CxW#-)b$H+8f(WVihbWg4X}e^{{-q1k-;l%eUDSJEvd+uuW)Rz6bh$JfYIyC&_B?Yj?H`nM?Iv#>+&_&+qaCn zp)#L&8YBi`f`6u}eh)la(hln@))ByX*r({p+%3{&HNJU*bh0eGc3VVBern*R`XMkd z`2^v&3c&62M(%_gOa1Qma>hgUI5Bhz?pXSSUey=pT22pevR+5=^rm^#=FtfH%kdl- z{uhbo3|nzt=mfm*#*`a>KoKsQy(e*JD+Mp8aQH3l#Q&$59^7#S>&yDzmXp>0A7_eEf7Pe`3A!)RtJ=5+pRxN#HKVvnB z#1$t@tDc5hqMj&mREmBNeuoQB3%>81@w7oT1f4gFqTRW-^pkM@W~ER|%+^ccp$9b> zT`&f%#4h9ZR}I{*oI^AxB#6)%kJ(8}H$ac)7jn&I9atWghobK{EHjMU$Slt@KxbA0 zAHk8np=u(&^An7h6z1Qe*)%nI4TgFTVfoQFB`Zf2)9>*iH0xn9Ouf7cBlRz%ao%X0 zX_kp&bC1xo_a@O5pMKD0y<%pR_7OVsr4_nW{v*=@ByqXP6P)9agx)>Ra6>lFgg-E;$?AdU(6dz#1z{QG>J$GBw07v{p^P8u728N!C%5vyoc(~S+6@XMkv!AXD@WN4Q+i~H1KkD;W7S$>k=F^L> zWS-9jq9>Y0_w<_5rjS?~AbF0y`of1ha^btCqkNP#m;JAatogSeoag=#*x~{Uea00U*=h_{ zyJSGKq?m-%I*=pnO5CQy_LQm%?x2c7I@ahWo#ffeNRKn1#~p-DPf-a|?!TLJ`LTs7 z+tkjbe;Gy3pD?7Ak0>`sJ`YuSBPMhA4%i*IN9ZREvr@?;S-ZI>S=rH3p(fUnb6rt{ zBCGPKbHsZLe>j<@#0`P!te;%l=}Juaq=)g_g+Pw}AUbUpqr(w8!2k=@=6|(HM zuq$4-u)n8!u)VScR-?IIHf6dz8zs`l>a_X8h|DnNoyayi*XRw+zC3~6kiX1LiL<5W zT_!Wxe>zBNT`RqG{~U8yD}fp}Um&3dY9vv28hyC`ANBP(j$=G$&_`+kbfBRIlQbXV zPLPQ1E-63%OqB$vuf2Qehth0f%JBD_!HV z)j#WK|E`27C9dxKbpAM}w;<7sexvmy-E<|uy z+C6+t-pG1V$vSP~y=sDG!FNKo{n22$AI#V)Fld`LaS3KY zxH3E+WdodWL&s~d$<$$TCOm}|ONBMqbUiTHF&j{rDdc!uAgK!tA!OAUl$J53>upEV zYqvh*j34gwVa-+HGXVOqNgTYl-z5_T$BEb&HF`5-C$-QFwY>K)oay)&%{;L@OveNd z3w-_|YT%ns^JX8Sj^PKmc9(_dQ~zA>&@^LC+c7+GAP_@8UqSc8W-dQQ#4?(-#MRHk z$*)a6=xNS}yzKWPk5}3;(Q8Y{8>Hwh8`7*m{&8Z2}!z%BVX-CsqPK*bHjPmOMXaWU)j*Ru}qoNjP0ZuBS=Yb2+7y{ zkK~Lzn&9r+b@#e(?`C1Fd+9| zuA&Axqwx4sIW$xGjLEfmxNhzZ!CPrSxz9?Zuu_~vtyxcw@4rB8zL)V*d_GlP#gRSs zEs!?x6IpjJ32xT;K+HjgRKFHQzcX{OQ*SQ!?@|PgiSxw^R^F(1NCVqm&Ba5E59TIC zBfnJ0)@3}QRlbAdXkZ&Xe=>qQ+Mv#SShv}7+`Rcj)-8)!_9u@_$r?*$+@3(HF3Zyp z-Q)E424#%8ElG=&oaxu{Nz6j|#~A!+6CMQ*JZpCxw^fT_ghL)|t~^4Uyfo4Lf)Y2N z^#($I+kj=)R_eQ^4XMfjURSdozw?e*wm28VUJlVtelJ!$8;@!dS(sIvjmaOXaB;aR zN{*jf7N1p3%7SN*HG@&yNU;hM-nWaSN0teDA|vuUE}q1k?<2{-IQnj$u(#5tc&ud$aTm9l}twxJzkE;sooWy zPZ}6|WD82zW(oavSzffKpC9k)fZ@}gBWhd17{!D1%>q?^M4>Kj7P=q&;snA^t|v(X zH|*PPb&@|taJ+VWA@0vck;)97yuPeRe=n{@$xWj4_Ub|`KRkvW*3!kmB^fj>PJ`xs zV5uA*jN6VPPV=n8JxyL9`REw=TWSN1BRxqgJ;M$3p9O2>T3Qe*RklOmQ<_wJ3%bo{~sKo`M6=6F)2Jyhsx?wC^@l7)W!(H72d}wHMO|q zMI^nSa0fs1?Bz$_zK`t_1g3n1;PW%sPM@0uTb{XCfXZFlc@3LDeyr(YUQ6hS{wsUM zj&rvIZi_9PnEC)3)st9N^G~du?Eq|d+lsa=(s0}B4kmA0K(vlMAX69o1B0v8;DNF2_|a3iPbm z2kd?D73Ypyf_qaO_)(39bVbZVj8__qxiczwrOoT`MZLS_chp zpD(O&n{Z|JJd_)gR3;+uos1eU5$UU9Wajk{GFf2TEjTPkBkwBH?Z4O2#}eXL)Le)a zS$P=N(9DZHx{tEbad_N>L4(YdSa`Y{Bd2n7z->L|-g$$)uctuKjEzvM;Rs2G*MfP2 zKRi~s2z9C+tn}eu?Ci(2u==-$z)x1kJD-!VyYn-;IBvBRz21*AlI3vm2VdHC=Quvz z_!YNq%i(OCo-o@s`Eak|73qZ2?c~^7d&YF!IL=-+9%YPE?u=fx5+khB1dh^m8dz&f zFMS@10Uli>JajwlACklB*J4bx#ZUBazKAr%29u(PF-mYjn9ceO(V~v*j7{3?f`C%6 z+jb8*TTxojG=;3(+=^a?5BRa~=U`3w4|H9gf#Rpmpoqu?j1|scf<+5ae03Pgt&k#4 z;Zn@>#0}J=dah-u^at+8uos!|sTgh3X3!|t0(y1&D-@6OLxnw-cxz1$S*$e$Cq%i> zP}dR^*Kx)w`7u}^%N9G>YU`0RWv$od~L8HYSw|~^d zP1!qm5$E|dTB`%AcKG3v-SPPPOcG}8_d`SLdir8V9OWv1;#42xau!@7VdEx{z5n>K z_EAyP*4U2e+g3{!#!Y1o_qyRUn`QK)egKjBW=j*k7Nd;nM;N()L93XBsJHVBK27iE zMMo&%qgR_zCm~6gI>s z4-NB*`H>M9dBv$e$&c)QlCm%W?u_<;Q!b03!{s;HUO2^D+nlCioKRp$p7x zQx|f7p0F;ubCpqY+zZb7lH|DG3N$Q9V`>iHhDGOgp}sPQv1#r2cIE-$b;**lISW`3 zy;E4$ssnmmb3md-6->{~#G{FUG$w`e3QRNkCd_fPH~hrm!(H4XZ)0G>e!-(1|AFxn zPujcA61A4d;H4!=^nOMZIjXRU^o+ElG+&lhUVK9S!R|}()THzygjMSR*BdBwnt#%|-A&9X;hZeXkV3=P6>NaoY&3#Sj9I2B zbXl!&?td2CZTUrlFS^0?1h(mkQ-mk5Q-dd#Ul#7cl3e&;y*~N2d+uLQzd0 zsNq*svJhc{QCPb-X_F{}%e16Sx zZxV9ck0`cWq|Q4(qvW-2VK4QSo|`q1aLK>Hq}u_qVk$vKaH@CNUxCM!9l+fVf%qPE zSTW8RcD`9oF1CsimFcxqdXEE6RXxky-CD+L7r*2+StB8@x(8&N)eNA^f2ET zke2^~;a*<~Yw8_oIWI1j?yA`gs%r!eWoACPt{nqvdmhoe4max1HbAC3)zfK4cIKbv zDl>_xyEyz7V%d3b2RT1I942oW2{seHLCN*$5L~}Vm^BHxuds>GQB@5OX1|852D?e8 zN(IRokwGSVNaKh>q|w5xA?Eix-t2M+@BO10m&kasG9K%o?{XXK_T&#MwzHAd8A>E& zCd=W|gMJLTH;SHqwVb{!x<@@T-N|Fo@8A>rOZe?9MujgL=z91(mx-?Q^XaR!2|m)Z zPNozWtCP~(r63l$TF7eFLe|~ekm05b!w$~S-C@Dnz14y6s1(o=O=c(Ub|ffs4!%uV z1zVFNarI$O-b>M!*Ar{wRkZg(*k5&adU`#(QDGPBX&M8G$K}v=kqrJ8{O`kxPiS71 zHD2l;i*uwbaQ#Omx?TG>Nz)0&6Jwv?qWOTEQtWW$qIRw|cqEd`OPQhBcS%H?lMvPW zMdBVRLZsLyxFYl@7rbg@$H~8khhuL*kB|j)?uw>f#?7qga1Qi}PKOVbLv+m5aC-M@ z9@ajzVBmB%!{?R{Fw@jtO;U-+AI)mbp%0eD*35~ce3lnF1a<^S-L0`@T zqI#mqrweY-zEp`7f8NT@un_nJ0{2gP&>l~UIFJzIz2wGfMKm0r21$Sa@KcNv@r}X| zPH9}gs=vr!)tOT0`#FiV*SCaknvYTU`5CH{Y6bF_2pDmuIAOI4jy-OOC7o4F$&?t{ z_x>#!i>@v!babY_lpmlbBSlg@mU9I%KWMc2Z*I=m5Ym-i2?|NsK%_k&i>?RF0Vg2B z{H&xr5Z)I)hkD%+oUe<}iP2OgM<@LU_NhNnWx+6>?cT=6%5A{BE4t)^#s;`5W&wAd z)|0sTKfv3pi&eO%MHW4s4De|SDC@mJg~4H>d1nE2KE4X&y_E!>d>>5NXpRPdwW$x> z23*T6r-Xh}O6Gjx^tRT%-YQ)~ow*(?%Mr3PVhzrvBb18`|z z4(yH{qC2EQgqcoNN1+kJpPs_lsnlC#Ms?Ww?*pMkGW_CuZ^|Q(P>_RF?`%oG;K;j>q{Q618w$z=10d&K3v(Cx zkksSf$e8kbBuBJ{T#f!nN)IEo6**0GqfUV5+yU}OED_E*3H+jO!fd|dD=vTd8%9K1 z!Jm)|C&a*g%$#XYj^bO>#1Ji}szY4}E7jo-7uL!>GVG{B789RFD7L=Lag4qod z;c1QnR17R`h&E_SZyKBgco87axW#(rv7W$#qCgv;+66F(7irfV5unCi>IN zX_PR(cy9ey$ZN)v4eU-T=HO4RN9WPOKUI(|wG=WgeJ5W(1jDG{lV$lK7wNV08Umv< zlw^kM!JOhG^ip@mxPa{#`&AK>e>_6@yg3+=umGQ(aU}shK9(EXb4l^&Hn{(6JM8E_ z%t{PLu;TmYu`6<-phfaD6bkpEWyPN$Eno?>ZViEhvzh_=-jiToJt1R#oL;NFOVYn~ zQPW3*IHqGf?VqwAM+nXURE-5|HW-Tjq`-lya9HZnO<-atg3T5@yF3l0L>CBIx%+rj zgy0*0fyF;$P0x8~kd3*G^khUTow492D>r@zw1#OzV(d$jHYF3*7A=O`mDk{%=Sx<8 z=psw>55Suq9Z+&CBP;s!z|ianoT+Ri?ZYzQH+w8m&a-7EO|Ygrh3tXS_Oa*!>v7hx zVzgdTjeCLxUqPi98UF19AN>jJx3_27@~5Nk!x#AP<~Lq?>JjYQ<$~9r#PiCYPjS#s z5fk;}P`lh8vkd3r+qhafv1=;R-z&wrn<`;gMl0@?2!)f`j|qm}fu8iaP`J(n-s|5Z z|C~Y~qBo8-jdusLHRj~rmyMv&5yUWKoKUZJF)cPfK#z>-#d#}JP}M0Mt#ZsTzkN4~ z%(#K#*Td;a@58V}^a2&XJ&qRs@j=^c6?{8+KT6dpVd=iFsQS}}#>cf_+NC-SigrWA zuR;cPo|$E&_*&f6b&P<{S(<-Sif#~n0!NQ9kUiNDF6rmP(~W20*R~39JLL+d2h2fP z=&6Qk?Vw^ti>T_I1Ug4PnEUrQ$?|1COS6X=`sCCDjIt)^%}l}UYtv9&w;F>lo~4Dp zRaEEEFZ$ZDp0w19qQm)2v@m%v@-nYV?+S-#;s)&_!JLJ$dGewj%$+9T|z zIa*&*Pj?=egc8{@bn}u|mJW8)Q7I%3$30(<$<|`@w~{uF9(@Ka!aw2ZEPvd(-TKHPMgQ& z&~uI}Xi1s$5EI>8>-4J)H|fGvg;Kf}A7JMmLiJKm3#!oll1 zd9?%2@XDOqsQq^pb=(vH;S2hi_g4Fv&O?*%ecgV%q3D4O8ODd%+Sv5u7~1~6Our2z zp<19G)4ng1nkLU8et)WAnG-Na9~&_vsxDa$%U-5_ckj?|aSG&c`)bblBdj1*jE1$lhC{YcbJ(ZPlDD)74DI9%IO zgFJ$BT49d=p|uNDqiY=~$Y( zQiWrL_gcOD7H23h3NzOB)7%9c>G?z}nr}Cm7Q9DdIC(B@^EDDO(QP=+)c~hyDxv#e z2kyV|h-ujS)$(+U;Pd=GM4z@6F-9IWbmM{=y2th|yjl7Z+9P+f<0TbX)$LZSB{{_E z9@U4ex~-)D&=Zn>G8xRQ3SrVZ7dpO3jA^(PNvqq&a^^`gsB(D#EmvpIQ!^Q^cZVqD z8&$Yp>lac<>l;MHlqCZ4k>>g*acex!&}o}waa@K79^V>H7?~pm8V_<64b2@W!7cPl7Ox<#(3SJO(3_tph1XX>epENS)gClul)W(ry z$paapVj@cS>t@nPJLjQf#Va~^`zDTx&7x1P%|^TNTX5TzLA0z}j%l(#QN)j;)%Fiu zuHhjJIr0}P1RsXBzXkSw^`=2?g-AZEz&TCokUS!a)eqEx*DlW?jP=@ zH!bq4`WMhmlN_6)ggR1E%`0R--%FVq()1s1TgORjQ8N9~b;>754HGgiO z_9JyYmXN>V*PkZOjf5@SoyZk52~tZzrl6%%N^Vmp1>#p1@ji}2~P zh5VSKG58Aa@RJ)o=+B^|Al2o=%8Y4bwYaP7MEg>~JNFfQ9=?aCYs679bsVYt5rs$N zOYwAc7^3b={5|$0+Npcv$!Coy3^?dGhh;>{42j44{dCoqcxq?%0ynCuVMvCQz{J`@ z*{hu-dF=p^OtoSrxO8Kx^MCY3QWb9AZNxMauId96^4E^!20Yy^^hjf0@#3-OWc^EZt|F+NuF_P*<>`Vi zS4;|J9job`)&EH7mpD?tqLlDDugS4HYe;ml1Imirr3IXrrAy&X+I&V6OjZm2d67Vp z5beRFZ0Hue#y%L^VIy?B4KY_M7EgwVP~ECjTY*|HwpVV z$C~vhaql$IdkPRT*Nwc@bRZ`x7ZTZQq2qVJhdRs}Ag2zF0&iU#y6LefI$F)7k<-58 zg=TlW>Xm~p=X}Kr&TsMFmC5*BE*=d#UgDkhAaZuXPf|GcImT>HE>nn#75vWY_|eVx zh-B6mD1Rjbk9Fc${nanvhuC>~K|6p9ddq?IGRl22pGzfYX5zqlS?iJGrwBZI(tC z6-ad|%bNXo4xP8U;g!-G5Sux`oh#TupN5^_N6HvuQu7*m!{#Olcd?`u4MC_CxrtVK zN76$o9;oU0hRi=K%8gM$! ziKOCB9*WExfT)iK)H~@Ew?#|f>;^3-vz(iF>8c{$t4rXte@MlEk#%&5t&-)sdqwb( z?S&@Bf>mJ7u?yM+AL!mSD7R=neS1xdYo7Co#^!}`o2#_wUR^2r$YmCK`rV_Qn_^IL z>?)j4phJTsf^m`F7@F5TKw=g4(zYKZL}Hvg>Q-f9{I-+$d9f9!Na=yY2wS@O^Z%rf>+6XJf}}^f}J@|?EOo=cB}-G>d&02Wf*-g@L{F(Povt0LkLC}P}2VqldnCvlxzJgWc(`11-?);M&^6b z2t^?)67qrOyb;*jyD~6fAdI=8=?oVyHj*&8W(aFAgiWVZSyEE~gSCF_h$aWf-*~F5 z(?FDNJ(C4TM1&07A5)>5^p42n-=da-kp%x$;}O$X^iQuN^OXF!Yi~EvsC}lmxZa8@ z!bDmhcm=&TFv3$dV}yx`uN1JmGh)0;~9~ne`3`VI6wTz@^q;sCkM|Xf#vk%nX9Oz+;m98mi zQAVz8%68#;DsN%ZA9#}V&9PK@%PO>0qHyE{LzniMfYOUD&Oj=Z*IKv`$@@om=BNp7 zsS3pxw%Y{$(-EAgFUcxKtHbGOV(d)grO+F2i-c#2k=_VJ=>6#k7QaTpvm=@izHSeg zD*q%|PaLV@6W}6;8)>^z2aUY=o!0JnfEUNFB4@I~>FPC#FkQ&`5$AWP8&0FafT+$j;JXw7a$xIy{cjF`Xg!aSO*wJa^~* zes!morY9J^@fEDq{(G!*bPPM=`WlE`s0ktqWLY)6d7x_8!;V=H4F_f{hAa1hkPb}!4&2i`ya-6QY&5%#(E-0IR6OH}h{djLycT)nZT`-+BmAlAl|Bhp4zCH}?e>x##r!x0rTPPEv^M>Hz1I*JW z-85su6tplbAScUIsMCvNnyzR^US@q{%&qp4cV8~xoX;EK_!@z+V*Hzxd!))L?387d z(}&qL`{dX)&wi5^$G?;BQa{MLg)W$|b_>76X*4g=TZnJP{kSYyTil)e41%{8vZD(H z7me#hHg#SWyKSNs%c{6Qn|cf@<+4ZkpYUBcy1_&bj+tBf!wY}C( zyT6FgGyCV2ec5D2OV)9)X8$&N`y!zoQ(NiHKslN%b{~~>q`|M{F6=8c#U9rz$kk#6 z@9j$R`Q%BMY_y(aM1I8QzA3z_#zDMT5sJ5>&B)aGLTYHO16-S$!A@$lW+k)hSu;Z) zpxz14+1(9!sC+b!}He_sK^f&SlX| z`9Qj)OPjtBX~19mG_d~cFUI65qIlDKENwW275rLQH2$+Np9+OhZ|0Kq4Qh}n{TSS5 z9D}RD&X8-u!ijaCNJd<$z?b_$zFu9$I9xEG#{zuFuIyZ)=k%SNUh7ImEr=*nnA1vI z-hZMW&IaOP-;p$l(Lkr?jx_#)p>>SdoauAD81u78h5}HF( z_T0eahlJM-8p~^}?cjawAM+#5FURZ~$MMK?HLk#SHgy)9vz+{1(!s7K*PqugEm77W zdSE@MPTtL(I8aJ!e$J#t@})GoXeM=jKbbb$A0RhEa$(}qKlB)y;OL7L*l<7-dk^dI z%I;72IS)4Tu(gHPUhU7TS6$)#kICV(z4^rDZ#Z07e3aNe*;-aUG!Z*Yxm{-n0NVJ+ri|=~^UYFHbQq z?LW|SW355Lc{w+JQ4W1KzM9-o8cS$*38Q;_E%z-qh)Y|x2UnW?#O8E8Y(4*mpRt_s zqxFCCs?+^>+15Aw#Es_s+=sImSutVNn1`~ zQXKIto5oa@-XV&&kHJBbN%}XakSc0OidV{$rC|n^hyOBIs6K>|Z)^F*EaI!77r1wq z6ECr^fFE`FJJuwuqmsTqC^d-&)d+pE^X~|HN_R8%y|>{fCBEW)GL?9X@7ee?U^i8d zS}Hi6WChf3XUL$Ox?cev`Cq`2%}97K8pIH(D7y88kem zlXEpzpl*Hu(zhGH62(RciSdIdUmbE%;|XSXp2UWc=0b+%AT7+!q5HQh;6U>i{Qdqa z*wKP^E)Uy9Ebf)1{bzvB$%w;S}l2Rx# zmNM-1?tCE{3{4a%DwPUR(QGauWLAhwjU=hq>)j1hq=}MBgOa48P>Sf=|NL-Wj&q%T z)_T`^p8MWIE{3cHQN32!VY&sjz7jg5g0P@B05gA8pzTvn zTyW_WIrC#INH#o1Jj~*HpT~JO`GQ*`wo{etuTVF??8e920kx zVaQrxAFeYWsp}N9QgK3YbrllQp2xi0uof1-_XEFRO<3x>hWy%F3DVVuU^meLB7$DR zRLLO1Ki>e$3T}b9-62+@JsliUPk{NXIM5GxO>?qExHaM%u%_t;r(-JuV?W9;{!J!e z70=@A_P>z2Gm4c98UnlV)3N2zNStAsK@;lpP`T_ShRiZR`xEEU?aNA(Jv_H6$|f4` znpfbYUE$QH>^Qx^5Qu<})|LOm{=rFp-A^pEW?f-vlhYs~|c+ z6Wq7|gUeUnl7A9&AZ=G3(_3N%-kGI>k7^B`@e-%&_1qvsnL(;J+2yN$`_PC{}z2e~ne5mtIMs=%p0;@T>y5OD(|rvBIqFdKN2M zU*dmag(PaI4f1Yo1B>3xaH4z@91DtvN(lg4gEuf@Q#h>BX@}VnF0fij7EY^r!u@?+ zp#PsZ8YK$s8l4zO%sqnH+k>#DL6@wpy9`J63HNy(M#4OGh7Onfrisadm*%AzZk}9@ zcOJ6%P4WduPD-QQ?+)O>gQqZOzi=ixcLJB%DUL~(Gcfy}8#*i1qvPX1-0rcEiyck` zlfDU{IrJCKJT8G-H*Ua`z!#t>wvANVohL^G#`M-09oSkUM#^y<#BSV3+A^LoZeXu5Gk?*ML@<+V3m>q5VGaKhD`Ar5zMhFhqVPw)Q zL2J7{J-IVRU_Sjp!`K_N`*e?xP27n!{SEkfYd5x72#k*t3ot(SB3-aG68@_R1CgAy zaHCQTtamRUWv&8?CUP2ySRr(d{%iuHg82|PB@x1cwIN&k3Ed$+gc6U3n0+#vAaJt} z*yx9VT;pFbhj}7vPMfa#)&?1@q4xr^SXEoY~@FXz1#tc|OYI zfu2rP!{Tf7W=uM6`QgNmm{Ec6?bPtPOg2(%#Inr_!r5OvnYd}0@H{_+U`Y{DvMdyA zN6e@yA3p$EFE@j{t`!L>y9i3QEug={3Q|ser_0s)XsLz=qdnkB3J+YPUEO|QZ|5TT zC3QGu{y3c9;7MI3iKB$K2^y|iK@%-LlcLkHps$_@S-Y))iGRh80D(n$pa{yQ{l?Yv z52D;ZH?)cM;+5y@$3aOJn}VL;b*GQ?%YY>6t1N|44M`-rhmfDCgG^@Hb5220I19JB zO&&cRpkjOf1KH<7uJRxwWElqp&-p#twQf4im48lhFQrtixv+*dmR)218#9_FA6kf` z!t3$qpKIKM#0#{#?hV2q#rEMce6({uD%V(Y-52}lXC@V1s;z~mUu0+%Tg7p6*P`3Y zXL$XSI+kY?;`Kyp4BVT~#pw2uS1kqHzt!$AvSb4Jz=&~WqK`mgu?N>|X26ual;xsF zwUV~iEXfyb<7R8^qbs%}br_e%l}^1z1nwENzU|C>KE9K_=$r%RN5>Jv*&m3#<}*&# zLhx3^eZtUNXHeef9zHuc6}?-%XivBr$++nSrE;F+y7YO{Jlx6j&8x-Qs0jRaV;^1) zE5sQ2PpI%G1OJm9O{PBjMvGQ9QG?xANNL#?uDrdSSVAjTJ6D$8?3sx(oRf*l zYxJR9VK-SfCy04?C_MV+2c&uASLq9*`RIlV+_9au=(?{L?|%}*(35`XyJH%L zZ8k=j2V9O_A&MHcaiOP_nBF15hp6$6xcz!T3Lglwiuo}Jdep`J2>(v}U3T({rQ>L_ z*KD$AM>nrkaGM{a{v9tVyP?yuS~Py3%KVz_Ni*7>b8p-`IIU|}s?zqnpxlfjMCq9| zG1A^ysa5SyQ)Q2ms}5aNOUIlfi6%zmk=rNX4wHvil|E=*k_4QH0V`@81o!v1L#S0P zthqCvOtDn(YW&~f-7C6pJxxeVzgfn9gsl9oDG<5AVI zc%ftk#w@a+vC@mF>)#KIw1Nzm?K_P;%G0zgcqK+vZ7O_`=teB|z5dP`1? z8cmOx`xDpe?o64R7^GYo2S*3rvJ&g>vif3{taj*(qtw!3FmyePSiuVQ_VIhAd9qXxx;p9)L@SrS8P&FMPkaBJLzKNcKQH`9To$< z$|@MK%N0CgUD+|S3)%6O``DG4?d+V;x$Nu!8`i=VSR;QqcIIYLcJdtyc4DFev^u|n zvY1(5e#r&*e9Yo!WcTuVe;#7b+avtG+THy4YYFt$*=Q6UQ$lY_FM|giPC~|Y5VH8K zB*L~4#*BzZ1*t zJ5AUr)w5YRw2ckusAR{Bcd=3Kb!^DS_w3%;8`)hClUaoVE!HK_g%wW;hf|%xXJHA4 z@eM`%>Xg0wmS-cdGndEDeZ;aka2uLAbfR<9S7yUmRVeu`p}GQZ4%<) zNbasL;aZMNCvIad&{8i%iR>htvD*Oj2Zvybz$0}%whYRTnZokFe<0?l9&odTG9YRYmCo z&>m=_(Unc~XZ$+ama-UTbQdu(Qlq&e8HZuLNCX7s?FHu*VZ{Fa4Dw}S07P1~2th_sw`eNtJ(|shYyX35J=x&0Y7A7%sFAWYr^w$W z#;`e9U>wh0&D99ZA4V@9m*pnH+sAvsXs-#oRI(kC)=nVS3#2&baa}O-cqX|mF^da0 z;Y#z9ui{uA74GfI59H15Z?wRn6y$=wL3q|J=Gv;+-0h#EG1lBn$ST`|sQwLDQ`^g= zu&<%LstA{UItb6E06Y4-13Owvjh(oC7WitF!DsOwG)XT6EgrXXVfo3BH_48*&RqiA zre;D*#xCM_@DZ*(H5n%7ZzqZ5FvLD~fxE{NVV9h#+ z&|h*{x}EqWXwjtxTVc}Zb>v;D5xqYx6v?bY3=m5vQMH11x+j=+h-GlMLUlpP_#Kg3 zy$&)wsgM`>5AM%WViid|_=;H`V~ja=-|1lI-bME|+aaeC%J4_qH0`EPeJ{Syg&waJqHwHKi?Caxg) z((|eJt_!64d>nJ8{=cg6zF)9t;|CP0?E{51%gIIMEL>sojXKITkQ1F*nEvNAEMNHy zl0G~oy$09e?63Kt{c(`;*vPzX-o#GM^M_LHO<;T39}YeqMK4I6rQx9-5I*cneK+r; zb$;_Pe@Y%~P(xZ6+D4W&h>)ni+T_ZoRrEa5-%}yoB2bN*p z1|xp7|0T>TokizF@_3{%0)Ji)#fWZceEu#3b4$0=qP@3pwXZ%*SkoiV zl@n7oZ5kb|jp-ge$a&i0zRpmRlJTCV=L(LN4^4l5UG>aQe>!vTH1&R;RanD%HchliDHigoqk80>E2OGLB zN#Nuibb=c#w^35P3@%rvkfrVZcrT$JIfZ**q$}J_q;!Htk_w6y2>hSY0CIs@fyZWR zqu8k5RW-urqWR}tkowL#bCeBJ1B>X-1Ru&T{|l~{Q@L`t7TQ%9MJ;_*s@N4J zM6=if=ajD`J9c)`Xq5tt`&>sW8wZe0e1hImioE88A2c;t$j)w&gypuM;d!J#`KvIJ zhGq4@rt%J)`C|a<5APu=Wk;bPsuQg@Do1taMG@6?`6T;HD{lW7fabJH_0ucbp1_zS804o`<0NLk03JWG-YT32V`q<*-!kE4&uJ3fU8q$XUFP zCn^TmsZS)xiLL3lFXePqjz>6i!0s@-^Lh=fCx1YBw;I537u<0?1reY6$WKub#{KUt zvOz%xceQ4sScEh_)1HJ;Ax0SgBM~FpTB*CfF6yTE(DwC9EPBWE&^w#YpkIL!%>4X@S1F;9Ao{kCJ{` z?zWznE^-q*mcAI;Wrl{m_i*tQchqXXO?wNKQF?_hPIMlNUJ(@M2k*z-ucA@+vN2wF zypOHpM&pko#k}ZXBCez2mcywkc=*d}vbf5NPMz6L>kh}k*Tvg7Z_pSt^Hnw8y;%1Cke~>mhoZ!Xp|KlhB-pEfs z9mLP-Zs8|jJwAC@#mjBb#pDnv^c3ddW`}vOY3>#@&N0MlD8X3kgLrM}9kO(|1DA>{ zp-H)OL33p!*O}ykgN=vz5xWF$Us5^lc1q$$v}RzTlrFZ_PsPZcGcjUHH+?`fP|s%% zZ7<8Na{qo7=T-PqjhAj*=F_v7ruzlojI!osa<<{_s&3w{*NC^=sm||FT)?joox~sX zIn2+u5jdHjJ<(ylGoB{X1y_JG8uqQC1wRL9S5Ywfth|RyFImz-AtSMo6iRkIPN~i&{Jr!u<7f*~E-l3)Q3shgR z4iB!ISoM6DCTdPOhQ%2TSh69G7b{zYZ#RtN9SgMhU@LF_u#Xh)z2^$QAvK=Y&-sdZ zTY;ag<%-iH33k*?#h1G?4xi^xxl61uGP-`sVa`0HICTDhtaYQyx7!$vUpe3G2l{gG6j?xxf+4zGO;;Kymthw4T@`pu$?pZ@4JKSQyKn5 zhPAhHwWn{v_{P&fD*LczOc17DRb}!wD09D`ild_27EU#_9aW}%K%2#jnce{}I<)L2 z9^Cl?huqt+HBy#WUX{kr4ld)Du1)31b1i<{=Tv^Wa8^9-`&NEay{6#OZl}$`12|{y zOIWaTBU*rzn-wBiBd-7h|LV@oi^U0$1p19_+Z1ybue^qJ-bHS zf|a?N1i$9$Fcrg>1s8%7FOp`2hd=glUG1Ool>2?W!_VZ3q7kM)ihx^-Ho)CK-z>95 z4-sn-@2aDwA|TbVjtAyEZh9P`jC&!Kp zx(2RxIqb+~&XDGLlgxDd$~?M07em_DqQ7eZH)c^Y4Vob)ux*71?Bz$bjl`aVx_J4C4>_}oFcnWeL)9vE)Lh~L(d+c^nN=hzc+JHZpE6)o zuM>=<&%or>Jq8yB(ic}KcXo{wBQh!npJ(lY>g&efA^Qt1tZ@RxV*xN-JqvQZ$AFuS zBu%O9B8T(E$?7vsMD4vJlN=`Wp+4}at6G8~m4Jaai|NnC4|x23MOBES90^eW!yD=w z(F>0oF?AqJ0Qzl$88%+<$<55Nbjn*ebJ&$P>&oCKYIk6cZVpJ#Oo5TM6Q$F+$qx_(epMFRa!&c2_)diBcA$dl)l2#&W=1*!T)xxzs7a)kHP_t`l#6}^2 ztWuIDw$*ARbMg5pl`fDRVG5`JvG{v+FYXmdM4#;o$dM2WOA{u)6arr2gGR!-CGj%EG@~sHG_R zS`k6cUGs!-dFfo&f35U}Ods>_hc;MhXK`Uh{xtcS2Dx`gmT|sm%9WuONxzs#q~FZr zF7-SR{?=#+c|0BrO8tnn`D}POWh!gF?f`26H`ybH%!E7EAhxGh4{xfAuz zNye7HIIe0rNbU9qqJ7WjCbeaKcdsmxHo~Ozt>>k5rBtK-M)gD3J1Y>r`&05H)$V5lQ z4!mLGNVMo=GC#SF-1oIWmkI^k5jKd$I*;(`jdpwy`vnuuRr8}v*O9vxA8^Y1XdL6| z!aPh~Octd~#MNzL=;5P>+on3~&8&sR? zvEmxvp+S5yq@D4Ov^s9{PNrjj|odj+)Z}nYmt=SnGvBi3bILP%SU@R10r! zd5lgQ=b{&!$G*3s!n&VIj)fRv+g8NT##|aY(Fkv-s?zgm>v6f_A;SDo1w~g)G+CW3 zc#`(g;{ipedRger&5)qVncL`f`4p5`oDXUiOF^o6F64CI6K1hF?BwNv?AVTD(05WE zlFr>=r(FBR?l!4s_Xb+CGQKn52iwfltahTs_J6SY;Tqn$VjLFR?nK?44+MAP7<|)v z2_MUi;^!VoqepJp;FAO?d{*?4A2qdsOue9rbL>0>CwBNq?}jscKgeox}<|dcBnbD=V9;J6DWwZI|V(m9F&ULp{`q z7rNy3g-nv>19ITG8^Hh{FqtZN0;|P@yC?z%zbxV3<|)v){5)u>ZGnW*`yhSVCumvp zkz_x(L8T-6aO|`v7+oxdL%H8^OUn$r5>o*)HtfeGkrYoqI*ejlc445w3VJ@g6JGD( zK~_bA);*X-?{uyvA6}m&H9vL9`P@KM{yvd@GPj`@?%YMm;xtrscx*XwKhl5<6XwcU z6>9z8ZIa#@O~0Fm-%pWQO0nqgQH(-)3^jkWFo_kv$w?vCaHHIa8Z`%z zp(1^dD3AiR$(E!@p@VkF{}6id&**8NDb!|40j2#$M09~Fclu=^35?iA3V#K%qh<-u z!n}TVeX=l$^fp`tn+Oy?recxBLJJ|5!#uf4*Wwnlp)T1p!OEO+k8N3ti~eL{h#zC7}nd z5&83b>3pY)^tM|biM4%245q9n7bo@6cZSbtTe2Pz4Sq$u?)x%#PaT1n-V!j&>4e0M zPH^$sCD<4o2SGp5z-aMWI6N2ym*&*KyX3Lt@oaO_WxSLgmEMRA$0uXxRtb9ZuM-Nb zB-&*s%N<(Sj8gRuRRxD`l0(NHRNea)LSG#m=KdAT;o{>3cIA;ml(gAhdFxJlRn;>` zvap_^AI4HjlWn-r%(Gls;5qsvW;0D(Hiq1pbcTEo_J1xi<}mYMHJn~>n>^!mNl<<$ znHN774kq7#iziQ#-!U%W86z-FB;=sl?=hTtmjf;hVkG(B8Wi7NL;0PC2<~C@diG5E zRp2&G&Dl#0ViaiTR0Z5M{XK5U??p+^pJ@8#Dm8!jmv)>vNzWPNa;+EA==pKBw2*P7 zA{HabbPs5y6EJ#6hMv9fik!>2V|l~J5K5%zPjLI;;=)zyA-y5*Cu7>b+ojYd0sZb%tiW+KBYXS}Ytb_^@t0q<@?vXoT_& zJQgF1N3UyRM%Egfx61}oC%a&1)k-wj6pa!if>F%#B3D^*hfYxtV_o(c}k(>O2k4aQr3$GX7* z?6ESzLHRy>Edls=_Iq?pam2)b6RAmv1F;-GnOJ>y;>xF`pw81|+SZ+g3*;N=t8{nT zo%Dy)_?nWm8doy2@E0k+qfQ={9Hacd3taTztg4#_gTTe)H0il&Mh~oe46K1U*}GKW z(OoGf)e2cO2^WBz`YnuTS4PW>VO-|kj#@6~FdOgDGCdt!?s$u%jFA&uGKP zs}u0ff&#pr<{_}af@oB4JT3Cm=X5(4)3wsinEUfzqP5q2oVsfZ^;XZoG3(ZYWwQub zC9I1sbw?px=t#szf1^?!@34T?#qqsn%!R~X;BT`}_&;*Vk&ln4x#Ha@Z z8h02xXI68fmt?7&QWg3vO2VH4bL7>7YgqE%HQG1Wgf%$=v+T|#ULTA2c508l9pDu(_e~v$whqwFlg8T$rXR0 zPMt#E;;W$Q?u?849$?k{Nt~)rA4#Zf#xd6fzPs2&%wJ$Ym)(p(ulb22Y3DV@CwmiZ zU%CU0;&QR|<^q2F<_&nuQ;Q!lbv4ST<>H?nZLHd_j&c3@!s`=eDAfc^Seb zwK&`-aG&z-41K{r3|;i{5jIWKQB^5t+ke6>x4@J?fh z95Nsy>hkHmfO@j3{XeK4sw2Prx>c zQxh`;Cu5NGNPPSBGN%7%;hFjcyu9~m{9)sZ(Yy4}O6YTqQ(KE0=|cL=APr@<#}m~$ zV~&>(W?qjHxBT||5#8NAnVx*MjJ^#VPhaZlLoPQTas_8_wqF?BQ*Q&W*<#Sv`<9)2 zWHfY({$XVd;)L#IHe_FP$EiSZIllw9htI)>Sw(n3U~InMeFja0bF6!t+tB|tqGrHO zbWcsi$gwZ*^M-2Pu%HYtn;fM-_Bmnnc@Ior5@A8mYsBFD9(|;cj-Emx{1g ziGitOn_$+vSEwFch$Y!k*c32>-q~~#z4bx~H*+5m(=!6=4K$lBA8v~08s z@WjX~AJHu50=NFAKHBMuqVBrp3X8JwblBk>j;tNUCI76&xrZK7bKh1p{CU(e;I=%@ zUZ05CE(b9q@HEl7<3_H;2N9#J6ry8WO#@fPK&F2c9E}wx54?3C?XfppR81hAQk_hp zqqLAV6V9*f_gem3GfvpYn$nUlMUXC`L8dLNA;bIPsBheVAhxT8WWpLWu+&4Dl*`;u z+eCW&Yb%}l_%N3@q{e*>AD|(Ei*){Bfte;L#>6gPj1ztxqDB3)&~IoWHBwlLF`=?_ zZ%Pq8-*O5>hc{u0dL9-z{^V2}PLiW#naY8Xhtj9e0~XJ1^y2Mz(*;D-~BZ=$m5SXfM zMORt>BN4AM>4U8eWY*;@Zo=nF^!BV5X!TO?S6Yw6Bz>U|6;O`iA1RzN1YQcs zP{FMN^4bhuUo_|Tojrg)^A=HkxzmKJ?<8}EgXs+Mhve_UJ{Th@%jA1=grBiiSi3c- zj@(M_lD;)5uy>|{9vG9hEW?#qez;R92X%)Z(f7?8F;T4B*-69h55HvU{S^^UUJg_$_p%HJFN?Z+2#s&-`@j6 zlTI>w+5xKm+XWV!yFkOAY$6{1o8iccPv9nZ1Ey&@RE5b7qpAFH@=EOl89(6-4U_g} zik_;{?>iK5Kc9oDQ=ibrq7Rr{UxT9cG01q-qRJCZfytZ;d>6QOIGR zX>lMPWtEIowG9b@n0lp2m(X{{uH2Zj;!hfh5*c1&!Z{5E|xBpZotNs{cfp#3Sp`V%;vf z-@zNRH}e=itU_0HuESMXZ>Xi+R@yoF6!-bodosG(8i`K`o@lV9!@XA2PH<9fT3t+i zAd^!S;tpfw98lxNXzWs&hL?o-E^@y;XBygtGU=Vf(_$F)o?XW2OL8Gv`2dtp=wYY7 zp1>+4n?OTeJd_k{A&n*PNusAEwbGkU)9jR}M#@oYb=!%$oRp#Ibq+ModK3C+Wa6Zw z-OTlgn)Ij3KU}NO$RyhfUhDY}$%2hqoVTSV2Dt2?ZTcPbWn32x)tE!QPXDC|eL`3I ziVZ%NvEdcwMDgphC@=d2@X%X-&LSWl=5#!^IIMUT41{sbdh9QFtGk<3{B;BF+_(UF z8RyxN{f9x_;1g5o@tmHF>A?~C_9!Jbnp(-;A(KZHQQN*66v=7A@x%fTwXt+ZT{m5r zdz4;RYzMIun`y360Ns@H4bRN@g+a^ZXs(w8S}bm*sXI%!(r(@?Yx#V#1Q zm3Q&7TDDl~nn8=Cml19G>)`Hxj!c;Ix~i#d8qCvgVl@s%K^gf zEAPR&YJgt6_K@~DDUt~dn?Y|;4}^aINLMhUK}meI;4Bb48p*yO<~*C$o4%s*ip%Jq zr5-r{%30bYLx}y=B6_v-GdX+pE{VL;36U}xFmr(tT;1CX&07t@%cG0VuQSHx9>fX> z6<%E?ordn$$GJ&HST^bu-pv46{Gt}HC_?U&~CUa{6%`QH}9jVi#*Sb#9OC}ohl~gsc zd`{@J^!DrL-KUZGf{E1EJqzlmC&MeLRye#q6x2RRLTs@x2UY$eBTh3o{ z^12?F>rEkE-pk?2giHZF>iMNt79GPM#IF;|iL1#O>@FDvD+lTOTavWU<`9()kAf{%yTNQ> z4;h*HifMSv64|(DQoKBrQOUJqLf5XN+ixm!pZAC|#%s*U7PS#VS1%AUm5#x!?c2d{ zYXpq-KLuWKy)gE!3@H!l1Fsno5W8eQ6zEJq*TUJP_2**ln%Yhp>5?VfvpzwS!o3)h zvJ=-l*TdjYOs_xCi5$T@QWU$he8^b{HqGA_%1rY= zOA=160w?!l;8myv&O%Od<%<^DG*Jds>)rv=WzMC1T8SDux+J(dmsZZZN^iPMqIGYt zl33qsmde&aB&a4|kQav1U+SAJC**gZ+E>XUcXi?`Ln95V;Ce6&$|y9yrZD8e53RWxPR zBHY%|#2841(R!^U40~_^dnF&^tpj?v*Ru#qtxw`ESts26O_vs$9>65sd8jXBa^C;9 z62|rEFvWp37B{R*aP03EdK?-s!C8ieL`0(Z>jFIRg{AFrQuO$@V?=eRfqOS%BbhoV zaKFDF;0_;{L>FnMbAL=cakfPYx!o$xDDON)f7c>j>B_^TU^5)IF_-jZUk7b9Nyr=< z3Sn_~gk1A_JgfZ|Zyw)=fm6ci&mJYJK65p0`L6({?H^B{Csi@>PMyrRB6&Kga3f5> zQG~fPj~>TA4mq zoHJhuCuN3X{yrhk+x?ZuriIfB@4ac9(BsN0KS48u`wgiU6NWwF3Hpw?;E=PHG!0~t z%>Po+Q{_5sw3r5@YLHS?+HMIZ?`Kmx~3Tw8s z`!Y1!pwE3RX+x8FFPX=0##`E{)Y6b#JE0S{y=q|23>rxm;MvGLeWdg9wh_fNLp1OnET3+HC$&l-{6O z!dZs+zb-ng7S4S7UT^u|vMf-i4t*`)WlcZ!;A=GQ>w^4HFTs##w+N;zYDmNlcVXm z`qe1q^M&+0g z+C2I#^RM5Y&i~*`S1i3tf_rCk_Kr8GxconQW2HmYCDlnpW!)H3J7xxVKDvv$tF@eb zT+mJ9`Zb964ilQ~K14e|O`;CVZ=l-Z12koSF@{W=jL{vJaI%mcin>1+D;F2wx)GOn zg|bh0Ti{bzsaVmSe%md@77E$?0tv9$G=en!tO4iaVNfc692VB!1@GgR$PcogTv)b> z-u-@$R?XpQg{qL5aXC(N+ee_r7c8 zFo*^HW!ipTMmbOV4o$y59JRab+^*Mg5cr3s0V=cB; z9v9|CV}Utf4o-DjA%CP7QJVdezIav#TA~kOf7BRq+pHQ+2A+Tflci9;U?=Dq%!Ea^ zS8&6-M>AW^{*mLid`QE~f5h!$5UKe3nYu4u$$fSYC*l4}xR&|jh(ewcwfFl$x}5HS zICwJURa?0?-~ZqQ^RM)|MgqNAbP3HXoKe3kA1m!YW4mDpWGmdwOB4l1a{m8$j8CBoNiOD}e^Rp<}o^B+MvrI^d#29W^ri7Xe zR*+af8(LYq5am>&Nci`KnC6qnk7ft?DNPc%cI{zoQo6@$3*V>bIz)KsH+N{%(L$2_ zav^7Qb}x<^*ba;GUx8iEKBCnZhTiRFw0mF|L_e~H$({n=wmJvKrEP&--&D9&kG+Z4 z!hG`L)ONCII}qtbS1oGm!f0mT7;?N{Uf@<5!=%)+E2;UVuAbH9x$mq%-jgqI( zwCFP~FIj~*yuR{Imaln}o%47d=LtCY?F*h=rix2y7Q^lHA8F}`L(rVA1~wvkM2kxj zdYq-Wd1D&R=`W!N*QyJ%;z6!p{R9Yl=MI-SQ8+OW592a_lU})ljC$=+m~pPJD(^KV zwqbH4|Co1`*T+L35xav_NX~&JNe3X@TLG%GL?Fg07UD*|gIDmA96kDzu#Gdw7VB=@ zHETCEl>QPtW=rs!SOIVQZYe)c!w7GUmchQxhrH+66y#{vj@b8vS@KU^wzBs}qB;>zZb2HV5L_VQ=)Nh<@CN}S2PHDj6W z*W5@*wh`0#?IxHgT@%_cZV)?tC*1hx&B~3BWG7CY1a1F?LhXBjzkPHfa#nh@W714) zIBbZo%z}B->s`E~iaLsn-$Wn%&ZECYcUl_$Ng$S=_t3X7m!Y(00{xzwk9!ZCBG>gU z;!)qtw7x^Q>wZuTO&ddCaA6(9295^5pN*iuLWL~aTSp3(_;C?UuH4JhiHu#kIrDgO z8<}@Tl$@!#02Sjtu!edC?A96f?5dnO>{{~(R`G`}t7ugNA>Z=Jm&e7_Kc2z)$yBL>kW~h- zOSy_QOx9qxA68})y(h2^!CTp7_VMh(#=~%LQYAomA^G(^9o$VUQElV`s&%R#*F_)U z1DtN~)0kX-{JXilTGRx*^|^^=IygW}Vd6_k%-NtS_?KHqmsSHx_;}(TV0p-D)FR+SWv#MO4G((_gS+M<`iSm;z_U?}8=In!#av z7kT?_F`X}XsU(*wb6-7nk+OBO$QPYjDt_A^Mu|4VL}f9E`|%7CA1JUQPdBks^%dFm zHLKX^FI~7(W#KS3aRBa{ydulIR)N?E!DHmS5x0$Aj0!pZXc+0sPch$(N5Ax9*XeZL z!buS$j@RNZKW7Xp-G(_~Ektqd3&P3x6II1ySa2hh{&;!}OfHmw>H8TpV}4)NwvI$f z6c1I^z7-)yzmMiHDU|6lmL|uPBWY>!56f*ZMAk@26B;-hR&++OVn6L5^z{dJiQOnD zT}$BjIxP^p>PzyrB!jK(9imgU1(#0POlq%6ajS;5V$DBGv|M7yj~RaqkH>b=n2TSq zl&14WH*WG05l8TCpf_E5^CHR?zvU$kuSdyie@KDtgJkyybr+&e&|3;vg_agM#C~&D7BLvu346ZA@D0Gu9GPy_1iOrdC8ZI~$=mRZc z?(RZIE;FT(MG-XQ%tU%7c^{1arbALDFMwkWAK}_t19tmbVBM^XS;M(ztU~ctR)3%y zqW2838Z`l|>V`$^SQ}^f+`j+{KAJ%KyfW_O`!|@nF%mhud&Kj}ExbABE*(~kXC;?3 zfS=nRJhQt5r4NT-l8q_%#wG_;B45xYnNaT5HE9HW#ofevJSQq}f>s zgCIU(FFm#ECXuOl46%QUAW>j3M<%LZ{wo6>V%o4QY%5l78NdP`P3ZY@6CTNm;n8(R zX!Xii(wLBhkPt?r8u~D0D4(2}o<+JO1g7YnRvNhy$<%usv6MSO8{+>`wMDn^fH%cl z&C__~qcQDV03<^87#w_+1`eK{Y+BV%c2Uk+Hp5tw-D-FfmRYTayp&It35NgRm~dXr z2)+5-L+SAJjL_4T)1dF9L=bFF@lu@{yoTg|Buwcob$<{5<0D15wCSR#-n^V@)r;aR zo#(V=`x`v^jW7>vEx1OjNG9UdVluad=jiAH%Qo3i`ZOql*4>E4j|1Q7i zD-L5w`Fu1{(ZEQHtE`!j9W_yKgwhpG5Zd+}w9*4u<6V*>9L(T(g8!L#=l3+?qWWkf4B+U4>}-g0>P_f=l^ zt2=M}vVs8TDd(Zw%LB~eh!5~B=@%QB7s`fQ`ofNL@qvoYxx{3mCWIaqXFQC(Fm43TO#)>q zI&UmJk-D^MKx8HJii}}8v*c-F$sx?&JO|H<_~QA}dfqsFCBDx2!;2M!@doM}c;CMl zFtTn6Z+mA8FFWkY2c}E#?gh(u(bIl7WHrdoPm>@|9lkROtL}hicNWAng`;YCC{8PF z#Cfv<@Ln6^) zY)*fEctU+G#*>VAUtBDUxc!SUo~hQtkU#y{KW!A=AN7Zqm`3qgEAo1ELA?I2o4jdn zFt7W|hL`!?&ucz(#pbqte){`8!fTC$0NH))q_w@QYELSxzO@&$y-$&_Uw2^t+S8B* zAE99#$HtZ4W~YD)t7Ufty2s9dUxpl%{Y-#Oq0ceLMH!=(w4##2ZTci954FF~Vhqhz z(3tN!DAu+Glb192ZSqRoZD5S~LN`5TPb@!bohKF__<>P>m4wXk30~Lh6B@q_!{%k` zc)8~gKXRyqpXBR|KPuJ1@#lF~^44FtaNi0ZcwS_sl$4mF+DuqspbqPe9>H6m09Hjw zAKtWFfM4oqti;x0R<>*poIj@taj&M}$~s4!B|4kl3?|ev!3Za8Jw|^tf1+I(L+GmG zg0Xx8etfn9v%)KJaXF8RejMY+Ect*R!@uJFvQhl(H!GqNgVghHjQLQ@l|kkUN& zb*My}q7=!9Qc-;nGWwn0_YdgN#a|*;8P9%^En8zMc%J$MX`CT9`DqT=1NwKuaMJU$6H9GbI-qpS}UslM;ATIt$YYxYC?%o*ACv|lCf5}r9J`ny!4Ww)W)^)mr<`tW>0)Zv#nSis z!%P(O8dL5bK;NO~7+U!RH6A=i+xhM#MGuvE4Y_8#Ch#6Aql$>S_a$DTOUO;6ts;?9 zp>Q|-FBHvcrva;CS>1|a818>Y?mU^mm`;B~lU4>Zbum>iq09$*WkbQh;{seSWchW0PG<@E{WzWs9o|lFu<3NTBB^9pB$RH}n8ye-YrODt z1fKmqj7|4m(5s1eX{DVJUE+TVY4t>!Y~+A*d!u3ez*YP!*34^8AWZ3`CvdiBKKV3c z2xS|S;r$XTR?~5qwGukC#hy1|`{@d6fdVV~Ir^`k zh`WUU6oVX7QY3qgJnK}UBRP|S9IOi&iyJFyZ!KYGk7T@XBl z22W9Tf(VoI={)Z9t;1x!STr!^X{_p5`kv=eGEavFecXXc+Hx4WWeHCCa~M@EV`!VP zF9d&YM^A|ousgGg+E>+s(RfEVD_0GZ2IA@Gz^|aUH55Clw$gO*`DEzOX!0{{kd;y4 zQM$s6d@u3<`SY$2RbdBrkKdxxE^Oy@9sKzHrr!Ln+ylI-ngze+ga&@qUX2aj_UJM+ z2kYn>EOd*;*abqKa@`GFF0+ni-=0f1ygf)W65irLV<+4c5`$u&u3%829>Afgc<7`z znnj<${lzEX{9`L_*^V+ogKEg}H=iNpOc5`8Ka94BeI~sTt?=GQ9gd59hT{_iw#(w% zol>BsBd4t8mbh6h6KJYS*iX zw~jd%X>$a1_x;5)n-=5MwDtT{gGhd}z(G<=GsePWfG!oUk#GBl&!_rew&-Cjidlkc zkMxE7m_Ay#9iw06UehZf_i>(47cEfos{go9w1PP`u$U21;e%$(FxNg3C;yHZM4Y zL`xyJ$p`+FEQaR}Q{kO(HypL8!-CB_z&Oh3IVI zx5%6-&U(BO;#-U%#QG}KwZ=eG_9|%89b~242&Cn>f$|aIt}>&6M9W0r=k`aq*DDZj z*Wbk}PEKg88H8Scd~la+FRI$@#yvtn^y#DvXqV7NKVFtB*0?>H?)8?Zk6uNRF)d;A z*62nO=1>9$Nd|~@4WO9h2jbE{8ng?PVgA!9R>FG&$t`HWfLaY&CVB|N?&{O4D~{29 ze}loX`X3yvy9_~l?y%ApevtY88ssf|K}LobfQ|2C@^XU>(~#Lsa}GX2e$A83) z@m?IA<4f<@?!Yzm%h7RxH%+(lN4-67aB0^)uD9kB$-NUu=JzGjJ9Z^Z@||YlAD95k zbfmyJ;|95ZsFn*n6bI4A1p>mcTh-pSSXWCMemiVrp~0fN@qaoy9ysW9M8Px8DUdPEF)KIE|+n%fv|i z{Z-tly_NLtia?yS^GeAm4+UbnaumH_FpGTUj7lO?#7Om;Op^4b8Aj|?1wHve7%kby z4NMz^8L9cO!_*5gjCY${p0TIk~Sf{Vyva~Hv5@%3pR zK=(wFZ*rY6ooq#BZkM5vYdEgOT$(I$|4Wj_jv!mN`A}oG=fW=fClP&|LN8x< zLd^oiN<{w2(Du?^Vm&96$`;AvMRE_X9<;{3U!rhc>O7_l{1!OHyD_X=o}Wiqw3C=1 z{A>w|H_Oua*GFl}lnKo56UM}BaRs>*@|9N2e@CSI`^{DPL!6h;N6CoaNn0LA(F-49 z>EE5(P<5IpP4B;q(vnSiAlSR+ac;LCaOr(Gm@+i+mt3J00pbh6;>CB`7hy0ISw#GDjR(klgizUTC^O z^IL#k-aZKfZU}wx+m|W3>kJpjCXo2O+H_2^nc$=!&6QjIBwmyI>6NRURM!6k9sJ&a z>Ixin_0Z?~272*m*mU%W?Zucyb1>LzlaO=V$cv4M$4^n0cs)~1{5nO8-gY{LO0|=D z)#tXLcz>8xJDf`VEkAd@4hdbPR|GbEJGN7(wh@0NCm%q!9%p!6_x(Gj{6+~aE4(cN}PL3 z#*SC*_TVpp-<6|*=;~l*DDw^K$OQiQ?cj2416BxKA5+&aC zqs`dA^u^ebAT5(dB;UP)%#?)`*00v~cOwGyqRNlY1CIWEFtVf3}4vQJ~lg@#`x*$bbc=Y*i*AS)M| zf3h5X5{fLMvkozHl2>5E(GJqv63qpbF2T{qlF?hd3zOUpaIT|1Y9 ztKMPjM^BU}4yVfJ zA8&w6m;+w^4k)j6373y~j;BLs(5yf){Py`B{UrE{^G7^}n&=gFG*JI|hqK{lx1|+1U2}IR@!;;l?H(ZuPQ-M4vrCH$IV|j&8rH z?~_0jE55_Ltyjk}{e?toP?}up^(Oqx<;>ut1STix7(J=IpBgB7fa_!_kolTR>(fs_ zTf%SqmIkRUl4zqam3O^An>SKf#M|tN;H{*6`04*PV(zL= z%vB1;hzFlhwNV}S&3uY}Bah;vhs#he>=#WFO{6Lh`j|i4ztTSyE9n*6A4K!eGBWf2 zT&B=!8~10q1@r5|MylkuAM`RB$P$6kHE#VM64e=D@mbW0<}YuDo%ZWUgkvY@EX_lS z&!P0fJ2i4IqX4`bY~UC-57t>#VymkzFLP!zPBDDPFLX5Jrz$J=$CQm1uWAAq!rvv*^3pMTe3WFtb)m zm*lRIK!d5?^ns8+XV2y1$~T*7cyt}EjT0|hme(iIiX|E12dkjBucN$j`#;usT#;DOA zE`VN-nL*2Qh1tN;!_;0za3Z>8VTIji>`9-2ITGr;&9Q3QKB$2PLm$C^Y$?+-l?TcH zdZB;%8i*;$XH_E=pl+@g)Y&S)=OtU=R^S5$&&q(N^k#C#B@OoqpO;bM1{}XDm1dT{ z=7#%fal)e^R1c>(#`YdL?mk4Nwj7{8Vwcf73a{vI^CL9k-%T{P)TRM=lNLYThm-#l{VcaO*>PQG~Gj-urPUVttUKy8GPNZ+2lnOI84tV9w zU^Vs`D{&WCeVt*}bbKB=O3je?opb>WeQ7S_@pqd3=P7Re+e@z-bkmo9y;S7KWV%rO z6g4=$m6?iB)Hqj`z7T0Aa?>Yqs;o0ne|`m})jCmD%N!>>Tt-c@D{*_nGV~+r)TCw> zrg$&pr<`lUFaMp#!8^jUI{`p8H4K*P9Z<6A~?BD;*|4RAeAu@sx5or^vZCE z?b*%}n`e;!cpKQ?jG;jVn`y(SpLG6oVOJi#jfTH&ryZ<4*JrwfwnQ>GV%jrKZ&-~+ zte8#CObMjd15B9U#~RG>%qCj9<2@~C*o9)OKhDnhNFSCd&^OPJfqW3TN^YC>zBuTh$SB_)EparfE5aJ#Hu8}0J8&fFv7AQrV2gY zB?FyA|8hHu|K~zG!@OxvjwlW9I!!-G57L-lZWc+6l~hZA7o*WE&x{FpOD<_f(?H8u zVr$UG$fQz~p0SrZnDwLt@(j6Kb&GIR-$)GHH9$7VT_~v!JB;Uy52NKKTV8C*OD6Hf zJ6e5h0epRB2AF;xejmJMF*xEbj?owRk}V!o`ROU*SQroH71yb=gc}_D&f@aX?pSxX z81s{gak}hEO!NJSOv>4hh|b(&Y%Ec_7higtvqL@=|?j_~W227JD4W zdIwLux;hb^*T1Iu=3^|TmX74qtF-9}nY}pDb}V&YKg8wOKBu`q{Fv^=0W{Ld3@5yb zq`n_VGqcDya?|ezj?~g5yqPwLXb+O%E)y#DU=VWTv!LtgQSkEK1fA1$AZgTB;@vz7 z=UJ-Z{~Mae{8x#uZEpx2qfIn=;Sywo`$mNGSuFJ_=10Xoz^ z$nT7WB;!OrNT#fYLdzO3(>ViI;Wj&Uns7$<^xe5L%Svy|*`)_R4S%@hC2DI|zHar?^g6D-f zKEHzI{8MHACgclln==+(;szEK_@0D_%oG~@nI(6|gj$$-_Q8bxCa^SaDQ(;KlbBf6 zlVO+)zHwh5^jILPZ7b|7Ds`dLW+&WNvw=%a{~#l73Vm;EiWXrn@%p0Q{IbYOd~9yd z%R3wMvy@)ogp*J3_Kx?wM286$Xtg5RO{7Z%#+37j{gh9*g?kU*MWZct7I!-{sl96? z9e$b1nTT3adoJ7}&7W|3dyMGJ+mpE5^f^Q#Gn}jMoDRy3+i-k|1{ts|Ag8v>C$q&~ z!>QZh@bXn1JALyB*5u=Ac51f`Y0^}ISZz-v3WVsb*^ISMwfI?+WwEp8r0~us&701C zgr#P3_^MNqpG7OMptPMXugYhpE-t1^J~*OYVmkf3R+%2%@ErAB52DK0eyaASmv*|< zQeW%Q^h2r?@mBpnqb&~5u|n2Q-S|5NpD?qC`{`2y^_paS}eNZP}9@&!ahih4&%qQ&Mtz4L#fT zADQ^jn(8W_=KPw^S(FLqk-Duj)1SDRSQzf+CS3o-I7PY9t|iOiipCZAY;v45C7nUF z74f9qZzIeYwS)9HwQ@GYC+WKf^SPNHO}!pD_7#HepSM~;14YmrjJ(o?zFB$vVNu zUL}&o)IGUM#KOj-Ua=`FcXSqcpKV0Kp8uky55f6}`G0FkpKx z%C&W(vi$@cv+)8-rzoR#NFFV$n?@J!pH94rvT0?aCOPaCMrK|-Oi#{zPXj&vqv5H0 zNI^~+jCwr^%@U+(AmfD))fuF0fxjht5{}%Q&!Dq0-yBDAT-bFv#O%nE8 zd$=AuU#g_Dh~^cpE}^4;bMbbc8P6*Z$ssjq;=k!IIk9~s*SIf&=DIi2rq>?i%x!lv z*K)GJu5X5kt@q%pog;~xL5FDL-@&Cm8#t5jXrcfVQ5WFj@X3Oc>53$zxK%(Z7v+O7A9~ z-%rtZgYNiTJsA_uCE2>aa)k?u|(B%m%JNAf4 z->{3bjxs@U$9zV5lr+w?oD88pLr{Lam^Hkzh?PBb56u0p!Le_JLdIwgD>1SG_!mvw zr(G5N$UGywXxvJFiNxcy!0E^t@@V!=g$^AqqF2wF(XF#>>Bg%yB=%V~F!qTem?Bw4eFviFT&hFYver45j zEnT1f=k2LbTL#Lfia8n2k^La#%sb7H6 zHOAzPHgZvGLUFeLbta<;#fYdzL&|qKShe*feV?}p9+-Xt z*^m>&IV}Q|Iu6l%?^#OLFHMC@i_Wvln|?v$mM?JAZ48`=TSG3S9bw4DcA%OG&Oz+cUxtU2|x9iM6KRS%Oj4VjQ&YZ)MOFKdm&Hx-=gG9 z)eDlp-->qI+L8m}e?irB6nxmd9}3ZnRZHFv)n^xw`6KqwMeQpwsp&u5KF^J`3Vom% z^T&Y9qHI_tE(3QW4zUxmblDk;!q^=p$KcR_F}%oH1cBH5V9NX5%=YH97!mZ2HaiZ3 zLPir&Ydb^2^8(0>5syjH8p3?P)l4ljZ!;Zt{xa2L@@Q3xK0UBnkwmW(rLX6u;rOQu zY17JD8aCz;v36F4Z1ZPm&?rkb&F+Lfg9V&oa~>X&5l4l&|HwnrudLWP3s$x8DXZh3 z3NcTb;r9zguNSz_@l1K-LAok7 zj;j+{Z&7tkjo7QqrndSAxl#LlnB_NiF}bttif>1@kSz5N^qGe#s`LnMfVb;VR5}so z_;=BSo3$m;nQL%_cr0F+x0m^SISiDZ)zNp4ehX~QPeW!alm*Mn3gqpuDax~B==`Wa5R-e&J$-(jEE@ln z++nQAjXe%jrCOOIU(&sU~jYN0FX~_i<4tphLGXpRe=B>n@_`)o~AR zn}}kJryjaWRFn4R<*?3_BWttO$c^(;pzi#5h*-56KD$&x?lmt6`85-66da?{b&6p4 z?=?jHP=V-&d!fW=BG|rKNj{ywK#YWY@tm>&Vh&zR=$4(tTq&Qp)|L~WAC634M=n_^ zGLtLbY|d}^?tpnoqj{MMadxZxRn}AQJ}VRU5^^h3p?je_Yw$7w3XaQ=fAiZ=sx`$T z{K*`8z~cZ(){>%C$Bn3j?GTw-ahP1oZ|4F6o^fW010*``7nd~aJjWfdW!@`WFs5}~ zIO$dc7vt{6JuDvL3NAUIm%|QJi7lfuY(|g@eNBty7j(F;)EHPelm!u2i&?o@Ch)C% zG}MiBX2-mpWctvxJOT>z`P1isXs$5gFyHzS?aMbAzCL)NW`r4{3qapAYSsAXtH z)6+(fMe4JNy7V|&l)s;zADPMA&<;d3`Kin!?{i#}-&0{O<%i*s&rxE*Gu(XlJ&AE% zM$H3)NYAHz#M`5jUX7kWm)c3<-0*x7Um1rwnajxBbrFJd{xhpySPaeG4&X2Q3r3Cm zNLO7lWp&DnAToIrjJ55un6f8`mUg`&T?Z_Q!M}34DfbW6NI637KXjnQCQ~v{^_)DL z7DjFUsW2@new?l20{XN{gI2uKr(sTO(JvwpFV8=XH}6HEep8dh;Bg~z<*KsajP8d6 zuL){w3CD9ycQ8xXBQJDYj-fI7IP2&jah)tmZ}nA?xNrIp#|5+Ff}gT#S?=rtnFvsx z&w|=gNf;|sMk4}41SqSmLL8@gPRW1)-iQtTP7+A6F{byLB$wm`tI6#_0%uw~y#h^p7TvU&IA#oMv8jDw2u!V6BOHOssy%aXjQ)gr-#3{m-G4vbyb!b=DGc$w-5e&)SX=rYR&*T*a4 zGxt=?Fv{Xx1PYzK$~0=f`w6Q`{aMjP=@6((*pZ`70{RGkM-^di+_fDm4aB&y zMw@VF>v&9*TY;l|{1`i3XPTqtK_puy!sysuvTJDu$jhxEYFBTfT-M>@1xsD&h73>Q z*`&%{zB2YhS(*WetC0smMaaMJfEz zhnM*1-h2E4C#(PE6vNc*fW9ne*%EnnyuK#nzI#K&XU?W28`9|DYbSEi{vz#yIfCnb z2q%8OLS4yxdb&i0o=yBhG&3b{9|U^xYfDzlAgVJ|F2RpG{qcM#PhJpY-6f|q0zC^`ScT*04}_7Z7AYaH*aUBJh0wdc1V z8iO8-c7m$tZFXcvDLnWq3H?Vu;apW?ynQ?Y^-hh&4Ly@^wD&38UtWoe`n)k<^>eag zOH;|&>E_J7hqsu*Z;qg8c%2-bDNaW`nn~Erio~O*g$TYhGBmn|*r$j?mFHh}rRM=S zpB@aY>!qRhK@)sd-2n}|2}A_ff@S~#WzG^*OSTZLO>6lnhd&AaR2Dz) z!+*9ErUcvpl_yUy-zJt1&cF@^N$-4 zu6OxipOaz5hHzmzo_z?A(%4Vj*k1qpw#syI2Wf+zMcP%q>9>tOw4LfZD2v< z{s_8UBbZ(~YD&+Z+DGliZ3QyW0D6~wz;9fzuxucZ7tgRO7q|<#*QKz(VJy3}zl)XB zY=k3us|4<|8ICdFnB?_4xL?Djti-KOn6z9B60SR<+qAtn*Y6zjQN@dvdC!C=-FH|K z<#TXga}>Qbt_s9Q?gI6WnGpB3862a=LHOG(5PZ)ZR+@~2-5vsm%Dn0cUOrcRp2P7~3foI7hS>uSw;FKl_^AhKS_$^~NZ}+_-UD<7@S*j^Q{&Q37*Fo&6Yw@P6-T)4wodjti+4%wp{x)2WoF3%8nJA%&NWeB6ZI^ zxo!K~&D+CdAl-JR;LWv$%)8eiX2&ns7i|c;^Ur~Fw-rdAj)&Ib;jn$Sx#kAc-1am*LGK2f@&^o78c z{ww6O-@A6;Lyc675^{Pj1^#sVN>}nt^)HcfTTt@u*dFM+UIOW>&ykC5J?Jn?3?kh( zLcuR9D6=2T8V5JY>N=kB-W(OV;5W9}XiypiU?Pc59q zD{boHb>~W9L;iDYR5K&D)79wr@xHvsVHxO}y##(B zKe$r3*RYiCL>@!4Q6Io;<&J zK{Rh2;f{@M^YGq=I1JnV7$aU8u@e8uvQuWvgY7H!z|p9qtWItv+&wg&wZG`gYL%{F zm2?f^rIrVn%sd5KO}&8GIF-~Wwo&bX8g9S;KKeTQ06BZ)KAKIsh&C2M77bJWqK{WF zP91rQmp}B5>$HAMYrTy?s^dD>_OgN3+>*j8NbSYC97FtZw38q6!vp`558|iKRN(il zcfen3$6~S7G)$Lx&nz?>WEE$xVV%e&@_U~<`P=W!iifJfKy@_>^#s~(H$Z3HMYyD{ z1|i9rr1{JXI=fkzQDAWm~Qv-!=R4RU)Busqbmh)jf>iovu37F+O zm2M6{LxWqU!u6e*;4^s~(UT1n{Haq|HTgXFr5n$VEt&>rFYbc!w~de;)&y!-G{}V= zVj#WqBH6Reo_KnA(#NXh)THVZV|&6CBTr5g98%3>kDoMVO_8Ukhjp;x)?OMIeH$fH zKZ5l2ZFphnH0%x*GJVTy$gj{c;yd*St+}a)e?isphfsfGTmxax$VccE6W6*w19qhVn!02HW+-ZDGdgj)E^)++y!gm3w zC_F&RHPe_-Hj9R+jG$%0yys;36BO6-w`j?Br>AlpX!cKCJR780qG7v}42u0F6T&`Y z`Rx+Q2kO!M-CyDKF>hE?ahCfha*!XNy^TNqxs=~wqQ<+NT84S!SL3yVF(5C@p?7^$ zApc#|!^Q;PJ2CQVYJ$X3p;w8tWg%258HNF{9YDD)CZ??d*?0Syhp?^ z`j)Ol(W4t3z2vwR=^gZ$NBtN1l7(gZO#ixM?#Z1&5Uc)(2mcIXYKSCB zn$rpL>+)Iq->XNw%VM4vea{FwaF#As*q&a7;>28H>^qnQ4 zDVm$9!_i@Gj@NW_*s~uKzF$G-l+m=dB9FdLc#YvlSgy}k2^5zXLd(i3c->OYTJ(j1 znaoM3xiFHQ*6Ypcq_+^WaU3>pUxOO0)4+43z!Cd3gwK{2@}kuPn2@!B*V+(;L3dTr zW55dIC9JW>XDs^tOvdf!U6@LnexiBf4}zP8^==F=ki`t`4q>(^aC9^RP%KIh zmqrkdIt0?E&!d>~*EeW|QG+l;zeRlAj7YOc3-ee~gX{P<1tgcfq2&r}vExU>lAz1) zS(T69Sj8S|RyO%0_zGFC_dEAfC*w4h{ji5UJ9#2pdTY;)xUK^h&Ev_`pI;&K^EC`) z3Q%F+P276%Bo-gb!<5<`77pv9X~Cr$nmsQA<4hDW=chBe>~uhHpLMw9Z2mVVAOMGajO3j5|V5Siz;lHCyQ#$J1=|DiH{aBU;n;>5T9&FUMa9+#V@gT zvm0S*&P5pa<}u#fQjPbLVcH z?N6h3CvY@Fb1AKw>B)~`op`A|FR|!;FJ4b-!7U?B;-#io)btVD<>R7C=FU~eQBheq zS|gm^+dh#biW`ygwr-GF7XrpRcaV6xmL!P{(Z6qZ!^1*-I6WZ{9KKXR-jzEg+x`o} zIci5SapgE1k~fAGk+0yxmpr&FnZr&`lV_zTwlU6QMp@*)RmRF4htcLe2U1Z5oQiZU zhVJ#kyRjy`q{(z%S8gxP9D0<-i#r4EOEcJE=mm4l1mOgbf%Fr5!Ktu zwLRU>*=$ZH|0;KY{i+%mGsYQK`@AAw2Cu;5W%|%LDi9Rw!ywvtCb*qWrJ>oXn7+H6 z=83vPi|=~)@wUx(w#otpMY#OkJ-QTaMtycXq6(z?0Gw%dMSLPIZL*ZqV3UK;buRouCD_V zc9=4Gd+dqFVl7DHFTs+8&rq^Q@U0b|1MM3JV1w;nLKHj9hnLF1>IEF~*Yq)dL@7YRX^!0rAGc_81L8Mf{GtK4B0UN|xs^$*VaC8|Lm>8*3aT$FtWNluu; zJZLN93Mcc21TmXBR2cmSO?*U{d;f-Mqv{p9A@@~@%^@qA(z*}x z%bT#gQ~^sX%W;LDxo(UD*{d$uy(gdbcqwun{%m0iqriSGe^btFRd-V>}!FqihGlr6_&@%0fnBRW~Y-gvC$aPOC&o@9_&^>sRuoR@$E5LRwXEI8AF&UDIg@@ivAaYKG z?*5sNk#nw?8=HKCy;GFQ=?ybW?lm%CsE3>exzi^BLI;>j zqVeGSc^5HwL-B;*X>sc)z%?nSFtn+N_REeVV)P09DAq{Yti2fNi*BUgopi|q&o%Ul z*$irJH-sb8&r_QT`P`W&am*u^YoL+-7UZZhad}wE*|$rh!=yW0*M~;#`f^cvW5PLD zTXU7>*3HL=bQiBq6H!x@~Rdn zR*Hpy$@UP^cOT*xtpr)iJjfNEGQXwuwyGZw{pDj%s5M4YRID+jYlba ziWQ!Zu+gFoTOBrI>n3T;`}G2E77LuNyK^wnw3>5rRC#&|k|NQ4#X^qx&_maT@djrSp4<2r1L+(WpmAlN2$A9jbul9jV_ zh*#-LuF&HfIB%W?A))$kZxC3`*>8Z(Itig$W8u3}CD>aYhpAhes90DRrXGxyG*bB{K-Q<|%2MPEKI1LHqsOjE{Q!d31eu6hk5;KpAjj(zNl>UVQ=6IX_|~c#1pL#6gAMacIsIp1=CLVOXyk z;)RZ5PEaGIeLF_ zMwCA)$yAafW9`Vr-``10u>^>m^du_7H^8%NJ?ylRg>zCxkhsDFqQn$n%fZd$ja;0t zyNQQdxhn!|PaH1VZG#gY`$*QGaU@5o2B(YO5S#~}QCzhK&yGEck@X=MqI?8tR4JOl zOI}p!C?4Fk3bW6~!Fe?Jy*@|kV-}S}n<}qImV@BHWiUZ+97udR2Q%0D2|Yba$Q=;@hdvEK$mPeN947}yE;~b~qzY7R{sGtZ z_JBssL-;GblbvKt;Jcn3vD~i-%;?7?cHA4hv*Rc~df*1u)Ohl1F6;6e6!dswaX$=I zdyJ=VdEgYSwK(fnAfxcX5k`*R4g>cRU~6&!#3)Ha(PKIANRt7J`)#m9*f&306c1;s zZi2=yfqC98LlO#i!cIqDm|k-lJObMx>ewkLG;9aA@rU8|xT%oU@f-3omO>A=ZYra_SQtSEt0~pYa*yLEGU~T-FT7gA=LWog9($5k?14)$iy>* zmhAlrhvR-i)Z!v|`_d2|{ZWT7l@u7b?EsDaHBh!z4fqH1p>5+6;BBseS%M5{YqSCV zh&os{uM5OB&V@6fN6DLZGf1l44|%1}p)6_$e(nu{ZQHJq4g2L`yzm~jE^aO!tT4lX zC2r)kL=?7&?cDrQdo8%7Cv70z z{f6ww+20|)q5)QH?1Ypx@n4w*MWI`g$2Tl|6xkIQLVJ3F~+f*H~;eyTB_c zQgW;H3AwQI6?vdnkIj93xU|2XUfvi?CLS2eO4d}f(&}AMKCvE5=efXae^cn2oehz< zw}bx54mh*X4^G9N0(*^Lu&kg9cJ;m_tplGK?Lh?)`Lh>Bv$3$jClSuR7T9ldmXw&z zmL%SZF+_3pLinK~oYNJ^-E%)hMvI$Jy%%oG=xuW7qm_(h6PNP3m%i|G_I9Gf@C4+- zCedjgThaJZ7Lj>=n4cQhM6MT=fg5pxeW@x?`#gyB&8#5ZTZZ9zZUQvb+&8~bv4F7{ z5C^vp+MwvMgG}@&7iO#0^st``N%dS0;+dUXUDX6|h&~E&S4<(GL4|}JNk?ssg|ybN z5H2eT{KMx@(ah)-E_&9Fk$c~9DXSWo$yq*lS2h{bla%@0!S8uTUoDJPQiC%Ak3dz( zD_x)Z7Tc$p0AKW)-rrIV3%0V*uILZBD_?=!v-5!7weW(y$vHh8Pqt-rqrtU0jC`O< zPQ`YUOsmaQ-((rt`mc^8Upr2cY}b*J^@d>b!`h^MSsi{U)56akYm2+@&*o|zJ25IH6OvZ1z@D#) zaPglC{W`RQHeYE2+sGo4QltS{kwGvZ?09c2aiRJXCFrvSo6&#h6<+?KNI%p>mE5;@ zfr@o0^ww}xaoyxFI>rwMl* z!50(M6NcBnKckN?_u)7DXzF)7AD@OT;}uSKVb&}Ql-%|Xm&ppumpUYWJ{5zMvyu7x zbvww#d$UOMQ-A;iIr#E^A*85ofE}&z)IEvdiQ?T@-PMD;BVBO%u3ma`*?d&UkL1k6 zXEW6FEmQwFh>5tjo^fbx<$^rEVo+WhSEH1HLC5l_Mf+J&s^-GI_IU#mb9!OgpcPF% z>yODZ_L4|7vj#1#v|wFO z0$Eq*DCBg|*GsU#3fUSy!7Mh@x)2cq$xDthB(8_LZn<6_mX(-+1e zbjuqznqX5)_4|V8CV2^}9N$PoQhPw>3Ipz+j|=&r&CoIaE4*n*frRy&8RpPzVmXH) z!)BGF^pOlYTM518>3~+z90skVpus#qg~VR6WkCYiXI@1ok1$v^B3j_ARDy`dGYy}NvHC6hs`A2{Ghl^*)(^d%g7uZ^}xiqSgpUG(@-f2LyDR*-+U zljlxO?FeD{oxF$}RcDns3_=1KQ_cirHr9y?l>dJU0Y%U-#qc zHQFeBA_OzDOL^6}945ajh*xrL#hiy@&98^wLg_7?xVXL+XFYUh!VcM5oF49{1-Hd{ z(S!_q=yDFj=dH$ZyARWeWQzGrDH$|=C+w+qaWp`E3X^~D8+k0JDWJ9WX@++I9q{e4 zsQ7z^tcjii&RaD>Lf{S6F!t~^UyYrj@{U!V-vNnh_1STA&$8JAOIaChT@dLI9B9G{ zE9uQvUdGXa_$s@>-H+k0|Mqcui&=(4Lr1axBVq>#J2j^n_&R8;uV5vWezJ!WH6hL201~H_k>f!kq<8C0ociS( z(X!43tI#UYSUeNSmmg!2Ze@=HfhmTN&8#M@BIFN=kju1oALU_ZFv2oF`kyy zrRVgvVr!f=Co*RyrrUm{`8z|r#+4h|MsKHUcs5-jwMj_p9CB`f%j%8n@7c(K* zqNGi!6?ImBCv#8CXsA(2hj`;uH1p>@yv6^Z>sy6jyzVHxai|aotv>_j_*uhapH9Iz z(M?QeG)vg|?Vu1KK@8L+0FBkbXQ&5Gsk}$iP%ZrVeHir)8Q_rKLF^t)--4_C6~CEuV+*&*Lp!H037Qcw;&= zeG~!m%|amO;K9~9>X1A4+UTF6C;0i7Dm|}q53giw<%|^qxoJW-@a(yKN#TA7u5hQ!r{kz zp}8k)*FviM1$3SHfm5xNz*^a@xIK6^{uh_aN#w4^ZE>kM(}2$doaBJ&&&}DcQ(knN z#U+#%JAyJYf2eHeLn8Yil&)Q+LYozL(p$GJY1qP#nE7ozCKyEFvt!OUlh<-sh3-S6 zt^8bk)i?}3G!^$V3Lv!En7%tV4qv-&!pQj9;5;iA4R3_Qlj<&dAY&{X2}o;@8CXFS ztXa&n+Rdu&ZKV>s*3d@b%jg*#gKj2=QTVkko>_35&i+pmSM2}GPMc9qQ#K0dlefJM z(~V87W95NX;YOToC&U_6)zEAEhfzBy43!oTY}U#|n|=$jYU>Fye*YRe2G--5(aYDL1fO_gEU=P0ey%wHPACf$8916h6C9@)QH~7tDymlw$Z_T(!_E9 z2Nd11k&bvW^qYn+9laloX@Ulnzi|$Cv`;~)f6ti`9dSD1n~wUQ+wte)2b@Lg70iuU z!RZ9#;_f3UARaaoPJfy}%zBRCqUut(GRS*Gj(0GkNl!uQLMcUPCp$)BnWL@&D4r`v zPW;fIBKi5GmU^In#y;A{J!f0n)!5^uw#4#wBu!nW&mPabg<|#w^!W9I4IyHF=+Gv> zqjk^lqt*gU*r|(ieyzjuzg~DbPnQsde{}AvWVFBjgtK_Kj~oA@j?a$G#qaeO!AXp_ zJLBq!>|I!MIpSv$JN<--(sXn;&m!-i~#sR5YI!D@HLf=5OiJDWUXwQ$7Yy6vEG+NAR<_ z5axO>M!7%hFzMn3JkYj{R?qu@O-G8kF&lPs+Ll*1v6RK!Sic9@sO|+fJKqb8@$JMU7p2W_!iDBUJ6B`JD77ppTIij3Ytf3L`&u*I9mRNiiljQ9`dW9ddvZ$`8}D&F4;&Vlo!%;#wEq!2 zKlnm0r)@PfY?KDs+HqjRb7J*Nicz|8mh~f!<@+5?G%8c4*=`@KyY6SveeIW+Z0CNI zaofOpWE9dgo&WnlF40E1fW7c{KmF5@j|U77@~j&jJi1Q~WkmMidF3n=Y3{(u&0)Cv zL~X;)eqmfzg*UUYuglQa-o%AV}0ig5elFfy}xUaOvU&DE!j^DHRmfpOz!V z5|2@3xjt3=Wz1Y9QUd)`8%XGt5F)p>o=Mg|3E_H=L5Fd-@>o9&gf2`cQUAt~zn^Z> z?TZ^9EI|uIqyjy z@ca5~l-?^s)=3Yb(X)AQbH2M^F25I2xRwg1zdADRt`4XZF%OS)cB4^R9%OaDB&Q|a z!E$siQ+%I_{&*^IGPwd*daEG%NFHI*e9$0n4c^<_!T!lCfkh@-7<6C_9#CA03+571 zQKk;1HTiI4djZIaw2>ZTBPPfHFgYdjlWa;~&zyX%Ouwv5rgnppC@}KD(AK%=nqG+0 zzW{n=9pWK>x%lSqMsAvUA}3;S9Z&w!#48eG@rKR@-Z!s?fqd24r5Wul0+TCr9hb1;!m1-;!$5F00X@3HoXDOs6Bd+J@j9^%J;z%`#MYxBw5FUx%}l z5$`YW<|O6%u&bHngwIk=Q+oy{!Lvbd{{Ru~=)t`$9T?S72THz{L@>rvAi3a{VC;)7 zQZ!&fBeqPSj~6bWY4cs_@T(T=e=UwD-%D^JL6@=ED1g4Sm!q{`S?ZTyMNaE%q=yEN zkuBF9X~Fkqa(&e%dU#S1zkIzxWJK4nI~@7%DT}mK`ZBMf9mU8!ZRpkRfi^j`twtS$`qh(3dWl_~U1lM{}8RDd&N!)e+3DKP%aL69213}$!iBHVvFA^N-` z*jrh_?ER_qQU70bueL;EsS1>xzY;A2&Cz7#RMemU1|@z>qp4%&kfgDX$t7)1dd7cm zgSD6|-FGj+TK={$F?nuJt!);wL%U2GTEn)WQdtHy^V^I$nof9ORRQ|E{)XwY50Tyb z5d+2x;iPNZV4)meYgx5$N+HA8EBTliuv!bd?~TBz!ZdiBuFYpWBei{CQ5AePN*UFXs2 zNB3}iWhRl2@?x%@9%5>9^k{aO5ph%(rCYm9?SJoh{TF`NO*y zUsx3J4Gi0K!EC!eBu6#!dI|!}sdMy{oFT4n3d0kLb##a4HWK#4g}i=j2N@TY$h*Pq zBx|7%i67lgLb<&lW8=b@Z+p)ku5hBVuC6q1%wP7%S6QpK?)v0rEr7YzJ%}xw4Es8V z$u;wASf%m`wg+W1*nNjJ=5rhtOa7y&$70}M*&(p_*$jMM1T5|hr@BoVR7uAQ_DoOU z83-fb(&a{O)054R?@7M%)$on9}<*>%<+=CKPZ zS|*b01DPO|X+bwAx6tGXeeBD1$JuM>M29!$HaObpf?)o8`o~s@HkIC^S&L&CAN?|t zJoAzDa??BfUjHe1B4WbSe{W`IT^**|zH2}j+L2JNv7|5Q4wO#TVHTFl;FJ)T2BGzC zY-UC*$+S&|C9Tphf8zu~&(=fYsR=Y|)(J8+@g>xyJCLM}E!LISETQ2_AtYxgK=Jef z-dnjGRun#hhd$Rxd#xjxQ}`W;hAIkwA0{zDA5mfXQgjtrPTM!yqGhWNIji)PJiPjV z1bhjjYtAiT)KnjkJCZr%&f_x1%Fl{Aw@=1fcfI-AjOV$ns=`#6d`2)P8nn`j`)4Dd*zY!MIeP`1th?dGf!)^qavW1q5JmEq-l21C9yX-B@g+aAt6=`n`b|4g+$u4xz&UH60V>_$?kY)ooDTmrSJ z@?dDvL0UU%=#iVspkt$Z9)6~P&yh>3rnTU z@Ze_#g$m}7lP@Ys<;m%|=F&sdC>To%Z?>U^+f!0f`=0!>XF)OWE-9Z>2kzt7kvnZ) zNu2ot7=LjddEXX}$0CE#^^7B?o%JRcX4S)S)o_q&(?YvSPx8`pE3>a-OoP9<>qEk zpYc^N#Ydbmy&i+YHs{&Ow8(~Whw?#rZUQ;6IE3U**MaFpUEuiWF0B2OPL5S@P}*vS zQR%Nx(JP44{#k)Xw`UV&hwGsBKm~JdJ)p188POgsRnoJ&jdVnALisd4dsM=M`K=*O zTY^>-oomZL^hYe@y%B+9=hXz#Zn~r`Tmz0B@r5fhoS{cIi?HJ-f$o$v*jU*N@6PC3 z53I9musr{PteSR}iELj2i%M+4!0jIiiT_L#!u+7zvI?f=bwHzt37<2hj_VIp;be&5 zMCV1|BUlL2)U#QWDJfVyP6`LVsL>KXecEfh9&hJogGN*(zAasY65;20W}ZH-|Js6X zZyji%;zfAbc8K3^iL%7Rg$>U(aFPQnFzv*8JaBdavZp05&bbdQrcGv2o~%Z#xDia9TnyIc z{I4weKpJoBljcR=;oKVw2)D0=q8BsCUrvlEn{*9qcSw^fDp!bi;%>qP?WVhS%gfv3y}QeQ&c0^RKSL?<;b!I-Tc^Ts+K8==p}B7N>|q*btwOV#&{E7lU2M z6?${6i(u}jIKkNJK4|yo0?)^?#P0Ppn3%VcoU9$7sBnk+u54i>3TD+8i~XT3rFJw& z+nc_M9VM?n-JzYrQ<+yD{&aZVDjfT>ifH(`5rI<=tW)|-L|`kbTJj#!C$U)m>Jt?y z(8ahv>)F{*MGu#oV&EfdJZd-(56w;Ernd=W)#q#I{pSi9{qhx(qHe&fH($a1S33M{ z>Vdx^^XQcAcS(1#5jyJEA!DgT<{kH^ZS7kcp8mUUeLid&Tk!fG`@(E3Ow{?2{!f=WHaBC^uMT>gdNOa9UnLpF&eU4RgK7`H#-vP&2VN;~MqP99>U$kd+=NA) zGplLz#rO2q3lH?%HW#fY{p4i!`e2=fF5aDa9i60&*rS3)ARL}A5Uw!*(KQw1eO)j4 zEp3DjQ_JzNn=;N1u| z1G|>s`5U>&Rq-|RuRQ$4UdH_j@_0gh9UYDKCmz>CA>mgTWUX059DjB&zu#+Mb88;f zgckB=|8&fkEvM0u_ULdjleQb}r{>%bnkTM;LiJl3QdEN2RVr=h8_P4=PTP{aMc>Km zCz;IUhr8KoH8J)?b|;*?ITnNsw&5y!X>QKdUL0y0Ch-Y(7W7Dtw>^qA{ zTxyns3re=r)Ti}qx?3>Moe8IjzW!))LW;UJ?qi<$meBUBJ#_fng@)!?N)62{-y`$> zSO?cp>ixGJ5)^q)WL6iZ%6-L$p7q>V=^dQ0`dhx&IYYzctWfw@VZ*JPQ^?)&*Tf@x z8cy)8$HS?ubjWZAba+{k-S#J;ENX-VEZK;f@iLf5a`4sbiFn9=4}ovTaLFk>YSQ)@ z1 zPFWgAW#*hf?bjdinfOP{^*E0AUA?hw@hm#lm!D&F9wE^aHNmg64t#_sqo;TYtx6lF zpUbS?rk2xor=!1D5arKu3R9)fC?+?zBd7~TXVV`bBF|L6< zQNJ0cRgSRd_Rgng2eN2r1F!$t76F^r6~MKdx5$%KJ!A)?2ni~G=#`~U@tAN6H|{xq z)~%?)<$D|uO%8&}pRcs(S`FmVt8l;L9=k$H72Qjh;PixJ_%MrtRE-IBk}YCSom-04 zR1#Nz8iRS^N!WB)8>NHxvZDb}xIjvYuGlNjs(pG!{WNQ-e&BC@9>MYXlx1KQIvXY| zsU}srMI_+A-=KCT8;Y`GKwWAH_>HX~m-3~ch4BW71Gj0Nfi%8wT}tf_i*mw+GdXJy zac5FJ8{$eWJcq)l3dzWYbf-(eXv$uW5J-&M>@I)x7wY10__ZgOzM ziPSiz(q&R3G~~1hGjMb*3EL{m&S<>De4N{ZqR}tO$-6yJkd_LOA9X?3t{+TwU&7eq z2kFVe_arGJgG@{`hQ#&|bd%bE_CpmQy|M^3WfC}p*ZJJ~<)+*wg{hblVv3<@512z2 zj)6l+2d4dwZ8$aOCgYtxm)EV0p~)MC`B`@oj;R{L=6IglBRmH!Tf*62`8Q}s|9U3z zj38W4+(9PxWQCO4SG`Wagi*(6S&%;;MT z&Y{Z%>lus5(xkvBli9oOKRUNMn;x|LN_R!Cw0>{rM{}Mofq>Il7-rsy+jS4H8?66h z7CjCiiZ-s+kxkx2Wt})w2Y6DovBo%MJj=<4IB~Gpj8k=ejM03Zl42rG)SZ+_e54U^ z+gk+tq^{C9=UP-(sb=~-Eb({tBD@td%1P(9Bcrc}rVBsPkgt!)m>rKuz4jL-s%%UB z>kY?gP4Mwr9!=2QkJ)g0>zUz^(Gp)YW!?q&oi9#QyXP=x2W_hQ55AyO$93|IK~Ir(H5oV<4xs#1I)Uf|8| zZTAz`FD|HRb%G65k-#vcvlzGKHJ>9~LD!jc%w5TTl5uk>nOUt)_q~5jk1!`tTB?;k z&Qr%#_2$IYQ=iFRdxV|{8KoI-y72J0Y^t)NoGv;rKxdoYV|<+XTvhE-y6fr|Si{{A z%s!+fkWr9_%+wBwPueghLK|=P^+7`WZ#d{{2*I1ivt?@1q(4X##;rxR0Opd~Qv+1{ z$Zz&s8pVP?6^#9%L89&8Oc$75XZrf>sDT?0%?BT8#}QS!XN3a|i*KbHmznT+a+;)} z@uud9aJZkq*ieT`6c#~<8Y zuiy!WodEMNKVq@5E=SuHByNTgfOb}8m zXw9eTWLM}rRLh%aE%#all_V9>hOMOJft;<5qY!<~S7m*##HldCsOx9Pz&Xxq)@8UudM6lsnFqP!96-Nah(7r34b}sz$X?GKq&Kq} z75|N6tOTE|8*cc}zDIvCI`kYm?+svIeqM}|Q^)hV#3K5&>@dA(a{_PkebvWaF>>xo z5I8lh5G-EE^SyL^;dzBVIemU=L(L91Qu5{lFcZAt;I>5i_f-}fzt{mZXH>#D8y51e zWkFU+1#>Fp;K_|>!L&qvo^;-pXkL7ZG3NQ?g_9*Xc@z3VNuBnDN73Ku zUi7ZL9gR<_#F<4J81+PeJ|>QIT67P8PK>4MMW%H5WGxhWF&^zzE?5^Pii1Y8wP4JF zJ@8y}FYG?F6;w7{rjMi&=rWUcAnlz4iGC^MKtUd@-n8ACDm@|RrfQNej^oJSp(oIE zl%IiEctaL>FA&Mgh5mb4jCtEDcBNhc$$@L+_@y{9pWMKmBnk~rE@Uev>7vTqKX}Ms z1#TAq%^tOO#F>>{G|wv#xq3_bcG`KO*K`T}cXqQu%RjRxZYe^FUV^|Y`Kdrvf1W@| zp$RUJOU1P}c%NZMHKV_|jlB@3g$YWlupoFWMm{@9@&aa%=W7&6`RyAJT{H>)l?uUw z$vb%MUopgZ6f%x$+;O434)d|+7JXd3p2j7eWPeRsN|X1_$JBdY=vlQi3|85SMp9=` zx92h*m5al1k@M;B6GbZ7mS6WCZ!*WO*6sL z-2^;SJ*su9DaO_YSp51GJ zfBqmxn%ua_|NoweKk%Jg866R%Fsx-bOc$FCyY@Z;H|KI_HBf+CA}^qFSqhZxsfGO; zB|&YzGm%x2LE*D^=;?M*T;9j;K6SIHac%&$_gq6|Di6}7&`y}1un8(B zZv;`2n!4lpY8lB+mkE$^*j%I^?>!CkCFnfnMRyahI(>H^#9aujM3g z6GC!v*CR(dV^=&`RvZtP>KTD}2+xp^I|p5ME1}su3s8Z;GS_X)0zo;i$B`kK{71dB0a-Ltozxp)Ja;^g_lvGU{av#ON}VM6twzs9q8$Ta3!syFYi6)OV%yMC>8DXlyJ!s&7l?X~i?i$Aan69N?f7hr~Qdj*DbiREJwcFP+Q;v6oOF%1>`G-Tn1S|Mc za~4)d=7Zfo0;4o>rtRcvk`rVJ z5PuMTcXc9<1;FyplK7uk24+9weOiTM&_+6!u7)H=?PxGa&EfO@-ZF5`xgNLxb=WWJ z1i9}ogP-AcP;q_743h*hp-7o|=s8M19f+mJ7Fb&kzG$)fWvIhk_Aq76n_eMT?kbYy z2D6xw{MF1;=QuPjJq6pB+=tY+bC}0PIs%EAPXrP%TLmIrZ6Gu8H@wXGL|v{lW6py> z`Z_0wl+7MT4)ZL>1YWyatL{QCN*fdNAQ@6|X^}v9a4Q_*_0~C0H^b&sGuRvz3p2Nh z!-j>+z~fR6NSaKgS*uz}hg+Vtn0yz`{2@%&$!+Dc8%M~TV*|8lWCgA4Jxg~<*%Q|f zwd91a8M&D1LN|1M;k9W=s5<@|T!!=QQ^~$!GbZ?jC(6m3Lc?GdrzfY-7WJKU)zV-#?#zpZ1v_4#VDAu_v?re&pLYn( z+1h~2M?P!k+b&LSO$=6O+vAwnd2}z&L62<^!%V4uy2va7FwhOO>*mzUx;Mc7kYVzC zt1M6P}hA@r#r%9um z1asf*5b7EUp@{Jc`qgDN%1Bwz^!!+`wyPx_3SrF8m&xF`^)~3-c>`KWjWlWH2mF#} z$|+jT!Vi3}tdJi^)25o!H|e3A>Us~n>@&)tVq3EbPd_9^712XR+YQ6+nFlQ+V?I5sGx*o^ z^typF*j-;j{_wGXHbMMnR*XW8Vkxb@(?cyCmkJcr>)`d^8L&?+qVt4=h>G!k*!6Wa zHTvpIi+^na;-SrUUecwDpSJJ4M#l50GA)LfW~9 zr4P;+!=Z0VkfOW-TB7g5ue3=pmvQ2miiThuv_fF0`amGtpbN&4GnlkeU#gvwf#({3 z;h#cdyioRy6Mbq)H`m|6luIV4s@4KA#iCUFNE%qMM^SYx(kIpJu)$P{yx(({q$FfB zQk*l{_$q?FQJhG7k1l8RGB?xpZ6D~2G9A(=F_oEe+l;h*3Z=7>);Dx6Z)N-VjK*%= zBIvDBgcsHt&@QzXKG<6crk!644_gADXXa0V)C~m2tQ$5FUdQhA9j3jC#FvTTNEXDP zl@p)Eq|0z(uP)-3>DlDi92d}+GDVk1%h2SB79@$ShJ7}{;AE&zh3_7u^O~-c{kga4 znYltVL+=-Tlod+-W9IQp9$TjI(IFBw&53Lt?+G&}e`2#_)>+?<`3Q&C921Ni?*)VT zmjrrKrbA<}68wy*6o~3v7pPQjhYuw(;CS61em+wbNbXWZvmZ;X+KyyljK4f)7|-Np zeO!-uy?*%nU@KlKxRBorH2la6dh@=sqAjJhO9{g_6w=-E+}sO)TT_D!TcNt>u) zup9~&$g$z~c9W;uudwg-PNK7dZAj3$c+$#qWJKWr#Mk_W3nnt~pU522@mLkUESV<| zR&jwRLw@kcNL#RD{4Rm!!FAxhUO^D2vQ6Mz8wp|c|M2IjD114r%`-bKxv8?j+*Hd) zc+sH&D$U~1rIgQ3z4i_CISu-v`5dg@;SN%hSMV&Q*TlvDKa}Nl3)W+#sqC3DRNl0b zp4Z+?-cH{@+9bBol|Md_3@Kw0Vzma6T}{C<;yxT|vxJoTE;7Tml&VkcgVy*abUV-~ z7^A)ePVzpJ^;3@uY~F4VR2tnEysUaIU{y2(>#jz?bhXDgV`CbW2xORd{7iVg<6jy* z#gz=G?t$<-n;7r++v#Tr1?Q?ju+9}F8D)j%0PgMRn@AW=8k$v&Rg0CFt_m?1s}To0?l zdG~dKWBp}<%=|z>P3uFOu^Qt9vAGKbXZL;<6domTZO?vz!c1dm#<9PA-k2uU_|3lyOH9|0LSN6NQh9&m?aGzmTc% zR&2?_1+aCY4a{?042z!D3S`o1;QZ$zn50^Y$~^ZsHmr|*&NDRL#~i_K{{7Ghl@S;p z_9A}I`T4TQGiZ8LPKKWh!UNG^!EU|dg5piSg58I=!3X&}aPrkRV5)|oq_-X}uUi1( zz5z70`6ujoR}BjHFEZH*5%i|u11f|zqR4+ZWov)JiRy^y*=LIyQ4EX#Zv*C-;|m3N1q;k3R(cEeWsY) z7>+(rQUClcuc6|zvqI*L3K;eMg0|PCg82c(@G~SBlA}YQ{Lf-IuwX5$OgRN=8o6+B z@lx__ToYZhW-(d0btkcME1;^+$|?Izfu?+wWE}O*!(?4AB5S#w#J)9R{$;e0YwZVk zPs>8c`ys+~xbH+^!4_l<8nKk;Bpo(lxm<+^?j)Zr)mYraZJo7@6Vd7A<|#4w(@hwZ zdB0^`Ujz()EGB9uasu;3>jkQ#eIV+0me8)J%#sc*2#tCL63VYhS57<>@a(>7FP6l8 zG9otP>rnMil$A|H0uh_7P4swl(&o@-y zYh?{ANi-6O_=Mp?cOd1w9--;ZW?m~PN92NLQL&cqgo&Lkzo9Deso<(!A|>1$pq`%m^0x3gzAE2&?I()Pn- zT5J^_aX5o_&$!^phMoA>PXkxzkLAX1Ka39BPmqe&bL%b@ttNg(PBg8dpOz{Zki^+Z zM3Cl2^%cM}p+arL%>rJ4li9 zY8n)v#~e&J47M|qh)2;8*!Fr3Ftc}qs;3q6zWfx1+>fVu$`e4$;+yrdrz=qIb}QCJ zRB+0AL$sgw5z1~emO@y>RXy{hx!c8#oeA;GFeLsJ!6&W8-;&NXSpKXs+>V%e%Rrk1K+UbsIS0l zk^idEL z@vP0QrV#XDAIZF82u2-G$&D9@7;G{O_V&S~mYoNRmJE(TSx)VdIplg=Bg^G);3PwD zxISSk&DgAjzsshfU0xH8ow9|S(N)Yv){W&tgr9T7;2P(nbDdjYcZi#PK)`vL-NOg5 zWvKb187Hj`MvZg2^xwMNrvYc!mP{AcV5wtTX9WMD_ zMTsas)PHmxKkh2!+&=8$GW%9?wtB!R?Rv)= z68Gcw)U|sf%>SZC?mq~kR-dFvSBf$lZMKTk6^@c{%mU>P8{z7Wo3Q-ob5NJO4$8AS zA!k7WSvB2?JctOj94GAncYXWe%0n5*kTavDCIJGkvS}VO*hH zG8b>5%SGG0<7A%%bBb@4VNsPEUJ%c~jx9#m_}v|?`sR|*n_;kIZwS91UPTr27Lvf$ zMPM~$C%bi4e0^$w8flGr%w+ES3cBoW7&CS(Y|6d?vg=1c&qf&B?|6_e^70LD$x-qs zkk7wvmZ7*r^t0kYm4wIG3r_j126I`PuO^+;7r-!~q z;kgZQIJNgD`JrY7yMkR9?&EDTG=B|qwmK7Rs#x-LXes8zS<>Rb8{C-K?HHK&QDEM_ z1El*UfH58qE*XmC+YB?lpZP+shqkcK4*r3(Bu&_}s~t`^m6I2|4nN3%5L|d3#{8KA zO6RXx`+AGBm&AO?OV<>p;vq-;W&~65%X&2FKXr_r-Hllze8xerBbI4Ue82k*W<9gC zUY)szw6@s8J@HI|*n(_o(($}OOMM!DcWkDg`e%^`MZv)BeGK=fX7RXYGdSmy26g&5 zFxSB!r1N=iyZm9k!A;>cm2*MzIzq<1c@TVX63KtKgH@c-L%K&6GHqcO*u=g6k@PPG zXx?4Hq}1(TYtkjjQW}60KaHpUS18_67Q;tt^l@;H3^%Vlj$7=gj-6LC@M@tSk%ab5R8HwbMvIhaNmgdP)Xn^18w4T44WrRG@aL2uwDehg!WH80c<-lrOW{ z>}o&y+W7;|XbS`rhJlIyhRIQ>_hiHJW_qW|n2dW~MMK=KlLMXl^*;|(&_=P{I3w)^ z)p*j!Rvk&gkmygiq<0_Q=?&!MT~j!%7X#efou-^!#|(UQIh32CP{(O5xJozveuf*C zt)dxo-?7?vKSJf%Akx$NkhZC-)9&=s%)2EkA!uzYd*ISqoV%GL=bqgs-@U)lckcBl zczc?JjviplpE^^APjPftraUprG-1wcqo|)_L7%OxW3%n%lQ6we-h*F_44a6P+z-)L zyIWEB$p9wsJK$E|FPy}hdH7^B97|K9(WGkxW9-&(;`W7@UUw0iKgVH#vMg5d8Q$62 zZy;LvJbAVGD_rn<3Zk1GA!GIdxU1d|B^7Ugbk?HO@FQl_DHMzcb!hi;MYhaFpBx#e zr+F(K8K>J;kS6$TNTz*MoVqP&0K9R&#ouBB(U)pr?wPWCMG?ixX z{-i@PGnkINRZuvt26T>PD4*a=lm7SqpBjkIouKkZx5j|L~_VYY|{3biJqV%Ih5?PY-~O*L$t?g(v5V~|+$ z9I5l(^q#RINyxrS|NcCVH)aRX+fDscJ$??UQYggaSuR}A^isUl0(f^{8Gf#H!dHn7 zq**D0JT`n!+7^bAa18@?U~@a`ohDAE;d^=^>@wAtoIs7scwMaK59WEsepLRojxpVK zm@Sp=pvK3iN#cg&Flkn19A=d0y1SH|1nc*&7gE^&RJ&Sc7t&x_JAo6dhtE zv1C9DQ(uJQ^F0a}!>pr?yw3Q1UN<4(w4-ON}3oEEzEyc+zW9X62sk9=B_t{9Q5@jC+y8DI-Gx&)zJ#*`bMYuP8toI#~ ztnD#w!(VvxTb6vddrDxasKm)8pX8#{Te(?1hw=DIE42P(K<%H-Cdrd;(G!}J==M+N z$kjjfmJz+pjAup|N%`wVuGu)z=<83IlNoOG>i0t8Y^cPp8j&I4vyYM2qn<>0j1SA4 zpofHgdQm+kPz&U9Vdsq)yEn9w1^)eVUe-CqG9vI>0{+DQZ@-B5O z?WYdxM3VQ@6#Rm}vrc3x65d~!_R)}j;eAN5SF~yF{cO@$>I<5^8?9SD3R!t9_MjFT z|HytGaoRiFLv0sWuy>T@h?K^4;-WGHqCJvevIPxO8&mZ<9}P6?HeXn^e;)>cnn~<8TlsU!dzip z(I7`a(%32Ug&#qP@6zSrp!Hyv(UZ{KeN|n^7&w03v5T#4Uzf=!PE|pz*IC zid_8#;w=~9-rzmRVv@-PMNib7l}a}rn`3=ue;6}b=gLIX<&kTh??C8_E$usVn#p}r z%ep-&CbmBU`Rra#;=rrfLT~*?;(i-IPUCx!*}oC!&=~S%juh$rM=(RO7Nm;n;LySY z0%`vb^uUQS44Xd|ty^1pmed7IA9N;KkDcMS_$fZ`ocFyRY{psIXCS{$350sD!NkN` zKHs<=#GiP=c(bGA{M94OwaPWP*mN2j^MjJ)ZQp3!7Dh=i`=`kL^2 zFpt%|zHTRa54fWo{|+3m`w8{2hv^f(E*O%T3K>~TVTZ*HfuvWh)kxH5(5YVw*Zf|> zxSCBMcfyMk5x2#xtm$CZ9|n$&qf}HV54ODNCG3Ak$%|LhVPX0sSW<8frYt?r{&V?D zK1tc5#ZXs$(=&hK>?p@3Z4)9Drymk0j}a=XV9s0$ZlGJ2m{5azTiE~Rs?p=s6H#G( za>MEgYGg3t3#nZ60FIezfDE4_d*<;Ys4jB>kA%(ew6vExWTso4e?0(4(tnfvVZJaT zp1-YZ0JM`7hGWD zMBCUc|K@>~-(7NA9UF|c06kLSMLm=*(pOI(ql~Q~RebEso?P*sHvQ+uCXS4w+rEgC z`g{MuDse*o78_$Mc?bS-g($z_9F5V_1V%FxjOHp~lKW|r_2~<2S1O>Ud=EKjvK{mD zBOqz(Wc+mM6Gm<7txv2@!F3VUU~Y4n^b{4r`tWv`U(XSXfC!i|NtgVpxeQZduY&11 zHFAAiGF9uG!d{i=qu0BOZA{bmSUP_13}^ zF`hSK`o`LHwg&S-b{{4TcHmfhH>)@1hft=$lqyTEhSeScFmd-xGNKen3|^Kqi|BSL zVi$>WW4cg!-(%~J2UVm}V+3rsz9GjNrL3<73So}81Zubr;_7R6+0HRpfnR0T;J9!m+*Vv65J785 zub)6SsI10}(NdcBbR0X|GLbAA>kOK0k&uFEd~Y#G-AZO^ze~{9B9Y{1m_0pN=|k1iEvWzMAojw796m?v zIoTaL3&LiNlH&YfX8MhZ^o*AV8Wq2yXDrN7Ve3W=cr_hI+&sBSz6RJ;dYMxc_2KkI zMmZ71U)(C8AUxss7PcR&huEM4%)yp&7%@Hz6?&{7fX~;EFWL{a^AOthFM8H#VcPbP*i!?&V~9 zmGF^l3Q>MziLyL@?!$*!aMhp};-B4xH<4-Zb-*5$B$Qa6J^sQvag#7Bm{tdAwvjtPm&xE_e?*x*E)R@h}s<46ga2yYjB_5~JNi%AJpSTp{|F#G1 zcuykMno8A{@%)(O4&-c~RKt%RJ5-xZtaFCmG)(k~C)uM9h|QIcWbPT3?MForOzt5y z>F=RVYZ*vQQzn)--m>}s3d#dp4oZh_-8rENgrk3j3#^LF3HsAnwX}uN81P&qd!Y8s?%Vx7dMWg7* zCF{U;(qhamnv6rkRq_2O3x4^+J-kY!1V-I*Lzn6HLY{CrJ+nUoleYI^kL({TNfBf4 ziLbO}!9*H^Ln?^AWo)aTMi4DH+=lNckC(;}tu3-Ll=hrcWU5T5tzbeWdvjv)XY`B!>zAikZ;%RL-qn zB&7+5MF|osN$aWxX6!*@l6iL@iQlJ7bj=rt?VV}}fA~Ck<=jl-uD=0Iy+`CszyosY zZ5xCL--;ViN1*4}dN`4s2LEa=gO&AGrnB|~Em?V%wR|hgAHDmTN1`+#bM%hP`KW+i zr7HN~mp@OA_w#ELPw}EVirDUV86Q;F@DjJm@!!NotXcgE!&VPL>6>p*k~>XjHRjEMp^2CHD*xheSidj@7L{({yG^7Q!kKlDP}crIZ{ z8#&&oM?PJBO7oQ$i=zfj#a9-m&}|pT5Sy7}Kz`y}I$}`|XVa2T)Yiq5p@Tta`u!9P ziRnOZPg%-^Kc>f5xkAcHJ7~x_D{zY&IG_8k;IYhZc8~KXh`#%fyJtR*duk|+62TuZ z{=dV#>a-=ioJS!)>`fwGk-LNid(`ocog_9W+v1AQahTHWL?7he;T(n*6SAg{+2wkh zCe4~mUd4NI8_#zViMn;e2UO^NprVNd4)t$Fy@$VXtl|vxIOK)mGGLCh%<>1Ch4{&Yb;D^?FIKz&E2XFk~ahDHV2^xezua%5%=O&nPw1 zm)sR!q@77Y%=g6ic!e!OgXA|jAQ z)xcKsd+?k+CFgE)WC{NcEF>P*T~aDJ0Uo_-|35J2PRhh;>RZ3#xa2_ zF@Cfyc4Z&HbTC7W!Amsr?`pb+Yhenn4^raVjtO^EFcaLHOO1&xSW|XDxa?+2FA( zyuG`PRq&OCTmPH$N`|x2WCg5MxB!#EXQI>C{boEE}M zOLXBrI|lC`Jc3X3o$y%gUtXcfjyFSR^pO|AB`y&b#!5ppHxu!vTKu%pRB$CkB#pxbo<}k5KMlO~R2i$(zhl($BrK@a zz#G^8;3PK%`B14sJ?1lUG+-H6}KZ6B)Hk1A1BV-K)!NIbFxsx*t z*7S@a=Vk6g!SNJm3783+$DRe5F-ghm1ajM1op)YFaig;z z#KfP6Hq*62Hf1^vipN#yom z?#08CoZaQMM0r6eF?#GvWlnD-p-uKA+|G)&e40e^7FEGVRWp=(^nurB?$FKS8JOW^ z%&U9(Qn%r1Br*3bjkx>Tg?YV^2_3eV+M>nz)j>fP$=LcDtt3mKkYA!S^ zG=w9pkcDeZC1v}6f$t4lA;bI%?4>S&RC_Co|7AcF=QokeLg75%eT8{3YK~KBau5kw z-ok|0{1Ek?iXlGg#-v8u8>ELufx?~xICQxWl)b1&(?d$U!O_*c9h76I#a({BTPhRK zZGf$_X5j0eKVWWL4?9CIo^>80!TLNBa&H})5Vrm%{3)vht0zlf?wB&ze@y~h)ON$9 zl_rp|u@cUP9smQSJzynpw)HpVlFOX~~~OEJRK$mjUc`X83M=JhmI^0zx&}_{5(yH*SHqKYps>!T)zxQ`Em`Eo@Na=VR+$+1CMB%8wF2pdJRPI2`mcFMhj{?!KS4Y^(#8BzN zX!6Hr0};*grv1N->5J#J_;CCHqn~&Qlf;*?bLVJ&!;g);;?N-cud0@QmgPV>T%URL zB@yf%cd@$l1>kHwjSipvkw%5SBilM2iBFeBa1ZC2!5GzlVAXB}LmGtq!jWj=?otP; zC*7G>4!J~L?LH}dx5Kei-I6=!&vWso>IHw@9{NwDj>G51(Wru(s4-5L_85$(4dJqA z{p>O(+7H8mk9qvC1HulpasYQN;qb9~Ft7NP<(bM8ywt?qyo9<0j;?yeJvo@iY>iU37W>^uWuM^2bvj7^ACa}yD){5`v_>+^K{}`(szerVdCYV<&BM%mhqM_f5 zxX@^K@;G7)|8-#w|G?%KeVy zK?My?n#WiOJRB+BMs#cYLFx&Ft~{HK>YfK^aAhiemDfrecOXh!*GH9?Nq9J66=qku zQEyXcF#0ShFr_J7_RxbKi*CWQ3bmNssf(AkrQkJtebk)viM|UNinHRE(Vvl~+?xzL zYBzY1L>eZ5)8$pn;X%RE@w5nN~f{@$g53&HnUy&QUID z!b38NXw&L_YiK^}#GSa~Mi)h&W(Fr6hmn$n%&{JxnbWn8G%u4PbXyOsef|zo+LuD? z&=^P^cM+nd6cURs2XS0>4ko+EVWID9^y~y0J#QDUet#!Fa&Hzdk=u?PZXZ$E-vMJ+ zHsYed6kI>k8_!(!LerPVbk4KUxaiOXfpxTr49iqS!-uZu|0jbkMg`g_6Id$JHWy$14L zOT*=#PS9HC1|F09>AMY5utaM;M&_sUGmejYV0v(Wk`3g#`y+o_e z)?-i_(!uufjJj@#kUKx?gea}Ve7d>E|u{~C#+cBgH0Rm;vL_3%*^OM84fiy3)|Bs?qyFI;f-;}YyVzNhI zbZ(D*4P*KhLDf136vRoyf8Z;*sJ8&9o(Lu%wiCawsR3`tKsXVwi3LHt=cT0zGgd3I zs?S#lF7DMdIsXEV7udVAw#KST1qm5x0IF~o z)(_kU0|Ntyo6iXMrbhD=>cd>OMXM5}q7x9o%*Q*g=({D(0{E##i?FYM5Z*qi=fXEwv zAkuj*k=_4@WXT;7S)7!7F_ zKG0acm6beylYEZy#%XoQXxP%n%{}-4&(&6A>#JsTHywo+xueXJ8=mM3ei-pFgjTq0 zLDQNebY;?FdV6fN;7unaXMF(~yynHI^=ZTCdF{Xq8%jp?aFFzMKPdNJ0fm^WWK2vy z=8pHGFE2j@Da*6u*2-9j)3^&8R|w~bdMQ>z=M9YJv#EUl7Kl)r%4(DmII@z$JvN4k z>vDn$AA$QD^oiFTm`5^ZR^z*;t&v62{HtmcZD_^TauSuft=7Scs`O0gvtHg2$%upgT$h z_YInHp#t+NIYyp_t$a^JqwVRm(YA2y#%x%*ZMo>i*@;|<)_9WY<4Fe|eI++@il}0M z2T4>{;Oc~DtkLcgIj$cA8P~Ig{CX!iM%BXdi`zi)b|8s)^Z_S7T7`1IPLPSihoah$ zGdR1glQx}xMF)C%;g5tbxmvG6yY4)PslzLo>AeeK&Rd>{OFLlc;6KP#c+GoCJ;j3+ zzbGDE54vZaMVTx0n1m<8S=kdlaIR1jic@`H`rX?QpWs5iPirJ8ACkpK=_6)vXAlR-ZjiE8Ag^TA0U@^#-ibxeKN?v>KBe&}! zNZ!z7a1_h3cB99_d535SFRKFwn+>qdJ(2`ny$Sx$9>XZ_9OAIjnaq>lMclRkjM+5> zPIOAZnP$N~x`TksUkwt^yu=|1 zrFq%@X?SPZZ(f5m^9olwc=Le{de84FNm-pjrPSw>Kay6gtlcTl$(AGW=~7^+6~f9d z(}pM+UGlV}2SmwUU2t8`J@OZg3pl};X9z^H;EeTb%fg6UBssD4wJ514f%GzpuNEg zmbE^D?4&Oc)_I#@^QVJO=2H-zcZDGemqCBGa85TYhvG}uAhKPJI7r1{0_TKud<(uz z+Q-imAK;~&$K#~>t9U|G!JA#x;tR|*`Jllze)r>Tyx)PN{K2w5JSwnpGj6PhRZ+I& z_45pn`k@P}W*NZDZ%!mj<`2x6@Eu$=HbYs=AlhVXhA6x9(7$2;o~P%)zUwDpa^xde zBf0?6)0D|6=^fw8DI7D&2cL&u$kB~=FlwX|69nf82ihf+l= zh@N&2qBaEy?rST`NHs!)%}WTn#)=}Q#6gXAF)Ov-653YyLBC@hY|J|b66eKWlf9H| zKiSCSJB}xn*UTa1kO|C|n+NA?_QS4sLm~8cDAgbKf-c-Y9VT4Nq33FMf^+RI#-!Q^ zT%~5C;fTLjKeL=Xw5;N1d-(7c)C+^Ja+sGQ%wCV(pbl3oaE47etXh2thD;FA8^7b} zY_x|Nl^&3PM-2ii+ep!IUHDtO4c=VJg}0C5fXZv|=WV-}?{>FTaDaYtBOc z6*mYSVhc}Kl|yao9++AEo>lm|6pCt1Vb`QSczv*t9VXU+kNS(Da^`55wyscMvh>r1 z=N1uj5=<>eYT?saGe9|F3yhwA4wGGnW47%cEDTtOTS^X+v$FzdSBxSm&+`VW{C0@w z`V9Ag#t_C; zkAfq;Hl#J{D{H9w7@7p8?_l~p)~Tipu3y)N^Gfjo+wK?2PKqV$ZaYY{e+3pT5)e8* z3CFqo!gjqFO2^6Y(&rR#YnBmy8`FqGmJJAZ**}3Xer!NBbJhx;z5%4r;Ai|ekW0q(EfkoLZA(r;}5 zxA*LZsjlW|ynZS)N+iKdr;<~&$#I1(u&OG7oS&a3%u$~@J&;=fJ}C!iWtcA6 zTDyc*2u&2SXZyK^m@udqUqPeCw!(5|1c);QcKQc_C8Pa;l}}Qnu=EMeuxLOp^j{Jm#zgxdmPV+-Mp!e#Q|rV|+L8h{eJH+Zm# z$F5UJudrQf-ercFpoCuL>8{yyOsnGCY26ts;8HA_*V2AT1 ztfs3sduGpS`1+*{zU^7X%Kx~)D#zHf(tpOVPKn-NG<_Lewf6%I4VI*p$p_szZ?wm0tuaL#$zO{&)z#Xa-ItbD^bX0Gtzjf$NQiqdzvV;YkzN>;rXd zu<1^??b*QkC8e>BC;Q+-^=?vaR0-K%3sFPIgF_u4Qnhz9c-CGj@1Z4XT;WCMCJ$0_q=g*nqh$Ev*HleGk2>A| zOKWd6!E@_nRE~8e`q3LebCowl+vI_LoA5jOO+eGipWx*j0fhozs7Ct%tKOl-PFOO5 zRsU`TvziDD(VR%XM$Ko81E1pPY%LttA4rOqB*UW<>&ZNW4`gyxI4ylsMvyNTiCenK z3}L6ID_R{J>4JxtJ2d##5;Dj%y>4P=HV0G+R zI*J`5WVA;yRRPkR%3e*fuXY%u@2nTcT#5qM*JrV#@eXaZng;c2D_IBU45*y62UYww z;_64~*!{7Gmx%v@17r+lALt;Nw`Q|L9LGSRN-gMYx-4S;+yZTjB5+E|B4r_|^x@Ti zIAYKfXMU2Um5bWBPf2q)Tk$S>?B8qJ=1@oPJ@}92$V#L9>v>$g=`lj|Cw-76nX)ff}Q&9U6R3tL6&PfOFoNVW#FjbV>Pe@DETo|~ z=898vedw}zl<0N8CrT4dXjE_oE^!`?Iwi*VmpYKgjQFRPA3tgOK^VY}!B{;Q+`z&1dEA?uCtxxsaOFPv$#N+HhnfHeU+BW|dSt zx-kZitkJ`rk~2~B%5{t!m4b&lz0pxm1+}XC>Gk2|qN=Mqn3s3NDF1RiN_;zw-m+8Z zqJ)ohzS4d=Y?cFF*|HZ6`^Peg`MzA-?_5&*^dCJRHJX$z*huR$cW`5(HPPgb6gCL` z6Z3CR=;h2&7+)qlYwtU0Qh>mn%xJ^W=hwjB|IBfpcL#X6T5(z`?{I9%Xxwo|14FvC za8O<8*!z>5loj%;+pI7ki9F9yJQAdXN6z`8-M~uj$s-p!{j)xOS!+U;XWgc68tzcZ-KTJ_k_m1R{lpD( z#i-Zvn)~Q_=$H%J^GWY1dwdOd=H z{RQu96%FvXLJN%jxqY`E^X_K5@bHt*I3%`$mkhto2iV&o_p}GiPY%Nh#U2cwv=Dbq z%0r#q@5!WM6;j;(j+=GURy^OWnE7vA4+*piWJ*_jBBbphUGZWi+3?4SdJ5dKuj!Hc#XRc4IBbG`)_@~RjsZTs=C1BaR2f@5ymZgkna9$h=H zb8jxJKsBzDUfXIw%FNd@Q?J;Q50g_wzZoU&l=&=jZQmq1wj+gfPx7aJb>C^y_#;gJ z&Rg{K+fM>3|AFJ!(GzHY>;-ybx(_gw3Pi(izQBN355Bz6Z;k&9vT4b%?Vyg}ylBI@ zaz{a9@qXfv;6d{Yyg=Gw4d?0m88g>U$C53R(Ia3Ds;v8snao&fd@l!OXA{b-3FZnK ztm&hl!K7)zBuerV>2y&x4S32CzsOIZ_H#5TkkkX!0zz|ED$v>#TY(Y$k|fSrPa>@h ziFL$yYCABGF4(#UwuOy=@YrrhT6qutg~&p#)D~d!h57qWd)(YrgP9Ay;-P5^Inl76 zblb<#I9qr}n=6K6RCOYj`Bx+H8-eE8?&xMwh*9M%jlOwR6sR0izF;JV%~(vGd~5+ z`bXnKkfUD@1&xXDRJ;dH+dIMLDQ)D}ivUmzVnhmS4pR1$3yS>S!J_)1u=b)Dr^)<8 zN%)EF`(p4(LOuFkDB~>txC683glLbD!yUPHKZ(iY6zya(;#sbeasX8aP^w@sKaboSP&oti<)7q!E9=lR59Ne5H;aRS#M`G+<& z>I!*i<%o1cBf;U(XR^jUoP3?Qf?V8}Eu1OmfO>2noD8ys zb)qqls(nFJ&33|>oUyQ#Dd0lqy3-8dO#1dW!#%z`Mo3Jahg(sngx>vRoID`}pK7hA zk#?ghRW_b>}@Kfo{ ztd~^D@((KgJ4PQqZ5CD6IWQCM6fmW)lu4X_EQ$2^B)Dd8l8L=5abHpf zLst9IJ$9iuWKfQ@XdWXq1DojNjWJ-lVk&7nnIx9){{gJ_ZgK@4l6N=_lJz^NNr59| z^hL9dw{AkE!8%x?ev>gZ%EF2$z;jCTwX)XDWDJ!Kb5 z%bTByx&jsp{-!uWqZe^ipXZQo^ET4UwTfhLS(-4{OM$aT!{ANkb;19c4aFJqAnv*Z z!BpS|&m4jKPOiYA^AhpU?CE%WPAun~brJdZ|KSC}cci+1I}PpGP20#d(3Thct%kB> z-JCQMy~mVXYfhzxn;k*=mo+)vcbGfmDo{8wd8+e;A&1Y#5;MK=%(z+cI)OASovfzsCpF!EBDwIu)gW<9&xN}D{9(a*L?+%Lw zgCHH8`LY^o{#cS5v6+(uhlni0}Xw>=5q%0~2&7rnn|*v1~>{`)4ilq`mDx#zIeVHF&6 zwt{-Mk#K%q2|QgG^8ZeUth0$^#o@IusrdmsanvOd0q5}PIbCdcuz_myNRbVtemLXW za~Kn_6Y`EchptawNqyfr+SV|NOkEKKl9$s+;<`kp`Y0o6pLUdPbr^w4Mjgyc;r&v` zOrRlCn>f#R?!@xFJqdMLOwQaip}9ql%-FXJ$dTY>T-mY?kTdmwz5G*nY%F+=R$+jT)At#Hn?-aP|uBeQy{vYPzxV zs~lNbt9LMa)gdP56(wFboUX`v_9NcyeV`x5&L#ZSec65ApgZ;Uu{X)%VYk`xSwrT7_vaGUYpL z-7=ndI30%AM{VqwNz;)Pdc+!ePx;B^YP>F*(!SL~PjKo1l;o#DvC$gRb0CnF-oG3U zx*diQH!i_FIdeFi@)A7TtGKIg57J3x3>~{*Ud@FjN$!*a(DYFc$?1{Hxln~?R5`{J zCG?QHS@?}w=$)mhJr|jqCbKx@pINZ#R1Op3=)-)8`zTr_?G7{VU4XIsHCbiz4tV(W zC2Jkj!Db0A@gc>pP$EKwU*MvL?{Cz@Dnl1gxbT(H-eLrH<}y&WpqQO3FzgDIH^brI z_N=S#f%wN!ZeLvMEqk7$>dUFBG)vWOyaG`lHsP*dQ29*ptp;LbmkIsriq5m zs3Ef3Ejaq_23DMi&v8fIN`CsDM~DvlAWGP~TAL|hr=1Gc`1}Erhi%Z|`V@}#ghEw#53E-FO_F}i zAT_BHM7i5Tyk|)~HJdk$dvfIj^LoM+QLFS=?%9=@Bs;^IV$d#f?_UF*HpGskRE(lO z|Kr7rCVeGI=|Y#xx`}R|B8~rD7v@r7viM9mua`_3Pl`>oS)I~V&@+>;jOGU1RImY` zHQwQ6kNw0C0qXR>AWdFA^)o+i^b34juS3if{UI+v5n@a)L#o7T*gjXdw+Zc1y z+0}zgsZlq_-TzID42RK0TO^3SrlaV%S|3?yD9`x(okJX_xN*0S6w$fGGaDK$;f6 z0>@4z;8gW!(91Z+-%AHsqdYFQPMe+=zHx&YAt270h5q%oF@1LiUNLY6r71PUDrY1w zKRpQ_pLvb@pDWYWif8m~WShFv>D( z1@5;9@6O7{xF~g;z963Ba$EW`FGJj{>q}f-%qH=7ImYovE|YN~pXTUqqXnnB3BNLc zlMh20ZCpy%J>lq+Pb-Lx@g@3hObFL~<}We%DGA0xx7z=Y9aJdB1Ks22G{;Di{yRMx z`6H9jaCkA3>XOWktCxk^QOn4~hDdV%x8M}M;|0T>1;T&XM&zB10$DOY3FEfSr}b}; zj#^#L6`!z2h53hQ>R1cHS>6`C?|4J27mudNOE%Ml5EJTWAf|G4on(1N6=xQ9gx0kV z(26I!h=t~MdR*cx6EAazroZeZQS=audpZcMn*^?Ch8~<+8wV$UhN9&+10hE_0T1ik zqKPkxN$2rQRxA8I`5oIycZhtjF;JCgl~r<1CO=@|=$n+AzZn553R-Cj$M>$Z}c`aj^| zCS53?*J1JC7o0n12N({q2gxQ5dP(-w3}-@)>TwSj z*--V+7Sa~)My7T8)5es2BrR(sQ||XiG~j+&-0N^hT$H57hzE42lB6<@%@`b4y@KN|OKR12`~^>|8l3rWIg_9&OPj9wi-P7)q*m&Q#N)$v zVsCm7)-C!jc*7*Af_EMhJCI779VLLseIn&Hf@@;Veo$$&fbk(YpfaV0gbgkyQ@^y5 z7z;7;T5~rkyuXl1?dW0j$wZPAw}U3Fm1nZ;TaLYF5mYQF?p3lFW#6-lZRbn zZf&|nJ97=m2}L5hbL1Z_di#YM6nfIfCEr9XpJXV}Eur=aUGD zL-Y)Lk>2|rOfy>hsr9)AYLM+qB@*^=BPJ}t?I+s6&efOn%x>V)V|B4+iVt=u06)Y+ z1>Xk;@lqbzyo}>-{1Z5ovW^{a)?byi@m|jk%`k$xhmXPIcC_HZ^T)`S`_M+?tN6mK zc5n`*LJp-3=XmYpp7~8AzIdO|uyDFDVIQgZW=nTf29kovIrQb$A?ToRnp)b|GhJJg z$f@2vRJ^5#X48B^ ztL7Pzrh?^Q{O>WNS;&)<+LSaNxX7)Z#nJV1(wIWyOE^?67}bIXncn?RQH|(RuiNp= zG>O|R)3z* zhUe^v&$g^tTnW6lSAZ*NT@cxYq(}E7%QDYzxvnsIDG{DKJ6|>h_g__Uy3XaJ;Dxoau61b42Pof zDptMWJF65wk2E_(kW}Y3@YIFFzB!uj4-Obe`^XeddrJiRfE$s@1YYntf+}tuePG5#i3;1nSt7>jeTU=h#p$1r;n=2 zl6bq(gdZvTg!jsK@%jk?c*(K?3zj~H35Nvkbo+VMcv&<%H;#wIp$3pSy^5ZfZ$<0A z@6@bz7l3vSDeafxD%ZXw7H%Jj@tdX0*=-AHm~Ax8e^o%bnlo$n9(Nag`N9g&YBzF1 zPDI(aKsmn;+_Cew>5H$b+=0w$x-}@Cd$&G~u6MeC${`zZW_K!WcT&c~SCa9*;MMv! zZ5`fL%HdU`Pw~?72{?WEWm-983~7m6&sy7DiGAw9|Aurz&AXXA1x0%wf-j-GU=M{^b?Me*T6-6(*qcg*L2sP>FpF$9YxfINny* zlOO-3j~6lb@x_Bcv@F;{PHk8NKPT^ioT7asH7|fn?GbwME^JtvlMp-aXxJCEHXLp%z^g@d-j4mkLHFf@kq;x;J#ORi>@?tvU_IQ}Cs|tbU88kL{pSWZxPZ~7kaLIvGevH8PU~=cgL#woaxI$Azqa?;D!gKa9Ja z&`p>}DkQVzKG!h(v&irI0?NH_6g~8p!!?nQxU7XD!OIbj=GSs)UUVjo^+=*d2cDsE z#d+{u=L$dG7(-%~DWs~oqjJJQ3}6oOiVu$9JJy2^jXVq$9a~{mU?W7eHVb~NW8}2M zAZU$DBpvp8q&RSalX$&@=%QjVZj!6RD?xxsPYcC!GTxDO{|Wp0TP(S#xIvuu;WH zc4!h&O+3s#;f9Bou0$EeiTp4lV7oOA=DYjD(Td67xbzUK+TsU4X%`*4HLuseHA|;62cDJV&HNSkbjuuJFXDsQuD?VbZY--gI2>Yy zd3wEN3##3V#7hn1@W|P4`Z)bJPMdRreCV4DK{GtyMDt_xp1BAw)T^TNL>IIOdP}tC zt|56RO-SJLH`MIBCC!;In-ljG!lcmcWZoD}a#w3ROxYky9$Y&~YPL6m`R4f~@7zl9 z6ps=#e{l+9-z`Uittk2NC+b=CWBHzdA|dc zS_+7pY9v`0_n!LeS5fbEtI0VBJ+k4$DkjD~OYE_xh%kdn>mZPl&PfI7JqO&<6l?6pKqWe5o$IQTGDjP7yFC8~{UE-DWWUwgk z8+IsvrWakKF!=o;oN2!oAN+oTRX>D%ki$yy(L0}N~Ko*4TV&4CVgmZ=uSmRyGS>@NSKzx5BX;N^(*bx)K;FASrsY&9^E)8CB!~wif z*@SNu<><>LS-8;tB8CeLoA&6L&|UomG~jPX> zlZ1X)Cn>w-$o+1!pi##!F{#mMq(E50UrOsi+2zCVq;U_fFf_yMFJkd<5sMjiojB8X zE>~MR2QunCAk|$3605UVEva?Tx=J4$ZykY_(PkLGz6!M#>SEmnEwZ;-7Q2%=s7#PG zmc6-%OY}sfGkZ70-uNW=ZEV1@sv9C*9HDUMPM8>K0CKY)au-J=a(y4qk_DBKG+%BV z{ixK+RC#U?Hy!ckdY9}Hmp(5hI^tfz1=xtqp&@wNAPr+ejd142eS$apAIgPCz}1`~ ztcI&ML?ymsr=RL(B_jLS(L3a!Z$lU!7dm5YOFg*q5fa>0rjx0t+DDFOucgxU-!LGh zQE+A<Q=%kA zr9qN3p-7@c(>?D#N|H!23zZ>MhRnV)eCHo%-L+cl-o5wx`#sN-04;ph_YZ3-7ui2C z+5z0|CQ|ORgL+OpLOxyI37U>q;9_AJyjIVIbzkqm-Dnfg*Nz~S@m(bQ>wom>9eIJb zH=6d?mWdv$T1eAMuM>-h{ha>q3Od^;jCNS8|xvJ=ZikS%bI?#SOFE_4JsNw(l{$L-u-g|$%bD{zo1 zhm%$pA;;eN2(l(5!k%HWtop-4kTPWk@SjXU{rPlaXSbZ%#MPl#!CdOroQbB`7|7t-VQX=T%~HGKyF z3pN3!FmL!wxYgnZv5v9A|F;6geEpfL<9u;i>~H!jMQ}Wx5waT#5^-X$h+5Xx3t5+q zobD=lZf|BP&UC+xJETfc=b{9zJYbJs{X+1kMmc746?nRrLH`9jMyX84tUqT^+T;qT zcaMilTUuF(3LPje3Zr(iwRq!kFw9qcPLd3aQ29I$xie(o!uThUcrpvhHavt4YiB_1 zqil!>H=wSK;ausp5V~X2QMxtYB<``kg!cN~qBS~;xTov8L`SCE(}sd%E_ZznTD!R6 z{I&r+yLo{Ae5-}kiv)*a>n}XFel)hvJB)Y!nqyt^06G}_!qMx0Q0DGl)Uov;@J=xx+e=CoT;KVt#?qA}v^bxscV%tAv1*2~cPpOz+&%$KfNE(inq-j82?3TD^C} ztHwzfzbh1-!^hItfkiml(uQ%HJe4;8v!%asX3<04Cvn<&In>$XfYC~AxM;sJFTVT) zW^0)6;`LIze!3$sVgCzzWiIhjBgFB!cQ__3FoTyuPNbwakd-3C*^#TZu}Z-{?1E|P z@RBzIg%NWwX`414~&z=~GNg;04Cu6lRNwD{YV)g~M%{FhzG9?kiq~ zeokpPYn+?Fk-3Pf!Q)U}wTG4`DKl!L&(MN^LzpIH=d5yP(-ZY~vFxb}&bgO`$4~C! z#|O>jN6yvdwaH3eCtP4>hP30Qj~rN^=tkd)UqEi3XKg?I$A;{93Jt+QF#3`Qx}LMZ zZ7Z^{QT;bj>0XRy+H^2}M+;%zdEhNuG4!6YfX>OBPgknXr=zdNB5Rv~asnSw)-;XY zI^IB|%ifcbb-U@qTg7y2`fIxRhA~dEVPTO!-E`NIUlq9(pNwD0 zOUMbkm$EpH`1Am60u15YoQp)-a}le%Mv09s6nL>;KEsQtj*#Q@jrL{S!s{u6n5C|Q z^SbOXG;JZ3@GE9MzYXD5R^6g?6TQjN;x*jG=-#^Y_Z8^IPPYH)xRsvR;vbsFpsvX zvrt>P9u@}df#$u<>Wi;8FMjKrtXn_tKQOZ1JFv zV3oa#YXSEzbr$_M#R+5jQ*hQsTU@8DiR!(*G~{9LaKNnl+gir7)LYTSa{i>SWehe@kfq6HHc^D5I-aX|ke9kLpRz3+C?m`F+| zT~G!ump(>irW{19KSA~_mR~ZRyQi%s_^hwu{5|Tyx%Lm@O}Byp5l8pqH0)`OV9nE21{Kw@E#1BOxt8 zOxwc4kgeGQfeReL=P@tR&e!GW7&Gd6*O`7wUrZkc2U3+C$I$4l8%o?cNc|>j(@83t z==xa-H%4co{A?F$D|jxXxBB5cF$b^@tRj~Ui*RPjds@$D!NEc+T7Fsu4a~!tjweAF zmgmWvYgO}#PObd3FA=<<|2}@aUlp&m>?huSrG+gcJ!#9LSo%YbGJ1J~s3rc0{G8|n zvn7v{i}hQ`Qae*%a`|J*YZcs z$@7-)d-=GSOMHqX;SXLYLx$5XOw8TzMxOuMOlHYbx{YXpEvlsm^U*Ntj z>U9H^(}!VH!BMcSY9M!>ug4(`JAO=eG_UB}K5lfRS6 zt1=yYQsy%mD(1|C&k;hWG>KSm{>9xMT|o7I`I83=1*X!nLNem>deF6a3>9kU;A}t) z%zfYnT93vN*JO39$~uh=sjm2PR18mKe)2j>viRS=^}PJEWZrm`A{Hk#;nhEvL2+~@ zxzg!L2RnV~uSrU{Hrfbl3=WHKFTO=D>|0G7rlphmswS9l_Z;_g!Ew54(Lx&1=0kJO zj-tnYSC9bJTlAx8Fkov7smaT7L0P{S*YgwFX5Q^_$nIC~+?vMYu zUjp+l=;7S-?Y!|!2mBB@i`Up+fVHQRsR3q?>u%YYd+{o9GCdAy*Ye=Mg2nJYU+9n3 zJSP!xmr!Bt9bB+_8xcF^NP^ULiS3PpmlbqEUvnANLZ&FXke2elo-jH=&_ z%g1eFB)r_Xf=CHkW4MCe+grh$3vIw*C!A3)ZUX(}HjUnyD?vYBdxZ+=nGVlCy%TyM zb(A|Y0_D0U(X*YxUe88>uV*XBo92=D@Y7`S_H7Oa6whJG>%toCVVR0oC-Bh zG)Z^4k8v z>6SB@T#`sq6AZW`Z=X=v1Yutak!F(CeWIRY@`U?x7I7TW1jENZqWzQ1v3^4(VQZbZ zMb`fC?c5a5Q*EMsrL$n=?*njaMLsBO4}s#_)o|*M9Xn#C6a?nXf**>;FlD2p=-T9K z+@e#8BE>DV>2zT$)+kwHq0m*MWGpM^exJ~{q*J0!Gufo38w zg&q`BT!wL1^ya~?tJ%zZ5Q9C+>tI4X59i~C!&t$GxHj7XQj)YGX!IW@Zhu{$KLQ=DmrtSTJGAKE`O?TNxley;sgWCBJ7~&WZBnY6NKVAuB(15! zyW`DBDqq2ogU#;fHLDjlU7mp>8rP74G5axeZxbCEV24sQ)4=ST3fZw`7w!Mp!7Dt` z$6fFk=jx56f7plI%)T;MGrWTNVeUn1cTZv!BTs;+>OOSu%O>gf-co!to!;^8LxXsE zOr7warbd`DgZitOpiN?QZ_h&l)-)lt7pK4=AyygngBgB(eUQC_Y?ZEK9z@h~5Aixs%5Y^R-34 zujrE_j_auI#vYov>nm3`C5ra6J*S^Pj3W|JtGK(48k|||FM7Z>j_!RwjMi%iH;}?I zdSY!A$v&J)-9!5!MMVq-^s-sw7*8n1CU(S$a44Dah-SZg&g;IqfZeqtaib-N^XBiz zlpPA>@ai^p^z;w#_wh!c|5bqMwoF(bTf}Yi-oo8nx{KLxzaCJ z$@sYYqSgI3Y5a}l%%$=A+{ukonPREW4l9?KQ?Dz7r1ZozX1>B$hx}c?sBV}SDQrIn zO`|tK&hsnmh*n+JC-N3M`mHCtIC~9N`CdbXJ>yaJju(EJSBo_dig4svff?j&&D2a$ zrZqhQH1oL@9aiNHmz4*}zN=Z_d3QXxDxRgfQ-g8rTfu)g*%B>>OoiOz1Ujj1DQP*L z&s^N>M$g=_BTFUoaZH&nIkc^Ym0PxspEvp>Bk~#K5}=PCryGuRemB04`-rby6NR4W zUE1I3N3Z=lN#kG05Dll@%!ZQ_$XEqs8e&{ZXVR^-Wc465*Z)aAIE`i+$KDln&5dV% zKU_}V##+*2E6V7(J)!hn*%FRM&P9dksLf|c379)=WTn+LkRH8_j?b&7jdBM`z?59nYRIH_V%q41 z%X+ww*@4~|67%8d4esEJopkS^i=4L4Utx|Pi-&hB;3?g;czPfQTRt|T z^nz*h`b-@(E8{TbWHJ5lq7e;hl4-{*X&fbRO1xLsGyD%LQr5YHe91Gxs9$zuviw~V z{mc+{_eK%(xCKOG(InAuA3e}IahfZy*G8!?E9u0JJ1Fb*2!qNWqmQgUCXd+eFqZv@ z8W-znL|{JZ6|JU=ZBNj8%}zRSvP%T8>3y`e}*(W3F%FI?`rnOCFtC z0^_sQ8MhZ+3{#cdz$ z@Sw#L+?O~7Q!dG1Uc@Xs?%2c2yS=3M$Gf4zr6HOU(vQBH|1d_#uqK650ru!JHBt<`+@4zT$bqvz)!kX+4ylS%~Rz^>x@4AEatXEWx$%3vC@6)cV|1*6sJkRZJcw%3cu_*OmYm-7??FX_SXie1ET`wW_Y zrIN`sv9>$jaf&X_svtJc>gbdI2D#=7486L@U`Bbt`N9{R(RZ-jrDHg$12g z;E`9Q{U+ECO>2cW2+o7_lr4uXR)HkFV+r+syN76Qk_3AvEmBoBinPy6pdl_(>8^S6 z8BZ52q9j>FWY6B9X-VJby9&X+>e+utGfU&;}ZPh8Uu@0E$a; zS+(vjtbtA!S(M&H4@@}-9i18MmhOjaXka9}C-om8!UHYP*?BDpCy!sJO&qcb^fRF}az5f9@8ut{! zCH8{3b3D=ObfLHN#&TTsHD-sdjmA7zLC&1HkV z|G>4M!$D_49_f8322IDeLRZvSShvpwR3|T_vN{&9a$Yp|x2p%%mB^5U6TRf-%W|Bd zs)RXLC(`r$B2*o}4M)-Mw8BJ|=BMri1^eY7dGQBv&3(?4jPxNUyPlC(`R)W)egdnj ze?Y2wJ2~|%gWB&s38$Y0uw%L}L(h*cs4h4~l3i@*%W7$XldZ}6mWQ!B!(PFu{1N1* z&1A@&e+hz@rh-Re9u0XNfmz3YV9zm6lv)x(^T$V^y`Lw(O%}(!%D%kZvrov;yLj%w zVccJ5hI5No()lk!xPcAt$n*HwT$HXYnJ79;Iv$M>o%DAAF@duqIcfv>HFZDO_-DZ# zuiqGg-5_;k5T4b|V8`uyP3N760uB5?YE9+QsreXWv@0^+b6CqwJYUW3IR^!7BooLkUisQcv z{Ks!8w0Mn(%=nx_o?4C~2MV!P{&ysq6SIjsQT79vwD;hvlT0KtmEmNL;Geg#0AYM$ zC%R`sf-t^1W*?6+sQRvxKW4xyc z-ZyvVC5|Pc`qvt;vfYE3PKWu~=U?&aw=8+dK~ubRs{jjLFGIuhsZ_yt0puLIKXMSi$u4t@@G;@xkk@Uk&= zyyS!V!nFyl;`^7MI5!7>&3}XA&P^o0R1YFM=^NerYB`zvL!9orph5Ltyd%0+l-c>XlFU}< zBKc<$$>{0sq-=~l`BbS5Wl9N9$8^jA)S{Z+i= zIDsFYd>;3o+Y4IDtC%B3|8PUREe;DG!;~z0NME1qK(*df>T21;)i!>m2dZ}nSuB0# zW6l{enVkS8!K2A>)eWr9q7tY&kqmCSH=ymKkbgY442G@$Ls~nIp{{!>p1Y8Oy5f&; zG6{r>7hXd9ZwsPd?1qO`f&BMMmKXCF#7%Gd@I!YK&PzCh8~l9f*K!rwR_upY5^BLP z$Pi|3OC+mJUgO@D`DoJrou*9AM6q_&x+Mj1RJ3-g==9wHChGV;vbQOb*37c8M2KQ zVb#hBj$Z;tDYz?V6J}zWr-=XVF{(OE#Z{vFB6;wRCpI1K|gHKz~QIflOctrp!oG4z6+X- z{XeH+cj|FoYTyq;4?E+GjpOj#Fy3&ySdLh(rv1d+ItVPAUhP4SkML4GM zDwEtXkNeg4nN(%pAzoJ^IP1_-&ZRnxtKP0dXKnt6R*{P!Exn(H6fVZE6|;Fp=%)Je zVz~3?ZBp%X0o$xyc`dI7Uiq*P@xCyFxg9LxQibkvaAz2=dR>a&Ha-|XPFurEulvC- zEE$Jx8#dDXto?Au&776lR=_Hs-2-1Le?vml4hS8)hJ+YvbGF($M3u3#xj(O8)0aCv zQQTjiL~1VJu1TFIie{!(#@nL>=!B&SK&xDD)}<@8dzNJ1pVk0=-(j1N!Af~!BUfU2=n=p>#~?H>&`1{ z=I}B<;t>z_;(0$0u-mT=5k3`2Lcj7pqD)+FeiO@8a`D!SC}I{kjTFuZ2J5GD0ULL* ziv4-O=~IYfucAwO7gO)Ykcv&^%&W-*AWqK^@sUqyN|YW-Imyx0IkpTwFJaQ+(`cc} zG3L0yI^Q|2pR90|CEr&5qqiR1!ujVu<0;c)7}nh_M=cBVgzwT!BX6<<~E zqF|9h6wT_q47=Xn7n*`I>E)(sv_4jVimO{4?9MI6k$R7?%PW*0+cKJ$POU=SBQwE%VJ7!?{Yh`fAMKpUNFR( zEvRINIcl(Csijc-C!SRpSp)AS9Z2o$M^G!{4=2l-;m+_*X5Tt*VwHN4Y;8X-y4|{% zxf?l=lvRDMOTJXZyuP!G%FXVCEpy@^S?ePN>gK|n!5PF#Yc3a1b{}Jedzju_cMvT! zU>@vv27V1lY;M}Z!7cTy`edh@Iv}M+`Q!h5BGg!709M*x6)1QFZ=D+O7)hsJLMMmHo zT_kfAUJ87~4EU=XOIEP@7%4jeEj~R%UH|zcEN(rd8E3K<+LPg2mn$Uh%m+igCe|fF z=+Y?|Lr0D}JA9J{Yh9!bzl41J-}c$$bNwUm^nAn({E=hspV6Yv9)D(P?px5k(d7;e zZ>$}Ts_2sPmK_kg+Mf3P8bz&B6u6s>y2vP(!V~>WD4$*nxtlMsYCk35`Y3fGn>wAS z7S4nwYJv*RLxNjvrm)|ekjYQ40zCZ;@%3#`vDga6ejfy1yW_0JJ8S6dIn7RJ*}=L( z2dfaij1`Yx2mF7ra41NaH_gA2)4$TmyfSgoSl*8&%veej9=jop}xwK=lH2v*(j;vas3?Zka*m28@;c|gCtM;aV-rSi5!S0XAtT=htapWW} zOkX9i)n3t?7SmZZuc_>WPzU(r`vtYWWRRm0Zt(D_8uNLWA)_$V2%fujSXJK>aB6%s znJ3a^j?XJ0XC|+NLqg`bXUPaE>wM4QYp#%)S=lRknYl}}@|>YVVANK!CT=w;GMELE z-#Eb}oo$%Axs#lD_mr4*v!FZIiB*p;VAVdxL&bm3AvJI^_#TSImD9ywhN~JZzbAv~ zhhAa&3I)h}kA;S>gIuv$ z4>PyJ2kiH{ljY-uyOY{nM%r1RbMG2Ur%M;2lvh3#OdZ7cr3bm|91F`{i6~sy&AeRB zQ}_HoQ1JOAWZns4Rjqf!Puo^lIdz`!+bIs#r&kDEJ(0t=_!`={LPUn0b;q|w2T)Sz zo;+~c0h5bIfS%Y1!iHU<)6I&o;`6Z+VaQ?;274l>yyX>jUhf+@Z#xj73)=)$}ApMU!$@^FiBkkQF;Osi6 zEpmmj)Nk-Evlb2WZNu^Qw%2HMXdB1=enpm7=YY(H5Yj3y$yBbjAzo)y$nRi3x^8hVt)JRK zM_e!D&W=IGRG~CPboxKWeA_UGw?87t zL+78|;1@ZP{zk}E-I0ajjh;|8O1KI9@q@I{0Ee%ZLfv~2IK_1ctcGgXyfYF4xQ*a? zKO1xwG~uwqf4HMq11)}y#WOZuss#Dl;_EK1=@X zwqwO*Ofjo1m-P2m!%&_jm=^$C(ZHLK$z{Jm!@5YtU$$OO9**AO|Z)f%t{%%*}xUa%25XP+iu}t>z=_bwpuI z$tqXohKC1T-y(3^jB{yu<67>9(PrY>+lVtd66vSaVM2qrnFQM$rt{h!lb;s-aPOD~ zR4Q+V#~qgNt7{g#a~%ue_k?+zeaXsXM6mMCnXoYVIjo+c3V!84UdQ;8zr&HNd|w6X z<|QCcSTeMD1sDxZ2KoBujGjZCId^7?vh*PuH@c@E>bA=3Brmk z$>UZ4x3vMFKIaoTyPJbi&6OlSu9BoY52F8K=3uz_FbtWRhx5@}_#B{#wE75*-Ig@u2XG0!D_ z($M}W+=nM|@1kOEOM?-H>w3U6s|~F9I9>Rc-$t6I-KV3A70AF?JrZ6Za1+H8;danz zcr$tep!8X|Fk1}P2HpU}kXDc}=>WZV%@Fp;fDB%d6I{K8r0Aa#G0S>Jt~HcX72`%) zvMrBflL+R^t)IBOG!T{E561~^{+LrQcwlb_;-&r9@oVoQyqEI?Z6c!Z^R#|^U2RMi zUg$B4!{$>~y$Eu?{9~on?y@?}3m6)95BgI~pzZT>h#B|*CocMuV~@%p2DISkjBLme z&w~{%QsAjt0^4M>gv_injIHqo@t6#9Z%Yt~KD`Q*R;iHZeKO>^WG=`|(t?VupXgAZ z8b-AHVN$6gUh}u5CAWgGuJjh#p9{y+%x6rwF1U?@bMVq>3x3A2T>9&5C_S0uh*w-g z>5ok(;LK7#=-X`yhvrq1{bNqUgBOco@Jl;1>p8J#4o>}yjP7faISH$?FFn-kjsW@umYR2sNBRuP~6^@v*@NBa$ z6mPW!a&G`$S?oZi$W7q6XB50#6#%s}_QGo;HF!kJA?k?%l;qrm>%;58`C&XLg)-p& zBLfco?INM$i@;DtS@dyM9qJa9Q@rB~v#eKu;(AZoToXv*F4WSBw-+&H;0tYQ^}^$C zW9gM#0%~igUOVzuW3eq|tku`tiOE`u`yZHurl-@SG0%nqG!sqiQ8uDQROEL+TU+M#`>>e-}RDt27m~;lHkS_IGbV890 z&eI(as~+|;UzAJ9^9^016%!wDhyUI~Sp!3Uq<$-m|Fap_%*`a`GKnBLD+S#QqWB4; z#fXVk9vxjZ540+5Frb3xB|ln|xJxehp{a~=#dW-W@Fz4_dX6y>ym5tf`n2v)IIRk; z<1UqF!+nouuzkAl#vd06hr`Z-|0E6^`wd9U__K_e%{y+CjR74|vVZ8O2RyMy*|p zTBC1qI)w?fy@%U*X~jMa6WriQ^24~2u1PTc%ofZZJ(6*63xk>7KBT3>86K~7Q+TzUIP-JEAg1?4%DSSgmfmYBt2H^ah~EGx_911F7M5KnlgBw+?wM=?)}}w zWCT4S(4tPfpXGtne06f>&oJ)WyzjiYst>QLGZG*C=*O_Hr|D`f7EH|s={M6)81H8U zqc-QF`HNM8cRU=#lp}-;x(Lh{gs}1!MG$fE7D?PH^xzL)hVs})`W)9td{}xpZOXfywN(Zs_Jd*ep$V<=o!l7O@s@`7( zR+l{C(z6_>Niv3zUR~6iUr&4O4w0D!uB2?i2pDHNo;kA-x%O~|R7*ONS^rHY(|^q- zk}=ve*zh)f@ASqMn{#+wa~oduS0$b}tbxCq4&$)}=9rehPq;g_@Z$dcxbLYpjem9u zV^^qv({c}Z@;935>g(WY%l&X}Qw)y#CU_=qy2Dsq4$Op`xazb4Sh8;&lm}gdV<~G$ zl#)0#D^tUQa7A2PCQUU4jxd_*f*saONT#u`JV=<$d7-;!O&%^i2*VR*65lniX*9;; zNi|0d%aFyE!oT>)?+I8$Zr~LkJ)_#sJE&rfDsS>~9zU;s1y1;Q4G+eN;Rp9K^!86- zmcH?Zx7l5Tl}kIBQ)(%K(=@B@)!`2ERdouCI&%}O#u#!HE$8q|$VmFhCzPJ9)1@7) zhp@)d5;KpcVqWHDX41-FrbDKO%s;S|wB6ZFYD^+Y;+)Ck>GTKWwOo|INZ&*YH5cKY ztD2Z4u^Ed@KY(`9B8NDmZqoR726Z`6g!5+`@FVD3e#DdAc)Oe7CCAUl9p((4X>{SI z-i+t<%g>`%_Zp~su@N36C_`>uAG9hO3U4(PLiaWqZ|^#axBf_C-^?gfx@E#k)Cc0$ z{|3;0y9nnl{7YLr_R&%kHwVwfFUVnUj=b~O&x~C?)h^-fWl?8uF7Z4chJK@iNcL-6 zQkCHkb z-oX&mT!!F~tP`AAD|l8|1_}y-gs#X3%(nKxOG}?)(`hlhv4HTSZ8L?=4v%{jSI|0t zKl&l)0yVNqBDak*xyBtzBxckUFg&Xc4$X0pQWXYqF;XyB=p)7)d=J~bUa;!_=5p?~ z`MV^Him$JU((cN7m5}EqGi+R}OvH%W_QGpp|2VkAzIxrsL2I0rYvo>qANmk+vcs4zgl|942 zlk1bQc1;}fSHn^8g9bxXw+DPSKMG%u?XLS0Fo;KoO@mn3<&bM&XB?RMJT$#k-$IxWz8(bXQ zf%Zem4)+4qkjQPJxMsCF)tJ^JeD9ahl-rL)aa=cQ4ctI8pCC?YM;S@3*5gESE#%wK zLg*>Wg&TYB!m>tVu-?9mTljYl$$b3;a+-F6$wq`rvDZLyg&nxvGXqtNHE=3qBK$Vh zU^PxpW|ixQpzlWk8xZ%C^=MtiO7It08TEhAdGtAX?NCZ{XFtP3XMAwS;-zR@7D10G zg^+?6E1WY_f+`7jXob+BTbNKpi~rm}cZYslG<+u=a5Q#Ew`-s^@~!0Zf&f-pYnzb8 zIS>2C3^2XpN5@+=W`7@o*((JH%@L1)utDp!Qkl8ig+e@u&k>So(#`Qh$xIjjBxg;TrTe(8ki3 z1*mxGDi$hzL2v&zl#}wrS%cE(JUEEDDK)e{(UNORY*!&4KZ@wq`9|; z=sXc;C9ZqHEE_-arTqgDpYtA)?Ry|O?k4N3=L|Piz2xFvsDqBfG{_0BA-dwQ`6Mp>CNS|K zbjF!J-1<}Cn4D>WQ+jT2{mWcdV`(^vU1mX^^em%4MNepBsykjx6gcaaZ|TCR&&VP9 z1n&LH!=!BB1!PG{*Tp%jI;hxQ6?K%o!?Q|z(M@d~tyTPs#dozama)VouRU?m6DzcB z8i_2t!owPGDXGz+OZ1c-9%kO*;@=I>1BJREktf_xQw0abFKx2eb_$Gr8Q{Ro_(Z%4 z!|9A#WbF2`_4W7T*4bVduB62%)3UJ77Fy#mYg}G;QYlf!FlwkBJWTy{ky?pcWd9 zdj%6eUqdOOS075ZqU%osdQc~m_Afp}+y8Z-g4i{j`1m9D$*Y4o|7Hz6w&Sqq=7MA} zNNt3L(>q{>g%dOQWdpb$ngoVNb6{c9Rp@KiV_Gh2)9ACp=l?VrMlgG6tx;*#Y{&keaS^^Pr| zV0QvX9KC`?4|*_g>L!ta=Ob#N9Dp-~eO8gqCyQltMDL`B$frR?5KEKcdT+4IZ_A&s z{eZQ!pun0|cW=gH>n38L zr$0K*+s243S;!QQPZ8zy)}ZQfM-mvE%Vk{)BM;YA!ZhpWpe|&M0;d%M?$h+P&!RK;Ucu6E5#ODXkEL)zTq! zWSh_pH?jf8;@5C~`3!I_oCTFSb}(zwdUP(BK-mi&c(u8S7tfK!?&xFausMgm-aMVg zK7WK}EnkRQ{KmRBlA0*-@hb7Ep3dCN59IczR#MZAT2x8(9crvgph<6zkOQ4hMGF1v z8H;vjGV;+fQn+ddRpM8Zys6&AcZ3AQOlu-jPOSp}<-QQp*ajEbXn4Oa0`O8P$?tyx zD_y>WSnqnW>SsPDQTCZ0JYghsWfbYxU4D4SU+^U!>ZS?$DGvUwXVLeaEJccyjxt=kd+|ec+?K zbNSf6dHi|JKt9W%k59c`#Aj`m<}-dA=QUnV#2P<&oREKod+jezP3}2R%i>NFPL z*<0pA?%XRPfgcRWxuMOVH0>BUYyVzk>)}mjH~)besUPHXV+xwDbmG^nUBxdx*@Wo^ zhPa}~o@fVDVC}(PZg2T5%uukUdpT<+=-q3y*g1y`%&vpkE3}E%HZ{`oz#W`J_LE8S zs-!%yl3CflLlpP&BIEa5g52LGNxXfH$s^tYq!le8v2{6#JP$Da`gZcu`=pSqwS!AW z>!5dVKNNLZL1>Ot~b7~F@ zoC=4fcVgIajpg8!!LaHs3=A6zg9P(%Vt2I^N5&rDNl61%kGPMrX{-6MLkusq*pf?M zXTWqe{l~p{pMZ)9QQXx7wy-9VhcfF8u-;$+r`TRjxROWYxp*>Ji5^5!LXYfYJjig3 zLGnp}&cCc4!>si^P97?K7d`zXaF~AwkvipVq^c$#Hp*1P(&hKbTrGfRp|>&9I+b1U z)sEfYCBd>?ms#nkSd5WR2A|RroZIjbJ0e%G22VvW#_tgFTf8yo9*_H~KZ4xn8KS)n zo@CxAHG0gy7Xn-U0A5=SjOrTF=A%n|9dyZgr()rragdJb-cM%M7mzpm50me{D~Zw6 z`y^IlIA~OEr;WM($c&EV&Tm(vZm|h)dAAjKeD;Q;d-9>a?-Xkk7{x9xTgA?-$Y)(P zr?N6t&*7z!5v%lW3q7&03SxuCF|^f;CYK+zbx;qaNBwH)Yd2@ur_u`Pm#5L&=Qq(C zJw9ZF>rKe)dI;vd&R}KN273Nm>8m|2>_2?{OVf>Bk<%`o)NaaVx=qr7KISrs_S@m$ zeO^SLuO3cU>D&iX?KDX8UkvxZ`+#jk9dvDxhqveYS*5^roVCgzJI`w&yC~F(-Fa1u zUDy49J@p|Hn!h-+hHh(FrGE+_dpZx&yX+t@xf{GsWkG?+n1(FyLMH`9bnaOUTa)b| zuKxw-{LTbj55YIAB)DZVWXNt2$BkY;hP=@}Pv!2XF{fn4ijGaOr!S`)gN9KlMCj=- z{lh9?md_w$iwXSPpebbj)Iw6a?IK9uh#*aCtf4rt02&&?g}&$~h*AE-`b8tlE)HVX zDL1gTVGJwTasVZ&mEqv#40yURk=%U!8b;{zU^pP~jfMPLb^yl?-^#9k zW02`^r%^}VBW}~eq-BF3Eq92-9k8R#v$NrRm=SGrIRewGs{W_wOx&q@yD&_~5Hgmc zhzy~Kg#E5vDwUFosFX_4L~}n;WJqSxgjA*yGL{)# zb@o2zeb>65`+<`2A6d_`&8*|8SFA?MJEnJ#70B`L@cKYA%>Skba!be3-CLi*qWVv8 z%UFgryPVBxH;EwP^BnT+Yc`R8XwOym3%*R*k(};-Jn%BETF^l)_u>WNRIl|DStnVv)H)6a;?Kgtl1T!ekEcM)Bz5es3X4}uaD)5OyI zWZuyMoM&u?x5pLXA=g=$6?q6EAl& z5tZlN;iWJB=Dizrg&x%wJRO$9E6th1%m0l--lNezf)2vQRSh&a@FJYrw*!WB^H8y3 zI_2jbB)^Lf(6l*!=xV)HG<1|6t-myzmRSsNzJB9`eP$MunEDcQSMH`MoAT)2$+NM= zvl2T?74g3^8ypeykDp`?yw7VBe44)#A7>BntCaoub1!pv|M1(qaV|I6!^MlJ=xYdZ?Cqqv9Wp4L_cW+6MR+{Ejr)fR<{LcQD2YkQ+@HxVKLq;I|H-sUBr+z z?fh`ruXwy-8?G$)i`l7)y!qBVjDJ*t_n(c!BIh48d&wjyvmA@kb6R*qJ0)Js$yMk$ zG~km8YWa!c8+gN}R^d+Vk8kyg&`vd4$a0F|5rb5mmC#6HFKL4k(}GqmN|gUy%^X~n zZGR)suojmHPNwIt zteLutg=m_TNAK1=K@>9DWiCm)ro1@zcHhBm3#_r$p^)EJ^`4g;(%>!ryy3kZ9$~iR^N?mF{Kg2l zieYM9R5_`Wh$URZkl*Y1F)BK^G3yeJ3=ZHGU$4gNCZllRb0t4vPB~Nh9PqHmA{xQA zG8JdzVg4R_DtYxULfjk59!FB@o=5inDyP*+*GT`&QpQaDE15dKhU7_$f$YnPAT!yR zv>y0Ki_`{j*gADGWMW3l!;gdI8cSFtyHbdmH<5Cw9Fl$}3hV29v2aH=SSw5-33r4J z^YCU~?89GycYebw_U>k<_5ws2^b#HKZA|5DKe)cH6^yU6SG^Ei0!Mo5M0&X%xaO86 zxqf91k#bcgdET*LUwFX&*!#KUh*w|L%z4RVbzlwk*__MdDn>FA)(Q}5p(ONEMN&~` zCKC?9f#TR3Q6pQsqSFtSj!qvbqF`GCPTS9S()T0czfD7&I%r~0h6f9dfu+GWUSp#TuYST#n3?I*Rw z6N3}yqFI&OMpU-xJ*?FIPEWZEqY4%yxY^B%&_(7nzEk>)pH!_#_i&bmrlzrLTL8jp zbXoBvKOlcHpjy@l-tvL)U*aazzHpLCe`X}{vi}w9E>*}cG9ikp*U9K5$K!~GQp-* zy!3%p__pK`b>*JYr%W9dx=hCSu<9d@~_!$%A6 z@<|^`u_E~cIkqhqB|e@(?*}vSO$a;lMAWW6~V5zku2;s1BLmL zxKr*2+I(_CH)&yq61*6jZ$3pomww!N){V>TA4$?H9BFaQZ2GxwwxIZEq3Y>fsA_Z( z(?3Mu*$?)3EzXDz$qIR)BL{Jcvv=sU*(tX=bgeUeXlCfuiX$5hx69XvD~ZKr*eA7SmX z8T9o)5wW_GA-a?$2^n+yz|Ce7D<#p*&YtDQ%70d;8x)%`VZ$%nC|!){V8S~^heKL1Ct`sxHGb>{7Lg!`Cg1qx& zN2oGr-g-sUIU8{DIu6orHWQ_{!>HduL!!1MntMDj0Gix=h}p6Wqnhey;`*-`#VL{B z>(|1^NsHNlF_J8#&Va_zV<4Z`fXv`$^xMbhMB?fS=3%@&u5oR{_0z7RgTy}j4y!XH z=ivkre&7=G=TR7U_um70YtC4Da-f8c5xM~<{vAo*ZrF_Kl7xm8DUl8P+sN@9nXvm( zCM?`;0`(y=?8KGD5G^$mEL`_t$=;82UVkGgo+dD`FIvf}PdAyf8F8?~?=%%Eu(ZSYiqo@)i3l%uZSU=?%pP~|8E{n!L8zL)>2gyqB%{2N(BK;5>MbG9FoR#(;xm2YBnHI-M-TwKoF~pOd z@Ffm@IyXU5geyjJwpwgm5jXc|cx`w}4E?)vUIVgUhwBCHH0bk=Y*eK|0KXbU*pS0O=Y;QE@ z{C+mKoCx01oRQ4;<2xuHm@Nw5eVgu{Jc`@*FCEA4lC4Wq7_PC~pdZp}Qb8JsFZySW+-AmL2tW3Om02J!CK026-{3(Ph+E zI>tc;r%FzOBWIVw$C`f8_S3ddx~2ivPu8Qk(`#vIwieuw@1wgTZh@=uQ&2r)ODeYC zgD8EWdn)-f^G@HGd4K&BX@6Zy?jPpplFTpM5}j|%mBGvOc$gS@zHt;eTq12TY5Hb~AI$ul2R}UppIcZ0Tq~|4qYS3QMelYPI7gvt z!&;_qmL+T$6A0>tJa=Yz2x`?@pqy=h=vJo$c^+*jny~r`souVo`|6uXH+$!ZOm_=R z`{-&CnmivaEggW1sh8nq!6R_{{f%U%X+xg$4@UF%Css^(1j#$KgC2jE1L2dxS=M1U zJ5A_2H!;~lmJLw2{P(Nng?-W~nvr~r3mxAN*Zoru=swwCz`i`!3hBP*J6{JbIv#Yk{K>4V{?DY4wP9}B@I)*MAK{CzX5VJ5VlCWYM=v!S84f+~W|CRGd z)MF7+=sgr^zb`P zGTqIF_Ktl?W{ez9^g)ffo=#xiZ+J`!PTP{Y=4mieCzyPos$d-Vi7w36rin7yBqzR; zx|j@(obzW@{64|r>O2hZ$i{ftY4G^yVR*UjCqMi#!@J*T;m%oyoWEp$nB`=@lann)CV^-Otuh+gWqT2)$JxcVCHH{o@vSl9)nF z#%Ys`dxhjMPNJ*x8M-fc60PJf+D9lqr#-dJRB8Kin)~(zM#q|cMh8n)zcYXiwG(4+-Yu4GbWAl<8A%(dIu(Tk;yN^?%lqn5v7P{G7e zbhB+mWnHlbH~n2c(egac&$kbv3p)oP?nf%58I2$>;|+K<*9X|RJDGaBpTq-i zd(gSH6lM2o(U=wcxRq;0qUC~3w0oo{g|t`n?DR`C)l8QA@FbEG%}C~QiX@S_q0iJ@ zX`rDCn&>36SQ_~|k>2#}B@;U2?0s#Ppxk*08ZFEhC-03z)22$)Qqo2FlV?eZz7>tq z&*IoA+O)e(f=f?&i6gSKaKw^LFzQO7zd%-j^#U)oRb}zGsf5hk7sL#Az`k)nF?3mSq=_ju@)zHM1FGPufyUEewXQ=R{k#3tlfjM4xml&Qerm_RBT(D#~E&pDE z(OLq79eNLSZqL9Z@W#z64^v?b0v-b2&rL8Vql<4anNn*7KmA?^&uxc!1;5BnkB=x> zTZb-%zGxsg9iBx6(1wOs42qNHLeAcR(ihK2@AUwJ$C9XbY8To=1|D`4CqL54NNKM2OZ7)XSp_(bUQ};8DlC(exvu#{x-Vd_XVmr;fG8v`SRnV{{1pPwaq2>d5 zoINj-Mjs01rsy|;^!j4qy*~mz8Q*B2_A7`x)eAQft4i|lX!cqheL(Pr>di)rw(aQt zWxKG~Zl~j3<#r9Vp+aj@D4XN>-i!)|+{R-ir!=vl_K@4tZC@65n(pPi^9_)U(T%pqE5bwK@B zJ9R(*gC8C8S->W=$e7=Q*jp|rbmB%q;ALf4F;5%1jB}xEt1GMG+(DxJrqim9I0!4h z#+`~W;-ZbV;+5KEwBUFZ`XnjQ*gN*LdeRkg%F&7#tUf{C>^g_@cj-|33s#h`ctG1u zSfatJ*(fjCgR5Rup{k5B9RE{Gw);k)$yWzFbg_&M{R`usosuE8R{LPp@<2FYIwUH{ z?xNpc(ve?LF|!;d(Nw&;o|8Sm4F12iTloOvO@1!n4JC?1&A*@1p5Ps`0p- zQy7>;Hoo#FAHOYNc2#ra6n6?tZ8~UHdl79IE6p@6okv})N@>K|L{xK2$H2UIw6kBC z9=sV1=v)LkK`(H{=`-}3{t(q^*aTIl^kBD4Hq&(D0(Yc-7ldrKhX%b8_}kXa&iwcY_-RS-(7*t?o~MC( zofT|K7QrYvb5Xb0NMcp-iq_T1bMN<=5|i4abi?q8Xh)Y)P&kR><`~mw$}^eLc~0CK zFEYqz_~f@3`8@?oWPEt5 zdS`yUTQ{%ldxH0zDR42ZNwiI&0tz&e*qKq8?4h#rtZlA2+`AGC_t%<(b67gat;{9{ z^Y3$yFI=>nYRS=Y)g7qh^oAap=0R&F)zI6*886`XbqpVw!u+Z!B!501#?)zrw5-#P zZksoq+Gsm4rLLCbfm9p2lxnf#uf1d^{JR2wO1?m{PZc?s(g{W{*I|UNEjC{CY{ z!C{Mb;KUDim_Pm9+_U{g^s@U^qOt22M|ykjs-JYOroEPkwvk#R1oPx4d z>*0&m7goplEDYZ8VI?2Lfp<(lNfw+=Eq+qym~st0_sC(s!dLwHEtsE_{S&VQ|8gxmnxZ)>FGGJ>n5t68$`p^ zr9}P0C>oz;%=NAgU4q>z`pT*%H}k_<1dsjzDH z8{oEIzThEnLLFNNex#;w&s*PsAFV!Nw%lsmGWHO>O=oxc?GSv88{wtzE>L~^ zogXp$Id9~$7LPlI@G46NY42WrGUu`frZf9dcdrFr`>~j${>g*WSGs6UQw*$E@WT8x zvv{3}e|a5w2IXA8R}CZv;x?Fsvde{?Xv9)l?C(#PdCa%}8Kp=I617k{QB_o^uVWw3 zBMGPbtiVsw4bJ@$gKt`Gtkk(6GUcqmfNc07D8chq~pRE}-8E9e3#BP500dd|< zZY{rH-8tT9PcDfc=L1KD-0_C+SGc599p^SI7kz0~BQdh38H zO4ohI@Lyi!z;rK8O8pqQy=e-4^2d-KQd~-=z5hi+FR$Z9G1 zmfoL5en#dnEy14Dxe`gy zBj(%J99ae`pLz25;RbRdX@~@fU8SvC^q9j_=QGXb7hpWo1iKp+KuVY3Ij|TaX*g6h z@TnWGcmBh&8x{PpY=8Pmx`fwK`o=Tv19&#$CX_0tvwDtFtisJe=<&D?!}J;;l8%St z!^=R-<|$gsSD?u(@2iM@m?`pATD znE{h*_3e_v+{u}22l~kC56RM6NIxZ;5jV{!^5SkGOh3GwJbeEem6rTP?%g^ptZ(4e zOK0KfsWULyW)DvMxtmK1xCdL0)UvYX@odP^YZsO_JsJ#=Fmi`SIpCIhd^>}p`AkI5_(I5Mb(`{_-b+muTm0&&7wYBbikH& zS4*R7X)CFq3&5$?lr_HU&zc&)VU-lEp~dp1kTv{E()A9|@3$1`+%4i@S(!=)9@fB< z`>}1DkVD&j zpL=5VmDMuyWyQ_c!t2x^d~x6ro>^r{i%oiIo2w@}G^7Yj;z2mvV@)dV)e-UE;iURy zK1g`n1630dBp&L8OCL+f`>Vepyj6~z3Myu5{F;c;r!JamoIxj?UqiKaQ~Hu&>8y?{ zI{VOCG`u2>$xC10ESm@n9bqGQ(e~l{Fb@oP>`g0l($F|S1*1y>&}3;ODKM^u6x zr3F*MF)@5Up3j%WTjG~6E=;(`3*I{A?`!FR+#8(GupWJS{=)utC+0_OBdJ;)2Q?ew znTg`Fu*osu!nKN>@eQJ2K2TK z(geqgq<{H#!9%-=ZW7*`(qI`nVPhT_9&?;F2>FEzQlHQ|+l#(B`yZEH>q?uJUdN_f ze{7tz8FQEmcxv1x8eK7jFH{H?(%U6E|914xj&W5CD=07g(^9k}?axRun+|F-od%;_cdW9|J zjVKwgh|`$2f+(sNp>t;f4f=GDsD9ed+*O|g#tPx|DaO&8(@V&fyb;W^!tG?l0W0Qi zq#t7+q(E!q^O^o>`)TogBbpWB&Q(YT(N?np=B}9%Etg)4P6;OHzS060X?&&4O`~y& zwm7YSsDa;H|Kinp9#gA7@Tv*pag^Zt0B0s9}8ZwV~q4otUEnV^0qHOdJ2&7fF zlQC(-0UA%+iSOW6=BxMwV&0v{iA=ZC$U~Cc!4zA1UAqLul$H}CxoT#haU?mSeV=Be z-{tgmRJrS2KkbzgGici0bQ&AwMB_Xjlh`{e=;xm4bVL1Oa<`kI&ex(*XUl#R7uUv9 z!d-Qs^&Pec@8uvS^E)^Y6v;XJ*6^Avba4+^hrG>-q(iX|lH0Q@!`fd~< zx<^maxDVCLlyCmD{YDF!H7=QHV4u;V_JcJ3@<)>Hu!gyMWjl^Nvxdh15=W_$o%qJ> z29|HSAvhXk(YE^%?(SGX6GvKOw6;hTJMaFy& zA`Q5Y#7_7`O;bv#YpDWC7wXujmyV@jc0z{MFpPOUaw=^YQ%X<#c|Z=oo<;Ij?|nkp8#&1ugP9M#r>oq_O+EaZTwRRK4bf8>AFbQ}Q%AU#-Lk8^;K| zTmhcCMKE%Qxp@Vsh7bt!lz(NSD)Gfswnvei z)ud!TJH%WL&_ktyJ@#e-BOWMZY{y@i#JpBeC2_K^_?e0MeDIdz{Qb{!_~i>@?BD!- z#+$!N!q2Mr_=(atFekCTDnQ4b22Q>zI%iSEW!H2Nwc?LNpYJABZJ!R$vFOzGln;=rCZN1H2d;1%x&04@=`*; zR^|gUHvS6~pAVv)H&4NE)eEegc`N8n?*#jyrGnG^Jk47$pXsaJ3g1pWrfowDc**|f zFf#Tcc}AX7Y`O(wy*lZG`2x?W`NUYpM@?!QwX~SRE z`{jF9Y*GSz*tQxX9dALW<1>(5u!WuRRF28dx8${r*J0phWT(i#p&RtxQ@OZN!gs!y zwyGVY=LnbuhC;qsR`qVu$aqWUgd zTCR|UPENMW5g9x5NzBJ#`<5WFnPs=ta3zQ_Pe{)Z0-D=Ju;9uIIIP>whD`EdSLj_9 z?U)tA8r_`@UEgc*{P0ceX#fA9CHy&2uTZA}B5QJI>?aU;?%BKbq~yq4z8&G2e4VBtCEyOfH=b9`Yk$gv@5rpML^YKR00KFM7r*X)J**m*sR( z#(DaBM-gu*oCRJll7afUzj^7lEC_i%ku)5?MP?S~a#=FRi06fTA~iY+^G{ggfu5&O zEj@}}hpY|HW~U5M67J|m;q~?c5V_buXCV{cYjcy zzuPG;P8K>uqDWrQZ*IAAJD!#NM~~R_kv>mt*mYFsub0}!nq3o~bNN|tEaW{+@pul) z=F36HKn{F}kb}i zCrdE>+aTJujH8tw?eWkf!9{jt2@ZF+LdVusv`p(KnpXIrk)e|4U;bF(9Vy$iRj@Krj~WcP3SNh=z|XKI8!s_%+VDCn9exO+@|@x5+kVkYV?#2w z$`dXnK4W(UZh$@IrR)@_Vh!uI!uldFEEi_xw^kDLRK70Qbtj`kVmzj;l){qThj9FA zCF;U-;mP*nxVol0- z+jtgFk8Hvaiy@588V*`#oiN&WE=GGI*CEVMjK|5qi3WrK_dBdKwy`TOnz9jAm;caZDF*f$87Zju(*iB0 zB%;Hc2{cAt4hn~|BUe<7nQtD_ug}DAfA)P$d~uVP))M-3 zj(rn)?0-VB#Y|!{YZQu&pU=FxnZaa+{@RVQS2mKcS1}+xc?w3jeP?p7?LoPQE_%8-Q|Kk0iHq<$eXSUe za_K7QJ0+a{TC8uk=J+&n%#Tp@ODn0&LmQ0Ew!raMUgDSZTbO0K26q+)@?$4lq&;5y z7$Xf$Iz00nKe7%mNC}xT)5%!s@|bBhJAo1>)5t*jcrcD=!_a{?eEVb^`7ru5t8r`z zdCYDk+n(7ouX2CEb?G;dCi5TFI;ceDG|ZU@MH~C9`=`j(PZ`tSys;I!jJKdklst}I zQj3<;b~B%c_6mECP1GnfhGssxjjJ_;nMa{Do_HFAIlig(wJ~R~F|GzT27RYJS0u5( z^*dHJhSAUQb7A_~Y;NOY21Z=?fJ{{*lx5|kN+-jQtKE+W?2ORnfE66QevmmgbOI`u z?}dwo99-*hfJ?n!$cITvkkWjNG!+by&C{3DM=2Zan^iMtUUMq0`LYRT6*P++n*L)> z3I0a)jV#VDScW@;gbaL@J%(RFd{oQenV%Y1lKvlFm>f@X;}U3t7LU1p2e8n%fPCC{ zpDUhz2bXR*LQaQ{L_<~+l*QFB+HM9`_iG4F%TXA7&=LAO*VKGpHfEuiLXEz387mhw;R=p6Sy1|dusncjg|}9)NMvz z(CYmMJZQKCb#r)(eIJI8FC=39EnRH6(~CyMC-Bfo6Wo1L0;l)2gPb%&BWE?ENG%1s zy&8C{^!@1XrG+;QV|i!j#=(oZyoozcd;Bkwo`nH0_|g#Gycc}olg_~>CIkxp8wMU_ zCNNxc1;Nfi?w@}(^W*Y*yFu>^`hEC!np|osvYk1B$-3mhXo+s%MjH?(Ya7I>#ksp?TukRur`xb}KxCr46$ zX7Mij%K2T%eJC8fc-6sDoOFK=N*^p?N1V99S_G@Hqp!_p$IP^W1RFQv9u`bCd>1E0 zA*V@p;~dhxdOLCT3FOS(oQS^KR*=1K0FHuR@t#q=uoDiX5r0c)+PSq{QhYs@oJqi~ zsi$!5s+kyIVusPrZ=rEao9#%v955n91- z7_7zhb3%B%`Of5+ZycHX^dYO#vKUf@&$T|!2tL<$LRlX`-q$jSH4!J>$958zp~Z}o z*%SKF@DX!x`azJLB1yczyMQ7HcMcaPI3qI_=C>Du{*6X1x_AIL=OM;QU%(Hcb8$<~ zNUU%Fh4Jl+@%;V*Uc6t5S2y(Lwf)cXGwWXQG8<}nsWszyovi0PQy~mJRG!(-?HPkx z=VlXiQcU`U9OmVa#n7=$pS3#WDCFOT&WO=>;Mp4vVq9!E#lVGjovELQS)n_bQshQY z1~-!So()XTrZ1$T2BD>M7<`>#2^TZ~?rV(#lj?dxhwZ|(zmjm9OC64#^c=nZc3@o1 zDKyqsB+rcvFn`BddM0u*8q^ARjo;RI^rAcV@Ak#ffjiK5H3yeQJP~ES7y&z#u8@rv zRba7G54=6C54gdL9X9V2tGa(De0ua3?4SF9hV@Puo;MA~4O=Nn`>>7}WXxxzezn-A zI&TMEtx7og%N*KO^`L#|E^EBY4e}B);Cj_w=FtrWbXoeFpZdiTr#r==PzFO^1U}&{ z4i)3&bBHf4+F?`mVVpc`06nWJ(e-5`sm|7e+lvigaOPE*m(s(IS=UUL#Y)p}nWy1) z=y(`2tOK5mUdbw~FM_IXf@|P~E~p(@0fzRgh=*+lDD;_LfUaZ?X#L@FX2BbpBRnHtSH{vYH)3dOkQ5Fb8I8Xi z#^cWyXG9$|0|VAxqQxb$FzO4yZSE$UyLCUiU49EIaWjM~Tlx-8UkwGPg2{0G%MK_l z&4MpICaj_}KuBr>Sc@VE)P<8HzLKKUzhdO^`77Yw|Ad?|od)MrZbM?yE>=?`nl)*= z%bIvkVzmS(?4>`3kT)kCzGP}clUE+Q@v5e+y3lQ*8J=}p zg7zsrT;t|PFn0Ml7^v99t_}2Ly`|;Y$?qzO*V^N(3gZuJLT<9YD3@X ze-NLrN$3nphqeFh2YBO5R5Fz5#JDw}?0p(%vg(q#cueUl2&M685V8CNLNjD#sZxlrrNv%^-j zz-E7YcEV;0R_kpaEB#8G-jiJc#`uLcSqXQrv@~AVE}R~}r~%%7yYXb-1|cIuNs5g% zt}}NcyB|)*P-DtnS6mDmEwAw7!<*1y)mLiw;}T=?@es-n4t_uzV}0Ssp)6SQyR zDxU11vEDz4+?;Nh=>CN1iC2Tj`}0V%lh9XN8BX4sH*sG_93;VE3Dg}!fUPE zh5hD|{E|7A_-wHbKkj)ndcSc%CAUsG&nOC{p436gPZo|jm6P15_rWsZ4r#bNlGIe_ zk=NO$$wiMqk}Q?Xc&#{&iu(O%7|mbq%Yy;vy?E z#)};_{we7b{e!T3)~ts2B=X<2|DfT7aOZmSn&0}5=X15!@j+|S_=Eqh@!>W>yi1rB zKj+yrY*}p~WZY+>sIdn3#`)7teh*=O=Q)UMpAI^kvYC&m6UbXPeNz7NC@GW?BPI?t zH1vKE`8tiGcFU9nFSioRI-d;<{&!&4!XKoq!3DS*g(R-8j=XJ|Ld~TOK@_}H@KH`> zZC@a>M&E_}$7XD(h zeKB48GRAufvnMxgV$-EULT%PVOvoBYSbBzdZ{NUJv(+$c#8^i6{V!N2cY#D2-el$n z2GRlXnP|8^g{+v_22|MtvVAtf_Pqz;+~;Yqd*N8}ykZ~ojwxYfB9%EgUu}q99?TZK zyTt0Kl)%6CN?7{J3~U+=c-Qw8{Bdh*ex2GT-p}edA75|4A02DThaIrP`v&__ar6(2 zIV4GCf{kHDNfay%(t*u8`oY7jyDB}>o(ntvkECd(6BnCFU|m8$!}kEm{Vs=ASJTjB zpA0As<-?h-Pj(MH?$K*DXJ~J8Ij5W~VxoK}VZ@x1c9u-^>SXKFG%nH1NKrBltvzVx9>5+@r#tE9~b^bTV(@IzK0Y z+B|>I(VI&i>4wmVjq1Xk{uSBtbsKZ8J)L+65?F`GG}xdrj_J$SzsmmW>(2g60J~imaq_%1fsFJgsR!&T!mG$3w zf9)`Si7>a@x?Bd^w>R-i4*%u7iazqQHMFrVvJ~Cl7Ltl5r7&*W4XWFLz+Ln5dcY(1)^w^pfTU^83CmT>IpaXX{32qCOH|TIPm~N4H ziMAc$nB${JgU+OYcg022c*O8xeHt|G*M45+{(15(xB%vse}Q(>Jy5F#LZ*@ z^L~E`^;2ebawfm_3I^$sQ1esYc?s=qc22ycR86xsyKZ`5|Ox%IVw94rKX+7_#WK z3K>W>A`1uH$?|U=bi8H?Et&h5M(R!`<$?pZY?2L9zK2>1S#PhdGj?)SESzb-%9JS_ zU=@oWqrH|1G&nyYd2}8GJgi_1jZE0U;4!SXog}L(e~5Kv@57#X+eu1rnIIePfj+T4 zkRX>uFKr7TmWgL*Nbg!&*VzdQpVG-AA|gvK#xW1J8PJTG9fXe+aDDxdbJGZ!gvE$jDl9o=n2q~Ah3J>AKZ>7K%Q ze;7A7GL7*o2z3x<5CEDFLP+1E9vCOuMOsgr3tqBks4=jOe0uc{}CJt$Rse^yt;DQo8M46 zDF;GbC17=SDwPQYp8`a9x<`c3fk^yK8V= zTQtT--$utb$GF*JSHaEkvmwvd3`Ph}`)f(o?0DY;IBe8p%r^?6=SC#6R&tfB1((28FxSc?ug84j z3QSj$GoNaydjADFsdg)uJ6n$H``ALSRNf&^8;_EcznsW9T_1XS_9`;s3u3fM03K7R zz@hP0NLy`4OvY54xv-Va7yLd?^I5dLIb2{${_v7xk6_+7jw=mI;+>PT@Y)PYZS#J> zzy=jo;%PE#(pti*%woZBgaib%|A16sXHTbez=f(EB-fz@R4UHE#K9o4xN{%9D3l}@ zY*i$gp(B|cJB^6fkQ_fkyA9`5j23oLe*Dzgmat835v#e8hjA*d*f=m8V;9*%h(ZY6 zpwjklS94+JNy5|Oe$~IJ9Cin)FZ=<+-9dn=}oSS%Ih*akkaR+v%;?XV!$CMrhy@7Asg_Ge> zvL}G~J#dwob>Ie>UTIDuZ5na^?Vb31&UR|*!lT9B1X?Qpn?&@yCCh`9X^#6+jZ;C3vPt0bZKKY(t|6Xxf+m!zca7px2=;5H{1 zM*T^m>SFa&Jm4;;O1_}R^09R2eH6;X>I=`aHNMR-#2wbNM^VIDazqH%b z)#wV07MKEqt7@1Q(u~pC61eq_8Y(aRO0Ua16LxhZH#zt-dAKK%5|f8Cd};3G({9`mHRs9u1I{_Bn|4*-dj6Ov&@u8M3U7YB<|~+n}(E# zG9v9#npzqfzw7r8aPzn>-tTi>ujiBg*o+eA+ioMZB?-d4>2@?NOPbLK7r>1jJ)kA! z91cG>5XaW55Dr%>`>G# zYNOEbTq^7~P1q~>Bd@ge6)shm3RgcLqsWf!A@X&$pf}!9xHT&PQj8vp&tn@Eg}Kvk z&-`XIKkthnXFuWK!T)gL+9jB zNrfltc2ItqJMaAXlwGur&^z;|+}g>Xj~%a}eexuZ@;BjRyO+Z3F2%ytskdlF<^gIJ zE-Nis`PI2MAlmm<#LM9feV0e9k(Cy^Lw-S#*RW6gQ(ysB3w7aY%s5(=Jccd$5^D9&hvsG8mqXt^f;oS*DYT{;MubPBwPT?8 zQ{@w#f9wtUT}Q#ubX^EYx(^;ualvvECw|zr5 zM;Cm)sE@GkvI#|W@#O|M=AzVGA_XC0+8f|p#^q6x#6-a)Nbd)U6l1{`{>#r`!jpuxNko^2|_4%L?^ zcFQJ?i;%N@@C?3YWJW2}g|*59*-WcD1+Uykqf;u$)G3qh&pk+4W#@$s-7X2q8~d`} z=+TO9J$8{}OEc@8`yW}jZGv@Y4#10(4p^SG3vO&!imPS}!o_E$?=OV$OE)v+a9K8| z>#ySWyH<+Z^eW`)m!k0E#cEQY?Et;}xAR=dA2O2E#8H=vFzR~{`a8Izaa;>)#4dr( zH(I#;^JSzKr44?a3c0^aKIQ2eiRr$3g#KBfk^91_ zpkYF!lcpG-bDjFDxzXYu&rv<=0R(Bu>Epx+vQXuDa9mwMXe)*>iN+G zz6=jT&*2L&cTy4LXQTbrdZyCr;%K3~r}T_XKT7-Dtk6saz-(|L{5!+z>_V87UcUe}o%7(TcvHMwmLfZO(8`GD;WWv{}&^md{nUX%JCkG5>W)p3r(zmwspa&kDkI^CeQ2?>1CZUfg3yu-rF zne6)B3$;>`L3Y(1i)>>=@%vzS_pTieo#jHM<3EbKLOf{y;pH%L>pX}~@}vu${>pN{ zFQ*Kz2r=QL3Fy6=EIhH8&7Q9p^Od7tcwJee7~1n44*!+{F&j?Po5=|r6F7sTDkYzN zNRCo}?nhF&a1+nGGllYwE*$(Vod+bOf{o68^z-gSUUy>Q1lUwk*UgvV&AQ0F@>;=FGRm1a{(yU`nPViDZ>yN?3&)F@)L z7j&I|5~imPWWmpf*BmK=j^~=`-26BgqqmTU3xjBNua4~8LvoS~AIwhbBlz2rAN_xn+>bFy;CF|I8P@|y2GucsQ>|T>f^rzmPlC?@rEkhYSg0vQ|#%c z&KXv{bqWfVyM%lNW7pO;=aRCIYsO4!UVWXC122s}j)#>hr+4!9xD%pN`gJyY`;rIy z7R#X9HX8e~jVhy@g)71Hgs|JKbaB!xvd!2HhpvBuQ~koBT{3PTU@B+@*uLiI|$M& zW(v#W0l3}(#@{O?PW~y|cW4ewcM@sOUk5QX_9dw0-=vjU)nfVnSMccdH?(dU19b&& zslYvmUN8EER~?4Q5D1 zj)Kc8a~NB8R16!hB%9a|V3MH+H9hBur}its&wdyjxHt+!9NqEAL1WBY)eDLaz7y4F z>Ec{FiC5k76+Nm%cw+PlpVnT7q~q)HezT^OGarxNq-?K_Q#5Z5*vn@=sL<_n51P_f z#*=seH14*+^Tvr{Sp69^_U#SU+$ZBTV9khVGnikH7wi@nlT%WCp6 zDSwVMb4VB@PBI<xKrpu~I-Z7&^3N{M5fHEIbincV{(Pc}yDrx6a#w+D-Vq-=h9)KJN#B^S3Ajm7PO zRT$u3&g)j}K=kW^T|5Y*ey6c{zg1#S-4@$!cic3i9!<-Vd0bAU z5L#wj=)c}pc)v)CZe;wFUGw&()sHI3b!e7Y-{Y7NT8@gBtM`)9a*^WUs}EHAx)~;K z1F@uv|@FDF4g=#%682~ z7-Bq8@Z9AM*X~LC8K0GqF}W)l_L<7sUux*lk*Bb~<$vgJZ^M>0MM8LaJ*XzV73iZc z8P1;|zU}Eoms0BJY^@G8dCh?;!)>(3{Jq#R!&j*5SO9TH{-ZnBEvV0|JFND<9IA{6 zrYFyy@lXR>x>Ml^F}i(GwQV4N{uU_j7A)-#ZA`GaXa}|!YjW^O2hRBX8V9@>iOyLW zkRSd3Tw{qHAE=4l9d`3v`x)HZa1&kY<4m2+(qOXV6w&8-7`k1NcAuwCLdd*_6yVpB zEqd$}YWB>cJF{#GZ_Yg+3+?h=d}tH~`!-pK1H`j*PuxNGHShAMY1^n_Wf0FBlEpf% zH`rSLAA01b(Ln9}cwRjfPpuEf)OmmK+`CSwOaS&=G?}9gZKu=|*0}Cj54c)QLRXbg zXh`phNf~Qtz?y?H<)MGV&qbOrv;S|%)P4-tg96a5LlwLT^gy+V(`hQ0N;#w-aO#^r zC6E14=&xuNGRrLB#nMJ0{n!htIQE^gO+L~;scW15Z39Q#dd&UBy=;70hkTO@1R+~r zcop>nYg~_F@uUp7=G1WXYjDYr99qDI|2^gj1Kz@~S=rF8G*srjzB{JfJdL@lx?q1@ z>2Co@``5h1a4X%1)ozV}>jPDVF|S9#8$K(P&p#v!dmakOXC#K;?Keb+-;ty31q!## zg~N$0FtI8Iu8}idV)&|&T!vqCG5C03i5`k zvty%*SbN|W`dUqn(&dh`>i%mrD zUGebqXCtVsIE`n&sH4H@ez5dgG0D5Or%07d3LP?cuqwZ}>Jf23^AA*n}WiAQf;m77NVlN$t#qF7}%Y#ZnbYri#$H6tpZ z@XTXKQw$LQocuwbtjk&U@c<=PP7;r-52MFJ|Ir0gL3+;}DAgjH9MaaY&W$^CwBJ>D zEM-ExjwhhG?j@Y?umHo(8lrq-dpw{W3!!IrL1fD&t~~w%LYLG*vGE7IGKU0Z)?SS8 zX~PpeT;bi;(PW?b3%du-Lzd>~KZ>5??;AhF*_U34)3O+iqP7Z$WdeC*{*#`~bdarb zX4^Jx`ltShmJG@guI%p2ZXGo!@ahgar3mBh*0tcWXdZWoD`nM^y+X>kxj1?4Bs6Ox z`0b#9g~uM?sdhb4Op~(pp4PMvCi9-uQv8$>g=YK^o1g`@((P&#<<> zwUo~sTzL2V3KBl#Q2mDfV#$mrbSFEC7AZ1GB9x04kKe|@X0s(mkSbg|Vt|J=RzvO+ zQ^ZY9@H4KKml-UiwPU~Hq2?wW;Z+I6Lnq_djoUFvZ4^}KZQ&(xJ0YRBHZ2-4nDWwl zuzSa4bbB&zV2c}Xyb(a@?KZ%~lik36TZ2%RqAPw|=EHr}rM#__aT|PRnqV}^L};{_ zL0Wky6%T&SB6S^C#kzzYlrkuh)l#~$;jGo-D7T^5uUQ-H4P(K#^eo2T-HNZI`A~p~ z#5;eL$p;?yp-9O;rFC&P?yvccduye;c=cg!x3-S6KAc9wrPYe(A@^9Pw1!jcQ^|Pq z2wpxwK@F;|tW}Z1tJ4#?`o02 zj9p7wIKCU}xfjx!uS>YyX)m@H!eLxjqDOs>UOxWcaBP=!2Z{`;NI&~0Ul{Y0_kQb4 zUv)g_i-9Ceej6+EGwA_OUIW^dlyT2HB|?>+6_n&x(t_bXCAN4sUXc^Xvs3*f2hJ*X zn_oqbF6Hvrd}rFP*i75zoS_^m>5S6)ROsb6m9C^$(svI(i1ySJZ}oZrs_t7LY2QZB zX?=+!Vwqn~62QOXRyQ*UDl2D~Sj7AO3ei2BAApe7Ln7fJ(##!)sw) z{Q_}8r8$Ib%Yu|iDnjX%CBg<5KX6(VI$F(Bk8(P-z~aJgG+{=A5bIY+0~@`0&fA&n zu(Y0gE&D@J<&mE+sMdA%;`2gi!b)?n0iq*a5!zS zO%#@F9xn7-vyRRU+97r=k_k7rY^BcEhOyd;*K8a8?%J2O?RrLW z{!v`It&{L?>kLK8qrP-)>1rIj=zoIx@-nj3$%6Accf+4?8%Z~^6cRdr1@jF*6*rvs zfooy8qEt!+_?!JEtGsjE!_17lHvXVcscX5+=`#_cLby(>2gl^w{Ng;{GJpAx# z7-uEN&gT;_y;~06cL|c5s0y@E=?{g=FH`Wv|8dup+mvM!!VAxx;*~LT`Gk&~O}0*e ziv^YF{kQ;5pBg54BA27i&BMaa19hW*7WoRoypQ6IT{|G5-z7M4%2pAZJ`{SCA%*%5 z1@p|gRCg2VgEzBz2Ej(;E30C(sgqC4HSi^lI94#y% z)l=VS%ncN;rv9K`9pWiG!G&Ig8Npr`OUkM1fg`=FWF`JV>{D5bNAA7A=+7-EzcZP# z3JMUV{?_~ zV;*kIxFp=JyH2?d9z1Zubc!r#Ap_@G!i}vrgiFfJRQ|Oc#OS}F)VYhrr}BApcaR6D zC9UB0e(U+v?RNCP6C2PcB$9%-7j%913+k8d!`)>c<=V+Xn3mDQ;r`QdDqOQcKA`V2 zRO#MFY2sMLgLOES5_UM6(g6=Ua!+l5vW$_C+W7$VvM&+q7Wsh5Rx8TWI!iJB%XsIN3^Ho) zgHQR9l$UcC&ZVs3!8Lk9@zxtSC{M&~n?A_fe<;R;))D0WJA_@D_2A&cm83i-&3dFx zd}HiSHddX+IX3EiOY<3D%8!EF@vicLcaMWpWuS0a+XaoTG~i+R6gZ;w4}$;7fqs`j zjQcvcY`727t(Jnn?@OWWP0}nEN3v(q~e20RvmZ?6%{wYGx#*R`}BwC zp0ly%_J8tr<1}gdoe*@Mmy18W3vh?SEiPOXi@MXhVyp5vdtA_0syy7rDT8&`FDRBv z&)49u4yriBb~`G@rc0l1p1kkd1g!FI#S3vO&@DR#zQ(>|)&1{bcJUr~8GeT*wJKo3 ztVZd7(oESogE*+D>`9O6hti#ei|FU%@u+2a9Og))l$C#M@uAlV44T(S--nlCm*8xy zet8A+XUK7mvm6aqp5QUthp~Q@jGwmWKW;PM+kOl3q7Pu@yt{G@ zr(SYTPX~GTYdhp&t3Kghy)DRLL-DXq9*)W`h9ZvuTHugIGwMvKw^kRStkWUr=e9>N zVof6)@r?)DYm;f=+$WTpv;mh|&46NMiM&g8VBE0E%SI9EOjNH8ut^uE_b82S4HsbaE|PJ{!p5$X(Za6 z&xgrveP~n42_dCs1iU+93qL-)VwKAxjQ=}W-u1CFCcccsResaa@Wf^@C%O}wjT#Gk zO)o-%k2X06Ugb}j!#LPoa$K(S)D*TYBS$#ls%o*OP7 zJ~#^>&eg}L>c?PGzZ$l-zaU<9mojq~BgAq?eY!QKUI8EN#3=pu!oBVDAaG(0<@pmE z?a!g&+(U$*2dx+Who_UB@ZfSQDf7Jq)0>9j?$pbWz4JdYam#m%jA|6gI2%Tn_zL3Z z_Iz}m18;h=kUa;q(fE^#EvrF?Hcxb% z*Z?o>_d|EZ1j?|`p+oy!cz|rA_}X!-@HEg=<}xV)&N!Z=(EaMdm9wD96oIJ@DybVp5q$~Yei)6qejtI^WV?GlGh z|3o*W+4ruB6{NjAlFmH*C$N;qloh|E#`t79IJW_|do>Px%^8DeA-Vh|gINw7!Wg;}3Ano^2dt8%bYJ zS@Xz;K9q27m`rONN&A*Uy8SJIhqsSp_sDqO)?+>=_xnXr6>_nr!xF*i;tXgszJ~U? zrl83DhA)hY$ssz6&IN0u?txOyyzYqCGY6o9?KLrY!%|L?a-Y%L7vSuLz`fI}IA1Z4 z?{pZfw40&IFN5dsjjLysW}~z?c*}2ksmb(S)rsOe2lBCmuF;2b=d^#T*gXxej@H%(`y)N?*xbb{SR%6X~wUv9}f&*6zaP-~EbV;!}mtK|>e z)Ex{jFK&uP?sy?#ks#HP~EehaA4O4 zIxxhG`y0FS!J{&c|Cq$ZjoN(wrL)pJGD&h)hAP`VKFPdsDSu35kJ)I$JhU*u``DY z!i&?eQ#FFk^#@XUbuFE|rKFilK2cy~20fiUnhknX(}t}xX}66kEs`=f!;{wXrrZ|Z z{`DZg_byhNUvENx}Kv~kE&UH={5{^m?rPis>C4sm-OP?O4QGp zPQi~C1362Xv*$sA{i0jq)YG?Uy|V}9Y7C)}J;hZ2{W<+vAIs6#Pw;`B7rD&BlfMP4 zackocPMmw0M;DIe(-V(#Lr#`*ulaqY^T1C^gRE&vjYM63*LM_!PBxH(_F?&mkEX)h zKlAAGi>*qlZ5j}As#SWHpL3J77til&$*qb>&}pJfZZ=>XS=R^XzT*;h{HzCc z3sQJhb|uC1c>wd}>7?UuL#PSq0gfwuX`F`}HOJ?3f8B9>He)R}o;k-?PBrrItDCsf znkq_YRr2>WN0r?tZ&7a1jaK$HJgS_cd5~)yS8#OsTDsJJ0d9AHhJQ|e<#7@tvil0@ z^DR1t{|!u|zi9yupDs(8mUGccwTsPc>Fg&rTy+{F9|1f`ID-3!Jcoh>(yTc%n_AT3 zq4QW*!M?MF*jcSyZ23MQJ@)ep^!nuN|57Ar{Q zfl}qJgL0UusnV=wyb=dEDmzWvpftE>&t+45v3{c<5JgD3@Fc_Pq%l$<@TGP zuJZ@+NK8ZCTlso^A%dI`2|(_tiWTn6?p2_72f-5s&dA`@ya1+gdGbac>A?h?7sC6 z4anKU+0t2m_1uH3abh#3`)A<9rA8Qhy%EawmJ|-(X@{HDp25PUbwWzcGt?N{3Muzu zsd`boxc2B>n)rS?Pc7=h-8CMvxdc~AF6~BJU2b7&)*@Vg*c_7Q4#fVw9#Wd_{~+{t zKiE;1%1-YiAtf#v*A40-SM!*R&~FGE^=RR|x$k*L|7ebk>OqgYY!%FRZsuVde=#3b zCFQ-NQpctVuQx8FpS{=Pn&0sd*7G-QWe+F{I0cE_(;AJ|Mh94vJ8QpowifGd>(9A-h+kiU3k!%uatLlv~*vn=bo$Au+Fn< z!n=2O*s1+XJoK+S>^A+vHlhf*Hq!5|Bwma!*MRreyHn_GeKEoHp>U$jlMSuki~Y__ ze3*1{_!Bb==2Ugn>oSNMOU%xj{`ov zAYkH|Er{#~3*u{IZuE{w<*4ncw+A zV<%ePBJoS_4yS_~Qov!eCCnV)+jj=rYcz4k zp$-u4w+BW(EyJs)I-|1JGmIQig6A!_LtRp}l>gVkNqhl2&d|nrzEg0Uxg2NoaK?94 z8FDovhP_|q%8qW49HVE+|K)`Wfxm}iO5R?i&vNh+Z!Ukb@B5-4hvk?=e4 z9?p!PgD$_v!LQ}JaPH{ASX`Ne-VLka-K6gnR`m{z6-wFNnFDy`vM^5HSpY}td(fX_ zZ-hYUyYF5fz|*cxXeV`8AJ5xFkB*Myb0@Czhbt$PZo+V-^|Qs~@UR#THmjpn zvNY?meoT)CoTU-huF$RS+LYZGOAmuSh#Rl3=ZU+WS?z~AhdrD~soOHC@k$sEIqo8M z9coU8k98IbciO`;qiVTs#3gc)?zN>i2E)U3qv(dF54By4=id_hu+66tbnE~wPP+jE z)^Ep#56N_Q@>I0Q?JKd~0;z7v4vsFlrPP&3^CORcS9UHuO#>`2QtJbgK zgkfL!o(^)}hceQB<3TImwDF9NBe>Y_8V9}06z8a9@|5|P*lplxh%-2dwo-P|d9nxi z3sPQf>u9m;$8*Z=xJ-PZeHc1Cc*ydv6&x6BNcXF^QQXZAFg*RWl*gBL{56tit=E58 zFlYy;cKVH*w-3dj&;mHory0h`rhvKmdH#7OlowCW!ad;waCYr)TnPPzb$d6nuX#P` zn`=SXuMarnOILu6eW-E41ggnNqiZ%2qXm?iXa_ zH9t!jU{c2+AIS2x@f57Nf&$lcBFecU zUdu8T&pk=N9UtRxdvlG%H~b`=UKE6>VQDaXu~PQXViH)UpT^XMBXMuQFvZ8W19`Yn zccuAfJ>^6lOXbu@>dO8p!?;??k0p0oE`BuVfnFyI@v5gc!rxfjf+r#ExedH;_Yf-Q zE*B%uslc$=*KvDrGj^O`3Ofd@5?W83mEKWxy6dN;+;d0i?uBdMY&Az_mf8ptHaPMAknOnK8`4az3uI9+`%xiLtg$jsg{iQi(}zm z?}9!mU#-ND9Xq8wW-SJ`TEV!hrozG9L%92d*(@I2$B#6um4?dYN{5A}%E9qLO7r^Z z{9UVwAI;s)fAkLVuef*YJ+6t;CbVPgHzO(H*c&$3{8gAVH;4W!n5=t3)dgoW7+TF^iQdgI9Ce?w#DJXBqV!X-!dI{>eH_oJOM?9}9?oXtQ1{WT!19 ztG^axI`5d|f*M70v_`_2nyjhXln<~lim!5%?!pLHdb(;4Kyq=PhDp9+b7F?9OML^$!+ z46eL63;xk|^f|$sGTK^ciOxZK=DV0x%tN?moi&@MEav&cXG3bf32bi55^toniqRb= zGugl4O2aS8?q4q}dlrRpyA!o+T>cd@-0Q(fY0X`BQ(m)aGdpbb5pS-P zGW6|M;^^u$Ty4?^RdV&jQ}gwOUV*`MTD%BR8fdMrH>y%4_1erih~f9RZBK% zs0fCktFLia-FUkDY71#j55(>_$ANnLv7DQ_8;$;@&~iIfw4b{Jr)9jtJ~grOk@w=| zcwnf!>)-b{bBr4DbQ9daRdVoo|AK3JA<(cx%2?Zsg|#E5iKCZ(7mP0c6>n}zgOz=n z(B$_%w6htDeWml+6^VzsZg;xS{PH_2>$RFY%`+K1Kk!Wz46;7M;GT^|L1q=@o_+9KEFe#11s--RXV*FFNse7D! zrIDB1(ZxzWd4(EowQs=l{bJ<0H*d*1e6Gf;VmxF7Njc4rtwOtr!C-fAC)%67MooxB zgZVpzUu~noblL)1)zMUT;r4Obt7zrzEyK9oxF@l7vb@|q0#Ik8M>QBuz_Tj#?0%OTXUCLe0wL`nVR?Q-j1 zW8|ZcGMXPefoIBY;``nf@(yEW;SohS7W@eiY`0&5N7mEe@B}TID{9KjIv2%l}E*;zCYPq|)7=Ni-wl8BO0<$8iQdIXL=0 zq+VYw%ls$#@jf48ss}U z7lRYC;F^Ym!ll<5+^4fwj3_q}-*)k$^x6>+eJ2?#Vjt38x(AnEmeU0*1!zCmE88t3 z3Gva=@1_B>>Vzb^hny9XVi`jaCq3dq2YtyRK3X%Dc%$c7H6%;clG7CdN^ zH--PatL*3Vk?)?gQM%tdsZ>k4OVDl>=N>=DyMMI7Z}kGW=cD!%=w^Ot0tv0$ChZj%XWoNGk0 zMV-+ic0Hz7X5$Cjby)kL2UPby&FkX+ieub{z|crHK010R7lo?ujAajT#*i+e{ewp= zUmqnNKbpiFIt}BvhKnKu2uiinEi6imgrK}}ID7I$A=7FUnqznI88wPI6W4=A&>2)6 za{`t=@g|jG4Vd*_Q}#8tKP?b>6D3`-5xf0~5QUu%@H=3s5N2$G{WHA5&cIE~d+JELHam(Rt_MQJr^}ET zr-a1YS0TW7xcI&KmzegsKTpav;4z&mD6h>I^1i#GNiQ$feftjtgW*_F^gnc(+@HPT zo7kh%N9trW9!7hlz$}eXY`w_|V**y-p_`}a!?85}_u{ND_QN;ySmZA6lXX_!;hiOZ zdpiTIzvMvn)K75tct=oO9}Sl`&J@a@HVcpP>jZ<4@vvb29N}Tk7^od>C#o9t7W-aV zi`0tyk{wsZ~obAn_npCi-JUtviiV9g4wgV)0$he7qHON^E&QhO_I_ zd3(_TG4OUUAfWLjq-N0F0k2XKV?t(Y<@94gClaR z`NG6Y+}Kr<6MCHyM@)IFRJlGLeyMba2WQPNe}o%+yg!fTC$~qtKNp0#rxsIDyC*_@ zWiiSfVzEn-U*TcX!NRLJb2wblTlOwtqlZK?`Ew0VTy^9OEJ)TAs&1qhe_6kl&EGWY`eAv(!(`CeV!|P zDQm%QgM839S#pzg=m@8VR0#SGPPA-I0Q_%{Hx8eej>76>S;a#EeRnUzVWa%8&-`pr zoYfnCcwNFF7Xt9&vj5MbB;liN3gPObd;B~qn-$+Yln(owFnQEKbem8{IcENx@p84q zozYgRyLzByvkU5!WWs0b%`oJE1C~YK#+suY;9O1;*k%6}&e2brhM&>lVJ~5P{X6V) zYP-!aJXOJS0UzM)NxG zi+K(uzvLWxwmn-tI?NseRg`v?k$f?(MX8?PCfBR!gnOrT<0Y0odAQn3UhWmF)YR$0 zcV3(o-<~O=jx+nC)hHb-(OQb3v%~O@eJk9L)P!v=r>TR<56FxCATH>a3P4X8tIM9N{VX$tX4Q;mDK#qcy zaQ&kJ^fGxqQRhzqo{9Sf1|NUZx-kj7{9qdl^P8+RG2P6yQ7L@kjtd>BwZ@JC zW;j!IKRWqb5E^`!LecVUG2M0?&a0)qV_uKNuG9 z93y)*Qu3@6USw6lw(kam=ix=5-Ep%Jf1{oUCk_$)+bRXK@KaJoUk!FwOoun;Tm`M% zAbQg4CS1DP3`@5rLU=_WTGXwhBX3%0lcqVORhn>=&Q@x^F_ErxPgLC5P$^5by$_1% zHCUGa1rxU^u%(~m_ex$2_Y(T@{guo3_e@*b{GfxPZ0|+kuf+SAB3wl8fB^X1zY1=? zK1;{l1m51GH#JV^&h4+Y$!<^8VuP{^!g9Na62>%Uk+8zN~p)*mppf32wU+23K)`3k0wO(0sEh1SGkxDBxPWrT990=YPMXh zv;k}P=gTz>zVYR%Fs0q@`~0L!24yd~LS9r%9sjoA+;iQrQ1bs~&tHQBHeci(hB?%% z7D7Rt^~I*KvErw#-{?z-H<-!O1&2BnaQf*1TNbPl4p}A8l(t=>+vzeQWta4w#&IXj0rdM? z0IT1b{ zkD`0WuR#}+o#?h^8Gc;!8b0592`A!T^Xk+D+WKx9_gDYR#xzIzng+5TY|C9X9EF@M zT_LA_6BHUbD6E`|SZm-py6QQL8gs`|r2HewEyttB_iwmkYY!S|wn|*J&WU!!Bw<+9 zGpctk;rutJIsD!=;Zod0x?yG~1X`Bxu!Gy_(7Yo`gBxR%9V-mkqN@f)%pAkEUU7<; z9cp-T?N2Om>H+^CRaSDMiq>6r6sP+=lkzv)*lSo7CBBo>-}VE*yu&)7$n!h}EYJb# zh)0T7uEyY`=}LiC*}Qh`B3M?`h$&WYaLkDSnr)@QMX(#nMb?;!GiieOsXAjjld|=lJFL;iYCzQP(iea1M;AycD zL@!CE9oL7_p1g}>b8H;-O3r61BNyN?(}gUP$scec_Ch z`FaTp4XWWw!&S1m@Q%g=SfKX8XslSfRE)13j0T+=@Lup6bPJa_f8U-6>WWtSKE?>M zS1iLCsplBK`XxM>Rg1w7t#Qp=7y7<-olu>>6U~YXQP*%PoWIu|N6fg0&R09K)^({W zFehIzaq3lRJW~k^_NCF-;6ysRB^##ic?|99QpDeJ4-`$G{-b$|>|oe2e;T;l8Ghb; zK(X8IQugq3ppo;5Qmp6UfsFxpUCJk}-*-u_Ic_69^|~Oa=YQqVwI^`l8p&lGG6Tcj zp64TPOnH%a9~$7{gS%?2P#2`G!e8n0^*4<00>G5%xo%)T&o4pqsBOK|)NfdKq{s;EG-h)T3 zgwcAQK?k}`5*qr?$LVvW?rHs7OfOJi^S)7N9e0WzwA(LMEW3*fDyHHJvqszy@sHkW zoEBU2qhZk5I2__T9{qzB;HYI?sK8u+10!DvCVhUJYul0)7$@0PS&RFU>Yzjrr zcY(?=>p&BJDh$W0lH=0U&;KEk>E^ zCd`dlf(pe$@x+Pkpq1RMPy`TW<^RFNfPI|4_T!$8XA(&&>(4Q=y!hq0p8a+&vW0O&vm)Y zsih+CUgEg(G4zW5C7R;*0&N#g!?>@P(Dq(GYSd51aW`+GTlpdDhHZ_^727O5jdxAPm1Ha$s~vO^i4nwdNnm-;(!?TubP4F4`MLC zFbmT)1%KpcJxn;sp!%&Hc;z^Qk*7c7oN1v}tF*dl|44mC@rN^gX6r;}pLL+IzS&IG z@GvnxEsl<9b8xa}0}`=0=pU5LWzNvSc|Ica&7^$lv_}`z>PL`9Pc@no_l7Z>{T6c9 zD8t=fp^)#g0#fBFAn4pQ7`JaPcW&-qG%RZ%29G3hnpPgJUz~}3dPB7CO}oIw7$M}W zmJ00FSls{dJ^dB)9_P!*(JPv{DB`^k$D2;3N^nsD_t$(>pZwzC81;YJ?w*P381 zStao8ui-+|cNpJcfooHb&}Lo<<<=Y1b!#@!>kSNdK{}0QU713)c8sU_i>)zd{&h6@ z%c9KIrQGAGX`I_wH|Ar+dpLOY9h`D|fgY{9$e#~e;9%Eo@Tl%b$-*)$Zr1{zI|pH# zUn}vm630!Q`gmvQHh5p$20=nkc)mXYr6>ELann1Y9oYt(s&1l_z7k3$mtfkQ*8-Cv z1~vL-p=OXAZt)#~=H({zbZ1r%LgHkmQ05lj#~9ji=-_&wMc}F@>r%EO%+uu zlm5Db_@2dZ% zG}=rDbAQ-j>l%lA2tMK^5Xx#PZAWB7+qnPqCdPQE3S{8;= zdA(dZZ0JjSPS2nl3%6D#shU!+`}fhGZ58(CBDh`E7n}3YxPjeb7Fi6zHc#BYVZ^%3<2Is3RNawl#tar@Wfmde;4i+U_nD;FJ ztb|NyN~|iT`=(Qo34+J$#A^(X-Gg-^^?2Iu5grX4qLHjCBcF7io-&j`Nd=*2c%Ymv z&UGYBc!Ox)H{wog16sEK8am{Rr`qb%aYn&w{Jrr!bIey@YdNT4mggGSEcE8*>=A{+ zIV+f#6OYloAI3waNjIzPSOZ;d3)m6zXOD zw{4)l|0XlRb39J^vJnowQw5`$bKs3?8K$Xw(7N-_=!E20A+ME&ar5T!s+StDq3;sj z>^Vq-?*?K}0bpR?3bHhQDX~dNqBAE?rT0pKs^uk7UE6geA!ICd1%VCKXh}QAkEZht zH3VGMhzpG$Rq9`?swnN_`3YyfaHpP(hJC{SR73CzUh7{IFYg^7WQq1;esUw1yioYg zuXsT%vu$v7R{-a$vYTwuv!t5ZCiGFsb&_S&Rk>w`0oV4j(fYCRLwcgeo~s$yZIu_< z#CWB4l2DURFz?hpGGfL%W?An%iXTO2|Jq7ki(JN9qCy_tjYic~9Z>YIg*-2iX2-r- z4B_r0;a_+k>ls;sYi8dCm$Kt{UbX>KUfUsmxgUr9=5wPT{6IKmNZN)Ix!O}oWXCcG znw;Tpo$%|T<@E(?=-#dp`e^1Va>MZ+2~GB9Y$8&Lzqb+BF>v)W>wTrFC~k3aVY;Y%uJOp`-gaU931Xwf)(2WaJ!fY&(zRbR?z z-}^Gg(tIcVZji|9Id2kXKRH;?KAQQhIs_-#O*BT0l8$IsFpn9><>v`3f*>1mWAqK; zvD1NTD|$_Hc4ZP#M_m{z5({H;?C7s|bE&dX5z)yz0PnXOL+*Y#PWP-TPW-zMM1K-k@7RGdXUIZs*^Y|ToqSinkUd9kXW)mXbb_V8quE-SH#M>1xaa4uVou4`Z8 zj*qGQlN&u3MWRQ*8^y%|r2eXrt3 zPX+S(?E@IIY$4337WkHsMntDw2I)*6cymAjs-nw?oP#EY`KVyD@LWAHEZlQ6b8wN< zE~2#KDlfG&3mhhz!)4{Cti1Y6)=D>sm0NEH>b?@Ru^@qTt~_oXxMU4x?=0e#`|7aT zP!6@%i~(P)M`h?n=YRjmPa|2lbXgXnN;d&hdK@N|bc5XX445tb=f-p0RUpM{FMFiRnWh(}0}(ftYJ z)XoHm)0Bs(x;0R?UJqBAmZH(~6yC)sf)C!^!Jqtai63!r2;{r(K& zwl3HUrjhP=%&!&n<~Cu?^%H2F<4L{^ZKPf+a(Pinp*wzXI@F!CgW=t`VbSIFUGdyrihuH(~L6hDE)9OMhqPrR^!wYGlz7902bi&SaPj>UWw0LytobnN6dxM9xgOCI)^@a8~_Ci=F$*1UA$X*lm1=3sWR$NV#Q+Nj1#P!MKr26 zkf`Mc@Z?N=yfIpdta5gxhQ_(%`l@tTJ9Pv}KkY{z1yqoi$ycHE>r%*UNrJcg@*wJ* z8xTJ&Shj2u37aBL`ZNxc&FZZrQd)$B@e`rkZz9C>io>6GufS`iKI8jR1qXc=LiM5t z?7rKAKf!ST=6a5WzjoH3{kVdwxigo%+sc!QHZ8PKvBlFO)#x}Q4IhgS(Mtnz{NxrJ zh~57a`mOeo$yKh5w244LGS-&;S!r$&^@9H0v#Pl zRQW_W_be7BN~=MP)^qHS&!nTy-mt1({R$Qfy!cU@tswSpI>=jyl4hqElHV^)wr?H+ zjfMTNkK6&NtvV2ITpE5cjX5!6K8PB% zgTeM#2sv^B-Ui%er+HPuU#UYdIPn@PZQViMyBfi@`@`_#nK!EzJQpW#+631Ry5gVj zlDwD~!y5z^;I`FKbZ3n4`ul!^pV07uUqA}**Y0TS6*5OQCH{EYH45rQ#8_1?WjOhJ z2P(FX5cq&$B*a~re9VX05jJykoV+1 zeCOI(l}#P6xp@M7iQ&jpl~QVW`7xAd?gWJZd0+#ZQGKBTH)0pdialS1*H@U4mEm^6 zT(AsJxd|DxA8DNOfd(8*ypO*X=i$#kE3s;oJ}=>3hM&f{Vq94zGt*!zl5~U!+b|lu zdJn3qC3Ep1Q^~IzG33ecEAIVsQG)Fy^v#$6rZYzce4Y)#CJj@_dG``NznclKS^}Y_ zdo^%%-ym7qg6#RaihSQ9O{D}TV@k0cKkn8s3>FW^rahh*cE<#zrP1oTtfVkEQf6N3 z)?(NC3VgNvC24)#f}F@{MDJ~QPpe(vGN+>6vML;Dsf)7@cXDx~hSAhU1J49Z;oOfS z&m$QoLZ-nRU6jt%mNa%QXhuz%d z!%EkLlYu-Z>OHW5CRwIqlhChG&=Hu$M-Su8JBM-Clqa-4_c!j_;ZAGMUV|CC6^VEB z96FQFpnj_}h_RpW8CxiU+TSarxkwvw6DB~VRS2Yy4>3}GnW33$?`Q%=mIT2EZyKl>etLeUmJBaU^~9`IhR^rKS4KV z3;v;9dq_*G4Xl$$A(c3u&MK=DKI0oyYV2pyzIHRCy5bF%eCF*VFVlLMB>jB|lR9FXoLkrtxOVLI?B!9WD=G_WjN!uRm`D z>o!*iFU+L10|xZ!$~B~BAe&yS2uJI?K{RmNBiw!J4zvBhea!YxMAzBtKy3e7?!++C zNv%BY@E%L>doXTzPypG*k`U^h35C!5Atmh{gmgX!+g+mQFC|HeANrxF<4!`=zVo7T z_pqrh6JzS#sAyh0!mp+Hv_T*9)?dZ3Eyqzybv&v$h0(uXV;D~%&$Z#yIOcxeGMaoN ziuP$w=3XvqL<^hy_$uF7=w+Lre_uN2DyZQ2rdgQtw3+IE=t8@#zF02tj-GcMgz_^B z@rBN6oEq58+U~D{ik&yv<@Fix+29RX9o^1MSue%Pum@n|wwg{=pGsl?G*eb2>rv$}D>C7dz8Lv2j)=zN|XSi=U7ZyHTJ`&jTVjz-fj$1p3+5~nqe#XlPi zdDFf{y!rVFyg{@*Z#)k8fWIP`9cn@Ue0YTB`=c-}q=-b1z7J;Cufe0y9T1tj1ir|} z!lzr?p-eFoiXW{bCq#nj_Czgs5m5)UwF$JRjskKbF^ZqExV-MmDPXQ$1dEz5q`x=68*D3 zFy(10WF}cK-MgE}nq`@A=gLyJvve2SQ&>lOS0&I`%YQhD`9$u7Xb_#}Axz;U8P4$j zG8B_L$4KPPAx487>W#Ccm2QG#E8I-@-oC=?MWQ%OVjT*iFdQCG#E+Y9V5jyF-kkD~ z*A@xH=Fm7^M8*YQtd|#h$bvXZO$A3rwy`snOV|_XCtx7J0GQQ|5P9x8_=~+K)us>W z(?~POIyD3XldnOS#YDKXrv(C6xYFtG1y}p(F;<~Revr`4LX>zb%xpsSt*Z(WgF5enRPW!UlK3C4ES)4$UiNRxvN{qyDju1z_ib0n}j z&sLLyR|;h7-RJa3ha1jb(Lj@f2k1-rO_eHZBCY+OzU7AM?xV`oCX{&IiTbZ%QK!(4 zlHLUPihv;rn64mrQ@H;(kTs7Q16yK zp47XK;?py6@k<5bTb)IAuK^ljxm)1*S(Dvo_LG7|o?t3J#26jbgAq}4NT9Y1lbBt{ z+#j1s*WCO|Th|Kjljv?*KPr`G4LH%&J9z5hG=rwR5%Mdk$0`dt-Dy;1JxzGKO<=;V z#9h)ExL70!y)Qn&4BO|JaH9xa>K0+o!vq{C0bZt)$Fl!?ux0xrI(Yppy{~P}9a3FH zZIfE5ey_kvDJ?~j=dAFvOt+4IafmkjI!UAUEG7MmjOjc%KiaOPO0uS2q*5*lm7g+y zlVc5iOy!sNBsu#8jbH6&6&<#S#GaZ!?|r(@Enbjo-L!rjADX_EPuQi%&*oO}674+a zwHwU?tQi+h#ZoGl-ojY zV7)%+9@<2XDC`&RS8=RP-YE9Cn>IV&@Fp8}#G8%UU&T7MpJN$NVl@j#@FHi_@prre zKl5J@hP_ZAz1e4ZxttIj(;}Rs=6TTc?uQ(SzD=^z>`8h}0HGtF5boy&Vte=|@wDE> z{q2pQOa6tT$n|>q`95&reZ%yD;W{$txQTSOf1{acgS6)Kd~S$)1~dP?V|Mt@pc>yr zh}y(;@IGuYOWKOrtO^M>`2BTu$5(SU@Vqv=sd_5AGHn|SkKGCD1~b6rh&+CC-bkmM zO(%moFR0g^Iw)*XrAe-`_}@M|n*BJ7+aO;}Ra7QW+o}JE=*}kk@ce#SRiwu3>#n4; zWh7~IW*eQS@fJl&-I*mPK7c~Q9*!BKVbSK*#zdGKlOhXaIyO5AQ;(!^7MT?guuTK% zBkr`;t|T^f$wqcdw-aQKzfRsM7-HXM4es0QM*J_@ z0<}G!kgEOZI0oE!5ue+XWF%U@a_Xd!<}+xj+H?}Qv>8Wvhoh*{X}WIH8*We8NYYa8 zN49#dp^X+cl?feb+>eRBNw)P-IOGsW_I^wv3PWa0{MI21dtiW;Og4!R(S&k?WQfN& z=gfexg2H=~(<39ofb4-M=@m^E$=^uFt42z$#h zsM3kSTW_QI(<*v(&Kz#jdn>L-mvFs;6LgLHQLgZ15?YzXkwX)%F(M-$k&AT?=__Yj z!pYAD^~EW$im?Q}HzQ%u;#}}bQik!8MP#VcgdCQe%4IE=wtmaJB~jNG!uZj(m=Lv( z^AAmhw80sq^*)13q{_kg6bH{LXOYX^dH5`67AA?dpim6NP>V-&+Xr(zJ#>L~e>u)= za@R#Aac|sGDdZl1+A=3!1~M-)mAGE1AgPQOahFC~w+fiAXV0O0@?SogkBRlSFLhlmDl3&JsXkkcUU^R*^UQD87yXn5H{bWkVJMPt?-8Ab)lHj%t zqd{XNY3Q6%rr&f7Dy|QqkE$D~}{ z$`R{#4E@=hN4BX?Cu+VOIL>Mj{T`OiDFm)BR*ABCcxJ^oo*sC4t#}m%^zXi zmT%dn2<|Wb0ru5z|?7})c5mpX6fs%mG(a;SlZh4 z(u@z@T;Ih#+{5q>G_EIu$UpDkZp{=p5sUaeRh~O)|C^w%MG^V_`<1AlfgXc zKDaE2VDbtLF=E+BH0w#{y~1bn2jkE2fqyJ`&Dj@t4|SHG$;`&@7A+VdTaI>FzRd1% z29UiajVpgx#T0EU0>=+q$-Dn#$lruM5}JP(jK)vo%*|iXwA_i@fTkyjzA}Mkx9gLG z`;L;+hT>e-tux%2s{@r2g`9pwTL|%L)}iO$?4uRy-_WR-R#@IAc;17P$iwp%5V`q2 z6Jvz@h?+CJ%}+Z%W6+c@z23>k>*Vr5XL9-2#J&6xwPt=Lr;7Z%3m8{pP43K?NToNJ zVv_qLkZV~C3v=@z_)#Au1nmN+BWa+*eNGg%Mz4FaG zDMqaI$ww+G@*BQx;kRj-@HCV46SlT_aGAf;cAkeIoDh5qw7vNw4LlLxAmeyQ(4 z^2SBdVK|GPJ3UdLf4{)&(Q{~tryU8iM%Xz;7EUa=4hO|-1-H{8R_TjA(dZuFr*4?Q zOGGc`b-xwx<~#b)Ld=aQ_n}H)B$^8}h1K+jyJoiM(#U zB(LED zEsoP|=g^eV!6fv@d#ZL#jKr|dImILqnzAyQ+cMso6n)-6V@}vIft%*ji^*e9Gg}cS z*`#9n1v`u=@*!fDxx!v$8!YaXf>PZN(6hk@mj&DZ2Y3_(;r1{kvy13{&r*g@AHegWrX zU$7G^a#+QvcOWLW1J^ida<%G@(CD~5tv9L%?Qu_q^Uop%w*R1ygI7R|sV*DU{Rm9n zC_zU36^zbA7svQKOef}aInmPT zCN+P7Dv27BGF?qBqppZ%sdzTYEvTB9r=2D? z_xl+ab#V|CfRdV6hVGxb@J*WmKVsq{?2DGeo5JojMc~FP+42;}WQXJanlwzD5`kUp zM+{ijOZSXDK$fVlp~p*}kYa(uJ;H{Hm z1g@APuY4{6z0KF+s-AIpVcmbwm=?UnXxcvYA{o2v8d)5Ia{rNzqgl`)cvPZ8g4x5jEN1VV~qszhtw`V03cm81|oa>?K_aaEWuo?0XZHBXpwu4bb39M97fzcH`ptwSe z+E@zNTILhRY<)%zZZeoCI|c)Xf8rz|-xG9lJ6Gcz#}vNa$eiB1gcK;x;Mxzf^!edZ zqPOD#xn@?*dFEYaEW9?*YNxZzjF)H7aj!PI{8~+u*8eAXlGfs~9bHVuhgdA`2qEj+ zd}-Co8Y2B%5~g&Igv60YAtiVa7H$3oN~%xEL*Lc(46}v&dKrvwWtQTXBv-6luZw;+ z2I#*RBFOgSV)F0Dv|#yaPA$AR0_N^TkrmJFp8ufG%ZZ-TTt zP?xKI^oX8&wi69w_fofn?MQr1q3k#xx-)(UHJKyLM5Y#zt3EIBhRJ9=Ak3r^<~QKh zRG#b2Fhg~bMsNzch%3#Muru~QEd2EdH@^wTsy08`auoS7GZ$drpaphz8seF}MBE^7 zeoPhK(bPCu;v$>QwF`WsYgm&;-npyCBNN;e14xXoP^*VA>#-cn_{H{X*)o;XY+Z+s#uZd!EmyY)21v5RgC zs-(kH7*3I%pc4ChY2=-w+>`V`G+X|YpY3x6qc1b)5LpT3MIPL6&P^Iu zh4{r==t+NF0AjF$<_<2#irqI@8FMh1uYhF9R2W!3VgZ!N(T+N8r7`iu;4A_nV$)t^F>21y1xya&~ z;ivdZaM9H#8uF7I&f&|SyD_Sf!zI=hxN=oG{dF}P<~1qM@0G?>bfzIud-n*%Hcg;z zHzml*hoYR*i+>fJrFKlzlda5uBc{?9_pK|Z_^uaNtrqmswLxZ8&kSN`K0y19alF~AL-*lETpoptMTyt0nBd;<8_|y=Jg(R@uNCI z@bye9UameDf6wy9$0_gV?_bs==wUpKlj$JY3f{!>?@o{?ss#0>*K|z62qs##pWJnM zO|`eR(vxpvm@Pv?^yY)l%ot}Au6&FklN!2qHfh^XY~8%S5v-~LA$+U|xseeL%)e9Q#I9{rwzYx=J|8B*>^Q3YCW4$h zdzuj+)JBuw545k=mpMPsL#>_Ssq551=Em`U@>=K?9@m+US=aVq=0;~Up0f~N+w=)s zx(Vd8RW~DkNQvHCI|}oU3Vwn+Cu!{b1em)x4L`_J^y}=zJD;7f)$b@8JxvwPVzcnc z?QR&iCY>JqY7LWi`*D%l2g%F+U{ZYNEQmZYsw|z|O~m@9(VF=CL^ZC9zBqY{b2hEv z_D(WIk+FNYV-0JVQwt4Ay?G|N-Ms;AruAaNM?HZl+|FAD9pTO1+~jo}dn!Yh29e=d zdvfxI8=CJDC)e%jgf3tQ7p7o~bFZAV`Z_rjmn!RF+Ji*8@@+fuQJy1wKi}aEVHbKP zUyg>2dXC!?FQds*dm`E3#HfG ztw^BqK_{rfkFe!T1l;>>3x0-WbiAi7&6qO+J&Zh2 z*4L2EZEj!&mzxp$=#Hr-GEHD|0p@ngpi|a7v@E zP@>UL(Nh;f>?iee7tW5Qnb8sSYTkaD_(ziXbqN{cgfu#6BS|MuSVz=fsSwBN8AOjb zf#$JtA~xzINsU%PpVLje_9$@-Hrk6T+X2r0E<*SIRNmjD1Y399qMzg+pkwA6;^@xO z=%_L>qg@}K#?`QMwnnj}a51}4S%Y2Bz64Ar$q<>-9c1d8XcA)_&&=L$NzQb;(8&>T zMACHz_4zS}lnZ||v5<6n>To63GK=L(k2|2c?`@`R{X-I!coUpV=i`~X*LiW{1m53Y zoA)38m7f~94zHRDyT!Y`wDj}}>#}QRM9uj&UA1`w343)z;4+RP>ko{eRWEMR;DsJE zGbVxQizp@0^Od==I%4#+Rtmk-d$aPx%m^||;|Z?Stf6jJ3&`5Hru6NsySP360+vl( zh?p}KQ#U7LQv5UA=i|!w+&&BWIhhckl?Luly&-il1q@b%L9Fj-aJkAd@xuzZwthby zx9twfg}p${TYorzQ=q?>uS2yAN;~uVQ0b99Q?cKL&Kb3ZMm~$+Mn^wquGI_PM?W*- zyu^dlXXZ2Qo3$(VZU|-8-T6nArX1onEivN$)GOhV_21FhMhTyei^Ojp;`r6Mj-NSP z!jGC!jN2F5;MB~&Q1Nyoyf~8rQ87x;LapJ^<9iVGUnM&_;SdRnA4C7s|3Mj;LhHnv z=(mxUIAifh8ain@Dh?l_BHQw5!bC%E;UN*mPvR)&`d*P1M&BngrM=|n(gh^VZaj6+ z+E4wKHIeYE1~h%6G^26021nZUqe7-EULO4ucgfAi+xMb)!+>mlEN$VBZ*Ipf#aYzlos zx%a2;bHn^i^b~i%Zl(R0GFuGw)D19s$cCS8b&+=;HsLK}Px8|RR^T(UE4Z=gD*c$X zmTbFsgRl!4U|#VcT%73-9~b&U`b-fR=v_dQCY-{oh4PrZ{26Ym{(vWC*P)4bFTEAu zXMOWh1(_dbOM|@jGiA*$sLS;_5|fxrA{L)!ay(PW$%CO>Xm<=_B=(URKPi~G+W(IR z6dPlrZye5D_XL;rSYq!pBP_WX&$FvVdEGoO-n_3Ddrb83$+tpmH=m4C>Y^Y<(H>%3 zG+^Sv9Z*^tYCSZthi>0D8A7enK&H`=6dN%Do8~WO9rD4#^!a#t@nSkGa4rfhVws&G zzv$u8e&P~P#SJ_cqj@U~IgNswFxC7mF?6p1wIG4BR(Tgz#5j@1eN*X$QAN07&WCY-BKzzrQhP+(ElpkE^Ol}wkk2+H{96C+kdl42SZrqy$dUS zatkyzjA8Yq_CSiqRh)L}6P@iJi_R+@P=4o3Jp1_&<~TgZI-NiC!jbt*{py2cV09#w zjc_5BU$Qvou2S|tbAEJ9G4%GDg$m{eaoOxG25_UUY6n%ty4R_&gx#_%! zrzKvuU4lO^XCcbCVVsR8UYS{htJf{XZA&^Bz0%!qH_sX3_ichzXO@GhX+DJ5wUO=- z$?)M11G}DSSH5UjLkzUsa6xAcUNc#U!(R_zw5lVrD2Hn zeVnuMKFljP2=PD1a!sZs^t9a-;Qi*azOuEfRDKgXV-~;%>un^U-H!X)T7+7@zD!bL zD!5(Y$@IhKLU%9>OUJkJBb(k}>55_eancLz-$bB2j;|sIYb>GqO%5G<+kh;(^$9b2Dk0nb zA*6cARVsLmg3F35>oj2z>zg9Ot{fA@x_6tx2NivIk#0o$T!U$usoNev1|1NR`k8H_qA+IK(ev+9Nm`)xkA0wpc zHifie;*(iMEiO19voVMyen^5B!`lh_R0(K-D)-4|78Vp)W2c-0I&IeBrS0ZnsOB%4 zviCpuzWNP(w?DvIPE27JmFd6?`89YaYb{ocNyUpA>R6OiK?mALVo1rL$7(0w~Dyq@FTc);5)mk&yHPdd7BMc zX2M24E`te)=W$5mDt;Yd&U*xgqPj+~;Fyy{CkH+Hxm}f>{dxuGM_1C?K{s^1@rLtv z*#YC5HgL-wEQzRV0=H{0olICTgS)*bi>{dKM$-C56V>Zwki2FVS#>TRX6}rKYX1YQ zg_bjG9Q7Sqor2-S``37#v&DkHaTpVO32$F_!Nz)ZhcPua&Wk9&yEKID>~L=W0ENfBMz257_K1}t;`kJk2Hrzb?xVQkzqCeq!x zvQWc|x%3Q_jsk*2>Jkl|0dQ5_+0D7)iDVrk}GDGK&e|DX>RpR?cEK&05Dwcc`&5?yq8{ zuS7%Jhc39K@{2M~>EyowQGVUrGEhJKoE^DUj-4~pgc!{2;U`bu!=H$}z>BlBm~}1& zYmEgLLHIbR-lR!FWIfUGi7bi#l#L7534Vk>^XRjiLiYKT3aJm?M$KQZp+#}J^j4`R zhzvcY(qoHB@0ydK{mq_id;W&Gxnn8$WSa_#;j@?)ZWX*2c8fuM`A`nuSVp^$ogjXi zl~MY|Zj5q*ec!*LnCToyS$rJQ_eZg_PO6g7pK82Uu^1NVZijQ#2WgMroQltqs<0=$ zj}>aq5HeH>-Fk{JNlcz`54}V@9<-C}mFBSIt2c?eT;#t*i9ke&N%(_K7fY`LC5-u%|9NTh%r0Siaiv|x@)|n=ewB4`h+7-^+x1(P` zYe^l=XuCrv=+8%&lJ}T4aGlrp@aM;ApW&6fD{)$#4VEP(VfE2nG{|BVX?|pc({8C^ zQSV5)NMR}IR0@IE$uDW-p84eAhjKV0%*H28YUW3%-NeK1hB2!B1aH#+58pQ^;a&F_ zh*;Z4qtiveVuBIutuBDn6YZdRS)J6WRFa!kkLjVaRwUhT9gk%?SI5A+z5CJ$9=a1WURZz<4adYM}*0+^ql4$-VHZKx$)OuLoU z(0tJ=l$<`Dm+(%cr8`5dm!Cg}RO%-NExCa|vwN|4Oe`-a=Z|&OW%z2IFG!>sK}l&o zC>O<&N6R9?%>HX-V|^_c=!BBaxwC1?hB;JX^;@(xIZfX;e_*F2JcZ+0S0S=@8=O18 z1h#$r10#YhN%5=^Ow!Q`B6nsr$xl4W&DFULL~=i=9BRiI*535$>$OaAgB6rcmjLN6 zJtd2M5{Xkj5gHe?k4pGg{{!e_$iEL(`v{)*BUYcT&) zJabNH0iC#<&l-9c(8qc0bXUbbXw2US$vLxNZ|)Cv^rS=JYkw0mt{sClDu2MuFA5a9 zwJN)#kU0Mpy4QJ9#6G#ndgN+Da{t#5jrWnq+cMMS)FOkB*8X;DcN(8%BTySof(rc=8q$^Gz~-V$xPO~s0{Ae$D>_|5BThx&rZ93 zjg_!;ftO)QaL(faySuIdYL;DqKA$ic&NBz+FaMzOvL=Mx^9F4pqT^bUWBpNE3l_*2 zFsQkPyWSAUj5=0Et~yn4^~e5V?m>T)D`9A}k^yA>2;+*xFEC9>JK*4gk?iD%P#6kI zB;&2Gp>z8vF!~WmWv*p|Lt7Di6f!=2omO!7d@3oCy@=L}Bc#lQIX1x;E!*_6lftw+B^CMQf${dbo-=fLo zCU`=zjPyv0;q8|-aBJKU?3i~8@_u;Xrd%nEbCrNA{)4ERZp1A;aTy)8vO(v$7u*eb z2muqWz{Qj-c7&1=^(%HK*xU-ct?ItF06q=IaXssM=n!c5}4D7RQ( z0YBJ}qN}FD7=Hs&@lFgjDms!u4}J2XCIF=8jm8zany9K5!EI|0g_DDKg?XzxcK08{ zWP#||J@p_vIq4hg@;VL|4FgK=-+)HucQDR7j&`>0!lDg|baFvCc$+VS7^S1En2QP)+0p{idP{pLf^7{oVhu zVrp05ui#qJb6ABc@27L$y9&@|Zw@~}_X)14NM`~+>p_=GF}OOth3Q7d&?G$?H#cs@ zAm!_5wb6&(->?wWJzPku{a2EblSrP<_(BqkZxeOLQ`~ohMbwbIrv}**srcwLm}Gwu z9G);>zs62*WhO%S=^wCTbqp@Q`yDGqg7Cc7H-24w!G<^S?23r;5vPHV|WN;i-CLO^WScgq|(rCXo0R3a~xDc;5Ak6S!ZrDFi zbPok5vFjk4w2pX8&m;w9=Q)k$m2^`<9QxZn#_*jKl+#qe)-sDc3>(5N4)4%8#R#(n zhV5M56Q36B!c+4@QO4OE8Ott`k)Mk)ah>GS94Gq1I+|Ma#-r+kEucKOjl4Yanbx(q z5Od+2al9-EudCkU$A>M&0zVbZGd9C~vp+aVDbxDyen;yL%h_O~x02j&Ya=p~3PJgH z2vc9D&0N@A1%{&AXiM2CCdvOeu2EK@jUIJmosk61{!&K5c zDUERc>6IEThA<}WA~_Ma9og~TC^Nhl_g2@V;^PEN8!A>&_-F)JpKEK2L<)sKeymtz!CKP3Tn^tR<$hIfR+e0y9_avYNI2 z3zKbPPYnG%iQFd&QpP-~Oa4U8R;mfS;RMniSVJy{=@H|@#q?(LURv9(%iSxLpeNeK z<5bBdwD+_+reE_%2k$EGN~k<9HOiA4u~-V#4h=BsA{S|7QzDtR?+1A_cM<)!&ww19 z6NV2IBC$<+H75U$q4RL(vJ2xlLXu>!h$2x`NaFXL`)R7zDxx%$c3M)>Rz!A@6|(16uV_iw-C>Vb+IXc+KeprZhg{=cm2ZYdx-amc*KVA#6)`mF4=!gT(Kx`LSQo7T zi$-Tg$Jl{fl-DDT|B~pE*WotTzdmOQ9OsdSQ7)Y0vvtHX;WSzGUq6}nE{$IAx2<}m zW{IcRUzqu{08_TS#%jNBxOm2Qyeg55E$t7{!mAcTw(Z99SQBg=Wra7&Pod0@3@r20 z#*LekX!XlbkXoEfK8imjEuYOXVP-SxAMV6)5t?{ZahmYCmyJ?g@i-=H6@fzn5BKdc zdPe6yS#s|ujp(-KYO=krXgo;UGSY$qnZ z>cv(a#LR&M7~!`G^^K+Q;HdGmcB?TL`LP`q6^&t76C<+et|vtN*8=Ap^^wk&#FaydQFZA4F*jjfuu`xd?J z{*Ih2)}Ti=rO_SQSLme_8=}=aiCuEwCM&(#jvYVThz)%5hi#kQ&rYm8#mb-l#wv;4 zg6^LNkX(5a6jweWikIzabg30dsy1Q7zfKo=OeWN|%7>gh;^xy1Z!zw*(>$ADg-=N& zBSyk?x5IpkHy}}~4&>W7rl)@=4p*$jgS*pk?er-0x4KCKjPj_>?EeT(oC~7{4^~}m zT1mf*5u@QrJU8pr4)`yzj0C72AU8*@gGpQLVWPx&@?&r_!01Kfp|le-Ms5c)a6*!V z>r7;xw}0a%cAcSFhO4Pq`gmGl+g^3GGMWi-EudkC7t+OBM^QQVgPil6VZ^Qe3oSkS zk$bFFh8jcKc;aRs%BkN&ix&x1g*U%oet9*H{{0fie_dZyo2Wn<4Rr-h&0VlLWPnQd zByrkNLd&x+kfZyLlhlAt5Nlb*z1@3-gqcW_FC9amH}N*3-Vwrl`JIgDHWnzF3<@V)M&s>NmNPNMz-Vw(cJnT9Zt-vN-s_z z6C4I@ZlCD`TgNtt*s>H>e$r-!Mc0vbel+O6Z=lE>MPr6cfc{BWkW=_>g6mlL0owPfki zi!kZ@4%qO|3WCmtfFijAc0&Hh!Fd6(J}OCf+bm_wrvByPZ?2;;lZ0JX&uxr4+KnTi zo0NL_F-a$%(vyQHFn{V}%zhMvrJ*`_-{>LEi0sCn8yhj}+&En7eGzJOM+u)B)#OQi z6fT_fiMNpbg~s_oG2=j@vG~KR_UbHMDsiVBf`OP+r>W7OYVDcZEa?MKEJ;Q_8SP#(TiS}fw zw&3;@@;asyt@uf+fS(c&!aKDl^Rg+En6RiRfQKJIvhW#|K_lQs>VDG33v);(1EHHN z@OF=#h1VwikQ1Q=4nw7=AoaH@=5-cqRa*m}0<>VL{}Md!y#{AfUxMEgH`sZ5Gw8XR zaQ_`~p*L^+LiL5W2p5n8WA+@R?0E&kysNDefdP3MK84KOy_ws)F9W@C11%+8xJ~L3 zuV5i~*j9|=jbo~miWc9+lK+BLJN@5sHdIcEVYDfJYp^=hEnAsw0nI!L!dKb#6VMC!gSr!6OT5#{}b z%*L8@uII6YD8=q0**^=}pGnddKms%ts937x5BqL)d+L8^2K8 zonP5F1xvjWVHP_cj;X!}Z*~ijGRlC*54PafQfYMEZVUt47LcZ1S4g?9!riOZvw5}H zp0PVWo|LSd&6xk#2FuTQ!k)O_;8!;QAzp9E#7n}wu1=D4@7hVE$Eg#?&T=aC zMbDF|*HMI3@FfG4^B&ueE+HZ?EjgYjwNKh@Yvzs?<3YA@A8FAWLE^m%;F!hbVtAXVC*gPkUgz@eI;?pbQ$EENPok zD`}D#1Nzgig2~;lc<7H8$S4iKiS*a>@!LLPA1#e7YqnyljyrRVlt1+CCO>daz-KXqa|yvk98R`W{87czLXL;>DkX<&_B&*G(Q1%Bw07MfZ9 z3;i!CV;NjMQ7%dJi#@s)7?iidhgL%?E~n3<}oLJ=oQ_vdkbivd<6Gy4hk;!Xf%3p z2fx_}X9DAogK^`TGrOMAAmRfP>%z&}ygV{*|aSrvrjleO#&J*RxC*;*yulq}Lc9 zs{5dpWZRjdh$%#c%zNmSUw|qP98pE3p6m(lK(mQA;e^&!vgsz`uut9C*87T=D|02a zHq$v}^AlJtFuJ#0T?@wCCahK!yuI0m(5qhzk8}l&KU_%w6~e7dJtk?#l8H_)X&{bOS*oOo2HHQj-fP4JUPXoc`g0h zIswJ^rC{ylQCJMkc-}i0RXx&hwdFmOJZ44HKMlt}1rzuU7|2i4jzPh;z%R(I$ElMH zAkY6JJkB}{($CvKv2qMX&r8J8GxJcr=QcA-#hYa5uBQQwGbt_;cxE>hK&rMJ{dh?n z<9Zz9n;80hX)&E_RHox1)D%jI{1S~RC%F$R4QJI%);ct zP+V*JMwsTP#Gh{W zdH7Q!jW--4FZiST=x@_8%-S)7=(2q^89ev{(}Fkgx@(73eVR6p)_sKO^pJfbMrVzUD*d|yF)SKyBn{#|0r$zu$Y!jodz~5B;fM$6n55^yR67STsTu6KvVNw zxcs7@yq~%S>dIfTQhtflE;knrW%@u_SvGn1SQa9hUvUkW_o1biy)bL7tnrk8LMk9pGO^4{uzVKzzEY|GU3RbzK9d6FeX7zsCLG6MZR=Q(2 zWDZF{@+J>{viMs3H|j9)cojvpw=M-4KO1n#^uef$^I@XPEaB{U#fA+3z=ogR!+M2^ zvo@bSSi7=JRz3(>6{GL4I3XR19eiNgtNG+n>}4YH;|2}VxkY{IO6XOuEKdLF8<15v zMpo$00O@l)+!b~$r~Yju!wz@BkY*5TR5}Wp|2DDX#@E3^mn*p@}v#&r-)fyGR&LsmOMDSojkOi z3p@KBl45M73D4$$^P+Z$v3O0?m`(_Z&mwPiw*j-fhV)$P!l|Bv7?%serg{AsD_znlRZ%dNyK(3ej5 z`-N5S~N6d8%;CpA{%pCnWN6#bpFf_M1A?++0ld zTbBaScPCf1)0z1DJ>-?dS38u`BbwO4Da#he%N zw&q888NCm9&(WHf_T*XWRQN;mukH<%Dv4&^N|aYUTOUFUUKrx!Nnv>CPht zJB8b~C7!+-Tu8g>UvpYN0>E_H0nD7;TXj3YAH@W=_lYOP`~th%{I0E|c&{Vtuu%EVo$ z?vGVWZg~j~^X#QkJ!8rG+I$jp@GzRaI|{0vOGxMNK@2jz#obg$tI7|Og&P+gSn*^{ z5FdUN9-F(u6(t9F)Gk3PU!R5eJ+k1ax1Ot*WQ%d5?NQ6934$Gd!n$R?0wXI4JO3JD zSi}=l6N!WjvpMQUZzeHf#gNj|0Y*6`VD!SG>X)S(D2*{88@C&fjf*SjD_Dr5>U)UQ z%~tO9^DOez_&U>5*v527hY_w}HjG@bn`_IX^g`=S0f#8;eBS(E#cdgwvo;fMt@z4{ zKl{nf_;d&ii)>kPItfw)w?KkVDoBLwhi%i`;m+Ypkb5zP)m6&D%q8>a(_iHfarXp_ z8JGoytF{y0mzoe>Tn%Qo)nS~tH#C!;w^I^b>3Io3`WHoc_K7g@BZX3lCD3jChc&-Qpe}7c#Fn+d(AkslPA8C^ zv?Pu-Qa{UTX4?o(o@eZ6hkvYTRU9khatUHD{Xtdw6|Yyv(7eVAaJid@3$5|M>+Xcw zX9FNr^8;MsZP56@G7Omc36pM?(+Tne^m=?f?YxxD81_1o(jIy4XGR^Uh?S#;zaG6V zI2RvJ&LLMvZJ_^Z-jS`QpW&m_Q&wjFZB|C;%J!OBg3LO3dhehc`a4!b(d;e^{W>4Q zzubbbByFhl%>`56JUk_Njds**?nW(#?~`WkSnsfSIbt+3zfGJPd_%G`e2#Z1c3N2QJBV5Is69@|*M z#Ux)wfD+;SUI#o#hV#S3$IxCSW8C^5ht|+T8g2Yx^tX9*@Si#zX&!I=c2yp=@9Uvl zX)ik5-i_n#UP0N|O6J-2oy4s;o>&ziX`DTt)NM|uso^`3opB$PNGgpGm2v^iv+3SN zZ@|IA4z}lZL1132s6S8!$juwX@WCty+hR>4XZ(d(7!3rzt8K zTx8Y^E^K8nU2tp!b3`SJ4Dx#;b@#hX>UbeJcvB)<-Of+Z<4v<)}>>jDYE z39!~ukNQ|>(A4)lxx&ZLv@ZrA)fK`V3Y8^ZWnXFGMSWbupCbx}nq=+g4P1~y9BOK4 z)9>B?s(fN>naJ&$B)HH`bjW-&ajqBG?pBE;+op**-)aN0S;LshO;h3Y9X--iD9!wy za)!qKOTdYr7Gqt6G(HvB#n09+!hpVL40g|@?zc8$`azZ^)mzidXHxXsKstIv2w$ta z1=g(x^3lbY98xL(!^jmNJ4TkU5j^vs=pI>k*ph53i6zrK0yyOlY0UQ<3QR%mHWCaQ z$>OWeYz*oQNOp=IDfg*hJbfBKcF_xvuk!$p%fJ5?^XU>`;tcaDB}F} zC$Oqh0$V-q;0nQOZS{Q$j=Ij%V@X2aeeNL)^e@3tGx~&H_{ypae#h{_937NgJV5UU z>u{}WdAjE8M0EIgl%}hnpqYWeq_1oh=QC?OmGSQum4}xS>-!zlL%xiQuUyETu~^Fo z9MRyTSSNh!V8&Z6ZK)nUl)zhijm3u}3}~}j4XP9!WKwj$a-S=aQLI@{RQ`UTYnviS z_VB+%#cYt_vZCm@SGIze^9o5degeb(edC^)UlWBF?5AmOV(GU=3e0>Arll(YTL6u^PB~P6iE_9a{A{e-kax zzDF0OJRrAw{K@ADNSeexS~hl{q;t7#wES2d)mb(SWzu#NEjk7_{w%`umNn=jeV03K z=KvbRnjx|!2t2nZL6hGLlKwqFbZTokj+_-ul;thZ?@bF?9e9ewSELiQR!yqOP7&Q$ z)(QS!4wIHC4^cd=fV(zk4pqD{4AngJs!B)irVSIGlF4}sMBg{R1J$C%jNXzq?x5CU zvgYzIvU;@#-F(r7s2*L$VaG+fE_@!f`d!QwY8DZrc4um)zXgNlw9>h|eM!U59c1+- z52`TYHRH1A37&lKKpzhN!QQ=s%i&2PR!p}!~B^0-u%h9Vjez@#N)0_oI6vDCYc;RR;m}%>pJkHn;y=z zG9~fd)5+_bNNx-{!1M@L^0%TJZ1mE>u_zuKcN&4$vW4KLD`a?NzY_<~DB^Ks9+TM} zM&qkWNsGl2ko_~4RJsn5a^DmP*W3lAdzxTq_-iO~9Sw)=1Mo$(9|WQvyj`;kw(rtp z^;UghC3;R{#A{9b{p%3#6)ekJC#YlKjYzCW*o#^F<#_pX>NLE^o-=L^=e!R7=Hkxf zK!>jfMA&SDlZ6}LzsFIe<+b3aVIM=l*ncqR+%nQHbDd1h6AqFmruL)jbA?yN+$dITi8`G1w9WgkyiB>}p zRts}JnWNvSUhETQTwf2O#!+6YxfOSJ1oGp~4DebO3_t#EGPeF*!;7ExhkHB9VEm<2 zxN$j%9XH?!S1z)wG3N0V`lx9mZh3zXSNe}Y zG5c%iDn1i6%w&1DRt5ZAqs{-f+mQE|bCjPx=L2ssB@ByB#Zu{vGdL`hheFfUF!$LA zXpatshi-xJZ}J#ODoh~XY)5A8*mmpA2c@=cq5?MMu~0R*7x=A$~Y7qx1=5+rKbp`5eeOJ_1}TZQ*s@W5z*a zFUTF~6nGH-alhmY$s9*L93wWH4#vO4R9jEf6f(4}|E_~U?^jqhIuyK@M>10;9j4B< z^312|INJE9hN)EgM!$W0ZDW~J&&_K;Nmjqqr-gCaI6q@J?YwM5JcswqJac>^4kMHf zoo(e+&j}fD(+Yk>PY-Ws;er)eBk*xh2CBYof(eU0!ULyd5;*TF6MEoHrSyaVSTVbq z9xLrfU2_i_V>4S|6DQKoZ84a3{1Nxcc_vEjISwX~j?bOMCJda zQC4X?ty6cP)@nDe&k|HU3yf>7_-StM}peS0PJd-p}dH{caXRs>&W`R`3 z6zC4vidEK+@WRJzj69*nWse<;7OT%gbYd3~tUx63{9*D_YY~_3e-)zwpQF~*liXW_ z^|Z5QA^p`qgUneOE1FO~iA3i$k$||lMDO@9y1Oln`Ef_KYR%JsI4pEGT88-&$EMGu zZsudE_)U+#7}TLoC@`6c3Sk}=bN7wfK&5;plY3??E2E~)j!jO5jNPB1&NvGmO)x}* zmv?bdgc?6{f{=S!Ux<_EG^FbLav%dpj@&9PEV1f>%~n4_WvWU3}fh-<_}ae zeJ(k@OorRu?+#J1#PpmANM}@tuKCO3!tqKN zV4)6m>Fs!9(+hg{iV6Akt&!&B3ccb!q0heF4;T7wVIuMfDCnq@ABwk8cF34yZjNKF zjC6zP6En%Z`(tRt-QBdi@hn|;U;$bA?;ehF*QIq!1F8Jv{ml8z8O$XiTe9fva1_}U z(W;wQ;lF9IWvH|rsO=k6$*PJT1JNcKPZ z$OO;Wg=!n0SE|J&(<8%@klpJ+{+8EqRino^od`W167(2pHmlsmefrtdGu z6^(OgzKROZJ}~6X`6PaNy^4@qTFHHTZ~{+EOvBGkLMP2{G_SVp8NX%nMda7|Ij=%ivDpl>G@N4JdPwE1gN$p%Pj)e(N}s}-06FhK=#X0nA{LemTLN-_9P*jIHn(+k9@-_*n$m{9-z@~ z4_@o#ZQeXh7v= zUvu>Nq(C%$?tsYPDOegE5&E&!M59WRaL31zC66?jnq9$6^Y6#RkINu_a|FlZL?8!8 z9wC8F8|l-B^9k*jBzFRTa0eGGAz6nWQBagcXYG}k&3NO<2mMUxmz^+oa4!U|7dGHs zoAGwBNa*J3vkLkfG4;bnMyMF@7AxI($u>!}*lNtGOy5ne%Oo==w6$Tgzwmz|F3lA8 zd5hYj6qzILues)eE%cB^2~5_lrQQ=CqqeCRsmWr9+jKn=Cpk2y7nWcZOg+ms~@8EC_Nx%Gw4943;1b1U@qQ$OHS>OrLTudX|8@S zZmoSr+Y)1G#6u6lA9JC#bxPcdAUPWSbSK@mY9{$`p^3S*p98fA6<{d6-v)P;k|?ca z^6JP#8l^m)%aADtzj|kyrfo+xMy8-vR}E_RM(}ECC$P7!1*?LK=;=;txUQeUIt5vP z{vw98K63(#kDtJEE-t9~M-JS-7t<@kUQ#zz5fldYqHF(twCPku%Vk>h?pS-ADgO#R z*C8I~QgMy48mfDW(SWHn)HG}{k#gP-(yOvai0@-qw`Dqs8#D&vvC?Fnf+~*8C_#JW zdDL#R6Rr9wd_;B=MymzS@$3hP~?LsZ^`OnRm%8vW`G^U$v(OAs12U zZ8rCQy*CX@I7O`=+QYC9);Qt08Lx4+ihjF$7qS|42z|t$)Z%Vd(e#}oPge}jX5u$!)07*zxZL^;N<<99adsLQViAdPXAyTTD!`HBc2@P6 ziKD{QFVt`PC#E+~hQzy3MuA%4@SIl|IC=pWaNs8M;BOiFr@TU!o<*p$qyy8l%y9lQ z7rZp83&;GOfg@G^^2_;m*jtkZzJKcI$;fF?Y_0+?V1Xaq4T9iW-+#ziFf zqJe`t=sv2)z}}H4mlA-IlZw&ay2K-Oi%a?oF7o)*M5P#JLCMQ&Gsl@hao) z3eJLRe)aYOTrhD5%-wYbWNq>x2bvWw=XgXW)^DHF*4M94Tqs$9+~$ z$KbiuP+zf?D?jrLR{D&mb{kg10)7I0A$N|}%LYMNkUIJ0Sx?loi{a3AZ;TVN4E+bZ zFifY6-1*pNb9Y2BS6dVW27iS&@Xn`{CYV#L0C}4w1!`cse+j8gOt*DUK*5a zeCbS$0@A754w$3@{Xs8avEaA)5T*eo>#gBUt{pc30tbHRA7s4yOu6G%1a6os{8zjP zjZUwkk=jSA%&xhC%geV=W;qI-?qp#?jSjv2-j7@_y9P7gT?g;1JM4%*{&Z~deKI@_D(>R4}kyT;FHAr#>S)ZW&V?CG!Oa&6_ z0GB!z!qLa-aLPLd;x>&&l}Y2s^bOT0v3~>|U3ZZryja6j|JX#!*Z-zQDx0x*=_?d_ za}qCKeNNu~31hW7-jf$iuY}#t2&@>m4*Rz(7ga7UBBR$IAm645*?+s`aAABu^H8n_ zlDDSACTSUPzfI_v#;+vGLXI~4GXwD@X`%FqvxADdix`?yi;4L@AooHVVvc?z>3+9B)x?E2xFL&=8dlMdbK}5a zSR;Hk{Dn$#?~(C(0V}>Wu!v%I0I*%Nw({ew*l zq+z;YEqQ%)Cl_-$o@Dd|laA{}%*Yj^Va8ZRP#mcy68Btcqv@{B1)sb@4tA8%@)jkS z<{!t-5N5<7dBx}y-Sm)8Q)p4L! zVgo0lXS3ozOIiEV#;l{xYG|104lgy9!vVgThI?&?>Ja{35;9z27i2<9#HZajip8WDK zfm_4wb>+1%zdmaS_lr6x@brU?U)F%?A46Cg8AOt=zNU%2 z${^EuiYPn1CIQRjNoR~BOb8wzO|hw@v3fZjXb*#!H;cffBLwsZ64?>r&)|{kB+!4N zU3EfJmC&Au|KcMrkn+80t2aCa@beYgATc}|G&X@LpRM-q|-WOXTNA?RX`qQ{5 z5lo2w%~+|NqyDR|65UCMx!E2o zXnfHbV$f+%Z?`maWR8;XCh5bw-vnt~L;x!5w}4$x9w+9tR^S@Grjg@@P`4+XzRgwO z6z)@07u?H>rxA=-x5WGAgP7TtgI_Qr z(Jgw9lGB;=DRZazHuy<+PwBJG>UJ3QwI`DvnG5WMlCsj!@AXRc6^n$;m zp8HB>F!VV*eM`s0*_sWW9>|JHSHUggesH^Xf|Xh1!fK=|LxuZ8 z`1SH7J5FyqJ9&2kyV0bUb@Js<7`6-P( zYAOmzP9jBL*U|I+3(14nopkg;aU4?|L)q{3==)6Q0y{b56qToQy$y%{pUC&SrgJ0S3xJ?s^n=%R&dDQ!B%$%O8uvAKa{YQsC?8@h%}IlYX? z&FCUWQ+A3Jhept4a^fV^xdM+2Eyby&#+Vazm);ZlFN?p1(7y8)IB$VIZ($ydFE{*! z0QryHWM`qPt2!Ne#t8Y2Ge6kLLmBKI*$2?xF+vY` zLlUtA{Fun-hP#2>n9X2ncb6*sKE+6=e`PHGDsrluMv;M7aiactGoAH&90mJ11PeBh zjMq19u2}WbYfInYdbf7WXa$USaHLW3*J;tCVW2LXhEe*5c-N(0c(vSNSo1;!@0on( zLi0vJRLd?1cWxz1w%f1<^{o)`=`wscJ(}G*a~&jUyTj)Fk>sPdBS;MW2gg=jf+-n= zFw&_W4%aDFh5PT}I)6N;-F9Kbc<2_r5j&Uq-|HsVR=AMf{!wI)#tWi@KZ(cT)9BGq zfeOBNFn!Z+Ea*zbm+Kxe=XbxSX^oz&y!maC{||Vj&N=)#W(b|O3Vl55$0#W~gaO{x z@FDdGJJ(N}P1S2=WAklU+tnZ0Be%wrt}i-7J$@pS^CKNhru}8^tDJ`A#@euc{}Ouk z_Gg+gVggtFb}3cfSwO>IEhpPEo5|{jvE;(Wm0;A+1q#9I$ZF~FM8!WwU=r=1rf(Kv zQtE3=@H&K{ZjWj7dJ&CzybGTH$c3KeGjOK*Te8~8Lo@|8vcp?vkr2GVdfIMe*_~%t zmGMpN8tA>7Mgj#J9yIKwU+k?DdHyzZjq6D8{1W>2M?YUrBP zYpIWiGimxT5>!8oBgd7)SViAkkahM2>wFSEx+ub&Y7 zZzVhDgd>#xCl9Y(G6<0khNDZvNW$JjFuF*aWK(?@aaDzrWq*)?u41BjcQrYf_=MyR z{p7BPeul9-jKM<3fr~1f3gdj8XnyW>!WEo@b&VNhVA}vm*r^FC$7I9KL`S6C89Zd8 z#;9Gar!iyy&}AN1VZPRPRyk3M^3_6KLG%mO$7s_TesG+=OBD#41rM z=np??gcEi=E=9I=E8fX30=1v|u=89dUTyNEsWt;Jy!j^nxPFYc^jn1?U)GY`TT`KL z@jBQ)Fa^FpI0o@K{$Ri#P9CugkRlW$vbl^_|Y~mdHGX|U|F3C%xn8e2F(QSzr_=X z*jx>3WemvWPhOyOW1BPoY}Fm%fRZo~-toJ`Oei#XRx8lBjUm_2no6mLnzjByFPxG<{=DVc!{WsOXb z*HL_68pwz0f8)&)WBK`8e)AGzZtyeOM_|t5VKA97r_zV-;T3@i#ND1ur-P7D)IUfc zbqf2>JOvt)R>)mEm%yZ#Unj-K%gD6_8@RO@^EkO%97WtWWQ0Uaekj9~XAbBtX zrkGt}_WS3MOQlKB%je(X@01)=jJrvP@@w(LkM+DnW-z`mAHW3b7Bby?9IAX-MsKHS zaJve%n2K*+g!@y-#7nFfWz3Qw3#~mE*$g!j@;wQd@|lq2V+O%Jzsc3Z;Z$2@3O)U4 z3#b-8BfpA7ke}EN({8s_xpHG!@zEjR6jeuZRZ6j>R5;UD-sH+mtFU>;NPgsY4~+40 zBj0L2)3_Z8_?3)AhoV=+!!V7+6xQ$~cl2X%NjE4a8gL8Fcc8qZ59&y6W@c8NqUWWo z$*YT7$iw6DOy}Dcdh3@tohraCQB-vpH* zccEc!1gyS(n<0}=LB8q<*!*w-YoL&fS9X1Z|KtkjS0)BMG!NjDYdd&n_fUR>;TpO( zT#XkmOG8|sMgPpMCZi0_;OC^P7??MTp3tr)uM%|W?dWK9F&j%oqZ7DeDI-DS@+Eq6 zbSZtj*G<&@K95XzX$PLqU18~lcX0K?H;}t{ijM9ZLEnK5{gPHo$j^FsvTi!7`a22| zN{*7Ybpa5v@gnH{e88GNyTDK2-tx*{*W(n!1{@Xk5tZZ-D~3P}H5FyY|H1-+e|j){Ip~ebFL&vpWNkqc3Fq3G{Nc+`%OqFiQ{7AEy)z}?BceA} zNYmNk_ep;C5GcI3D&)7G!lCwH?&s+P)bz9?7wPImB5(9T=2dH`Y{+9}N9ThFa|azK z{iX>$hd{|UfZhwL$C7vBQ2lZao(aFnYqurwGM$yY!-t9VzNQ?%&j<0R-%litv+=0G z77TmW!_|k3hu9;&Bz09L@!-FL>)#JV*Kl*?kkF^ADfvvvy<+0C+>nG!$%V5ddlV`>62oFpQdU z15&T$5dEFOIHFI;F-w|~qN6d?Jtl-~$TH&23oO;S`;2UIdyc_`naZTQ{~MfE7KikM zeQ@UL3gTT{0LrTm6Ry`B=44s2GO{0_Tz3`(H%mdH<#twCvV~PHmt?mDtAP74bBN5{ z!Sq(?Nw2P{=)fOej$L&_c!eL8Z-E{avC`~LYFD2Akn5WXC1xd{=vpUw zSKZDuY}f|*8>OIo!ZJ9lUPQvLJRtpB;z@eXJ66Sh0$b=%!8)4?Jk1ITjICXP-a%JM z-0)&HiU&xdnG4;kNoJ*>*49yHu$jJ4sF5Hq`m^}7`^OGWOA|Jkdq9{f`pu87<##iov=fLoxJN9 zt0VnOn8l2R7uwSyF8MzASo#Ci+YY&tS|QB7leJG)V8teiL7V#{R&BGQ@Xip}6T|aB z=sV)(Gptb@5NQqaYT8BnByTt1ndD7b`lMkm6Wt+ydF?lgom;!u~pm&EzhgVvN?(E4r&tO5Z8tK8{r{X%qY z7)^o`UFn~R?zrf(40l6jE&bHLiQc(!6jehMY4-Hnbg*>{&1%%+L=D5aXjfs^Dt;fx zL=EC*F@u&aNv%?0guc2&Gb$gN1q=7BhGQo8ShIIa*gb!oVRmshH}u?F^3Fb*37y1)B84o=@ z5+n2rpUhOkxM6{K!ajk@ToIgCN21XD3@^-E15i(F2a%g1Oa48YOjBOzk{(S*+DQg6 za{Fk!B2$D_v-i>ZH(O}!$9V3nt{khgB>?`JD?pK+IXi6CF{lp?<{cW2LVHgzYvLFJ zr@dp?k)E0OWao2KFMG#3zlg`(rfX2=>smaWJBHWeFJo180$wt(r$K+?XzCGt43p~T zrJMTk*i%hRa_YnrS016!i0{-y_HgC#r*=eVRzc;yS0(hwIt4C9YaF#%5st_FW$;o) zBF3uJHZX@&LGArA7;f?nQCgh{fLdcqT_j9{`F3tFxkAtyZ6}9y%LM~N{n(3#tnrA!p}g&%SrFXE2q`4 zQ)3ENx=ujNw`*~H^l@r7{Wwv0Bg2)jK{P0%nxof(=-<^8Va8x?& zw{aq=UB0aH9bdF^dqX5&roqh*ax zc6p;jj2ZJ|rwU054<|`Oi|8k-k+k`I1Wj#k#SqN~jAxqYv!P^^RNR5dB~S5?ggcBp ztiUU~iFm2vbeuo95EIm{;J45UepP)KCi$>9Ykx03+9Hm58*cIcQ*@?*San?(MrBqh ziVR8S3@P(-_Bw@1DoRB}DI$qxP3o0G#t5N8iISp>k+auH(j9gc*-u>jQrrqb)9eIepn;!9U+ph7+JC5^ecXtW%ua&&+ zP?g{kR^>NXAEzJm+xX#T#{9TmBNBV?F`D|V#RrGKq1ksw%$_zA*Z$VUc$sbJWu#5D zKTNMhvX1g^Co;>9j;HdfZK8$_N0K{fBRO++A5Bl0UdxNpX!V!BXdlGTJ6`(#<}` z(a)49&0HwBe3S9Az92>E&%j@N2YP!p(RxP*nzM2f)39#>u^Zq?%Y)0z)O{^v#G$El z>9I{jNi3V|3UVZAW(<1T-9pJ967-$$OnG(F21hv^$4v?=@JRDR?3Lck%TJxaZ=KV| zOM1`dXBJoUusneGdVPW)mQ*0j;Z{PyY@rL}HV*!2irCp_4x*&K9{G1%!Ge5JmvvS+IOD3C~G2dSLJF8j%*`x>Y0T8G$glml1Q}o zUYeERMTdu+MbCY2xIdGf(QC&o)YN~2IbZ$=j;9t3%2MIwM=-qoy+gdm{5Iqd_VKRk zJ+b3!E8ca(WiVbQ@$=z6!49+~UKn)tV}YOm)qRfZ<4GT4!L@zkFooH;6 zCd``GlYLu+eay8&lAb-l+_M;4yKYV;sr5}G&m(H+I;ToG(%ajs zg>3u~ZdG%@Q{4yA_^t}cFe#@u-dsg_lSaI>r2|W!uEhAteqK5t82wgv(bl`apng;z zD8~Ipi}b~Kpy?)2YEGt?zE;D=s5nTt6AA~r0%5_%KyvTD-GoN`1>JT|S94dqAZ#&_>{RvjO_a|I$_l5EP0%z*P5!O6;q2bYeXgebt*Z$&Ymf(uD?^r@N52-L~-XEd5 zUybNAZD;!5N@+SOr~$^hhoRIOIjkC(hmjW>1rFCkv@?5&`=ndYU|tUn%Z>r4cn;bzTc3MLoD=z8En$M~y7#ECHDG9{YAQC4m&;%Xf{4Zvv zBzmyPl>RvM1NSZTqt68vNKVgwJhOc`rf+|V9yL9Sn_f}KN2mM0Xk};+ZJGNR7hP7Nt?qv<962Q=2dp{?B#55xC-kio&y~lzN51L7koKZgLjNK=Ee6|;QJLD@v6Hf zulQ~rFIgMSD-3Qx_eFnj54#nwKfjJHmz8n%SrN^h_XDNAd0>3kIBwvHG^3-on+7z5 z)4AJr(5F_B7^t~|L?q~wk9*6BM1MS7%z6x^rlDZIE*rJ$R$?A~1ou8KfltN<;Y-y< zcHYCW?0RiOHu_Egn;ewGTD{3)rTR>;Yo`vcFK@&jFh0t&KlO2oYB=5xci=6T9^k$7 z)$!MdZeH_46+d!N8Q*jYS^ht6=osdJfx=Gx#dSH99J(la6LFJf7Ddsd@(GmwGp9@H zx{>`BM_LzL=a@|!X}n?|6bVc;%YiA7=;Do47q>!eoDyrO;>bF+`hceNb=Jn+mff(m znB8|@iS=Jx&6@iGt1)m5sq5GMa%@rI&zu#cPC`Eqkmvr9Zqc1)euf$i6 zp76t0yW^$lBXEO_KAM*5qlN8Q3`;$V!5_ELkt_bvF0m&>_&(Q0QYS8|bORdGee~Lg zZ{+hKLe$KEBk?{$jS}b6s5xJ;;ev(8K71FP4|xu$>$0h0o*NGmt@@E@y<&QHRUlmEtt6OyaoC(B+5?&U4KLd$aAW8YuiAv>ENr>2Y@4|_0t{Bx|%y^Z4nY>>S@ zPjJQ^pxzq)nAG4{7~x-rn!~rC_Jj~L3crnU@rKw{F@e_^{)eCRcswudvjVjn zYcO%(1i2X9OLCHDiw=Idh5Az>;B(VF_+LCYD^S7%fHH+L}#!nD|FZ? zW$rj%OJK^hRABmy;q*_iBrpC}0d*qRV{)?&zA2W)_#JLoo3H~@{&dqJjbv&t+y*tG zJeksMT3~jvir6l!pxbpV=?}3>7$-0U#t!9T;xI$}vD+VOwdC=9SE`VMoJ($&YSC|d zEQ#Ltmmpd*pFWkbfaS#=@H{k~hR8cZOuGa-Qcn_^J!9GR%2KSXTno@gL3F=q0Q27! zBUHL>3*uJQi2ip(o{mR|*TtC_^>`1{Zjynzm8)r)w&0>1K95-X{$rj9UY*0Mzmd9v zAdX*Jh}#?1Fy`iea8uSV%J&=K&O3<7KG~RH>WB;1dXh2a)ez+T6%`?nX6gN8Me<2t zdVUj#5@ryCZU+)&u$UcYeT&r$C#-XiGh1LafmIT+V?9D|_@TfPI#T}}YR>jU%3^=Q z4_<}7+iT(R^wS9Qe}k6XYr1rPk{!Rgn1(&sPpwysz+r6(+{-zi$yM%jt+sABt=HCO zI>*GL-UWTC>}5|>$5eq+Q#(v>iNJ!;Mp*AYfqP8{aD9`|{gu@swD2x!ZWY`M!(8BW z!a;~4Phrx;tt98Y8}z%)XO*g+u@+mGv(gz;SiSQ%ShJh0tokoIR-!2tgzghN@%;`M z8221@Di%QTlW8#O#TfGOim>l%o=eJwOkk?wH4<|?i$wbPl2mCw+Az|H270ceJw4+{ z$fkT)aAg@pZP*|(ubf2dg}d-%&&$;8rZzqRKWa8GlDtAY7%}55&2>6QKCO0zK!q>p z^F9E-$V$+sl~%wQA7r&so7qWyitLuZFWDvACb6P{`D{eE6nv0f$a))1VBL4Oz?E9I}b4_6u(R(aH4e>K^)^RvMjC)=Y{^|ASFU zp#nEWnrr))!|dP+@q$|@Uddg|EB_qAf@3YneclN1)k1$`(MiHCu%NwuQFv(AeBR@C zE0#15M>8!2_&E1Axe->#j%TXaWoeWRtukg~S0%GXM(@};rS0q#l?A|sb-;YTCm`iB z3X*#4L3>s&Ij&GggDVt7!;9&H-8JBqoQN(ToA9QTozMro%1al&!tk}WXs>=1 zM=9I#+9p{zH-9Z=$A04C{9Ng`*e=Z2qJTB_GI&$x7jN2cEM#OIG4@O@%z84PJ#@XtOlfJ&lZF}8BKaq|Znc%%T4+Gt952S$>H3%^#5fvKbc76VGRDmv#q}9C;S)C& z(=0_KR^VRF3C?3kSUO6K|A_nB90f(22XZ}^ouLliG%l9jic49Se6eY>pdjvei#a_6hCtQdoklL8-)|1nrX+ZrLwAXjISxdXAMs);Szw)Y54-&7|vI%5tydj-gB7o7x^l zKZwlPCuE=&@M`O4qHDJ}(Qi15^9qA;#JLzW>Fr^420pQ_6D|q4x?p%ax*2Z60apAq z!d9o*@VxdVtFcW3IzF8x4ScWN!WCOc_lzZ?@4Es}Y_Be5-t0mtV?!L9;YL=23oLqb z6tXpIY2-3{Sl_mRunLI~Db-JIJRS~--5K1jeUl+X;WJdf@1i>vP-yfKaa)R3gQuMc zb903|hU9zF)%%)GYF|U|^=IRar*63UwIQ?MNH^BMDP!Dhb?HJO7brDd3-->t1K)dx z!P~G0(5@l)@@BjNg<<bv8|w(SD*BopLX1fce8%OYa9*0g6HZew^oDP%d3F8;muGq@f_g2 zEikNc5mOxfiCgVjLU#zCwf6r$p}1KR>TSrR8)Xgv?5PF&D?CVEX@Z6CYT-<7F+5VW zW{oQ?S@k#jAfP4_P8&$UxP*VK?`wH%)Ap#Xd~OLPpGQ!adz-P~p9JLsR^f!yJ7m@F z+pzA-W7={1JGM&9tijE;SY=fZN(z_^ke-LYBNt(3!Kxfx&)QVz1k^NGpI zx7>-%iNw_GB@tb}gi_Vl(DB(ry3b@bh!xBM=K@`b{-gjEW11ja{}Wu0@PW5SJXnRZ z7l{118?5=q3*d3;3;bC<8;!J7>A&V%%=3;i?#D_6+@5>_%U?}HoY;>}#G|@FdnCF> z|K+DVdCD95&OqD0i%HLfG(0EFI7hSI;J!i+j*KaRqR<-h>f=iCCt~*H$f`)vInA!N zMAna!HxeV~`m%8jDB$?*XJGllbl9l*ggK@08rH#WR(1JrRzm3E|Jf-A8Dj)?@d7D0 zXkkI-A9DgTwcogYy#{_e=T7ruYv`~=j`Z`yv zCNUVaK$?blyP?+atGJv~fdDgKp{60+^B0Z*{X#!3zpVrGKZi39ovxF0udL|3CC&8T zWjp${@p8AqxWU1({qENeG+C!FeC3~z2Eg2$$angsiez;%3vsC`q} zVc{F_(}(?-c|sezg?-N_qknj|y#g`eGH;f67+(vl#WS+`s4>5YUmCj_UubNlk9~wL z9gM@msy$rP+9uebww%6*`M~^0zD1KGTIgguU9MI>Q2`&guIXlc_y7=$gATxkZd<5EmSQw0jA;`%jxLPdM-kYbJHteEAE0lfh6^Z)VR*#66MZoj zYrZsKQ>ZO(I(id+>>Y-EiekL_lqDEbyNDneMr$QMV?$m9cK=z1iIQIU@Sr<(p8P|< zNAe^sKM}{o2UC6T(aimtP2}9DWYXaz36fH$nLVM(G_C3`*Hdmmat6!E;O%yZ+n$W} zlXUU)o*=4`s{rJ~J$fbd0=d?~;|$rmIBCT^QENpH6n+<6OLr1cY7x-46V36Cz_|O{ zd5c$FR)J}&?0Bsgk6>qwI+QBxKzSK=^mr|JZ=}?C>1QGM?dnZz{CN)zFKnal=Ki7C z$+zjQF?j(b|QQE8OdLJ47N=w$6e1)<3@V} zFsvI%PagC@wm^b}#sZ^pk=44g|ggGNv&&eyvQryJ(c*ltVQaP9!Ux^*Az`@MO& zyK{My3tFNByd*nbd>y1eeNelta|M2lGQ~kFF@Eus8(8cFm^t(x+LYfTOMc%KMOUw6 zv@*kJ73(27EzA>n*$0$Wd(ZSGXi?5Yjhue@keX(;fLcNYMwQ9XJ6K5CH7iKVxM3J4 zHwKfg8{plgu2|ssn>KQb&_QW4dI2)IUsF-<=R&O9-h_eE+HjtsA`I0?F^$F^OljaR z)^GI}=-m2`eA|$UDcOk_AQR5ZoX$Y03Fh?IY&*(_)iT?Iw+rv?W29iiA#z#m<10^=(jXAdcU%omRZc8OtB-;SfxQvWQ@Wj*p3nD2kkohM)0!JM&nHDUHHE9 zKVB)XpI)etC5dv0I5XrbJ$Ee(56q2kK4&A^~rbysgm>9QK4V(g4{xS!X?c|j19*lOKh6VX0IApgQ?U&k-akcUo;A+W_p_zh{(haLirolw__2|6t1}(KK z1!bw*G_dFk7`jK$_=#t@;4O#YY20#1O+L-bmCInchOXeC{f2UBo;a~3N9Yl97-ljY zxAPxp+R>Zzd&M4{=AKCJ2actcy`i+UXppF^ddEFGZA1rFSdj$3H%w#tA9|=^30>YR zMQb%`Fl6)_-n6g*gXK2jI=>Nk>ePL_SH6sdW$vMl6Q|RhCTDQW=!bDLF2klft6^p2 zd8iro1iY(Xz`#=pxMFn;zUw`NCSwL7I!DmDmK9{6wTGTL_JQu2|AnR`HDkC;3c4ri z(7_|=OwOe!)a#T5&dv9a~w!N@aP#=LQz4Hr4QYrrvbce(n#A@tSPbtpY~4ZUf<7Jm!<@ZgosZQSsf ze!m_-ea~IPv4;PM>1j&qm|ZwaC!0Cv^q02Fjiz=zZcKQu7Rp;!F(WUt+PWE)B?)!yIPxUuCGS!jd?JN}A(t^{*AEpcbWphnJ-&^LJ1ue=; zCT9eG(UftN)XY&QnU9re-mbg!aef>b)f5I7J{_xSGy$Wc!fNHE0hd>x4H7ai4$rVDx8VV3o)fn?7XZTIXWJs0xN8s1wa6koc+gP<#CG76pXRJ-+Ki2HtXm+Bm;LGj%jLVqGcu7}|cd>iMPhvjvt3C+) zj3=*neGi7#*FWb9V~v?VZJWT?!3OdQ9+CF6UnJy!3ejBFf?_3`xR%~Q+F#quS=Xu3 zKPvZ7Q|JV~-gbi?u?R=`$s!!-cnjyWp5%_!%_nU^9dv1-;OUbY3AI^Ipv#oP=AuSs z&yr&B{gJ~)MO(7lHPqR$a+<&gd_`&mW*cu}R*PcrtE?x#=jtqe<&Oe>%z>-IIZ_YXYgK5M$5C`2 zCWeLb7Tj;8zoh$eD(Ss30X?i=Jz+xA22a9 z7CmoVLD?Zy%q*?IeW?quc-})4Pt?O5gPGjWKo=d)i%|A)D4i~v3mQ0q7sGd8s(w`qPaAemnGBy1QOkV9o{=Jrj@l%aS^MH%!rP3ZC z@BF}I>{TzYe~b13Q0g~ z`zrL1*@`|(^~t?${mi;g4rsf^5^}0G2yEF|L}T|jn*Y@jWwmn1`%i+eVN)=eT}y{K z#zQ25O`z#B#YyjgCQW$MfWx28q<%X$k%u1jkm6iOZXR(YPaZsgKgYJf(WSp(a90O> z>mLmdb{9a=&S~&kZ86y;FK_~9_2G$#8T8Qpv*2=c4`m-7BIL>ooY^ybA8e3&}~H9DW!EgAod>!?u-RWhDdm zLvtZl{W3zMh{`WczLb8`qvpO1k(iq}vi@ZLD$Odp++ zr%A)Uk})xsuqC{Vlr&v}%?t~oQ9|E7Vl+&+c%FGa)B<`ZwHTvKdr8udAX?gUt9E_< zEutioL!OIIgjFFTaOqtKYkZA>qy5mjdm3vc=EWN7c*3Sl9gw}El2zMynvCE21>>!i z=0?2E`y7Aix~Abfy;971E#9x8QXhxxO%Vl^MlEN!pG*|L>b z@#HH?emc%3C<2=iJQAZgL!41K6}!(73~q@;HH8GhlU%{;4Tgb)(?+WD>@iG^^=HNP z9}~+01}s!x6L7yq`cx;8PB&jL4Oz$3dCj0vWm=5R^be%}u?M++s*_w>v5eYJ_y@Ce zImlzH(5O%XrXDXQ=MSvFjk%NXhTUjn#7;6fox&V$fi^#_HI5%WSjwM$uZb343So4; zFq^p%$xG&0^Ugb_@cW%PUjLCXt(|6xl1bgvr(ioH+tNzw20y}*1sfqdXDYq_EC99y ziIb}i`^mFz74lPM6p?V+Me1fn&?g$EL@G|m1GX&zb1fq>VM!nNNait2lbS{%t-5e+ zv@;heIs-G!H_|I3EU42;Q#>@+g*-WDMXD?s`C*T|_!Sv{ajWG^G^?4-of@NyZo}8n zqKM1Y>6PLj^(4C#Q)B_Is{DAxw>Tp2dWE=aAAUN*4u;g7y z{#7J_P@#q~-hm`yi85XIv4%!3swHP0zl9NP>Ezk$^DtrkAne*eK-YEvuF)wtZe5_d&iK77fawl zNds#hIf>1Yf6jV%`mqx)*|H(rWLEBGE_79f!S5`admI_ zsBoBF`Lv4F3X)=*drIJ2P9JuN5AiR1bt|{fq3l4c#z5Sr-0O$}-2+ z&W8PdD}as=v095O(Dh9Y?{s!HW{z~idWm;1`A-d--o1^zoc)gVh?&Q3UbK!qbU2?q z^U<62yd`2!yxhUgv}BK2pDbUOEn@>Xnw5^ zQ+`u}aj+f_#w*r=r^W=57Kx?91%D#fvryVfad^;AaY`~`ZH*2$p99Tn#E*K~J z26tB8g_utVz`v!AtXh)}>l@_Y(DZ$9Z251Pd1)s}Qq?0pF@XY~RgE0iFd_;kZj*>2 zebVT9jZC=nofK>KZt|LiHt4-2O!&FMYc7=FT^4M_*mDMGW!=oHs!73t&^fGp zyan{%h{u^5@4}h9|3D#R3=WLjz?WL<^A{r9`R>`sJAO{!?Pm_><(mw!>RAXm^ic2_ zmL|~KYw{V1!!Kdt&qs7tf{3=W&b6+4eYqzZzqs04CZJICo_p6ktwe%S&Hf3gv?bw!|MtRj_aauSWv41fA&RV z;U9qs-JHPsl_V3Xx;y-wyN`J@GKt^1?F-(^JIOmR%dr2y`SjNmfobsLEAH@pNUwK= zG8($NG*;y^%@?xmb5FT3rmprxW2*V(*s-sur&t^JwPpq=B_AXwr81ePdpz*?l0CJ> zX5TSA{T>cIcE{`6U*Ru%Rr+beQ-KBhf|XJKL=s>jEcL5n*v!YQX7)jxFT03%ZnT5u zpKa_u7d^IQ?;rReC*+3a{^7mqM)T8_q~TrTEp+?%Xd3v2TonZ@Gq)apeKeV1NOEjo+nVx|9Q0k%_K`X@8s zU{2fa$J74*rZBIpO-b4>7wRGEL5of0n4wyY7q3j@rM^zWzn)X^;DKShl=vDF2j8FrMz~y zE9O_%f#l7lSUcVu#CmLrO0G1y7BCBqmur#h-%ntbc_kYCP>1?%!BN=q9<)mDfGJWG z&rSu2Cq+c&KX0^8pG{ADEad1z7pl84pE=^Sg&VAygHlEh=!GNg^yT#`a(aOeZ5DEw zEBbBF>9sfNCMDu^!;Ad*;v2lg&LpH|HvEY7CV1}e34EYCg`X5w&QH}@zz1JDLm^-- ziT|uhwq?x4kypo}nc_0q`dtT09=$@92a;&vCIhKwSHrk_!(r*g`H+8ZH)-hoK+gGi zG9794sQ+LTIddtHsnF8Fv16=JrC~bVrRPZl{TylK3mLNLwE|OM&fwIE-t?Sn9WKg` zqrYy4U~|w<{FG+S8?0~O)p~@Co!?7dJ$4OGc6RVCKl*s@#5KroSPQGR4r0jo+vpiO zLv-UtZ|x5I4qQ99nHP82E85jIoELX`hspNH8F+31iP?Q1e?XsEyK6WM_xM5e_V1*z z&1ytx<4*E#QUqBoF_|7Vf5rS!)1ayyW?ajqLuBJ@F~(bYA&SYDay>JqaJ8+sx!;Z3 zaJEAVHYeT3rsTEwTxK;tygUwbAKe%J&=OvKQwT4SX@^rHRzt+;HQ?EH6Eh+n5iam5 z9;sT74zCX2i5pq)GyE1Bsh_}{t3PRReiZ2z7{=14+~}Kk#vE2NKE=dtwhFj{?|a!P5D0h`Q8T_5wO?p-9DI+KpR&Z`PC~ZU z>MD%c=fOH<3-g{uMi?+n2@~h7CF((D)P;4VOoOtUhk(KBtaEC5B+K49VZ>=r- zbOrM^uSLJW-KfZHCSMhguxeYY*%kk7fg1_SNJ`)U-dX-1Zg`b~@j;uQFlZlZZ2BHv z8d{Rc`%7^6=pkAibD7i-U!uC#fE-tr5*>|MLVraJh|C(dQ_?h-sID`jSN3$$^&fV9=ng0cs-@L_Js`pdNBzIP@y%&Ubt%ZDBLRgLg(n2!f@sXjQmqcU*6wH zPqF=+1Wz zbWT-s$yS|;FTK_ar{MmV`|LYs7s|(p>Q#q0{G=?5nQ%m@wEYM9^ zNY6Gk(^G@V++_C=Fn{ebs;Sb1pBa1n^W+!5RM;I)3HgOjEbe1iNg(OUIZp1xJCK8_ zwcLe$U8MSc6&=$WN1m0RB1!LVla;2~^x7~WZrat%z`hVN-khVA+AAs6{0DJ1OQCF8 z7pw6-kPSaGlg(3AVb?9X0e1f4IC5yRaM!Sa>L+g@cGX+B@S&Zqy;@3W@&Q^{UqTlr zo+8U91`E9M7sNKpx;D-*ls;7(PGT%uncf;*k{lp(cpputHTD5i#zc(FPR$m2wPiFn zQHC@$pQoEHe8VLp;_17dRJ@i4# zSEi^g{|gItb~B}=g|PE%KUv)vgjz~g7K2ME1SNVg|Jj;hxTV zF#D$lF_kBovjaQP*|8R%Hl4!0B6ED5T8$ZjeyICK64&KKpyW&wx&m;dr;id7MHBPjU&!vKAUGX{S%(tq`*~>p;yX$Qw$mB!r0JEv0@}v9hvvoR(b69QsA+r@lXp2_LC$h~ zG*KT9XWhZ3T6L_q5yLY$}nu)Dojd=75oJZgL6L8uj=7c-!KQ;@`mxlLa*`DI*#Do!&C8I4U3ne zSK)|)6pVGXMvZqD(2{FMyRhphIZB4M|5QM>ct5=nz64WRRdL(De0t?p2RQ704h!G> z0lnn+Bq_(5HeD^n3nM0?aehD8wta_714>YF&J)(#-DbxG9wi}117PFiW3-{{Y3=pk zYMl3grAKFK;kuEbFtTtVH=W)VI>#-jYo^an-QdA{j61~3^oC;a@sGH?avVDC=s^?1 z6|^(5k)DyWrmd?M)AH12&D_~>g;A_jnvfg2yO?Gu`3s-x22x_%L+uV^ z5f^3-*;DTeCi6q+$);_zzvDfw(VfR{)s5yAWv63WI>qHv`O7*^|KZ&4H_(7_r^6H* z=<@j<^n|PtD&HKWshXoO*zFl=&QwQx!$LePm4^Q7E@J82lR}5D7nS#i(ZmI(aqfOS zwAU28MFm;tWv(w|xE4S`;(plwcm^b;x4`td2J}a)1L=DBu+1fj#7&7pgWWf< zaPSPJTC1@_=^`%~_KBwTVjS!HvW zN{szLpC4$X2ipX`qH;bxK3oQM#P?vLaNiW)zK0)fGZQZ>4q@ZSozyQpADtVLF?8Wf zF45vRxx0J_#vKkJB{7ZkfsZU^(PwzPU@K-%P3FZa%<<-36Y^hHIgU3-$GXZ#STswD zw+WTQKVm)u}(PMRA8t&eiMDNK1GzaCDV)%8&NCRn2ymO zjo|K1F=IKo5Wbr_G*;l@k_>#7%<`k9C-I{`49CBdci?5yJmL3cpnmo-!F@cQz8kEk zso}MF^lvV%2zBBonf~G>Ju>jo`O|oJb)1mFnvCRO9PJ)Bi%Ui>z@?KYFD0A>OUz4f zU}P93d#KVwA@=m`p$AO1@*cWnVKZ&4TSnS%ydn#=T1oEIFSI#VSWbU>kCPW1K)DyM z!E~u6b#Cjx>Dslm2{$B!{pn-Tu-6)bV`(W`wpCzU0pQgUgbu5)p>C;Kt`*{GTbxk9So*B@H8%=oD{3)Jq&&9%* zs;FJ|jhuR*Lr$p8f)J^%VB_KkS%YQd>p4{}Dtasz-%u%BuNv`m^DL^kM-pngA|S&* z6AUVOQv11x1S)?ce+4GtOUs)i>*o-6byXwxvRybcBm|P~(c{U-X+EUwVIAZ*2GXFB z6Co=o6gF*sPFC99V-Cq4AUV?7Fj*lB)H0Vsf_^GiofzV$8N1>!!7V~>>)|Ksk;Kqg z3eqZuk%ogFG~|OGEN=^gpwRDVTOfhtK9JWcN*K{M3CusbLc8FCFHPJB<@cY9a!yNt zRk1lS%Ad`2$@dXfOqpIWPsTCfb7)dSG;>k33cR+z1tay_u=Ce^vc#+aZeBl5-fsnN zQhFX!GKawFu58$6rUZf79$ZSi3LZOMg*T2>qt@{uyp;A7uL^GRqlIRqL%D}qrvF6B z8i35x9MC?#1mUA#g}gsi=%3Z$)R_~Y{EZGfdE{FtvulBv4pma}=>a*FJcS(J@))Ml z6uSSFKmC1hKc{rU8-}ek0P(dV!3#bXq7|P(cu5sVFWJkEI6aSh`t1NGV;jRsMIB@n zE21Fd?f{O8?BaBLa?ov=Hfjd{#mtk!cfEKwzRT(3#aGz#vZ*%08O({+hdm`Q&Jr92 z4u<(67m&&EBwx!-$;E|EaBpA^YpZU^ssu8yb(bk@yEKiwD!4>C&4$B_P3M_og}$ZQgQ1PjDx%ulSTV-!O>t&spRAfF4{j>j4u7AK<}KF;;T+ zVODkGX;x0)J-VcyW!1)4vwDV);nB;{kiXy)IBh*c&L>rqa-TWeRi6efYv%@>vB8aU zYz(NKp9d-9^&mSx8GN+9!`$>CdiC34=8}yb7CjUr`;MHXN7u|1HI*4~8>j6?61VfSjGI1yYsj6pqq0dvY<*omLH#{JbPq1nTibH%d9AnNT% zsJ!U{C)}Trf~&#gcb3pQDy_l3BxYZ3VYK0K>sjjZBX$LWQvR#N~wIW7=> zP4^_Fct*>$CltY3eRuiuHTEMBKOh~UWW%`fLqZ6agz{-equ<{9oJ3;2q5#j@%k5sdo z^9JE<@IH8`u!d9)h9hzapwuFV*PvAmOl!PER};@UhO13Uf`Gd z43f~Xqd1(ooO6iZM0SVh(oAD<8jf8a3Gx4Wpgdp-IH;DBr`!BUd(dH0KkhzP@wbIE zjhs!ktXWB0hpwVedm6}}7K7y`duil9BYN@TeJaW-p-cAcK*OI5?O896O1U%0L0fxF zPY^i0wr#LbV+FUeMMg=HxwoQusu@cVcQ3{6JhvL}|LudWO%Ciy~q z`%P+7Qj1LS4q&$!lVUy{vW*JCc8d1h{F zT(tNhDJ)$@#LrAb?`b8%eM<~gue4#v#UgSli4zUBeWP64Nv3ykHW(F-fFglaXXqV; z@yscF>+M66qgiC4DsmUmS3&Gy7B?MI=WEw z{@(vMI`4R{-uI73MwHAVgbHP)WWUdSol4P=R7yijL@H@%Dnv%e&WMnal#mk6eH}E^ zM@nTTN{hCVO7nNVzrXx{-X7=N_jSEq&*wD3)9hLOIlWvIl4AtHK6RvUMFR<%m&OFi zUEsU~FOq~Pl%)Jq=3dHuW43=u0l2#fu<@RQm(+51D@!#I-M+2KaC!GRUqN! zPX~%$5%SSqRux1w?-x>p&U!&mP){(fna0X^seMjwQrM$kx9^P!1 z0k3+ngC~0TcyoudyiTb&{W&*?Hol9bIam7W^o53Cr4dT9v|ccpiDSt{TPw~x;SZw+ z`qbdF4)-=nlWTl)k4ra|#g>{|^xqN%EF5_UjmKQTg`*qk+=CBcfk7)6*)qV`pMwS7 z9h}xhb+EImMTMsYxM9q1lvch2YU|#?2{j$zJ~os7oLWdf_if@$54!M+3+s8q#GAaA z&UBs)3B*A zHkE`=eOi@bAdf{Mx6$@d9R`mP(O+k;i*}k;lb-Z<5PLM4Rm#4`ikbRA^uiF3S^5J; z42}nb`Lz0)*c!O;+J==(-UaJgm(!{*_7HDtDr%m+2J7aJ#4aHp5@@&<8^4U=%}@Qt za_Lp{?+d|)we`$za!kG)oMR)1hg5S*hHR;4VFP*u=55U;1p`elVg>0-j z0AoLWLiKO%eN-t?Y>5K$LRJK{j^SyJlWF@P4E zHW6ll1F8S|fiZg803t7A@>nj16o^K0zoo{As*@P<-!lVnQf`Hro4ZM)*)=j!`6Y>r zf6f>NTm$XQgW$V=A2iK*427lFp|JZetLiQf!Kz9SL$0G~-}Gu7kMAh;ONS23AC8$p zNw8-0R#+Q#9*%rG2CwyF;b_t;s2$k~U&5S8m&_NEH|s1eoA1drO}j~q!&iZ>0K(oh zm<-F8?*U(T6(J9}l`77D#`USK6Db`wBu&dqK+RMhqMeOl!V^t!)-r)@f*0qJ<hc zu0b1nrCFIPh0suTiZvM}#cF@IhUR-eVDZr!?nvl6;r9(84@Nx$)y7M>>gXS2kNgF< zv@J0DXTIRT{SB^1SAa;tfHlmz$VvrnV5J^fz_88JNoZmgo!N01c7OOo9^{(9en)?B zb=xR#2j#&a?l6@fyM~k&b(6l;6b=lZP5v`K33EcUAbH{iII5ckc7ON66Ypb0Wu66` z5&I9mp7sI$@lkkFejideGaxnQi;rCyok6H;HKDGd3mL_5Jlx^rztA@o} zj=>7awb1@*BRf6uAv;U?BQ%^+gLLa2x>V^E`Z&x6^QGFb=7%&`JnVyWc@nVr@osYO z&rT|>Ig&{!wjyk|l%v}xI`YI%H$!Dh1$lK;)?-+W**OM!I zb&$Nj-Vf5#zQYWyNLaUIE=m1V4dXtmlC@TUVc~jdGHGEtTqBzVPVZ`vYqzXE_W28= zC3Bf;5mOL**yB=;yb$kXg3)f7y*GcU|x`&Yq3@XV0JCoqt)xf%YN&w&QnMAC8c z3rz9b#fi23VJ6L!Mzx4CCadj4^~Z|2wD$N?n#c#hD1pmly1WA>cMI>!I5|K#}&S$yWt$YRM=0RCd7a{{uLZRSy0+$M4AnpXw3F-;hS&@jvtXD7vyRn zJ4l0-ah?VxV&`BWyGCH*-G)0(d5|*Go5}0VBwN6c#5|t^N?Ng`Z$}5o>u~1^S`jX!-*^qo`*4$dvyCRd zGWNl|t$QHaw~3Sp-TjkoHk1o`MdGF>kc^*WNy_N6ByEHhIpqJCr2F}ki~5tfpe0>o z(qn-aT~bD$XFdQWg%+aeKMeeW?rzJMl;=7x9D@H93pPNmrE?x9|Ir4)J})uZ#4g`(8&(@{;}QpI}a(#y9bu-qe^a?ZDjN&Q;z z`_Tg~&PL#-@R?e_jA44_%F$Pg66v1Ar!=en3C>$Og=RZcP^oRN$vbOfrg%>pSFB&e zG@0~NY2HcW%2mQ3QbL~ASnE=awj8wFZj7(ZdwDTiS$@r5RXqRm9Ix2BhaY9_L;IHX zu@d)B;u3R7l+RM5HN$pdS6?L#XF72C7g>xyt%oj~WpUN)y-a-S4;-c!1rodxr2bI^ z?XtsQ^2Cw0+8A;(+fLy!-CP(ydZLKG|CqVrWJ!aj`H5P4+eo$Gs5`2)2(H-L!lyog z;iv8oxye&u=H6&nH_%VQViNJ%Q5GF8EWvdiMli8z2z4{2jo#s=uGP$tw-$~6y#+3w6Tus$7#cpT5Mm=%L*kUx5GXzqf|v(z zWX3Ggv>)kRmmKn1R$%OJ@L>E;>yTub&9I<6AIfg#vI}Bc*|Cvjta(K%a8u>s(Sa;* znV!drU1%kbE?lEO&t616-VtXCedY5bQb4+m5X;@dT2(AL#=<3AvFn=%y4h#weNv*5!=Ib3+d3+TdY?*>g+#ig3 z^$XlSsIv=(yRqVXue0TKF|2cqF-+=qh9%_&AUmTMHouRg7prz*{B=L_&wmac8m-Fj7X9TwGZ~?YB;)H=HFop9d?!r2G>MXKYC8 z$8X@USomJV33n-t?QmvjA50AC0gLIM391jkh2#_PWn>X~*u9Wk7?}bixEmq0lU%K9Orlfse1yr#{S-^qZntqrlD= zl%`Xa`*EkOC)LpW$Heb*p-`ubh3VrTkeFR*tZhjW{8%*+G)Q8}zw-7YRFWzI~NHYEV~3c^MVtOJr&Q7 z4tmcoTOi_frmp7-Pu<3O{bDrS_X4Ch4v^!yi?IL37@WuZ;@n>es4&rxRPQjPOU*CP zweHcPJ2n~Qr`uifM`stw>-e!dw zFOk%am(vsRbg~zH&3HrI=#98y|9?V;N61b4rxB^2EnpY_4onIyNmE`QHD@moouG}( zy-(-B;fy_TF;ay!+YdtEvtrVd;7pUF{-YN+Nk&DAyVkcOzGUu zPkb~Le`MU^SAFBLbh#s3s5Bs-^6xQW<#MRCVKqj&PNYZX_o0tODDrJx{F>umc|B%5 zCK`UF6&pJtVSYLd?;eR{{7GCk(ieg^%EIv*kKx3mHd^$%h6bPcO;eIDk?mLe88%M ze5jNxKQl;;D61a8@th)_xTT6)3}Z0&$u!(;QO|tb5YJ03wdFkuF5{132H3gVf+^c} zAKd4!1}Otscy)gpIo8(!AFPEeN6&IlR&r2Q z!s6a(cc|N`93NOMsc_(PKCR_fggwSb51Mh# zHGN*i*b1vx-^0jQc|2pAf;Ef&U}vfv?^dmYFXs%RAl#z$^|v5zS2JsLY6H7=uR7VI z{{fWp-wJccbhyxI4q{uxt3#uHF=ku05xLs`n5UJ7#IkHAjC2Qbx?0%ZeX%6diyom_ zq9;i%f6tC=`b}mSw$q=NobhD+JnY!{o$gnW<9&_{@;;gdyo~8mOxk@*ROYu08%I6F ztHJK5l@u%*mSc|&aR;%?+nU#}Zo}r)US!)NPx>dQ2ei)5Bp+RrS^H&~s9~^Qw7$Ut z8nzw;oBIz*?{_tlU*5ym%1hJ52MehAePMohSO>&}^I+|EIUH|xi&hxjrk@+MVPDZd z()BcpEW9v>W+Y6<6(j9vW_x*cZH6DOwCf@-X;O~MN@~cXbGxfz;K6Jm}*jfdRJ48a>N*2@jo3zh9lRWt3MLxg`Iwfm784~h& zasqdx{vc0xtvg1NGnDE4=VtT>xMBXvqZlbGgHBmyOosnKPP_Rt(fUa;B#TO!NXG~6sesbp{zcQI$eq)&OBb?>56XT;gU|WS8d2hK1f2YoX!0-2I z!2@6V1l+j7XD5Vtnio6f$P+SlFa{Te_~PDD!RL{oM`UM+apw+wp>y`@fW!8CVB#~I z_{yDUp3O0#n}_Alk8wZnzp7aJe8*$z&Rqr5g{H(WWf}OTnUeFe`(f$jaq!p0l}^4N zg}?Mq&^q(w)N@)sGKE$sXE}t=_x~j9$_c_As24I7ca!sq($r$o5wbLx#Z~c%)YUc` zhsn={DvNLMH}DUNHaVixQBAs8bEQZ={R`*X&k&;|TOphM5me(_VY){mxuNSuRrWm- z9Hu96Q|)*RvfGI^SMT9L`RUacSuu=gFJ#3d{tCRYK306IINawV$XX2>Ov$k3r9``g zEY*1uws;(U_1_8l(IJZH?9t;V`0asBPwHWO=4l){v==Q0ZlkK>HVjtUBlv3PvWs5{ zZmT=%X|GB-J-SWEAzRPoqI*t|GIwh_A-@Ndw6m*@b&Uk+pf$uO^a33t6HL!<2tm)y zYRuF5zcFTTI9jLu;9Q*UGlRoF;<*XCp?q*2SaISYTHQhUxFFcOz6(A3)df!ZaT0vE z4OLQ?;5CPNcxoVopB8c&DmUpvdbAVf9bSsl=Vx$FOH8X%x}HO2fCYT7pzuxcGRfKx zU>tUx8T3)1U!&)cG9?R=I`20Lf1^fK-ty$1a|6c@>t^&E&*G@J1~fG20M}xZ!=2Z^ zNt&z9(Xiho#C!S%nlNiB(<$Jxp0sFE%hOR*#@`vma%1qA33B=IeY8nU75%3q;~dXp zxZN%W*H&JFnFi*dmyG<3^(TaE`*_sxn-1E|xvZ{&8ax%;RU7B@lRl>?(E8R-Z-~vH ztBXBgYRO^}kbj$~-nSxWHoPQ54tqs^t?p8p+g|iPb_xkGO@=A!KhqgrUvSwT2O3te zh`BlEI9cZZm-bhQIO7>J(W~qp+Ak`mua-4pY^@jA{EZ@k4WVHBbQAg>bjQz|0Ar24 zh{o2-RNitsuNK{lKQ3J*m5upOSNj_h?5g3TWf^#=X+qF5OYqkyqQ=h;pv69YTKH0? zGEL!9^;ew&=26fgk?*M)RCJdz!KM9V-muGDNrOD>Sa}2_cbp@!XKuhzZ4anFT>)=i zB-@_+d`jrJ#siTZhgznS(1tlqq|!RT^ieTcyLkgl*DVKwoaZE`E}b2g_>Ev#B0Zkb z&MU}nMT4xJOw_VPus_@i1{(@k#eJ8^9r6qIcj^)W?2k&`1b6?oKuHih4Lb^{`QdkD z)zsO9&q!n9ie%}hhBOjuT0`~;uDhRprsP%LW0d%~jPXAzc+HgZpk??QIzhXfJo9)4 zOOmT0JRuwFTf2C1{y4^j$#WetFNksCU7F=ofJqBCFsYTZ!BAWa5(+|bLzO8WnzRo> z*AGE$YzI_3yk||eJ_n^P9gsQ(ob-VZoYZ8CyWj4?xs&zLJ$@KY)|5f5>L4Pq;R~vm zIMVydaWq(eA?Qr3gE{8IVDjIDs<)qdaKe6Ba9t(LawnBmxuXKSZ9WQ4vpc~gV-r!9 z2m(aiHVFW<}4;oM5poOS{O`JK2+ML6^IDWYdYIiN>8*iE@g;%A=~ zvYl}x?Y$t(Ilce z%DO;cX~yz;Rvz4CcT?mtM(|@ZcVkqAKi!geg}RKM0hE3O@Rnpp%okjb;UDO#71=oR z?EnT3{zcD*^Qirt$Apuc(CecRDT$1t*OVXN@W&_7*enI(wqL=y$uYEhL|>I+u@-4K zTTb?!+YGh^V?eR>JtXK#lS0cmP!t$OlG(lB7xt1om?VWy6p}@y>nVn3w9?YgtYT-$^ZC(xr?lq^sn{V@h%g+kD`H}pp zflK^^@1D5-$wV+pSOIq&7DMHoLa6w$5qxdp!J^?d2iJqCa3E{b*V7_;Qey~4;>J^H#b9D#~3%>wkURc1sIu@pHNdv<` zeGsvBpVo{Hl}Dp$Wgdx{S5 zE+kI5l~$+D?wlq<@eoKx$P(fz}Op!*YadtL_z_I@C_!?H9|X;mywiT6_D?-h(_Eq$0Va|bi2YYEip1uWWKg)_dXR65xyKHtsf&|p3EP^j(CI7wiqzNC6pslpLh|Anf!V>p`>jqaS?=B!(exah8NoSb_ z*+%RbH5n}N5_TdN$FtKu9cLFuWwSE_pMZ191(YqA#v2X$40XZx*_`V<+kQ}zO%mXh zB_-|bPVFu18pZwWy2OvHN$W#aQmvFV`)C4ue-LDaYC*~&BS;C7g236ZGH7Ah=_mxGv&2z|mRvyHi6v>z;JY*DYrz7>(;Cy#^ zruAzIt56yX?`D5sr{)f0cN;Kpb(a*_f*g*liDUH=c=q<38|=LYo7uz0mTX8v3%g}W z4$EZjhs*1yL2mkNcGN3??xdBlc1jAQMp{C4i#x>ZUqXJ~7ItEl$ek(MNMZ;6k{l09 za(=rWw=C9~Yh4&eU-t<<+j&#D`&)WRmhg9J^F*>v`!i^?t3lyMcbYV!hxxQ9lDhu) zNfZ%K2q6zfvaV5LFz0V8?X}FK>APOD6Xt$qC;Q98eh)|1Dk}ymIYY=yK8U6_^Qc5m zGH7n^gjn^_!cMA$48}cz1!pASU`G*YdKd;vwS^wn_#6`4EP@f9>LCAk4w-iGJX3xm zmpcA8L~omaC$ZP#=vkMh>Y`Rx608>{WX$t%gR>&c`LSBa934ex+6GC|!{A1>H2QyN zDkl`^q;vU znm7NZiC_2NFz*N)weddc*d@c#L<15!sElFz@?o;YFu`-~y&VPD%A4i<+M|p3Gf%wufG>6Yx-f0tGg%*hUD|;c9;MOX=>(^VPSe(z3(>(x zonGOe(L2v&s2`3Md7NvaIx|{H4fBiJa%v{^P@PCuS_P05LdSV&eJ;Jedj<1wzBqj{ zV~}2Vi>7n`U8Wz!K2joWPx+gZu%%cZBR3k6k2e8M{1URV4^LCkeM6ilcL+;vJm*D` zhWwTb;=HXvAFpy`5x?m~E^l-skoO4It81Ky14L)Ni;7P7BJ((N{v>BXSaIDURaDXsGZL1hBD# z)f#@zLVb5KdgIoD9# zb=@RH<}ICFviN!G^g=8TJhL6el$KksD`hF?9nj;X)G6J96{oY&;^{xy|max{D#Mql`^yG)}?k_ z`{KEDZ|Qx82E3q?Qwm6K#VZVZ@q~+UZXxlKFTi|lA+XLnVBEU(wCYR|bGB5Pv5r>4 z`AG>_AYaYJXQ_bO#~JMGI!RW_=?lqq+|JJ3DuR$0JMyqh*e6|?i<7H{@aaP>e&T`w zUTT^b7Rj69nr6hnll7Qc>WKzbBWToL%DFj+Q&|pagwPFJou@!LPX*Cs`JUv={-xYg zqX*=!>oihux)f(r%9HCaZwkGSo1p7-Kr~RWku20vq45gZFy;LoTzL5n>cnI+g;60~ z{lRD8y-<_gE7ikFYCfbNXGgQP&lUr#oJ(ZO-{K695Lz|=6^8eW;^*m@;Go117VrIz zj*42Cy*>gLj=xH`UA#=9hm7e|@lY;}TTdho@8)iN^CB%?x>RPF4J{}iAii6lg0!9` z*;2Qd)O1V)RS6R)`BF~C9Nt4BCU3?OWt%Wb$e?y@x8jv=zu-44lIE^oOT@cbuB53W z1J1S_g~z*>ac@=?l8QC5yvpfndS~P{G}O&zOcfe2$FGMss2h%5YdSEsa0i}nFUP|} z87R}=hmO-W(3xiTv|xV~ne)<=2^x2aMjo0%t_D~!UT&RqT;oboA6^2JQsxlX18F4e zofpa3><fqaq0r#fj{iJs&AJK{#)! z*PujT&zy-9kq@~GN#~Yz@b9z^jBx8E_b2PX(6aSV^iJT~lsSV}^dR{axPWdN(qNwT zU*T0(9mdm-GBG}n4W|6P%we)DI(%U8aL5^W`qh$k^bp*JgYoS6J#Sgp zv0K=!=hmni@>0oPs-i^1K!$aJFc{!grl$P!X1xqaMy2$blaX|?b&(obbJD< z=(ZF>=?KAPxSIU_?g3$QiXiC7HwX_g5_YWTxLLX{>En>6^ohI+wHPyu>)2OEa~F$~ z(+g_B;JgQHpOXwZ?KN=pv>r4WAA_6Qx=F>%w{*v(a4!7UWV|wQK0g2PnjZ5%i?vht z@U9g@{I(ZcdHbJdQT}2)KYjclmc%clE4;qZ%Cd1_BZ|V$ho-~o%?ru%$_F4OG69YF zWYTMN9S!|lVAwSPqk?Q0-Z7m?__K&R>a&(6R5a61pXbv41~+;wLYG8_s#00gt)%PB zNKltP26qNHVXjdQSLhP3oGnjpZ~umuY8T<1N(GEo+s99xw}oF6>4J(aGx+K1I{5c= z4=zb8;_X_7=&I@BIC%9TL!27ewOcdktE-L}?|TGM*k_o`Eu*s+NYFmVtEfmdXwSL< z&_2>n-fq9mg$E1?v&#F7cz-psMt3x=+VzJHSf65E_x$9xP3$9)lPLL|J&Ji*VgrYY zAAsv?Ww{=hHlQh+lFKHIw=EdyZ+KOzwYtF z6+p&xJWsLtDdu;q1~8u9m~-?++58QeH_sG4Iwfe&iknYO(Dz z9aW|$WcV`p;r`d@FPCs~Bk3ug4;aAQ!b7-VW;-b^?}URh<}>C3k3+)wD1EBbO*32O zpyT-m)a}6%?qU9Va$(&Y8nQK-D;kI=qaK-%wvHw+NfGw95dmFTDPy z`TSC6Z(jFZHLrEa44>Uufo9sK{Mf`a#@k^rx^GTHzEG0BQaX+I&P4EHa%Nc8E%azx zAJSXxTHsec9K;O;o@(3L2CuOLqR9O`LC^6YWUz1&z@^z_n-{4Fv;4$>yKa?%*=`wTVT1YnN+JJR-4 zg}AIbPA>%o(o^u5ezN)jDr^^spLHUl21&ZBC$DnGj^DJoD}jFAXoeG=BWcEwFfPTc zj>KGDL4rozqvPEyK#_F;^_lC)o7&@0tCR=_H&vtVGd&y_=;2KtyWsGW6WrH@sia8A zu^Gw^5nekECs?eZ%Vrg!&l)Rwq_rAf1sBtFWnU;06=C!-b8HFMV@&KGke4J6k7wSZ z8hg693a!th=zpEVA+YcR zwyv6wd4GyAe8*MrTI@s)nslMQOdH)Ga4g?(i+Ryc9nqmF-_hq!GNvkCqI{kO%4FCx zPo8;`A{B+|uJ%1Ntwu>Wi=M=#{Y!DHV>2$yR;MHP{bp|M+D(F<3EZItpNXfZ6RZf; zBRjX8B41ydK)#eNRbTQ8mnMt=n|*)q+T}Y`{`68XDRv>B-#Xy75wD=yr4CFkbm0|g z4^-C;Be!)sdHD&Zc+4dblUx!x`?wI^>E2>2uMx+TH)B8{`X}AjGzaBZJx8@k4mehJ zUiJMC+o}!y4d~-HWwfwWm%AJJm|pMhB4#@>NXUgeMlQ3Dma0jTz=b18!CtfKkjayn zYRgM7IisH32wO|PoqtT?w4b4+fe|~haU*2E>84FDtzg>l7ZB3-8pcY!#(!hY&^NUN zue~*+;l*ERj@BPC+r$^Ohf1)b=O$h8Y9+1P=TBcu>qaoQ#)U4%#P7pEmA(hd>B~ja zM3X=+e0n+UtgWEO{h!kJ^Cd)_$2#Wn=6LS)?hx8+kVQY-@nBj`JmOY;dQ4^4|D>R9$oaM`Uv4dy(k8gbB&{&4#% zxB{feWJy~t;_Ypkxj}|hmL$^-jdY@;o<)0YlF@RHF=Kq>JFQ)ALu!A9lj7@-ah%Nr zSbA{+nSbAc9Tq)`bf&L`tb$HRU9N_c4=zQm)?VzNvX!3}dkKH;G{pWr0XV8nmUXOu z%E~Ltk`HRpQ1>AOD*lu)g&zCx^u{9cPKg{58zFVMbJRU2x$P zui)$(BM&fGjUhTi=bNlR+7Y57ER`az}^nZ#T2$S2*_{$d>txi){40&b zG@yl6r{AET!c|c01y1DNi!Zb*xEuDU9-t0#XTCZeYZfu18;~U}e zwN9)MxISuaXV_7`>X7$Sj5j{1#qU)9MK@ddLF9`}a-+)%%%2YD`VXa5XH_iW3^_fL z6a1EjFRCLJfxcYZh&YlfzKZ#qs!b0~5+_R%T(}jUzv(U4!(66PAiWtRFo}AH(-;5l zkr8z}xx%j@B>j3KNadWMiLKdSH!2+N{$}7>^BE}6n+LJeJfN_s82l5$SaA&x+WJKS zJ^t!dj|(a!yoNoKP{4p+-&qL!n-Aw-9)|NDs^FLLT2|7t2eMPffp^Yyk=Uwo>KJjB z+~0MP&iUR+7JDtHWm0eGEfq5|^UzxQ{i-Ydwt5>aemjmiqMu6#zR#uwq5p`xtB9IL z9w8t5>`54Zg`CoL19z9{pum&^ztfjaRgr@b@2{*FV-CNQ=fZ{8KfudH8UCzGg}dRS zsOtPqj9oPgK5E)P+to%$(H#f=BX*OV59h$QX)nntuY<+k2Z@p6J>s}qgZR9hgbGWM zdzrb4UhLmXPh~9PD(yoVuYo=E!P5;m`SDNMMcwE(jS`%bFGI2=_f?sVvjmHnB64zs z4g|PuW?rqBPb0?OgKa0YtFooU*-^DOpm9ndR0?^se(PR#ic=fAk(&!kf(V>^r21=Y1x3*~I{2qHnQkhU#=j z+D&jvHNT2m!MVS@EuV;kQoY8rOx3MvXFqgOw_9 zW1yRSlpg_ZJI9i=&~iFugEx(sd5oqFEQf`yt4VEB7fCu(BJy^AON+&WDM_Elm8eP5 z5uwrox3GviXKz6-Om|>(ChaAoA6Sz`+jX2$VB4plu38FdqaOS)@IX0n&mHvC5UFiFU-BQ#Ae@DiW-nbu7u=yuRmYPim zOOr@_dke5@yHUefkGd@Cp_pjHrIrr~&$*j~2JL3*Z!KaJOn#D9sU#*_vx4q88Af~4 zWoXQ0twEKOCDf{0}fVA>POk&HChu5g^4D3=U>Tu0%gK5?>rUn;At&c7)gC?sHGbUFjTHWc`OIFR{g0 z>wLIa%_W@b%N=y>S847)n{cvL@X;=K`K7wQx}EfHoJZEb?Pnyuq*fbcMiHa^ubJNY z*J)a(DsdOyp&Db?u!@&AKw@DoJe%nZ2X3aq+0{C%SynY`H_nIEe`ySH#?v9j^a|XP z_n<>j+wpa@55aH6tn9REJR)YzYdzV?>zS^{DOV1m^He=D+g#{^zJ5rqXt?2_KwRBCT6&J06IW zKgHjP-QE&N&(i_}Uw245=>$DG&g}f|MwSs=x7ArbOn7pk%voI9{^eeRCf|6J!NFtoRzCRp)9jV|o4#whu zeLOg63FlbhEEH5DhZa|^;bL|6P%zRRuR^cE))~)Xsz>$>3`vSL~dTw_+e*ydBf8?`QRcEzhzbo zuOvB^w;T2g!&{yR{>u`k5|!%T>DgU^gpXg2gN4hsX}#h8P`O2++9a4zFg-X995_FeoyGJ?}_y0 z#z)oXt=;gf#4^m>l>z}}!Jx3{0J%Dsub%Ly6YuS?K&Iy&9=KV9uiN@j<$)JJ^7jj- zafuD_emjE%8+@cPNu$s!O&_Ngc%a{l1vpivn11C0$SAS&>hD!NH{I+jSL3;v82(nJ zPltYx1o=ZWsLh!^wcLfNj>=de^w^J=zNI}Rn3x<`i81Oc&^vxLKVsfS>aqGhzS$Ck z|J}2~R=ee(CFO#_?|x#&I|tlCj`5R1rqk4e#_;mTY8Wm0k7TZW!PV)=2rO_7GNXJg zsTQ(4Mt6SWhIUolP;?4qy1&sS-^Qbiz%F|2zLAR6mT<*WzHp0X=hNEV(KdlAHJFok zdg$O|51jg97{;hL;-Z7g@bpqQjOmM_Dc{t|9ZkCgg6MoarC3mRb%jc*e`iNc) zh++#3F!=c=-00*-!#?cbo-686vzN|Hj^Qz!WPAd*uF(}30rPogzZ1W1S~E>Ei38JL z1eW|<1o_VESgV^gtl>miwt7t(8+2$t*w+Ds7B9k*gOBiN~|kUg5d2 z0+haYRq&cjCSsyLsJr3`4g1(cd;VAwqtqHY;i*53nlnLEX7P?(ysAcD6bn9Dq3?S% z<`ah0ThW-sov3u749DMF$A$LfVA^~O^muKIuUw<~MGr!FwN?Z4>uAS9m1nj?Bjs6z zMI0Nke23u5oWaJge8FlR+)vu~X)`y%bx%hbw&XzVNKZyAM{{T ztQ_m(6b5Clp0O&YoWN<7AH7q>(-j4FuwiaDszkWrJZPi;%4Kl>%63#R)@DvOTaZJi zHc@3vqkenKNJZ}Dsix5#s}<#aY|9A+gFy-a~MrJdyZBxzzfw2phfdnu-=Z^GTH zw0PC33Vy+@3)uR?ig)N9ME4V(II_+h#jQWl2F-9BHLe>@ZwZ8unj1JQP9BD2Dj|7g zDqK-I1-I}m1d3W|cB%qB_Tvw@e%nvis%uq08S@zxWfs!T;&Q4xb~f{*avU97nMXH# z3#XU83&^Zk5%;va1$AGJ=B7Tmh|72 z#CO42rv(9ir1!`V4WpIGIe;pp^t1g4$;huYgCx&KzK!vzo0Nzw3CnGf>^31WZ%(q5tFPJp5|>-#^|?LqpSwWK@cbwC?wHqEv(^kup*#vy70e zl$N4}(x5>!6pF^Vuj^z*W=aF0kgbf5Exz~f_ZOVUIgfMh^L}5~>-Bu%{#}je-?*7% zuRlUL(+}9FBh6~HC0t(GW6n8Bo_}3(hO{!5&?bSUR)6;m4G2-A%#XVGcF#H-c4aw* zh5#KiIZsQb&VyAob|TfUAwr*b6Wo8c2-2tig0>q&Vd3<2GOV%{I-fU%?2jpK>zsjU zWsCV~;RC6#Za*^J23gk3_9K-_}c~?A*Eewn1b$LQVg;ad_zJGE@KwD1`bB3 zf5;ZUkHUh`ioh3@P zUBtKUh9L^~`0nLtv~F&rM1Frf-rsx^zk6|fRdyfrvyc)e-*zXtQG#cxwHCW;{UkCU zhtZb#-vyt#CB>ZXB#*6%v}4*P8Uh;3rsW?Yr$KVtdHv^ ztVN5~Ec{xx1)IHeaZ1@za+Fplza7)4aM=%3_?JT&w`-|ym@#$ibeG7U8A`7&Jtnmm z!t40QOD3D|q}os6bnuNd)n4ePhZ14$C~<=VHiarRKSO8NQ&y34nC=H_)4haylzlds zV)cFbmU&JT!=J{(kIndHi-mLY@6(uE;biyx>jv>#MdJDmk7x3(KHTwIJ-jh;Dt_Ak z5${464YnOfQuCiu$%+lMaHj{&?bD;>r=AE-_H)#$KS#opcTn4xe96?j2#L(8D0-ir zNf+(6P)x`|x@%i1QMxHh2WIvskI479+;$Qkg$q>EeO%Jda};H&T@YN;og{g3f@wVa z%9iy0ppcNA*f~p`nPgqWZy(-co51S}3?Umhj@?CBM1weI3!SxgoS4E=7%V_jF zG2KcyN;cDnV|Jw;H72Uk%YogJp)SF~{^BqHEmjS#mwEFJ{mm)7{wvAE*Hdh^Ic=Pk zKxcP)V!oUvZSdY8kyd|CtA>bZpQdo9F|eTYshQ-dVadffU%=FX-FP~&ismIwW&I+a zqJxSyFInouKTliFuh}@99ngtmuQvr?cve0>0&UvqVnFc;@-$T^j-nnPrsPr0bh+A* zt{%3Lz{ANB)!9Duv%-%eGLEB*?FW&=pf{|eQW;C`u0*e4F_b)^KlMG^MOsD+@rRc! z+d6J8FZ(+mPy5fM0k4WE_=nKvZU~_|*@a{#?9Vl)7LwzFk9c{8Jgufgn(;Fa|J)nN z#bwu$bjuHRY=0(>TNIBjCMno<@GkZLev>9@9;1Osc@U-y15R6)6PFf}3 zaQen-mOQD5#{Uxu&p8pPFH7YQ&mM|@k`pm^ZVkD}EP&#dLLq;JW|#fDE?x z;>0JbAg4HqMo6wOx+KlV*$8fu@?OD_@{GP;`AM0>$I#aOQ&B&6E35tQ9@C$*45G}k zVAie^j2kMP*}e`XEyZ7?Q|d*w4nxS-MaX4m=F`z{FEFHI5++<+O}4@9*sHc0&+Iyj zAJ(?>ldlUIpPxr?WVO&oTJ#O?Ds-Wjf(J|UG64IFeteDPML0dbn13Gm1k!f?6v^Aj z;vv-xnAhO|=%UM(7j{B?;WKhR(m}qupT)1>GhDmN31{~rki@+d>1H;;b+yyr59cuR z$!ykixs^S?dWj{u&%vJq%t`qn8MPtmO;l|J(0ai05m0hhLdwEAyi!vJWVXIvrzHv!Z0ndA?E9P`uJgtOi13>L z`WD;R!jQovrT+x`GM@A9l@IZ_=@|a%q+A|tTWO@M9O|0&k+hX7->R05b>}^4$lMer zIg-dGZobAIOlPpyupZ)KR>M1Q8POEo2KdoW7w*4!0t$|S6xY^>SDwtmj~6HLOQ-qM zeA5Fgeo75!q`!lnXlbS=-3gNTrEs+^5{ebZ!MTcsu&?MIcR8mIyw>!=##~+K&x=X^ zi#EDgeZw~;%TG)STfvDfv9dK>oWOz|xL01<`k)uj0+O>WYI9EJ=emk<}uT2K~7U+QY zkq7HhN}x1D$Wrt+!O=1Luwcdr=&?Qwl@lA`(Tzwb%6at1AHo`ov4Vdx-pf@NOT1<|x2uzl7d z__gOTyJhD9rw406Mu!(oPguz$mTnlUR!yDrm(jNk&b$z7>% z*hp}d|2_y78`NNr`XTmuUJ!)-Qi6z0%UG+M6AiAOMlx?onS-hfU(mb}HPdtXBkPR# zMVEWHyvEzu(;;vV6g0T!+MDsOqaQlOZs+rT9+F|v6WqV}1n2sAI)t0Ahl;nt?9B8f zn7>w}KSjLP8umV`~E~Ji8TNcj+RV+C+;E)KGFyyWl*Fpa(|1bYtarN_{qgf*-iiu78(F<5fAO z*6HD7r*v9--wLx54uXvOO!iW}2cG35KzHOGsE*BmhGVZ_^@Dtf4!H>O2d1-fmnL3y zh#niK?170p>sYwI9~)-S#(QRpFxub?K71X?*AHmNSNk(aWBo#wKx6UX@clS)k&$@h zDS3>3pMmMW!^r*IG@7)pp5ilHDPb{(Wd{rKsM8g);s6^>+^IZPSzxaJAU)H;RM=C> z%04Y-=j$}so5Oi5Nudccm7*Za!%n2+-^pD4`oTg2T~LaTWBs=;XEl?H*^|Noj1@R8 zr}zIygZ7(XOtd?eUu)uS3!cy#wjNIinX(6lzmrz@BH>kaymmn-P4K#kC}|pj~SGhKf$=_bv*<3_7%7|M8r6c9ZYh3yj`aMK*p>5gtGMq;aayBeCgM~{J!{LQqosiytNV(GWOdQ zg+F5D-pzRQ?Q)v@?KJ(DZOC7Jn@#RkBLv2EJDe;?hMaq5&^z-jT#IcLX{s!N2NtTZ zXhAdk^q>W#%BRAvQv&yD!9)J}1}VN_L=M^XW#hju;}LfYE|F0+v`b4Ki-jz)*TW!z zTazes6%|=Y@(W1&<^x3=w~6LnUm`N>-vfI^!$7Z}H`$n2lI_eI%=qoblJj;#vd{^1 z-jD?A>t;fHmptyx`$GaA0`ylnL9pbuNafQEXp5L4WZD;roVAl+z>v$}VSRuFRoUag zoB&W^!t->W4d(m3BALNEY3lN2s^G`>g}n_`PRf+0NbVmS$lN zEs&|`%PLL&h$gRp1#y}QwgWe3gIn4T!S8*XLXr+J80u;fGNPB?L6LU@>x*8@vYKYrTnM)YFQGfy7CenTZ<)`AE`3I;iuG_?Gq|=OV-e? z$C0r5T|7juV(>d{6)HpUg62|Y|2~F%*}sW4!#A>l(O<1#rL!3b8`;iSNjJ1@AET>>02#4J3||y zXMF^w8^6zXpyX4>z*=fFbp8D%np_?UDY;T$ag?*$EBuC~n`aA|JSm(U_moDx zHKyGs8t}ltt+X!^$@0-B8ndYe4NG>A{kUv&dQpheUs&Uw-K_#^sRunIX8fdATI^zh z9Vw1qf)*XRuwLm5JLnloll^b-w%QhS{y!d@M=pVn6KtUWeRaIyqE61PS=1MC5ew2q z6mAwKkxseHM%ycjM!N-z6t~of#!a3EDER@ZLxw{8>~ZKNr9+7di|FwUnnubf zWBlyLv|R2NEj?7mV@k(<8J7s18vJpAVGyVb%Rwy7|dI#<8{6yuZ<~Us#4A}g9MJFy~!IU_6 z;B~4+WAaAApD8P7Owtcf8GnLS-td>mwkXhJqfI!qbO^R(pCpYIdm&?(PWuavV8vJk zp_k%K7fzlaJ?D6s_~I@*^Y9yGOxLHxjx;*qq(H@wHKt*Ba$obbcv~R592G?PaUT7ed4X=v%aY8sPo-kFXA<`gEy=Ld zIBE%5hh{cE@&0Oc{^&jt_hFg~W?9uk(wy-W>wimp#=Adl(CEYFly&S*%}gOE; z2${A`k0?#Jg+zNS#Tz>paYsxR@Vh?OVnnbbj*BN2G%1C@sicgOhfC;fX*=~DF`@7@ zHClZ}MPfI7ITc;ZBmE5#n7ge~(tne+#B%>QYRL_j^z$12`uK*^NeW{ z^*Qad;nQatJ1UI+nKei>i^PSb zXM1@E3-Q^<_ND#d|9qc~X~jNR@kEawm49C%6LypQHoU{qx-5#ZGbLsFUvvV~Xz;x_ zIB!?f<1o?V34aI~@Ik6+)+1W`Kk$@~r$=lwv<%bGu(R>JQ)vjKm+FlDC) zneY*se@Is7=ACJ|K+Nth*I!~x^ESHE866$AL!ya~WqaAEszlhD7zNii4575$-$CCv z4`#+EK!~LnYBuKK-SA2p(bB~=o;?85({7TH&p#^2o6cLl@Wy5<1KOK5i`hLDfwJ)~ z;w8!06Fe5gb8o|lZV~%2_%o{tI17W`y#_0pt^A9FU0g}RHdgHRiJc$&1B{jTV{L=L z(7H7rPfSQ6?Z&hCH|`b}ryfu8e|16SP6a$=*>|5_q@c6VtoIi7?rwwH6~-*8 z)RguOKF;_5+>B1U^Gn-snGCsnQP;tp>cFK{+DLi!m;*wYk@tNt9MC5q$NxujW; zmFfa%=f^{Wb^+}5asn&;QV6v7!~^1SOe=mV%l_kTJ7A?PnDmPk_zO9Z)UcS_yq%|! zMTMX=)f1=Poezhkr6C|np83d$;eOw2IB@Aa-qW1U#0?e@seYE}sJ~KYC-=0^{Tctl1pIZxtb3Q_TU>wAKx(z1}szAUh72Zib3*63GL4q$r zmv0}O-Lnfydi0_CzzNtX+X8x4r{F=sCt7D8hF8nROEhecN>mP?rX^g|FQ(;O)1=qO@Y7B&mgur3^p&+h0Ru1ME$l4OyUg|&`bi$w`?{H*mRzy zs&dTfW*Q3O5P;K!U+06szBYyT1)5R13MeYsGm3-Dy)?}mc!{JoI;Du{KQoX+|OTY?eIVu#gt z;b_*%*1T7PbXg1bV)8eL)cOPZGY|cLKKrMRHr!Bh5E(lNJK~)kBAts)A|sk8lKb=+ zc9|!z`j7IsTuz-&+=KKZ?1{v3(m=`h@zRo^DKR9say4~Hl~MA(er%FdFkZeANO|qX ze9?L}nqo8;Ru8%a!4p@pXJN7e3+NK>@L{vCFRy^9OFQ^|U31Yyx{y~Ze#J5m4dUdz zk*!H2zT&|V$Xizjrw%l;Smk7}tCNBgXA4fyR+vr_yf&^OHBpXlvl#CYkCLONVuxThyTh#v0(15`L<9;q_48BQ+X3s{K*BL}x zDnMH3ubDl0A>5sZ+C>iz#o=G_Sbj?>$X?dvUjNeKn*429tue#Ac~{xkz`yLUbqb7n zmjiB*F>uWw6l6UBG^gjo{WrcM>&`-vaUX~RmpB4-AX^?h9%gxc7Uv3cm4!W>c(K!z zGPT3#&4is4UM1vQHd)Y$KdOg*f=n#_g8GskRbYQ>8AdK8|lNN_p;hRZwVX{Xj z>6e#N*oh#1NrZ^qQ4u_Kjx~5XwUObwZho$>HfC;hVzXQA`0Y9+>~T~T%sL;#zKSg& zGjI&l!CFY2upjoX?-0qp>kti*i4^JXy3Jy3Ur_QqONrI%74)skjWS;hrGF)pxYg&k z!9J@f?(#8B@xY?5{Irjubmny|{nv0CziyYKO5!nBhkoTnjMWVKYfz zx`6$9PIFVt9x#m>C1xC$&pLe;i;F7ESaSc9jF!5>?AKC|*yINtP0k|ID}f*$x*ilG z*2Da1;jpIm3>@*bg{dRgV@3A{3hr&AbsoVKn6E-_ZYkmYh)dvU@tgS{+=^+Lr?BPb zA?!SzjA7+%c%(p!d>ssEpG2PGWG`dqoLm}p!j^n43SHxg)y!L9+7#4ZBHQ?he0`6Y z{ce2EvRr44(@ri7Viub~L=N(}UZaj{|>fA0$}+WLm$%SoV!I zVC>Py7ThghNJY7%aVcRKBX_@$u##Wd=ZyWwvafeShwq4vlaU$&U2!x@V z&+`Xgo70FvdvSk}GhW%zgz?{U=|InU+V@sqkdI%?RNcGLrzspmpXx9lRwaHhw1!Rm zSOQXU8O$LW(Yad$2K^GiC~_KP9li#?w+x5pL7DLGegw2;hA~d~K6@vP1UE~9*Mr-{0F#3lk?K5$!<%0uA|KS1LuQ3sihW(|G_bqht zxienldq`(j2xfYGAxK()gESqPNBnNi^i@5-Y|N(Gk6(J&$U%SkTc->}@=x_d8tYX= z+VdaO#wVsr3}?<2avjgnHvJT={f~)M&uN4G4Kr50{~UWWVk7@h-AG(8HG<u`Nwl;bKBsKSz*>g+E_deucZ< z_8+hRYd4L5b&;;sB(ajfW2F4viutXKV~c9EXn<5IrTMmy=Bh@%gLcB`z(TksH-j}d zkAbwAPImuAx3V|`HFzy=1FtqGviJSQlbo>INeKQ)0pkYq#?$K1_<%f@axk1pAJt+f zYc{h^!+d7&?IO2f(;AlEa+P~cQNuHG zeOgLT6HN1xQ|%@QPJ~KfZgzH@3}1S^i(Ro%gP6{<5TCplUWRkfIzC7gFepmY&nT2B zb?v4IWhp2-zW~}S!eP%<;B$swA)~dIXn39h^*j3q*Szn>qpL^qc?X`c?Xwkx4&5Bg z`m+R!#p!nI{|sRRF1FK@4H34-@&uONpkVH&; za+&)+=`PV@pAsyiRB*S|bOEk0PaAC}TtPxj>8(ha@g6}e>VR?#gOKZpI-MJWS+JR@E z@O<_6O3Vw)C&R48v~+tbne{tLP2&S;`Zj-B=+a82d0+9;W;qm7 zIC9TMFg^c+l}y?$k}AEzHeN46i@#=Yntc$B{=HD79x3cqA3o%wYy^hp(bW_+>=XWc zt4tfpw@AjBoTRQcH_D!%Oy0H!@kHu!QokIGCF7p4PN%%uVN$NV_2r{1;9VSUy`4;1 z-2#6qxrMK3%o3k(Ri~tH@5pM6E(_N0Pph<>Y2M~@_+h0VSoHeh1LtM`9H03Bcud8V(*Hske+KkJzdf(8R))N zB5k%*VmW;>WyQY6SoLh0I4FiT*bDwl`8QlsvNvh1=lC-#&*I(tzPu#WfIQ+#Xxx&^ zc=qFUkZo1LOVuaYx;ODK=Z_~He&vKu=cq%faTX+Mh(W&K1?-yI0ckJ$_}JynBH8r- zBRvSVkJ-%H1GX~p7A5Go5iJ_-rU0KD+S!{=7P!s1)=oy#o2unn=(>36?5#z-Z4o zEa>)JnDf!Wc5vE$RFup|2h|bc&-GKFUt8-cH}wj4tryK-=$iTF``W{V(j?O&jF%5!i`raZ1!J}qwblVQh29WQlMI^P&iErF? z5IQ=>U~6lyL}}JD)@t1kQkBlKAwH?J_3L2TyW|UnC#%tf_y3XOm#_FyEs)j=(R2TS zQluW3${OWHb5>FF?QW|N;Hws#gZU8~p!*)e?^&S``RzRneQ^=pWT1l*jd=2 zJw+s?=PFXvw1)Dtx}r%gC!yYZ9(1VMGeK$v*X)JQ@62hX*H1Ct$6VV(RjZ7Nr~N} zG4oNh;v%lDDyS9dZRKYwcEZ1M3+Spr*r~G-v;=OPOYAxLbLludFV7S>LmDvj)=ZJo z7Cr9jy@eu~&jqZ=&st>6eTU>DdqsM(EzGsR4(6BqWl5TP5M{bv)bGk*2wJ-vBKK_q zYyJk<&EE~1ytUyhHy0IE0MrvFG|)yP;{`@GM{h)MoC6`-fCTa;j@Z3YYaoCWaN<*H3HUit#--WGDy5_dKEV z)>XSV3-;qz-6Oapy#NJHKMpOt!7z-+G52lQW>+GlYBqr|L3d3$z!*+qur?;s9EB{UuKh|7MYQnzkzR_Z;zivz~Od}-2 zI>TtHbvqndya0~W$C*;Vy89*{ z^zU}V@9|AA>`%2w@zZsf-QfpOhY}&T5FzX25m4`X!){#&fQM}{Fm87k8)EGZwuz=- z(Ygv!w*LjEJC9iNM`_mK^i}L1Jc3Kn{>FyJYyflfWHjG0gFP{7W1*LRz^Uhz(5CVf zw#&&tNl+BF9eFKL2%SNVwRM#3d08SAv_xW{DkU-Wl#@8RkVNJDF+BGd;LJ`x*y3LX zC#&wj=73Bt@EJhy6nALI%!P-xOCY{_3~SN51cr(~;JErhkTvpy3+tA_>e**OMScyd ze6pM`Z=cH_yHvwMHS5_w^UdtC#txRNipW_^0+VYwtoKt9n5m?}o{OQNSN#{(xmBQG z38XWRjPWPG9^XuOOtA}R)9uw-l>I41GPZ3lz4Y3}#aUZ0oSXwCS@y7Y#C+SHIh#;T z%LPKr?Lo_J9+(~81O3O&X5R)bW?eUXS!KU3aL_&-5_0y!`tI2=e=NPs3pVo=ut2SnWlVj~>ZO+A`+P&b z-Kfvk}8qeq>8rwT1bL8_RaM#zl?ViiyMJ!6RB0 z#e0u3se+%Rx!M;@>xXccg0sPTq8u3Hl!7_Af^nWYc-@obpJ*-QcBu?P_udI?>YOrR;07y`%1A{a(TGX9E zC-2$N=C#Wuc0V&D#)C(b=O0ZPTB456gp8&6fMF0BdxH}AK{O}TmXEG|N6XyvNcQ*# z@c1tY&OGb@Hb)Qj#L+)umnB*e#J;raOl<%+Tw5N(> z_L~ri(ai>mKf0Z~Dh5NW)J%}wrD50aUoLto=MYyjnznZ|l8yg&bQtIXPCM3uY=u5w zbE+Jq&JBVUY4<>V4ujzX#;{|r#$ka}ARY>Oh3kKY+1;74lu50TFo@~XEcTTW9&fP@$foJezgusVR>H&M#cG5G<5&9v` zEV0@VbO$N$???V%Z}tylt0va+iZcv2mHw*)PE9i&nQ4fX*XPr??dhcN!a!4kpAQUxULtea5BFGWt}%*`2}rO&yKRmnT7!nZ?Z6VI6zrVTIE*LYVcXSl0FOK6kbBIQ~=9qYBGabmnp(m9OOJRp&9u=$&bbC%A^5Jh0x-pZOL#vMUpu zSgB1Hi?&k7MFPk4^K}cR61A3{xGD6E^a3eV&4UzL)M>)7G)f&OrW^Gp_`<(R;Lj4%3pT&ELwy;{j`XisM1dF$;W-h{K;d)F`nCwCUFn}&vuW@7yP6>NEf zHTzrqo@;7KXHPdI^9e6CSid=;*z+}$&DE(!d5;@-Vqh4~?fOT@0*2Dxhl?fSYKBPk z4c+O)#)%Yp#gLYr{m4B#J`%#G*pjT4Klrw2VEu_StXlnv6i<4P5HQBsCJdxqf|$jD z;e3j^JxcCejjBm!H7Kc`LEhwab`WPpLS!V>!g_fkX0~GpPMZ)^8i=e&cJ&ApeVWQaCU9 z?mU4vUJvE3drYCT)1Q!H?qkxqxdHdC-^h(hktZu*&XfKjiezU6gKo*Dg??64jE-xl6r;YHRF=p^_vk~q)9DO}qj#v=bt zVG^ZSEVlnHbpQQfoBBl1QqzSYLXObj))VgJgp*V!rB6ORo9O=Z6XdeO3~fub=~vh_ zD%tgp%DT+ym4SptO+l9N_B9S%avv+p)##kO9d=|5VD)d~(6h7{RKLx`U{N2$1dL;+ zmWp7$zbaQ#$*y-a zE=41V9rPWF1h$c4^%2l*|AE#jkLcFwOEhhn1GQCLr_wd+$YEqCRoj_RWrZa@3jata z<|^Y`vz3&*TA3sjTgd2U2DXl^0-HmZ#Ug(Tnqd?H)$+#I53%Wd(MI z;q7eqwTU$0{4|!Jc#jRM-OH=pJHVFbnQ~<>68R(}XOvnx#Ks|S24+UKqxH2jQ1#CZ zqHn$A8m`B{q8#CQcwsv|5Bo+>!dxY~G1*iuz9f;lXikUPPUGgrIsC|Of0Ehk3n~XI zsi@aQB9-k*`JKJA^Y}yj#~lYht3q0-a+VHcofbP9HPA@kzqAd!DYoexc1}K1yE3+p zhF1OMU9}HjM~fGpuj^yCcOS-Vtgt&%%dw(cs%(thC#>HsV7b#y;jUQ~P=2)sytfGM z*;=0F1-8@aJG*JzxYgv|zJ#uCct8i#*VEC}{i#}^iLU)Oi;v2=L(dlmQo*@bRI>UG zHQq0ym5D+h%B>rJFXt&zv4W1s>eKSlb)>kVkwm6E4gVrkcOKlZOK(r7z))>G8ZrRy z3FqWtRV{2vK@^L7(1`=xy;<%CFHBu!Cgj^|$o`ivj5)Q1rAWuKpEJj>5xW-Bdgsl8 z&t?<9Y3@{55%Q3=zjy$rtjf6?+#-50TAk)i4WaM}7R+knS-!b93f%Oc(r&A96jRfU ze$S_a&CPHsj?AIc$NaE*=|+61$x*5j$F;Ny4!%yXYm2O;gNLfo?Wi1H>+eB^Cmk^* zd=55jJkA_#ui)OINu=r+g0~9m_}(K9ko{}|3)x`6eth;~E`bwKEIo(&YAWVMF=0^Z z&f);C9_yL8TcHMSMm~+=bR%o6}O8yPJ_j_cfITOL@R^5#o#g)|1}VMdGT}Yp^=O8c(Yy z^V>5Xk=i4FrnZHlwtF&YcDG`}k#pR$qchmzsug@p{zcoOZFN*+;7bd)TqGU;vk>q( z6-L?af`dPVv%T^YxZ}PI?poR5_n{7S_{b(sU%i&5lx~v@T3f>V_!@)B>s4IvsBugs z^$+fxwUo?U4Nz~f6gNpblw@WXVa(=3=xIHQ+5h$;WyKBryXL(-zgj|COWN59lMm=| zMhlag_Fz(JDY);r12Wx05JudCA=s`O;j-1afEXk$p=u22V?Inxqc9ZcLxM(eFf@##Fg^2v_;gYJs28W`YX{r)WDy&HSf zY==uc$1{JS_ot~Vux-A*XR;cp;_!NayUnfqZ}A7_+5BDP+ie6-9*)OfncpSG-JTLJ zjb@3ueHOiR986q@A0{7v$w$Y25NE{*?jM1ZczxVdWYuwORKBbDN82}cbHF?_yRw!k zdFAnMZFb>NV?aurCwS%ku**9I>;L+&-3{-!g8#C)kCzs*6h%j-_c4tH2Cri`-No#? z{dV#GkmEGJ*M<`I?xR{GFN!o(eV}9H1G0-+LGP0h+iCG0BJ8W6lIq}j#B10x%#&?7W{YQp znVa5iMM^9AgJo_f@P+<%X1)I_gbb+1MBg%SZ$Ad^p`$@Jej6m)xU$PXo^zL6zcKUL zV$4kLM_cA8(_vXtyb>bD43}+UgUf~(cl$j?FSW&MlfIy1%@yLDpK{_+!;#DWg!=L= zta8#*7`l8Zw_i?%>1rLuKFtBF+C>|bgO#E0On)dxML1}^56a?G?I!t|aGOr7=jP-r zqvUk7FzmoJ|!r%(YR(kim&@7kt+KkQQsg(x9&bC zaq4wCa;!pdau22nk1yedGzD5ZVLruh%Za^s$r_LCr_Fxt%%Ug@96#$YJU511PHZQ$ z{*RdVvN4djV*wnDwS}r<@*wt?f`;!YuC4U?&+SWFiqoG3iKfRqmY zqn*<)k+jBC(g;c?KgvaAXMv5Pw~%J7@k4{=9?01q%x<$>ggZmY-`ECS+~z{htTZqe zc3qQ}UWM&5^&m%ZlBHkW0gev_){kK#ALs8vE}+dW*Y7#C9iJvK zOL3P(G+Igqe@vvT>31k%&UDH*b)cKCt7+NJft0;q2c0@-NBW^=G~KJ27KsLsL5LDX zOkIxOy$53SohTZ+UhqSQ4<)DAWsv*+BJ-Y(U>td#20i;tW8dC^xP7l!sj3Klujql< z>pI97zXHaudI&9V5@FQa;q2I^S1jR3KZx$g6-}Iz0sFoO+FrXGPrVj#)HAwRqVY6e zGO%op#CY2*y774%otv|bD#j+_%J4x{DmX3OniXj3fU)>jwGk&DSV!}BIN~X-{rGF~ z6aL`vc>Gg*or{xR0)v(8!Qt5=7FKzjdu8cD4T5jHSa~!=>q^tm=W)#C=5g4)+70&A z9);rm`6itg2{7}7QDicmqf2=3By8 zZpUL&%mx0b28>A<#yuConaN#T-4(r3so(^2;wi#@F z-xmBinujZ~ib98~obCH%iWSNNTle$=di`Xf#I4Rv(m&Rj#YC167IGdf~^`GX;8Hs_ue*~ z6%3Vt`B!=7owXG`n@-qOC(MRSwP?7d{gd5ln$KJW&-_wt54`$U1bel=io539V|AaP z&Az>!T`3MA*<58>7!dR%ayXFeB^TARo}>^*ay!+X&{KlUP=U&22)VAiJ`e$gJ590v(Ry$$u&PX!6m8R;&O6~xUSbx z;=b&AIQ7G07-cw!^R6GrO`M*Ny2pwLRvg1x)mnTW{u7UWPQifn;S^CJFxt16V`8Nq z1P)ya)EWX`fBuA7H@-n=yCTf{x)M?obx7u6bB(%T0aeI;kcgV>B%XbylF<)r1rO5; zn!Wla8~Wa!{Gw;FhsJB+ReKekn!JP^cq)f$zCB{P8Y}qNP>ZY)!-nA%DMUvl)lexGJOkra72v?gAGXb%XPe= zwH>6EyMYWp6aMdi`of-DoW;fgtZK$4Qq3=5-=CkrQALMfv}F>PKD2{3K0S|>MJ(mz zo@SCtgbbQ<6G-M*Futp*Vp9}uaK#pJu%mM>dmg6(`k(7)_{dQ3oV|!8jlK!OFO7N6 z-3?dQ{9_B!Kd?s8D%$LwPhA!25}9jS615&izh3{Ked0RsNeDput9O`S2?uxeSoZw$ zQckB+k(}>7X5&}i;9G@GVTHgQe;wN*%uS|&Qf(gUl{7GAE*-TUg6(citzo8HOL3Bo zB_3o6?D1%4e)Ii&u7CAj-q>Xr3^O-pQQcd4?PkUey7-s5j+U^%*U$KrA8BAFa9fH3 zQnB|^kVN_Ld|KonLDMi}km(V4!J5UOvLX!jeX#~L+avH`S1+8jiwA#=BfQ$j61x@I z^WeP9Ow>1t!U5+FgHyyi79ZvbQI~|hkWvEcJf_7WbuaK6`A2wkbQ#Ke{uJ&(CNQDq zH(GaI;w~o;8&)z3_I3PbKVDAYXRKI4L)O=k>54(TX;@nSa}Uf(i5R2<^q(c_CoLG^P+*W)gqb5 zX814D5L%COa4IC3wcS)?zg?o(h<%mZ+()Z0J9-h#T;ae^_Me2Vg9|`4*dNDyeh+TX zWgwxw5~BY{(V6&D@x5_eMar68n>8ef78Q5SGg6eaB3W7_rKCNTqKNEk5mCt!p(F{p zbDoK`&_aGw9BGk;a1&u%K-+yJ-F?R>AoK zYcgjwYc^T%|LUG+=hl8^H_a_$Wz9+=-^D+ZKGWXRIBuc&wAVpRT` zK(>nBGrQ-hg33o@az0BRG{m|Poa_q)>OG`0pdNj4D#oxg6XMehcRa3+8aiI$na=RJiBx>o3o%6XD!ZXcrWe=`As)d5uogdW{jnbz- zg7nHCaNA@NIpk*yn@Zh)<(n}~x9ozi^`3D1!5S#YoCD607a{Cv z3@A9}llGBYxiL%ah{1uoIInX)YT9VfFjX1)`=>EobY7Snc>Ja-QapPg>&jo0- ztes}Ko@Hv)<55oFzS~T?i3;gsn3ld4ys;_@GsM>T-*{^}_S*!^sFBAR(T!YeL@j4( z<56|;MJY4%sF-Pboj|hProh%3mNa$>bD{sj04Du|nz0IymT`p8t?6JP7J4CZdtq$A zOXjFfIEjr8KoJv1zuLFcdW@#qhm8W|Gi&gOvn+Y+`x0!n&j9B$Pr!4D7pRY`1MGc6 z3$upM3$=OTQ;9m%{V=6ZE;iwa_yOEIILJvY;^^*^g&=>d6*gwC!eyUAaO6ZYax&cm z+^QLfO$Z|`h6?2LsvzbK^AfU?>LBIjXCkTX#9W`~NLtdQKxMQxIW>AWXMG$VD_ST1qA{5sY)~h^j8m{AB!zZ+KO*ntw!!qhwdD24GmtaS5%@|6^6-oa7s;$< z&TWsT5}(xg;ZOQ_#Vkea9GfGsomLX*l1?g8J;vOnDBZcJN3-H&kO^rl%b_SF+kcIXFUHy}Zd4t+wTWhQZN{FVskh%WTWf1A-U{ww#u zVFq=nnux{+e}F^YYQa@MfRCgcLHED{@MnriobNtzH(ubopUMII+pEFuvp3=7W$~QR?EU+jCpyi{mfIcvM2ZJo*g$1aN zRScYYRn04yeS9>{dr?lBhpr_#CdE)HaD=@UW-u)e^9i@)1o_}$O?t=s&{JLgsC7Ra zrs-5d)WaJnsai*>SIU$2V+YCba~EjIf?`p@XD5_hatG(l6*!KnrBr~llaOe8Vvwas zntqPtJk^uMg3f08u?43^dZgbp3|ATU(#(6yiE7?2 zqTeMZnes)9rE3ORz2hIbeyCBH6PI%K8A{x?k$k7rd&@NVuIv^pVz z@Q`KX)>Ao{B!8RR*+0lM6feQ4*J??_lE1{s*PVX+r%oG+u8>vJu93dbBvKOIM^Z+< zgoS4Hpi$=n6C)zX!%2OdO~WNdPUASOF1N&`DXL)5+Ct}#O2PVR4fL131E%bA;w4?w zQOj~9ol~oZ!Jl5>#z`~4<(4Y5NX18Bupgq5dCmCpw+ugHo(C^|cpFwkZO7d9IJEH? zg*DYfP=CQ0Oh{OSr!}VI(f;>1bGQm+l7rl{VIy(NVs*^#_>VW$i{i(P%f#-wb6ER28B^vq&<(eT z;10HguBaJ_N1DE(+w^g`>dPv+&008rNjgIg_8~Jkr482Dc#D6mT>}=zYe`?!IcBbI z7@c{`jgFrZk7nMcROuwE2N^FKSF!U!8O%;YX?teXmZ6)CyaCv;g(jE6ld4}(|Q zU)IDe1e!X}6Bot1)G+ZHPVufp+gIlJ=JFkUGdV}-tDUBwdLl*U!oIsRa|)#HxCe6k z`Y_o~=%@#9yM~$OV&gIL7)R zT8+rVwo!L*3h$1J(N~y@AE_{#xCz7SwCS|Nk7#zyIZ|SnNGtoAh=unXa_Qw%=J2vq zVmJ9GNd4SJ#-|#g)u%Uf!=w)CcI_T$9h!^dwKGw%Oa?={oXP#&=gCs5o8(K^E7%w6 zP0XHH(3`3Eu;fF*lPH4d-3uzozcbJT?{f$cHdBh<>;KcIxh4pV4iu2nr zS8*|}D437&zGqP9#T1mQAA<^G+-QP#6n9shVWz5l72GbTh~C~@P@EPFYlfVL;ky*T zWM~(&R_!z0y`X?rw7Zfy3mRZ_cn*{P-!xS4DM;S3GF-5NrQ|a?iEj%oGjGNjn(ERuf z^yaB*4z1jnb$%68|ELzNOqoURUN0hEYjbf_#~#}D{2cc|BM(A)FM-9r_2SpJN-NpA z;Uw(_PwwtE0a0xh#Qhus>R%F=s~zK+bI$gnb;>cEZT%xgrn(W0j>HOGvjWV>e2jMb z<=A+nj2xfxla3vuL(?`*qF%QXxwsg6uro8H!uFdkF!g6fcC~O97P%7X1`Vq5-kKhb z$l!`B*^2D}|B(jk5oCS$E*k1$4PzY>pz>D+P|Zqe`{jV(LYhK$WQob6aeCOODS>+4 zFX=M@%6Vy#8%};N%RIFhq=$Cc()=Z9T)pE18l~JqtbN-^T518wytEqRP;fop=qFjL z7BK$jk8(w#VO-1FFj!*h0hU-xtHRfSPT@(oFkJ$UEVdzea!@@D0r+xH%}Kbb@BWLiN)Z~^FS?gNSMF{H!YgNdtp z$*o%+NaHLb>9_Y6vB7B;-tqS1%?Doa#vwm=72PE`N0iDWKYk1Q-xWi{dLB}3azW+H zX1LxcykjIDgYBiCL2Z&cxw0`HW)_FXT19a9*l6K?G=Qs56KLw)>BKB17K#W3b%?{jnKNn6nnOb0ZzV75 zQ;4r>KJ$|w+`)I6hp0quC$2RMBLT(oB)@+e9KYYkDs0=ss(Q7-@6Qi~_a-UW`ey^s zwU6O8y9f$weZf1dNyzrRV6M5ekrA>@q$Vi`B($wTCv6`%(D$ISE`TTsOd^vvWw^B~ zmR@BaliUCG!3nQlBt|Y8XS*p<_2?v0t!7Hb9i4^B@BtG|b@5c87cO^sDtJ*8_;qbh zFnV(l-LPB>1~%S>F1K{Z2n}H;-YI6S7V)gHb~`&)E0vYy&O*_INw0zMGc1_+T>*c$XQ*a>PUM^7ORMyGRA7=RHG09+%>4;j zcH>Fa&aq6R_7zAwBm-r79iVM=3s!HsgjuyF{CK>{#CYZ{fvpb;DXX18 z`s>ewVy!vz?*ZcMTRzat{KD_%e%QBX1m072#Ea|W@%x7$OV#i)qKI|MT z=b6aLvsc+=UTW;N-QMivlyFw#-8{IlHy%=#r?XP5;Pu^;26XrqxD;*&egpl&tX_ij zOZt+(0Z$=*+5jYX9DsSO3{!_q%;u|2r1{<>!Ly`D#=bU(b4r>}oi+n}Cpkicoj&-5 zbc5GiIgHF~=QTnd(EI%$7D+6jy`^91zp6|q4f2PT*4^wFlOk3tM;j_DeOZm2FWHsd zC)migM%J-)0;~SVo0aa^&yJa74wZg`;2EU|!9lT*IPnUY8|9L?iX8G;Z3`%?ABTfG zC`qO%a75sljn$YzURJJvxuPzp815~2($+)r++KD>&0E$&c^CAoI0;XkgWyh~H!mx3 zA1A$1Bl$ag;le$4s2XVsWx58eZ}Ta3rsOHs%0(UC{-|N)MvY|0WsYMl9)4%XwwJLI z@h$AgeTwj|J{vk?*TFGmfdlI408Z*F$kG)b!7V-q?`A#l0| z50W_TBOtRfm4r9by9{^eisTV-V)-Hd3%_jUOXC|$`ci>^Fw18#}wZ=MC9RaUtvH&Xmhpea2`<|W4t6g>AN1KFUj_3>)-ASk4kAEe8-)c!?s3AFL zF%1mcUxS;RJa{XehD}kK;PgHK%o_KRdQmw!x($RJL|Q&5v+!0PE8h44uta{2fg-2Lq} zJ9MHi*u8v?bvZh`LhA&6{Y^dIc&Z^joHz+<-%k~P9w|Zodsj(XMp=;D(y`?4Oe@$p zQ^+67#e%e@6iENhV-D|_O%x|}FilGU?4q9njqfC--(u+2h5Nb0(YbWG{aaY@${%)S zgGh%Mpf1*vR|6UJ^mKiiqj5uMl{g;tnrH|D+FR$ARDdF6O#;HW<~;rw?2f zVocdwfgNz3UoYN;oa9tIqdXC>^BN>=TPB^8BBth5vjumBB8h%0Ps(e~6Y=nJ7(Q+i zNeoK?>(OJ###_#`MD{K$i1LBZCIgDx(l5S_a{;>>1$rbriYVI|f6Fr*R$S z1-z2vIn>QgqbDzUVVLnK%iomZ9yIf~EpMB&C66#bjlV9%B* zj7)tMb#c6a?t^wz(NSK={C%b`4vbGlAi1x zk24NSz?2|eGDIf|Bm6>fjc{&|;p{~j&k^LB(^)zq!4C`kJV5^LOl)xb3JWI4!-;5N zUZ=Bxmb*rwk{f}0g}Z2S^H?9|0d%l;n9D57etAuyholc}?-&is#PjI~{UZSp!a;VcZhK_x; z0un|xk*p0@aHU5-t}!U4_Unw{{r5q3$fxhnZ50R-se72h4i(suxs=ohywkl~g?r)i zBl2X`UpjAKGVOVo2GU>#%zO>9rz{J1>f2#f+B>|Q;)CAD#xf_nO6i+9Gf}nSIy0`! zo}TDVVggNSY0t@Pq9xyy?J;RmVKX$ak=rhd0PSVOf3sH zv*$&*GljFpeK{oFeFqG9eiCNs|ACeCabi2{Gd)xHgFe~PNBy#*XiK^|PF}oIaL}yb z^-WDg1^+3MtHG|gD2YMsTL)0jbT=yaD&e+G-SoZwJ-S;`mXmzyL$5mhVO~p5;LInD zr%ZM>H{UiweCTTyshs==v=?osxi!AD3-wWR#5Xh#w8LXb%9z(Tk(W6ai2PMR*PJvw z64}jko~?zE-et7;_dOCdF9NSU>SxC3F9H82CMZ?^7<0y8TTf}fu6bWp1K>E z5|u{52fpPX>2PgkZd^~JpZzDGhd3N7U%cZb)SuHIvoGW0RzrNfM-nfpN}#v&N|drJ zf-~$A)VSjfIp$WR@H0T&7%f(HjWaMGE@F`MYaE*;gZdS_@r8ROy%#Zz))r3WO`894 z)$SQ!r*jgNq~}xXISc7`g?=hA@ST}-yO)Fv3nc3t#Z)(7IX#q_PHWjM#Kqw*saYLG z-Y@(Q)z_?|*Tdv+F%En=N0iAa z!zn3I7(dJdO^FL#`d}?h)4qc4%gTvc%nHtWVl7EAcuN#&FO#$#a%96q89GAYDGh#k z8;4Jwk1j_A2jq!hmY20g3s@ey%nb-%qiAnU!jEB6J(BIf)(g%|o0wLgq z1uOck4lY&yP-}V|KJhq7NdJR5p@?Bfdzc@5l6uSFk+j~8X<2PawAXNuriWyH4dbO&a>%{3*Ru+W*R0K znWM$Ie41*mgSRy#`5~tNpVwAku7fh_hL2;1Z;OM_F<)5ix}(scy%}c3bP}_h`ph#w z3nE{C9M;iK&|Y3e8w8eOLH9`_0KrJ(j{@|ZS%YD3q_I@8gdeFcLa$O)bY0L+|H{j8 zhrceQv-kYtX0AO<(hOd6?Pp@hn+MbD^z)`=%)+Iy1P19yPo~uH8P1ixnrMeOnG{XBVPF{zbh0EFFLM zSMpOd^X@ei&GRQ{I))OR)}2?a*$)GwuWyaGL&A zrSQ{dHEmHc#CJP&@$Kbcet7gm{9*GK17$kVIfI*>ayt@3$ds z!~B`2I+>um>JDfhyu)Sp8!`s#-=TEEPWswnDQ2wnCyVWu(8lRQu;*w!_GlLJQx>k^ z4SprzVf7GPt5{1_Eeu(9$#gn(96_qSi)`UV%yR8nIBxHH@iWPJXnx=_Z3Q#(;hY<$ zMyT-0=2Ow9I-ZvsTaCBgN?_gPJ{&uAG~F<49)03-msa)M;p#(=k|z0tM?4|L{0vIh*Ws1oI{YxfgtvdUjUU$1fzK*j zc|)HgxG7J_iEN)qy3ftQi%-Avqy3#A=BXEFFPsU=dL|FaY-1P2|+~5{R7eoLnh82D3|iQO@Z({@MQma_wsHc0-UMIDiHiy34b%h@>_a1hqy~Lc|9(eqy5i0yt!I&BUxa0wMIwIu+$S;2cvpo`s zm_o^zs@c{|PUZCAYl0aAg3){Z76S+!jv@YlH#tj~S&VK}vrxM}X zgR$ghSwBB)>NM<4)yDZ-H=#oEYa(?-U{N=m1Dn@`$R_-NAkqv6icE#~nSF3}cQx4W zabo1Y$`iCxVvf>{H2tv;7!T6`$#q{zqN5q-sojmCUvqHlpHHB+R|=P)FJ?X2OAFVJ zz|!T5==bN=;6Ee<4lcWo7UKs%CbbbtJ(KX{-%@_*%yC$^F$U`7?BU#86>{yL7cPEp zfdSiO(SN6~yxeL>*_iLR_u&%Z-Txp7TA~ZFe~ihc?IqxLy zfw}fxvTxmMa`?9e$l2ec?5xSS?MW)e`vjuf$qq8oMiPhJQlrbvOHowu7IXIb(AJh+ z^rodJw`Ha(U9{*m?s__aPJanLG9RF6huV34f!BX9{RAA%EQWaPuc$Swfay=#Nw}A% zC{!<_d|(N+6nz7&m1$(_=Gh`G!G&6!?gnm_=fEjU@U-4#$=*qqiS2*un5^7c)NGAA zY!6hwOBh zMAFuXCC7d6()cc%q9B2CWk>m85fg>{d>lk^h1{7gN$}Wnk62~TWU>>lQu|X0H0=0X za>LVx{(80y?Z1?h^oq@N&T%(7{nRoN;oU*%re~33j)g?V;T%l1en%wDw}ZviYashL zlpOiEnA|!VLki}E5vm_a_00t*99;o2F)bg)l951bQGf`$%;Cv1@_4OEnvq+lJd|#*cl=# zx^vr;LDhBi?bd37t(N3?|4Qzr;CjeTxq(M@o-#J-#V|Ye23==+j)-9t8fJA6SNB$C zrSOh(^}#19D<5)A8?0c#X+NC&!yc6a=F$`26mh}wLVC73mpo`aBYGy}gk~AJp-0** zu18Ug`YVQs()H&vwPlydZ6!mHdi0g5KATNUUMF6cs*>Y_HDw^=qBXy;+JOJ1HjdXH zcZ2FkOd)R!9)gJ+17;JR!q6}iW>=pD4m-Y|gdcgv*k$~n{EA5e2&qkQMy(}LPoikJ z>lEhd@1xu{vr_R-GL}eq$ zWtg|n{}yDD*s)i*e0v__4K`tD#cj-bwhwi67-Hpo9Ucs+h2y8+K+ur2q<8jkqIRd6 zZZbYZWCo9usG?b{gMT=`>DdSPO43n0xB$2(% zWZ+UFnHRJgC#|%^vWvbbVb+HRsXOVjmjcJ&R2-s>*zxBjgG{nXcF1jlSgfKU!{xW>bMW7O>|57Msjs9h%?^J zaS8v{(Fzs8vusSL`3fG@q#g@Sruj6{`7(7*@1u0AG50a`BKG zU0*k2{`eMb^R&gXoGjt2dpjH#n9ZV z5g1S7n-=5L;JFxjH3(zdb8!1N!KX0K2#t&zMVIzFap(6=<(%%X0pnvYX^ze}d@-S% zm%6eEk92wx`Yjexc3r~F>x`hW(tusMVHvd4O(%z}Wl{Hp8of05bE%Ko393`vO%023=v#d~`n@6; zr_TC`Cx5W?-Kbv}-7F$U!#+V8DT3tQ&tUXD77ny4&}cISn7FqJ6Eybl!z5+#(bnbs z#&+SodzB0Qq=wq~Se^!q9#~VwCF~EEz4PEe+eBE|~c`tg4>R?Qt zy6>Q8{L{&)@(Cm%U>Q{qpQO<@j?vnHfOHSOT1aTiy9BV#t3qKj(>CwJQ)$8WZv zm-}sK&4~;mDPse}7HE;dP1z)5kqww0ttNq6AHm$qw)BCuA3kkv;7MQxucn~I4>5U; zv!=;#mRi2TH6p^~Pm6gai%edBS0%69H5S|2v#}~+Hod>Uo9gBARC2okRVsQ;t+0=r zwcAX6b?xZm1+|QI!ac@u&JcRGVLdb9&kDMFu$s2*Qbe=vH8gDF1RP)P$kg>&(u*F0 zID14iZP``F^=(-KIsOp^h>!v4v{E7{yPmgKZf4!RU@t3W*V9iM9&^v zNQ@u9VX_|{MycrzWbLe5WOhXHL!!IJ;NaC$ShXpYH@^6it|+?*4nl^^+VC8!@nt)7+ehOO z#jEtH`7S&Z#^Y7D2~=@!57pIvMgK0=rFC2P6XnF^bo?uhzKgm@x4b!VO)Qm0T%Y^V zZ*p3c`;^E@HNB#{H@sjX);^^m^u8;Om?C%VA*Sk|;3eW&^!eq8WpZ7#`Mn&8GjXOj zya>6SP*3)@&tg{Z+Kl4XR~WXh0+XFA$(mnnX!d$5OxrFCk*_s?zd8*A?}kt^?--dO z@T14o`_o6i?MZrIGW{}ejC&E8MnhI}+`df#RLfB~qqO`^#ao6@>({O{@0LjPF1dk9 zf7232U+QGm{Ca^QyBA{7p-}98lZLfw`q;nT8ch$xVylu2b+@!9?^g_P)6#!4MvvD* zP`nIiS7w4s*LF-B7e{Y*&Lcg?7m~i{Nl=~3vmx7-L*&jU+VwxOIdy~g}XJV(##dD2y$**G?sG1t7n%kPsg)n%}ws zkI@6@JTDh*ef7i_uk^992cD1!VXl|(W*z9(Dp9}adN?@N8}d^|g38t|`d(!S+Wc5Z zj>~Rg&d-0zsLUIIBlfOkCar$Jh|7nghT%HyTwNGl;A_F1keo(mZ&$#`$isNSq6yuV zQgO)sxAaucJG}UL5Oqc#!PkZh@Y};Ql%0H@_!ibt%`k1~JogzCCuh=%G45FAuZ&XG z&d{>@4|HsN!?JfCz^m3FOy#p@45zjahpzl3o-^YQlz2Y{xzIM0@;pF)bzP;l&UaDb za2cKEA0*1jxJmDaouPT!Db&2Z5r=&LL>sJu`F(H`PO;jBo{=x{)Y$}VEeR3*dor4P zqiRMT4gN-r(>nY}RNA6;S*0}5t zYiLx@GH-Pl!}D8c(e#x<22YHdk)F8d(tPk}n!&`Lp2dmt%IIQ;Wn};Cw{*AdD%2JB zrbB)FY2Jdx^vGes`)L#{&I~RVIAd?ASZM_g5d$W@W-!s=6P_?W4^l&ZWALTP7&Ltk zZGNjxKex8<6DDln7nGUe%=cq(;QA?yeryY+j&b7Ld1iDj`vVM5+0w7vd|2<*O@5}O zl7YYyew6oVOvu{GYr0NE507f{I_4#)Tj_B1J8ik({VVAHWnNS%{wr@y!_{h8n3^?@nhw1`s`V(DLlyQU)zN@ zOWyGl_1t)?rYHsU4fR%h55YGQIu#CGLO^ixVr)S=&T* zicO|&IB8)D&KWurwM{g!c26|Mg#STZGdKFO^qc61M-p>OuaJ8l-Aor=(?S(!3ExlWRo4zp}0uicLpvtQg6tz~f>Y0HM+&YAYu3JjdY`!Bq%!$5iv88dv ziFkS1W%OPC2cwq+(cX&3s9a!0Q*QG#R=I;XGJlyfa%r?i;A^(^93b<3RdDEtRGc+B z54|R9q1N9T5E-8)P2mU7>rw{f=>)^@f9Jt=*8y(GP7iQB6h-H{_QJob!t=*J1V6Sm z^G3^eaLkFB(DN&Z$2>i+;vbgN?e+S7DLmo%n@nN#(=OcY63VDY_~SaSHR z@VgwRCq?$Oarp!;rfC&3p?Df~9b!qO3sy3+OFvVsn`24hp9$QNr_bmOwi><9hM}r^ zCXF~7$b|a8#WtZsc}l~8X%{{}pnnA~uQ~=RWOqQ?mO2=i!J_vlRe|@B%gc>!<7WN1 z2?4*akt6&yoDlMp@x3jAkdkvKDu^IaS1NFOgDZD1YZ0h%jh zDVFXDpnJ$y`mW>~SK)ktdZzeLC-X}*e2ojW>vH7&3JjUZzFcxo>l#&gnS-5Gi?LE? zHpX;DlDNP&{CT>A`=)rCwe7qCji)70DoCBz_WXX4_q66E&3e~D7gmlHJRz~DxuOaq&)*an1~+kvu{n-ROrVxa^60{u5+o`~ zl3AhTZq+K0Lk|9`6=$~$r9sDpV z?(+hrARlhB2DAED(MdI8w{H$6ta*m_W{MyacSCSRGidr8gp9g5ur$;~IENnsY4!UV z6Kw|?@GcETw6)M@ZBkrp;Q@@?aRNgRbfBw=9rZn|OoAM)(&|yF;&+-p^un`ZR1MN3 zYOB>m0?GlcW|ZOl>|TLwIY5FsQ|OtHau{!EN#@*?fSa@h?mXE7R{r%QKW`MY^TK@{ z=Y-lH=HsKAy1d-6KfL-@bzxS|qeX%OoUqe_^Jxtb7i0_1h3SCW?_t~@+xK8Bu&Io8 zC=$h{Z}j(xTJ)IjjP~2_3%$fKG+Ta%XzaiY>Z311FZ?|qdTU@s#_D~dsn*-+@$S#4 z=-op*daBVd#Gj*q!WsA|AFvv4N^2K4L6crC{Ms+%O7ay*$^rwyd3S@zNG!m_vPpRD z)p=fDW(} zqdw2*`&sYl&XF%j)`0@DrTRGu?nxy+Jyx_{aWS#Det;vcI<&OL7|lH8xtaeBqO$T7 zGy+Z0oi{FE`E?{T?tVx2m5&#%I#LAw*@mo+d^CIuoDXubmFR!{Jf7Ip3(STxnt5a$ ztxjwpb2KAi{oVz*(?brjh3tx^Q3G_%RfG$ZzJXMx7f6gb1tYhwhVABE8gGDLNz^`#C868E)&%YO7dxf9@e6*d*6L^}#qn3+r|BT?$ zzA!X{=jb)d5%jH6I%i|*O9Es+khGoz(&c{yCjD^%yM;znTF46Jp6$RuVK1EYClqxL ze}Qen4DLa`4Xuj(!;f0_l$U<<5^vA*!^_*FNr$cldKk&$H1X?_F)%r$Gsp%Tnw)=`$?$I8d&-u1fqh2gid$^Y_8E4n+*L( zsG>rDPjT|i?Bw~)4kLB>rcmrFeC zY&FtZl{{`32J3Y$!;^vY@cQC((DiPB**D*^vSBwsM@^1hV`l&nx+?Ir@)fK)HW{`K zHuF*q8GOKmFy3vE6F+4q#ez+aXgPL(x{i1R@fC((l`9Q0zoW>>-P6eE`{zB&1 zs3KVU#2%8xOTkP=j=0X31xt-IlCq+hiy5e^^7MS?fc%$?^E{##8d9w#;ywL>aAnm6OZ_aVSpGJ^=o$KVzuG1v{^A|F1 zZZ9>N^`65;mX%a|`SgF&Gv7-TLaz|?;`K}qQhs0KWPBTCb7 z)M{0lp!bbP_}fB-!2`IoYaHnQ6p;{N)^n zIpgU(cPMq>#B>)`1r4<_=AqmG38-7=5Nrc+Wb8mwcV%j!}nWa(XR8j zW!Zdmc-l&1T)vR`u8Zi>j{RKVhQmZcn0B8hA%}!&+ktP+wKRwD@?UQriNssxqLa zD{s^8iQjms`)0hEL<4?bmyb!WhS4`6V2il6G}t-5%l8ezgv3*Ud#*3pXZ6;bPg_$>!+_G&J%?U573Xv0{1Xm*r#z9 zbOx0`J8>z^A6(8YD4af*U-vNqk;VWxeg3^YrxTV7aR^QhIFG15EW(& zAA0w&l5rkTbX^1)1F;})Ihjeb8_zWyav&cc%Ye=pX)=9!FJ7v+!jH>T;z!1ZDE>PZu5Vri znF-Utd#Ld4-f#tW_4U!R?@hQOup7qZm4ngcbmrIBF?i@vEG^#MLnmk!VAaD7nD$^Z zzCI-6KU||ozn>mlT=xUYrhEa#d}pvx;$i9EMV#r&bugxt1MdszqLqAGh#psr-*{EUrw3%dY+G!h~q3On=47=xl+P zU+=J^m4e{Gatrdx_64LQ8p6Ta-JmmBg(&q$5f}Gbs<+jUOg8;X9-4ol_iy-87pX{L z&8?D5F z1JAK+?;7s6iyMh6vf&sx!5iMzM=KKZ$bm{zam4+2`f|ltSpLZka)W$WwWb328~v7U z&sa*^zFLy12n~>XBjiqu%gA(1igL>?q4Dc@967g`tkF=T*>0hz`gt-L_ZaBLORtFD z>on3R{hD*N&=k&=BVg*=dEnMN5k4(g0Rug1toqDtaB%i{_*w~7HJyHkbvRuQ z19#8Cc;U)oZ`$*6UO{B^+Gu8vhBBPrW{wZ-ogqj3mG=2;gf+}9U@+g`7gMJC)X=T7x(zxUW+4J93>VDgZ9Ql<_ zVrJTLE=8`?ttAwK&xJ#~BF8E^XaM(1l3V9^0Dji`!3U>_(E2X|_74vr?fzTf>$h3F zYe5eF9nlYyn-p+)LK!Qil#U5!_d$=sJ?y+D0fr0rk=%t3=*Zv}N``GEMFw)z#371C z7<$0a)K55KaX&LunCBN~`g3Qx6Ob%zp`p6p>7I^-jCXDv7g3f=?Jb^z@rrD+ApRoF zCGSAe;y85I?Ga{`b+Fo`O~@JkfOuhG_BUrXys))nb(a>?YbHjp!~8WZzivSbV~R-k zhZy4V=5Li!yEe6U$)l%+iTII!<}iPhvWV2>FStAZHma}ROLKQO;K;XkagxD9I`P^S zoZMJ}@;#a~_+%Zf;sp0y=5<=q+twE-%-m-2y3Nas{jX8S=!Rti(~GIaBtBdmTsGYX&stazG^x) zw@>G7ch4Z7cfUqmZ5@o?HjIAP_rO!@1y6yAGaALk)4v_3=;Jp&^kA_(nl21N=~4+i za-kQOb*(_78$i9Q;}~(>7V4Ctf*Knt=|{cAjDfu;c`LT3u}22UpYivg^FbHM*&M?p zJUc<^pKZid9yzc`cOSIvbAktH`rIF>FDR9k14|CQMi`>JU9;~A4yzIn%VM6gRW(wWdxq^_bPiWx?EvA20B#coEf{t;I=wop`u5}aB z0@dN5qcaNPm+XPQy5X#JNi`_kI|vDhNtiF|&MPStWD|sJK^+!43PP2OFkS}Pe0ZP&vEI; zpnv`=#LXH84<()9{4On4%cKB{Pd&hP|14}^OR?d3I&b^t2Cwj?j~|}+22Yzm!*fHY zqh9h{?&hX&j5WK24pWNgfAxmAY~BlM{a*mh`;m%K*QC*0X%u~6e2gBgJx^z=B+^*v zR?)vl*NK9a9GNLMj=VT`h)GoKp-Ea+L~qeV*cB^yFY6X__fkCR83hk=wPqsS?jr_^ zPgd~ktS8iVbb{Kdfd5f+Chl0gZ5Wn4J5dxVTcxZKG0#0kvJ|0JsT3_*wC^RmL{hfO z79t^`2=6@iBuUYtkhDv*r?mKq>YMLBnB#b7=AGxhuj@Q{m9Kkv8R--pe(4pKrVsEQ zn^)qmdEWSOw~5h|-y;QEb=Q0dnM`euFz9v2P6M13DT z)2@o+!!ptHeI1=&(ZUU$R-q$d!d9GoXcrzh^$o{3ExJ8-Vxx-q$T9;a3l+~o2MFYnL9%t9V_ zORl358)9i<(@#!YT9Tv~mxIjxJuuO6BYCms0_mEk3=DZp(rmNIMf!;T_tzOi9XgSg zy~62(;@EXp41In+<@NUqd)f$P>^|U#_XdMdweE(n!Y@Sr;*9gox@{*fC#KgGpLe zCu195N@8!tlJOT_k>VJ4^1M5nObil{r~3XvreY9A3tXlr9~NP2q#Q4=YQc|56PQjr zb9vjI#k^@^7=AhY3|lV8V}9*7(4I30z18_pGyNcF?(W7Fi_f4{X$5_>>;>`aopZ{2=@*@j{C)KU~RZRR=3w-QegzDIDJEpSu?3yc?@|g?7vm^9fz?G<_P}t zb{KXg2e!(~l7oyCcV=8JUHRq({S+NaU4L7lY6tL2FZ=j|+1q(`jUyi!n#o(;lE;Q` zRs4iAiM-}r37*OR!{pxG0zDb);a+_nv^F{e`_2?*U%yUMI$AKc+X|DW>S9BRz?7OQ zgHajFY5wy4^m*qny#M7s=B^D!+v)){Jb#%=*2ypj?R03|S161d{McW!_|PsF{$M-sb?=P%J<9j6uh^6~JN1h< z36H@Idu2gX`kS?x9L^p-8V-@m#=*(L2lmxF&G6d!v3UE~Q_NkKh;c#l1jd~q^%;6h zf)BmM;RDa{P~9xtcuo@+IZdJyLuBdAae2%-$92``vl8qTs&3F5=c~B;>qjyX<;hIw z_fPb8++*~=JcLWTmY`7vk4<32@3V{LSC06P*YZ^34_{K_{kLjk*QT|+{Z|uSeuEeF zcsw7XpQN&$Bh}anwR@z`?;bpj&nJvVHRiZ_@?w!uSZLaY#|-n(&NhnbDy@bQA1zTT zItWX<$D!-~!|10tg*r~(LmO_Dl8&6wBu-}=caf~HFTAsmW~%I?iCLaFUsV$SK2N~} znYDPn)D5FvW?{imRo+v?@E-2sn8xShz~@!GR-`H|@NmTh`wKXEsXsk)Ae`K}GzxmA zyR(X=wxmpFJJ*x(2oxrU(T+VKxK%#{j|hWHlf@M{CajaoeSedN|Mp(FCs*Ju_ z-^0Xni#fYLe`)(bAWDm+($~yWkZ2kMX0xu5`kj6-=J`Zg6XAnSUD9ZF;0;q$vy>TQ z{)dV9@sgy=-z7&PgXzNi>qWP@Z|D%vfK@)NsO?vSt2d6Or*>*$Sn@Q?zgdO9nYEag zrHSVnYGL%Eqr&lLv*7NvCjC3K*)ihvteaCXq+6E}h1$(nxi|y2yuCw*ca0`{4x52w z%x2NwAtU?X-1Q_|j{`455753MaIiN{r{NMz-Kiuw`1R zHneU61Glei+VUmY|Udbd#NV*6m*F`WF)DOIiMHa@|nN?HW3rGPUg5rpuPCF zJaTXBN1CCdBg~Dj(B4!v;XHjE<3#K5&rzU%m#o81B{vMy@Wf3&=Fq-t_u=`DK-SPN zicL%wJSyk9A@Eo})k?m_%{q9PSh$XaaEDXibh4K@dt05_&*)&zKl28=Dd}W(<#e!X zHGmmK9&mQtN^pvFC3AazGs-TGxa~qScj8VF*X5?bymeVeRnwNyX;B)~JAVO*$hyJV z&bwhB<-CIG?)69axj!)Uqb&9tKEYbD2or}4QL|_o5p}GDTVfmG+UxhMibOga-0a7y zE}ak0hMR)W!Dg-glY|Wa#bnmWq&y=|1D>>Gv;)^w}xQ)xuv4(;i9p4>xDpo=pTr$x2#YJdw8f zu0ww@H=MZn4wfBA=Ed|*;pK0gxGm--DPuGs#{37l!Mj06T@fT4Z-w&@PqTUfJ6LfE zQ&#%xSXN>n1zz9n#qerLG}T;zi^X@-(x7wXLV*{(__m$Nnk*uh11XT3CqU#rk1?$7 z!4dCQapxbo(9kX+=j;$c+}BNF9tbmb51A@*`i2*bOuWdI2wrBV6+N`udK1Q2$I!4` zbL5LHaovWwLMJ4LesHkG8doU?xAHooN4lA-DD|*KUzrbn{uIsTP@6I zYY7jUIA+cpjI6XIG5C|FFDRe`O@<&PTf!jWn5E92RZt7HpvWM#k@OY z4L+X_lCqj(_CHN}2}&!Huj}rL9)7O}*Ax|0du)l!lSMRC`vW~!y$#gIe1{{slw|Cj zz+D&4RN6rcA#PFzyV^2Zw927_Rc|nZX;m{=%XEM69(SACpO_EEVF%%(TN$eSSPki_ z(;#om7&1P?p4s+%J#(va6D%KZ0FP_>;8&m@oP|nqRCs=$7@Y^3s5_+fzaQW&XToKw z^piIm=aClczaZti478;~!7|#M96cK^3P1FW^tDzoqr}|7ptBvmm1@Gr3p?RSt`|GA zNfTZeJ!e_>6jnag1Pt{zv8K<|i1OViyfpd@8Cw@a;~FmuyLk?hik)$VjWla$>I~UA zi$GQVHM|JC%8E~SgJVh4fVbNLDf?r<=~Dx2+TtbjaaNElkNco3JRkM0B3k1t&PX(R zlH{Ln!F#3{jI7-+T<;O8Fwh9Im+m1!pL9vl?qJCM@*L(`FM$wAOV)vNghZc@?8NJO ztZzv+E2B{g`p*bo`!Q5n%ON=SF#3od_hKCgXH$^ zhP~>B&|t9~qB5eOuJ0*nKm8p-7knYHmrL;`ojg-D#qL!U=l zcp4$clbBT96@~wL%6ql{9RB zKt9j)0?o3ipfJl3Y{M#H*kggWGF3y!HCI7V-$ih|@PZ`ds^Nl?Iy_x^k5T-yrgL%RyM7}`O(Aigyt&H3-5<4DHY=XzKshxRzphfCy;v$VbtEL6!zTE1mm51 zNQnO>kPeV!zFxdaGL~vH``+#5mSu#KRoz!P>Q2b_6fR z-o+~AW!%rr$Kchvuh8|=2%g0*1#icbOs>U$U@UYsoP^He6`3H=-m4|_BBRLKmlr|t zNDPp7YSg6WD>=OQ9KF_YnU+5>qR-a$*u7s>PLHO(P4thTIeO(gPp#NmlF+7Gs#UIJxC1ZOFSUWNP*>6(e=PN9bmFi~B&q_{~sW8VNp6Uy<&oR^-Cyw3;)zLQc1B zKPr{1;>N5^g(HVfLVrvKtMvB+B)JE&y25hJ%zihCh>c(dYS!Y)wJzA?SdB4L9nd0S z5zf4oi)UiYG5AnCoi?$HD~;2=GJFasPzS9F;8_m8Qz_f!qY3h}^^j`8qv@rC-NtZ29 zdE*JXBb8-bmGih+OFrAv4t1{a!(#3U`-|A@5~G24x6`L*Zg82wbu@kTCHigd7aG1M zh4wG=CA*h;+wWzs(4vZ3`wWNYOke707_;M?!038~()2Q9*Ob$m@7L&?8QoYi_9{Pb z=U!em(9Ujo(spR@6%+a}jU;*ZcyLjPr@svU((TQsEs1}cIr-nY2HEpyV^(#m-4jsu@@?Q4FU_OrPIPLpvJaSL_;fs*z{R&cNUx^ z72&NU+;Sl)Irp5LJ^hb3VJ^3CO&I;XVio=M9-d{L*>@>;mFgTRQmEnGNY-U`JUrKwQesV3;gOyI?SpmJ&iOixYz7uKv$I)Ej%SF_7UI*&LjYIhZkLaIWJ3%q2 zokoN&pdZDY;r4(HbPVn$;U6xeiE0=o>=yE$Ar-v*HV0nW=O%8HEaWE%J=z@slXycv zHQwy68=jMOrFY_OQG4E5%t{h+LF>%vQ=bN!V(rX4_;89E4;OOM(r>2sE@~FwVnZb4 z&?p?Hc89JoYbV*(6x6nt)06`$IBdZ=sVf^wyY79j`xG*U%~{(pB9`vFdEpt zAy#>7D17;OoSPBO*q?Hgg_-N~Y1_lmy!nRLc>QVz?hMc67e9&M`BD z?cXQn(*A9v`H?Yq=cgY{{T;zAv`C}kPxo5k0m0AQ*^`bE5am{g2->xgnQN5+i9R3425#q=_ zYmy^}ADp!RVGvJ3K7Yfw?=Nsvn81J3bAn38B#6%G0qyDbN!8N4VKx9wr zpX=*xwk-v(h04KzeHeIXBJ@ zo}N*G_lm2*df_&9e9C5aQo$C;pKkI-fu>`TvHnW5f^E3&J&&obubOC)ZYWVNFQ-Y3m&ul}t@L2PW7_8(Ph}UTP-8t6 z&f-rY9Vp+$B?aDr%^kDAqv5BpCs_qb>voVr;TzWFh&mi`)5Syey5w0gLsZVXft;4$ z2zYrJx~%@NM~0Bq9UsU#Zn1%W4M}z##^B6ZJfgXrG!_4&f zNv|osW4@j`f3>Ywm1KPTk4SINvyVSF84TKHa5>J#xa{Xq@^Hg+>RSXDb=L}mtX<*c zUpXP?+JnwV>hR0bt-R^&hj`06^DpNlv8 zlJ&6_Bu(^>2@c_yrO``>$F7aU^}Z|0%S6(0^G_Vdn$jM|6{k)K!19)4T61wBJ=eGs zmq(>yS6CcxA1BG1y;+R+%@*;?8){LM(!!736@XWF=TZODZ-|$J4JV1WtUpa zgMQm&=$HQvE9TrIDFr3m-_S@@Ye>cXM>{c5&IGr~U7}D~og% zRxz7jOy;WJI@1eJ>*xm?Ph=(sG7iJ9pp}yx`VM@iAv=T_e)nGTyjX|o#V*F~RA;>G z%wb|{V2#Pfdl1_j2|Y6|3p~?t@b~2=Qg`MB{l?#-JJQbxelt&8-ByLF3;LMPlQhY} zX)RbRT8if<30aRTmvH%@2R&s{M)NDK5zF_Nh)vaL#&D>UFsskn2OaGui*Ee~@&(I4 z-Jc^-RwmS=rVuw2_fX9+fn|MSHtD!y0g)%JlSGmSH{acXxCeoFY;`rBPcFoZH&QXS z`W=)AEGonJc-HF2D!AS%hx^}Y@jgPXf6{3KVa_e$wKf7T>rxUw3#3@INWS0s(<#*q2;)5%*c!h|ac+*CLB*kR1@{Y&h z!{{e0^Sl!D);yv8C*SeGpI`9{k7V;p|CRDv?^*NS>Qcga@c>@r3bFY7DXgowjyF1X z(JCJmX2e}fa{7%fC;E7l;>M#w|22-D&TSzrhyBT~MKWM8SB8ogmE-t76&PXHg}Ro9 z8I5hr$g)Oj_~3DeoZS;czD{1ux~xls8ky-3x5F6h3(b(MS%zynClSNCovi%D9(Yv5 zli%0Mu=`C7ul+KP4+{?Ft@mc~$=l-io!%RGlTBv4RF^Pswh6;V@s;>H%>>1Lj~Gs{2P&b z8UrQOSIOYlU{dU82?O;8U_S03giHLxuRDyXYuYO0FCO3}g4G~u%P>&3NBU>CH0EyU z=arnTdD+YEy!L}h{K}$Bym#vq-cBlq*Z;H`(;ap3*5g@tXVZMtiujKn44P=UiGtu5_+FZ-&o%myMGTt$A!%68INV45pcpM?c zDc}80H=p)lCD=2lea)WU92|wBl4P8d@D0D~e#PtO?U}q?-Vh~ih#m4?5bqNWdVOoq z=5jA?@)^Qhw@%(9!;sf;zsZ~AdZARYA3tr?e0Pp7oGapCDm zme{ItzgO=fA8zLn5Bq$wMaP1uEfn5wroNbBSB!MeeoWR-Aq!LAk^i)GY3YXZR5=(?w&r0&WsVlk_P92ra? zp|9rn-dJa{{XE%ccIyw$xwI>!Mr>ZXDl8`z2Zyhk*j*R2!>x_D zlj%!o&uU@5>3e`A2c*FPt<~&uiOcNDx@gv~*qc>&IRtCt?@){0Vsw~Wgv-+YkiqK{ z=)5i??EKx1>&`XcgK3@NXcWM~!D{tR{1t`i+=`taZ$nqyscVUXJ37 zX8Qfi6T0DRF5_}$87>fV+{UiXSYRZJY1bUF;q5g{WNoR^buVOY-GouLdE`{7E*Yzw z1-q2h$q&UNP`=|7Pa(8yRc94*c7yqXufWK8LF?drxc{RIu6UmnGVUYoohG{CaL*q^e#Zx9 zhN&h|4;vwJeYlV8A0xwkKJkYH&)-cJNDb$5*!_&H?^6=)+)4KQHNi4uqQ5vX_n}x^M z%7DzKB=(?IAL3{Q&6} zTSwBtlX2nHi6XuuNoyQwk%b~Trjg0~m|D(-yl%zqJ0kFD#zou{D0Bj~dN8H!7BSRs zKpj&hF4IIz$P4$t_LdlW@lz#{Oqz|onM-&XKMRb_JdeKxhVhIGaVYoqG|ruO0jJy_ zCGhP8f750wGGm_!S?;Qf8#b%plmI)_u#@AOpAP3#X1^od(Rs{6HB(~#Z*LI?9B1^1PLg{rYHcp!{#`A!>j`URJd?Ajp|K1(**uj* z{`^Jnk0^kV(e~WP7Qyd&Po6t3rAQRU&SBD(UWlff3@7|Eb*?)^3rfq}$V0a>Qu=cV z*g11xvI!w}Tsf^;vq2;ZSdW`DKj6c$Z%}XVRzmMw0HrC)q^w|&zBZM|jWP=`Q>g^y zrA&xKb`t#YQmq#kF|XL8{A`x0nPNTTg|A4%PtFj6w1gmuq&0oA)tfak<}ywvs%@UtM0 zJoYX08-~-5t`>Ak^CQezvkmR4?5SvLJUjw^h^vm@f-kW1i8PJx`(9WHbC7_yb9n-Ee7r z6~;gPPIVj4V>T?ojM^30VXlOOdC~Y`X*#xSOh8lh@o13WMYXp~p+{z3rSsqaqq7?? z(EF?E$iqcqB)jS-^)0UD`mH7rqj81I0WoQ&uxu4gOM5_0i7qqg8vSIba~Yidu^fI_ z2SCl6e_U0dI4I>elbj(1rsrS}#A%wrtk@WmwZH`)o|+2JheyMxfnazw@hYgKe}uFN z!Q6rBAH=qv#pB=KW5JX(TC`A$;hd~dd5tPfC@r#&R=Pyw3;CLiN-gS5eM#b<0OrQ> zYsCIU6JdrD|XkT?yQK*&jJS+e7fE$uw` zA3b^D6j?pN|B+f* zf1&DAf^fq82n^=e;VcPR4CR#QMXg<+u(t(_FK5AyHMyd~YcHwC)bAj%+KebpPl7pG zU#W^$CAD>oqkpvzqw;1=)C;~r6PGoDuGj=FI({kf8PI_kH4UOr{)7IS=|YE})g#Pd zT^Koa1UL9_7073-rPfwnT%OB_ni4NLVs8JQaelapraG*zk!~?3b3AI$?Gn(4#&w^JjNPBaidN8Smt+Ae{?&|l*YxBOM$(IsmV8n>AoxHf|u+qoZxUDf6~ z4K|Yr1F2Njco$c9U=eBcn?R?3j^a+3@Z9jG>Y8H5izxoi6kS4|p=HJ=v|lj=Qx>ek z3D2M7XsvWyuU$ra7r*B|w2ku~k4c;X{!e&2?YJ38>-w)vQ5{R$@t-;>2TS1{3L6^f!Y zu>5-q$$Y3wjx2B?gSQLlm|K<5`YIPpUU$;x%xmtCz?_UV7yQl_&3QAQ4g8AHSMbjt zL;CZ@JMwnlIcRHB1m(Z~L2aW8{0qz`9fJlWZ0tCYvzh}_f5s5`{%X!pK3;HTP2>hQ zmckfo8K&Dum=oKo(ZGK@(O_IJH9ziwfs+l8Pu+lH9KYaE@(N?QnfOC>IVMCoq1;P% zyNz{?P`$GQK9_pH&-=ScW1Jr*%l)GE?I-cq(M(>2|H{u^bemtof9Dq!za_)|X~GJ* z^&nFH$_}fq2JTrA#E;Cu;a^RtyYw~^K0%&t$r*}TNwc9j2_fzFO)z>Iwa^8G?C zI7yp6484bW|7PG(TQAt7Ujq7vW}&h}6hAiS4nKX{INq_t8aEysgrH1Gh#xTwn))+f z%vM*Dxo`q;5A=uOH}{fv6|br1w0-1$*;X!O^lIuC6H103{7UM|4C%1L&q#dAIlA?w zDJ|WUMdnp@5%VAUjU@@iKZ<$KCBy|;o)edbKmJ&th~ zp8lF%IZBfr=r+Zf<@@M6mk5Cs^oJg6T8v9Vi)f!xAH?2kCs$SXK<=YyOv1ws&Phs= z)o?3>g!Kh**<6K_>vzBhooe`8z$G4pM~Dlnc?FWp`1N(u8-J}xgIOL}ZdZXhSp*F> z`lE5}YJs2FLM(<}pyIqkbc2CPwOFr|o&5J9=74~am}Bk96&pPu9;<||>_8D3M%Y3{VTa1|C7trs3K`AlGvGX0*m^~Le{2vKOFgd z3YQFIW9>xYUK0ASA#s*i(49m|2Ykr$s(QMs_(avhZ>Q-xEp!7S;Q3b^U*sbr~}B!o^DZMobRJ{OToeu@KdE-e#(8&!F{~ zSEz8>5BJw9;rwgD4)ba_mWIp&hyF+?zkdQW9m3gwu4Z2b{r9O?BD6B1LX zleE3)P*9ohjM%}$24ffym^;VyTiM`S_gU55V_@jCBRl5m1Q0tELtZ!y*k{ZscGg_^kj$7JF?JqyJMXhZQ zxogFr$%SH1I(J|O4Ge82<|j(H``;2l%O#q4L`#y|x>)8%>IKfDL6a*k@BwAbaPU_5 zp#ADO;JI!V$VnjiR-Dbw@$q66=8XkwDGJ9=UWByfKcqSMBP{Z`M23Y@?e26U?>bw!I*FI0D@gdB_3|bU zH?AQyI~O89K^ex&e1WtNp{#;sKD#26uVKmf!qc|k%wnkd){8NM6spipH=|BAoBBV{wmV8?n^t7$C` zy*SNF+iYg!FV1F%Tc)u-Q%cxTV|_qMA(1$Ce8PWCt^C|8))@6~F3lV1f;;M?m_q-B zIH|UsKI{8PJ>Pes>A&stiS2hgKb1R=eg-K`6J)PO{GG{&SpM3E$)Ku@4j$meD(?nld z2a*0)TI{?9?eOK8K55o*!Iqbcc)5S!$bC4@`@VFe=RU83#UowGYTs^f4edsEsqOTP zSP9N`UxsIPJ-}_p_v6}BJ+%K2#3Y|qMv3-x=F2Qqs+x11o-s=xh2~-FRlkKYTWTGlM&!_Zr z(=7U6i53PY51`wuRPJtt6v=#MOnW7=m^FQhBUwZ1-n(?yoZkdnud22EHK644 z4IDOcQ^do#P;qnaSd1D8Ttm2saqq~fjmJgG_GK_n@c6!Il82L#H;MGs5dc?g$?z`$ zL@ceIH0iWJwB`;7b(josSM=b#JfRebsx4(vmzwL-sP)gl}aTZ)ed^)cr72{e3Ljw6CN+TC)9zKB!fGRbk~O0Jf0 zQIl!1>QQ>x^$7`j{Fh9>NaK?8?IJMg1*9`UX#IjkHvg*<&S1sc~>kaJQgkht#|D)=9T(XH=T%^iDTV!#k{ z_OBJFU!aWOs|RtPAbXLgJ+-qwOHSQB2wJ-fz~X!iInq3v-1^CqN?mz+QQ;)Uv?go7S2V_)n|Ay%kB6hVKu*3IFGcgJb=S*t> z9qzn0j*;1P2uluT;Uo8*cs<<>pAe;0~<^)yb(KXR+M=WY8+ec{7pZT3!S<4JqbY+eC6j zQ-e-#F@h1h9Gv;P59}cy99neB8Kzp4pd1ZvRL@?SCWb z*}yb%arQDw%|0`_Ax&I!*FMylD~*5BZ{Y~Z+eqCD=<@vc@LERjU9>Y$AC*MCL>`dX zR7xTzcf*#(agh1ggvJ!%gk&1vAZE8fJZX2Xb4)i6~|xEcmNQnPaTZOTgh8x##}ufy8Z9 z|6x-52TXSAsTufugbWqC(lV<DEgsH%wqeX$;Wg zA30%P^bgJQ7m`srOTaw;GI_F3TyPlgCf^n*!-%X5A|Z2#v+b@ywLxiAJfu%^4)06G+3J8N@_c1J@r5psg#OGOtH`BA1_^ zgcE^DaA<@A6f+KFT6h5U{JfqWyTu$n&#nc&*$+;6CX=4@$GFOwC0%WwFi$KK3tcbJ z=>{>VtC5ONOHSaWTh}m0+MM3=m_w&UuVmD8rc$G27pURZ07iMiUQ&F~frvfXNy2X) zA^{Iykt;I^=v)qliFXr8*ZDT?QdApFnEbQGyUH8KZK)RBnG(v#Zi!*?mRN!3s-^J4 z{Vf!&-vwo3I>D{B7LsyuA^t-G1Z(#}eESpD>R<+m{a%a56wl+GwiS$x{TjN)+lpy( zt*2!*0mMeWos07o;oOB{XwthC5AmD$VVmO7!j>m`72m-1Ng3%67XcGmKmts(K`bg7 zoQ-~qiu7w}M*S`NEmxRx7yPDKj(=!rP6-+x`#^2B*D*3v*2DNOg^>Ee61uYOxWd=M z&2r>4Xl(6)gv1l%AGZw>+CH&zbEm^m^@(so=PG>GXUVVJBEerTMUS}8B~#0Zz`66r zU3c_xNN*h;Ok9mOT-V}>c}<*-g9ez%=Yhtj!=&lIW#o2!I=Be#xe1rfkW|YjG*|T# z(Ode9q-^XIrK+8vafp{F+yJX}wt;=qS*oXMK*mIj1?dIfm>Y)_$g+?z^yTH9WZPL= z?#$jDB=2hw7nrHY5$29a@TJej0qp)MmP*3#}=>Qk_~-H^Y~^i=~OBS`65q>pK#>Uo++gD?{-!- z^f$78nS6}%O5SQ}F^v}3$MdxsG0AiqzSK>_H^Zgyet-=wGRR{V?~|a3o1an7MV?%% zQ5)4-|CC!kO_-@HiQu-~{X}P5j}$#@NZ~eIEud0-E9J*uq-ir9aHQ36V&=OD)vxTO zE;=cwm^TT}W!K@H2^JWi`PMGUs1zo~I>V)|YjC9d7DTIB!{`2W&?a=L1`7AEBh{1P zne}tfJoW&xt2V(>f$M(t_kJ>{sV(vj@j$(~e)N6GIb1aVGhJca!QFIh-fl?s<`&beeT5wIYp1F3iq^>MQRAojq~q8}R2;8JPX>ix`UDXc)bGTmC?4G%h3m}M z=GB+3;`Q2Nc=7S?d4->Ww0G@X*62SCmffBx?4|FK1dT%CKgXR6Wj|&PjOc|bODUND z)rflDe?X>vQGwwxn@HE!XeK0Q8#$m<%T!Lu20Jk&km_FsQ(w2*AC1Xm{*`5N)gCF+ zOUu*jyNjOEe8V+hHheB82ZhwBPmFG|97%IuZop~Iv3UPr16o(<(d5--7!nfz#Ee*kPg8pud^{qq)z> z4vSq>LJ`SDtGmJ(>OSb?x`O2iN^}7|wiDK<^%OzzoY2Y&g0ZFAa7IEY(UpHg_pL!YW=qe;M!arI|N# zb-~hO)ue00F&MAClf0R44eEOS@X?)N6@Oo14R^nUH@1;5{k1M=?-qQ08&&9R!Tm7h zX9YNwoFJ#`W8rG&Ejat%KS&mGB_+)(z-xyGC^%No=8#7);u{J%axL;^TO7ovj3Wp3 zSrU_h-z4K;Ax>&g5YC&kQTF%%E&4eV2SqpW^wkI~zV?yVsx9H&9+>h+gRJqXiW2Ug zFx=kj`4YMh5+G{yUg$a>1m#RQ{66@cRSn68$NL2)pZ611-q{8$#|vkg9f#nC*-8i* z<_+N$LY7}g8;;7`KxQOD==ag2E?^juS+o+w{;0$5i*G^pLn0)dRe&+K@<`vyP_8L` zE*)Hao%W8Ih*pg&(Bg+LMpREgt&lC~6VBmS^B8P?Gl6$s4ET6Hhqa#LP)F)7Mmdd! zBXf+PXq-IsDx|ZD1qWD({msx_|BTgtrOz7ICxb(G0hhwnf}6=W=zg6Bc}wh|mg|Lp z`A?zojUunt??j6?32P%>5K5$&K=0B&Zb|!S#l%llIZcL z!#?a54%Jp{`6i@d$SCN3I373_@(rORS9hx zmq(UY?;$wth)0^aMp={S69cu(=baIc4Guw93gO>R)@pNC1XTy zAN`?vR^u?=+l{H*m`~za!W}g#Cy@?nbj$xZIuA#z-uI8&GBe5^C6rW($T;_PM3GY3 zOG-mpv^BJB8KJ0z5|vSuQO0xb>y$`F8Xt;^mQ*C7XsEvD_xlGt9>;T@b6?l{{d!F- zfag=sf!4PHYPG7Lndwpo6UXSmL5)(l6aR+JKPcRzZUvH@=5Op+g_Ce6DF!lJlfY7G z82lq0kfOL7>=ylGs=Y7Lkl8Mv`_zRDF0~+C3MM4@;2KzKR0fBpw8G`q9;jY>9mc6_ zf{Ck*KsPOc6Cx&{9%hC<&Y2h+UyfPBR6mb~;Rfeeg}2q@ zSNj|AWRKv22@Iy2^x>=ZQCQv)Llwk(nW8_JNO6VGOF3CZ(&j|qlI(xG}FT*O64a};|t}syZ5pu_mfQ)0gFt_gmw7qH&vPcBKmldzKs@6)Bm zxfP_hZ3hUVIeJX}4Cvabz-771u(yN-O}8OPv@L-+w;EXfVHQcdeVbOEKj9$tcM{i< zp$T?&&79=;c_d!QWXODeLN<*`1cmwm=6d8FT<1KGw3I%Fa|L%;sefT$pZ^xc|D3^_ zMdkePSFw1?Ae~&ls|weq)xg-!67+9YpekD0kmH~Q^UF7pb|)FC1vOlM;Wc<2xt`1s znC7Pr{ve{j`$YDU5;Ib8ksWOD0Er(0S1j_AunWDw*^UozaP}QdLJoc+>eW*~UiJYK zIV+BQ<3=+9z1EYzD+2T z{F>e#_MDM=cL!hFe1)*zVwjJ zUPA2I9$iBCua;x89gnfa-*%jhsy^g@8?%qy@ z+BOP)+A?U${{b^}qTzU6EJSV%fE)8`;N)y!X4aqz+YKc^@A!D|)jABC9z!rI?=IBJ z#>3vEn=pJ+uHYs)OkRxhCdZ9hL_18k(W};ziI3NGaQD9pm-5P(dsdEc%Y7P*)jkcy z1=jHB&krVh=VfyJz;cq}DlkL3T98+$#Vso?fYsG{u3SEsH_$yot>-TRRi9GuRC0su zmNQ{~-%8k-c@cv*o0NQ2_9y_6aA35-`bLypoEsyb5=Mh$&uZNpmF@vhQfv|Zpu zh$mvUSp{wC*bQoR9rRD$YWzSfXs&n+Y16g_m~xc6yjAUvPZL@I@RYqshxNF9EM>PT16Q%i;keaO>gfo0dA zl(`KjVI=ekv)9Zr6^L-PB->X_7W$7@aOsM@e!OvrkW4hr@epuWre0JXtKQ4OB zE9AVzU)JOCo?{qJxY^0XJ6yzr|5-AZ_D`f~B0|&(r`w%VSwUo+PBBNXIa8DFNwi+P zf(mX*8ooJ@`!~=;hks4NGc$#*)O-^2OTYf(CDzQs%k59lQEe{f=q=|BgqcO}q!wPwqX<8I`H0us z`grNOIe5oY3ZHLw#BIN8QP(MtK8layF1_?*sNh5xY8H6%X7{;}`&V)L`>*s`pExbm zyMX3G|8VJ=HCS4F4>c_VF=JW~F|e(p*F7(RX8j!2afAU>cxS_}j68@tBV*tFAq%A$ zWnNq6G_SU&f?EFF&)Zbo#D{(9SmiN*5z%>Ab4U%(N3*#EVEeG~@oeSwOB#+bD! z0jo+^(kwX@81FoXNT|)Gg_*+t!1i9?ynIMg_6d?_El0DvKayyBUDV}f@(M;`{Ib|u z-t5+QUTp1O@@VoI+;sB;9h;+%D!X4`(ZD)K*ai9VrhJ6l$$WI|44!bbJmLb*SW)kIZe^AEnyx=~`=4B5D;R?wbe6Y;~ ze4-c8^PT>9>B~tPXCd&4K3b!1;wx-OU5x49SL4V1fxN^>U#1s z_s&Wn;KYoi`3kd%&V8or&!Gxj{O%k2PmM>r#B8p4+Hj2D@(C^48hH!%DI|B#FnBVf zn>=!@27^PNxM8}vAb;!tj{W(K-ug3|rrT*#k;gF5TxyLKttPnr*i@7xZnRpvglqn& zL<;@{;^?upIA7>*YIi@y{FWN@H}b*O>LgzBNG<;G*nr37n(>5;C;d4%L{AD?%e>u= z)L_F->fHH>>dzXErC$$`=)Yqz&LM=P+NIH3lG7ZX}nna3RM5Lh-?-f+@7A=%d{}# zI5??S>SK4mE{4sK zr@&lRIFI#h(A&jazyu8C+kbcv2>GUP&cDAI2m>QUzQ96ahr zaNMCz`b+jS$~?V5FC@z_Yj3>5jj`8I?$&1d-qT5A`Wt4& zfm>ur#&-ytr3e?&V;IGvr67K8nFC*N7dQ8t;NFHu__HsKA1U;a&s?&_{hM|my^)L6 zW{ntjtO&cLgRy&52@WV_qPNj-)D9aZaP+(A*;c{dGE)*gfB&G*1rG7(oRK(vDy0eL z)6sjqnc%ffB`Mdw@}pARc-c8GcP72gdm=CeShu#{9pvCD#AV~ZwUkx<5-vriG?2J!G|5#E!l#AJVAKeJvNU)TP` zpYC5UtIZc9uT01EmM8QF@t_GCN8tWY2i!N9jJbk$EOKHlUYMALPDA%l=TI}Iex8GF zpN?RO=54&XxQSmFH5W62qe$Pdb5L6*3Fkk0!>c7%V9kSUR{p#$yU|_}F5PZn4Q>9C z#uwiqV16I1bSuV%IX}>9;brc?%1`8&_jZgje1z&zbEvw)2aJopi5)K%VN!QG9&Y@N zkvc7?x#}Eh_FkYybN->rw(%I*Fbt2{v}4J#+gQZj#IqZ;X<+07babu9^WheF7F951 zuFxZt-^^W%tASY?2T0~qADFdH&7sjQ9oBUXLcF9t4E!j7hwBsJ+Hh?e*(HYOr}M_K9fP}bdNI+~iB~F4pquhF$k-*WU^BfJ zZD$FdS6N>iv)TaXzRAMq)2+Ct>kqc9wnNXoPw5ooh5U7|z>BUDJbc+z&_c9~wg+qDyd-U$8mdY&s;g-4M_1Y!*@fd8-_pKMKByyp z06iP3u{*F3MfFqY_r3b`b^1g4E@L)*ws$I(nJzqc-bo;cjbQrkI+zTz1Kw{WtFui5 zvJJMe>LsVx1Fn_qO1;g{tX>IQ!?sbk1a-{JEu{hSH_6kaA&k&?#*hcP%u80AUe!=S z&7A3IFyRi}SZj|uXJ=Ab(n;rkB&e6QS@fZE2kI5%;uOtN+B>_S=KQxtaN2#x$}#J) zP+tq3H~d4JF;Y0pyN51VJ)7Qe(}if;-@q3nL;aYI5Rtzh_^cjMGpG;wl2&kG$qsn9 z$Az`(HeyF~PJrs{Q7|h%ophg=M)ptU$duSY5+nLVwRBqP;MkXFz1kQRj0g=?Y`5CN(@5_hVTN)zhwR?5fxKKOa5(r0*JUU@IhC(1VoXZt`E% z7+AXX0ayy2>Vi`}cHfiw@h2 z1de^jCM%Gf+f1&Og^^aPgD`5NiD>(*Z*=sVrDXJPJ4Tf3PJg90Vp_v96n8#`g^xd? zSun+MiH4%Pb#f&5(|hVPNrOI(e@4IQwbE_Z)Tqe<8OYV3aMD%{jwS4aC%c7R=dbUe z{C6kmFCKsqTgKZfOPW&uWR|%+W(kef@ZqvPxls?Fb<9?1;2N`M67;$y8{ z*t#%sX7@Yt*&&&ktDh~pl9T5kQDo0lRel5KF?+!5$vz0KuZFnXaWEz21qpxVPVVI| z0?XlJz~Hhrx!(8)%8oZMOTM?@#>A<(@1X+DHE83NmFsxRBf3Hl-vKA2--HJq3s}j3 z_avjLntW++gwuZa;e6r_*!F!ij-Ip&HVq5|byHpH>VFT``4r@Hx58{|5AwPo5wyNm z!{`vf^vQV6XNhxKO(Y!cX3VxZVR`UR(|aG0Vsw_bPJDq@Ou2%Ob zwu9XAkK$_ijnrk)QLy&hC(LggDVGDpM zAB4I3X*lWgYg#g*1jo#r0|}Zk&>;N-F3cE!Tw%Jjaasp?dvz@sm(GFm-f1NIs1j9N zSnW{qtqMlobR(R*0q{jf$=?+vw7+*3nNh3-21C`{m!U3VeD4jZxGP7VwnYj4&Lw2{ z)JpQ}{w^?ITMfFg0T7)X39AEA9FoV6MT_$*Fi|xJQ-iMIi-&qRcKi*JJn#fs+f1Nt zI>5}rcifDGA5d1h8bZby!>_k3(4f@Hw00{(MO}@+8q*}TYewLuwf-o1jo@tm*W~TU zO2)}$A(%Azk{jMiG;G@s`fz<6_j#lj8Qx(d8mdSnMyJM-sW;bxOLritm-)+V&3FY< z14h{CXf?we%@z{dmBY+pSiIl28M`(lp<%Za=1p>@Qk}emXU15fCUDdP%jU5vDbrC- z#}Oj!Zj(z7n<04qY?_^+NNU$#=7LYp!nl%tl(UG#C#&Nyb@v2RS$9)p>$Mrpxg$h} zKTbu@XX1{M30$^+4~Z+{$e@KAFq@|^k?+2N?X+kzZH*^69aB%;Z4_zf=ik&(>@R3c z(I+Yn*J;F-4H%;&jqS@P(^GEg*n9jwCLLaaD-H_T(#`%j>ft+({vky_efv!<6ZBwH ziNHG?YCwTufTi6^g4^>JR;_uNg#XtSW|dG8Ox$ML)Nz zW0LDzyy+-}orgJMW8)%N@WToN4Er%CJOob$E^sIu@WzZ8l^AzL9wvB4!nxWZoUrpM zKjPkLEH=;ObBjg`sLD#&6%nlm?#*JR&!4elTZmJQG-%ppFokZFW zCxgx0Gj!#zc&@KPjq*nhV){IDJQ8n1pNkU7#KAmZvRtX|UVXBdiAKlQHMl6DpI3}e z<|X}3^3Kop^CR>_@x-)A4qb=R=ul=Va>?h>3!jrILdID4YZ*?-+QR%7*hp?ISx&!_g1lloOm#Z+0&_S6yPbwvhMtecpBSMRIyMtnwNE4 z$d6VPSdaEZECOaSR+8=$>>I@CWD z!;fv_Nrs~=WN&t69VS0yHJK}{y3>?=-9c<*4{egvDRdEq~!O8m3v z6SjXe#AHzomX~&t8POx{S%Vw25c-%nA)lMy??oh@&f-d)?vw1gDDHu78A%pCM(WGZ%nn0W&@&Zq`iReLmQY=Phn+aQJ)b_w=I@O-=qZ7V&GJGQifY=#?r z^SuNEvxV>ZnlP{||H3NA+klj&3o8>Od^ZU!B7Z;eKpt;bX7c>6fXtmUkY4bG zGif#>HoqNMsUAkit(FPA%M6lIAp!;UL4oZoc%$OH$-cH^x~`OvVYLg0>F8jX?OzOg z_8o(md48li#}f`R&G6>$W1NznhyK4;;w6)O^wN^Cybc!D7x;=A+Buf9iki7N-m@b`?_~V#KCbLL_N;s9^JY1|7Z7W zdwqd>Jc?Cc)&p7F6QJ>Z8)$yXhLeK@L_hdAC)M$WyV)*7FjnYs2CW6P4a>Q_g>#ts zR(jNZ@C#kw^AKBFm2raCV+>Q7fUZWH(BbDY2$}x_Os@YC9Ku^6ea|k)FwutiZXMA4 z5CR_Jd$|?){p9M8-!#Oult@pjW2ULPkg&WNlr{P*?6Eq_RRtHY>A`)1ljtfdc4H*# z@T-c|mVE}%yPaT$(lu6Zk^!ywFb0hCR)F^9sr2+E3mBOv597uj!zjiBFXZgQlM%kc z>?4sMuEpX0d5h`HZC+f*#0Yc|ye@}YbHLu#6|`4GFqeFez~P=}FtI%o&Nyuaaldw= zmTu~B@0S?&OXB7Bc@W#(p-KeXylP=iu3M0uJnPhr^6FM49hpO4)-VJ)h6C=gEuG(PnDPG7^-@1Q|IDn5?g zLu0B_K|ib+cKUs0xaxdjJ2(YK$Y?`?N&tALs1hfkbD>wgRg`ZYO$uvWNk-6qve11l z~jl9^v~lYy()>DFD=h- zQ@p9C2PH`#vuWzQVs22_w@QTmDSq^|~Shf6RXo{miUeM`uc z*@*Eu=P~-XB5x$H=JDM#W=g?E41IGSB`4%#OVe=}^=bi^8gmOR8a{Ixf5$P=i(f+O ziqo+Aa|Lo$MLqNDFm3$cf)k4o{Y(kWcF)m?2I??Vj#n+!l$5 z`bS@s$T&q+_!S`0FJ_-{=pXn_^nlub1z@+S2?{xJAyYR7>gQ|FIM*hq^F0LH*|{j6 zpn)R&QFN5n0L&^$fT-&l@Y}u)60#=3j)DVRYX5hd|7HzHxRgP|^tG&5L@1bPjbnBg zE+*r*)iPI_QmK`yGoyJXm0{^u#$d~BGQVXC-7(Ay)yGex#@F2GDaBRXFOd#fhusk+ zims4{tL|Xd$~+Q1H-xtR4I%9iqLcSX^KN>df+j;t5i4yuJ!oO66i%MY4mZJOfg<>R~uWJJ20s+(+47+O!oZ78ugP*q!um z!YjJgu92?nDqw`09`m1#D~?Q1r(I`Q+Uqt*Pk-#C3Al;6#r9BW)yCaNcwAWXjtN(I zMI>LkV%;=Lob4S<_EtT?bETcw7*vWs&)w%2`1j%cq4O9~twT1IPlgc-JXyBs2CSQ? z3@wXR!YGZ2IPYRJEVawP3HlYZas52BZ9I>slZ$bV#z>qMUx>#nG^u0N5b7HW+%dO> zjJ@dtt{^p@J|5ae<5TByMKx2Qh4B;J{GsbAY4mM0v3zpwH424(gAbr?# zk~PtT9=sSx?|8o9<}A>|D7FVLjrGLczl+gE=pnEZC!>~W9!e|ZqjmEM`sjQKl~t|4 zt*uAU_1QgKH!@jd-@?+}S48x))GGSa?kW8~r3sfF`GtqwlCU)G9`;5`Cd|QCK7ECCq2jd9?+) zyrNbTKFvs>xysu}_arCqJpG-Oo$(SD^j@QH0$P}+oI#)u0#NML7`sf@hafw&0u#>Y z;5fqsD#ti6Wr=2}_xBg`>;^am^a$6nBUv4%gbf@ zfxGq>G?Q5etJ*(7!;uFNIA(^B=hP$Ga>6sgJR$2}G?PY7DdqY#S<$5%JL#_eEP8Y3 z34Pc~aG`lFlY8l-10TrY)Mx4R=g&=a{?Y4l(W9cOv(oU>T)( z(j-1WgD$Ea%ls4gU#jVkakFa(mN@vMsmf53c%%n{09N<%L z6jaBahuF?!k~;VcHl0ud6^UNbymcC^t$2z%zbn!ES9p3MX%iE&RT)jK6H&F+1*LkU z=^E_>)HHKN*_P9&sO%-$61j+8t{cJ`f%?=vcrGf{d(#;Eoy?5A&q%1~8g)8gKu1@t zC#Dgb$nH=*;wJqW7rfq#R;N!>F@Xo5-{R-cUOb13+@FJ0LvHkH!5Q$FvJp~*{N+X( z&sl72DF3)|8eP(S4_1zBBVpg&``>hWvOSvaUU-Qf zf9^&HKlxCbD}~I^>6Jv=*pcRC{beqk8^c9!;^~BN3zCv4PM1zTM7CLvMemRI9y&aK9Y^6h@KML7_yji=$1wBeXIJs&M9AJd#a=2XG-4E<;>c3zc(`Gr+oEGm8FaL&vA#ux+Izj`W*f|7isbZRe8C=EtQL)V zZzbvAB2`Yb{|`ZZprM4CvBJOS#2r5m0lh_}iVlzO3sDvTsXCddf04LdxxHh_wAdx`eE+f47i z+jN`uCYrE#4n2|IM5oUGasbiSYqs4Q`~mrADm8pEM#)Wu_O09g#~K+!O7K&q&-cBU*dky7ITPM_I4J$ z>5*ez>aKyH_N}l$Q~;S#C4^clFgK-kp#}Q}k3Lq$1mhH(=6R7mDV{-pAE+SatCy7K zw^h=K!h9#@T`?%FEGFPlMbqzv37kqFnoyoZ;{9vMlSz^!{?ciJ4Le|XLK{)txK=nT z0puFzp#2jcVHqpD&eUA)ua^j|r_X?%4>fddt2Jm3qY!%K0j-qWNjw&(a7qayP+4<6 z^GwzSoK1e>Oj+Wf_{f2-vYd#X6$5zptqVPyy_No{T1y)z$kJ9TO&a)ODy?vAC!&IP z+`s)ulU2f~OrjfR@A0RH|2q|X+V7}`X zoF7?E^0ugx!IE||OHSYe&j^8|`h5}37Dhd#8@G&gV4tjwGHRK61x8YPi0; zD~S4KN2*volTp9RFj+=R7^|7Rt;F9c2IgfRq*2kiB=E~)`fvFq(y(D1B)Lx)UPliX*MDGkybK^(^7n-- z%MCo5kO%7o7Wlz^n)oHKmbcn*3%BSV#t=6t-fY)9fkn0oi!IbJ%h??d6-nZw2@kNd zXCB8#{N)J*SNKrS2K^q>=IWRp0|cKSC`{QO#F!1z8y5o-4?AZW@A|7KMXH!!Ly%& z>G6Txl&d*QVz>0r`jkhs{mo@M<)slb{sqgKd;{(<-X) z+y|CzZ={Q+4hnpx3hI-*nC@ONd<;tBr7V9Nv$KqJQhZ;=|ek=4a?A+_^^;4zGw9pDk@UG2;o5$ECr;Nyu`4a2pn?s#7FV_c!jDe-auv_ zFYX?Tdm~?C%qmN4ZF0mnw`Sn>@})%N=R@w5oPhYfhnSoR)4BWy62krN3mLnaB|+)+ zpglpEjNx3!#MR?ya^Fqr_31d%_d<_JEDGlaC5_QUF`nMeOQ#>Cv{2`@8M=;mj$#(M zymGcRW;|%6vA)6dMWH0gO6f%N%Ws+2a>__o9>uDg6EPrc4sX)_iZ@eL;}xCtXu$?I zbURhV4TYA8{@xfz4U>;S#_WZVA>^UQ>Fc0&nPwk_(w+rtKVjlTx`a9$l-xenZzmW_ADxWUa+yTC4B&_0uI z7u<~w5RxTDKJJ?UmG9)q4bcxgvvD;Qa}MJ&RSwf>iSLQR)F{&5-9x(MU)>PH|X%a4Bs1^p8Aznh8PAvcbOe z8Ei`Xz!g0|2-5`)(TbXRcx+uH8U0C%8Qhge_1agH2-mGd+AoVMz2ifVe`qDEyJYDh zso%`y5kt(+qDnMfc@7uNHKj*OV#qeJZS<449OfPK90molha@L%Trr)8vM9HFlc7m^^^VZ?xwCRtOnapE^=;vwEf zOXQF|jQmKHEi35Ra4S0NpBOg&J1scrI%)ZdGO$rR2xnL~T<#x1bBrvx1HNm(EZ~gb znJXv%=9{zQ73Q(C$L?c8&WXW?@iwf|Di03j@|g=aRnTS2baE|zJdV10iAFCPL`{2* z@&*|l`pEhg)8?&D%KGAo;kvy-cOjY@n~4#(6=ux0-h5e{<=k zy$p~t0&5CCkVDV15hr(Giv4a#WEElH_Ca>+ltoyS`hxPSpTU_IpCI*HHnckXvQt#e z*cC^H;LP7(XdMgybt!x1*dlq{^F@bO+W#1Xm!Cz)k|!8+vFDyyT3_`osxQDA!eaf|3&-ypiS$Db4q`$<;`XS+*ZhOzPvoutd`7QLgF zkH#~Xz^)({)?#lgG}ixexH8XzU*6{pG6{=GOh7gbHus=PHl>h~*#@zz9`YgmQv8Og z$N8<{a(s|lJb%<`JMXFeoj2WLie+wZF}0`*VP!8CJo<+RTI-Ny!mjJO`U*``a)s`*D}U;b3g0Atich^D%U`B_ ze9V??-udza-t12ho_HLFw~M}FsK+Z5Su|s4`Vs_>|47)Ro3x~T7_t8Ho5t*lu)pmy zjy~_wp?T%!Nwv^1yl9*RN-yFe_*)p1lxaaOVQ7QCHeSu!fvPr3!Sc%qk<8ab++o@a zq2+$Oq2EZ1l8=E6+f<0<+0`(qa1K0^bY^8b!g+S)S3YstAwJP~JHNdCI`6&Ilh;)> z!KFcCaqGDYxZ_Y52CV&va~`|n#)xkE?y?gZR-izq=4Vhb&l5~=x)s&ixtHns_mMgF z?l?%APh@8QIw+Z73mFKJU&Bs_J;GWgTxBOstb}V-+1%pCWjNo>5J$aF$3tyWRPNMwyux3= zq^c&|FzFOZPL~pLeVMrRWC1<%FPD+6Zg;p{ypE=Bk|R2c61nwKVx(l-HKHh6&WvMb zQk5@WBs6IQ>@E-oOPg}=c=CX|qJ9Ryt^G(}ezJwUsfje}(>>l>s+m_iI+>p}SBpES z+=zQv5sBz12kz2(_?4y2UJE$DF7pz)J1;9)t*LcT_p}X6oEAX!BaYs8azM|waTs9d2-ii@B7Cw?Y@;nlGO*{K6z0 z`&1X_1O(G&bfPgmm+6bY&qTLeXQD)(Em1#zmrSwWMaO^t#hG>Zk&LOM$TestITkiB z?y&{L3uzlYZFAyVW5cDjc0;Pr6Z6XbjB~3)@wVeL{Htv4pra#%|F6r~2jOP;^<@F; z9-PM}PXc!3n+A4e=2vzV>(8eBENAVe7(+>-Fh^3B!TqHRaCGJ*EG~%^+z3NBf8k{M z?S>Z4kbi{AMIB`pXMfV)DhJ6Gm5W?k?+Om1XHvBbc64K3B`1cR`rur6x$>^wfilIZ%L=t$xra_lRMO7<9&2q+lg1K_M<_gG`rYIoP`6e ztaj@zcK517Y`k$HySeu=Wd9O!B8)!?S6PK-1_F~(V;)|2zl^o_KcZ`vFFG%GqtEib zQ>}b^x|W%V6PJ`T4}S_hr(eR)H63YRX>f}BHlmT13zLfXwo1g2-vS<2_Yunt6*MVj zJ7hGEg=YP2VC5V}#_sGuYPSJr!b^xuks0-W&}o@#aMAG~ z&hRCN7Eb>3ZKpwbXcj}v`j&&$=63>@OJHT^T_@G$kHF@u2gEe5gDq+&A?*EjVE1em zy1sgF#8pViMmyE{zD~MDV;x=l`3od_gn^rL7N{H-`cQ%E;eb*+6IA~n z$ykIV4+?r?I~IC{!tJGI(8f&2cH0r@wL zbcJCa#D4jYmFe$>(uKFc_{js3XE%f)_dXEcKh|)yAs-Cpo8Z|=p}f&62|lB3J%7&n z5%ye`g3Q?~98&iNpy%`T96Ry|d9VBo3?eHb&Lfv>Fnb46cda4^lRi=Bopuf{9$S#% zx5Px2K6aFm)1#FWTc~#FJha@_M}K6h(((J>qjZZo6-~ZJHg87q@@g6cFKZz8&Gg}Y ztP`ueEfDG^JYXkBv_R_X0GKiL8^hJDgO>jc;o19oaC};c&DYlPQCSXn&h;dWQg#!% zAQz!J%@>x($udv8z5*0nfV3^0aCf5~9NRmZ+RWcZkE}5yeMS?>%Wrk``~!co^;0yO z*_@y|(o5(Dn@$>{cn8PM)DVUKdPL>*H`2Q|>uL7gr{rr}2CF1|hUZ^vfqgFPz>nPq z5`B5l<2oFgejZ@Od)-;B+I`Hu6&CDp^EI%hs-C7=nDCN`A;SDS0K*Eks8f3#xZ;}dyFMQ$Jmwa%&vW6VL7{eXg`kWbD@`y|f@S>~JlxcLw73y}Nh$_!}fKuJT zoPX#u92+Y`&xcr{QLPI-vEnU#I`SQDGbsmkpLVF6_sK(|h{>;rLU#~{?vj*U$#EN` z;&7Vx7<8H;cb3t!XcZvyEFoWB7O2)no>ng?Vj!-)YdwTZUuG73m$J zd-C?n4ML>`ScxdytO>-AOC<#HK}u#uS$#J-*1K!+S{P}at@?; z^uYE8S%FEd4wJHd%Pp7eV48H)nW4zh-0w*R4!?hFwYP9^<$Uz2xE5^3v<2sZbaORj?m_|*}Bs;FPrGg*SqPr(2?}F*9E3=o+i_++(j!QB#6<^6U_YZJQ^1}jG0<# z#H};XWZHW|97K=ANdDFZM7&@GH8Pzo>e!t?T@{Sc@Z%R|bm28J^6`2wDLDw&2HwIE z@*m_pKESH5>sZ+p&a8wva#$>V(HTT?zRlvM=jnj!06>cB8;Ex}05#q# zS#@nAc43zpYv}O-PKQgw?4&FReshD)Qd&pSTYtG;s@ zJN&v0JoD><*s3D968sBR{<+QlaN1qooK%V)n?}*NyAJeb(=sO5w+4qn1RA%#Vp_tl zQ{Cs*Or%*4y>xRt_eJRh^)2M6O5H~4?i5DeHwKYqa$#ippFrw#sF#ip5z%Q5O61l- zd9rEFT69q@r*ACnxVk+{Xi3CwrpoXPx-BT6-xfX~w*ow1VUrWYwP-@jMop+%5(A5M z#F?&ppJ0BLI0U?N0kGB}`G)qGG)8dY)w`fh-)W4ie@quCE2C2H5bY2bGSxx+*5IJR5uqty#-V~H!^=z_rv6%WtbcO zgrf_MsI8wp$VF`7_~)xo+PjQ?i85tM-faf`7ZQ;DuL&yeHbVZda@zG$=#^yB$-(`qNx|+Ci>?ee`3Mkn&g#%??7sk3~_O`BMp)3q>Vw#8Rc8^ ziNVGf)M8}^4l4+CxD~maNp1KCPK}M*XsIYlTQVvN?Ie{Qkr^RM5lPF)I?sJQN}`Okhn7St zDqm4b`96QZdHKOP_qslx_Zw@CZOO^(ovg8V6aP-5!d&~a4%5O5QQzzxYOH#|bj8o7 z*IQNSsVD&{;Qb=!&s5PFDLQn(K@}rUWueaAbGZ0N9>$%!f+-%K=^0Tia^7+&sxNGz z{YEJ?^YDJyq%fVV<9Rz7#$xC>zZqj(VpzFqdB&o8Hk=jx0AWT0px(fHL5&+Qis#+V zITVKVR@3p``&+ni&tweM?#7Jt?>zrt3Jq8I4<&Z-J*u`onz_83`QZ{l@*ID$%IbN{ zmEo-*w&Vx>x=FyZsg+QE!62TTSB{NcQFPz5{dm4GoCXK&L3>v>T(O6bQyCt9y)OD|7Z$>&+CaNmeNTD<1X%i8Kx*egxrEbLHv`y)1Q;waiwY>Z-Z z4Twr0U^Q|F|%DVlcM3ck>x`?QmJtqH*e?$1?4A>PQ3ma}l zlFB6k#5Hv)Rs3Vg=vq6`tJ;(3-_~5J)IVQ%_xKvRB>?CwOMfbo@}94fcy^5%&b@RGouek8{a{oI-@9+a%^TJNTZ2LvdDja`PY{&fwz0S7d9kglL};dg3;nrD zg0`Cb(xXF~ba|x)x%WAouHC(!xDTJCx<<;RzgC?+E!PE_=Yb6r?Z*n$d^|T4%=flL zDAp-JvVI2{HB?2)hW`WqC-N}Hy@(F0x^naI7&peuk28C^mll92ZLZ#jMgIgC?3G7H zq>D(hL_5zpIu9Z<`~{;+KL~WZmC2>I#YEIM8*kM8#+dOV=r;B%Q8u{D>{{JU#ZLE; zmII#b-K%4W*OFUoynx@gHwW5?EejwcGB23V{JVHmt}B!9+K!68;qN;R5im7vnm}`f zxIq6|I^-wmLh9BLNa^lnqHiqb!n3=%h5LEtTIF4Qb728&YU{^igZChD(K?W+eF@92 zJYv$F?8w9NCxYdnHZV5nm$}ygUpZ=9!Pjd?fN}=4nQY4x&00s;+9KaZOtGn zs&+FkTmCXCGB$kAR|WU0)zCKgJ*1hxBjjF5AYm(YAUnkf8#g3_!~>4VUmHXA@s7mP z>qN;-bvMq$M~?H$3gsl~){}%U#U$5gh^;*y0PUhrz@Yvh?AA{PmC|)Y>!_~4=$Rp? zjCsi1;%CaX0ax)y-*r4zwhIpwX3=LmuF?UW*C=(dmt>fGk-3L7>D-b&_T=5C#B#e9 zjq^E-+?@i{+mS^~*gibHU@30c(1EMmlznFA+6{F%@LiVpoA-^7(f^eobdG+fZ z{>%=>XQ|)#GinYdhJS_K?kgewS}lFM=R8P-?S`|xk7(kgt)#SKyTI~iDG`y$COHNL zSm+?crsc2DV(k?4nNyB)X6?6ru~(i-h$+(>9luHDc_F<~ID_$ORUjv753`0bMtCo@ z8^vWqpl9m}`u$%oK7ENiV{#F<{DmxNsqTbJmU)EL3y0lxVh}Ad7DC*8ZKPE+@bJWo zV5?dH{>ArUxuY*YffaeER{-^O>!CL`AL81b;A_)Xc(pK3Xh0_6rBC|!;D;1GAK8yF z*;TkHkN2VE8PV5+EUO#3o9#F`lbJj7AJN#?N4Bl6XN@(Nvpv7}qP)~TDyyd?P~rc- z>B*I7@@<$C5gAJ+=*xoVykw~Fb0z2N`{44F@q%qk5Dd;g4uKzQNJDi3R80RTFndxe za7*0@lP|xakIw4|r00wksPFy@PvZ?hPf$y~X?NiDH8tp?vJb;v2jJVHB^b2%GMZj? z#rYcjw9Y)8dQK~6ZGu|p43{hDG;wlO_G(RG^NS3cL{_7F5s*uh1yFAlL~dm7#}@~c zIGvEM^jhgmddf41O!6}qIPQBP&{ZpfmS>Y7L@$mrzhK6hcG_aB;SJF176_(Q>}3Dw z29WCNW@zc&2V-*s$fCzNuw&aRxYS{RJ74fKsMTfI*SQK?_r&9|4pACj@*7oa9;5Vd z7ae$vmA?-VqW;nt*Bf~e-|w!>4Bn>^vDzP1Hvm~s+bED3tcrAls|2!c7lLdtGu9=Jn;q>D39RkZK z?QqDrl!|?>AkB#!T#qZDwP{^gko5&WfBuAZ^)0wO-T}3hez4mX%hL9T(|OiK9!YU8 zBWijg^n6DfVa(F0sKa=4zc2?bb!C@E(FIdx?Mtq&`)ws9F86IVyUxpKie7oqTS*=}SP z2tnrT)atPYzp&114aST6aHICMVwHL~{qX5K{h*>s+^28nGg*aX?dSdMmN8DmSXqKT z9MgrpHO`P(JJGt{G7uMFx>5eg=y?Y-+-rqa8_3CC+_fru_Sxf;LyF(y5>m2S6 z3?-x5=3tu@&&rs81)CS##2b?pQFC>#Fe@XT?zUJ&M2~Eu#s)9xEeiuGCDDcA38nP@ zr+VSpYilUZK1^>NoDa&$tKrEBC#bRP0$1BAxH#AVsm5UfwJ=X|!{{EIOFszOPqx9a zC#l%pdzRDQ)Pcn|qV!VyPWox#KTHz3g6Sm>@}W%`7T^9t46ggKpBv|5X?d+Mdxtvx z9%zdN^OxdFrWp%Pd_;xWT2#ty8F{%OjpyiZr3)%HQV%}A)|dPcXHNE^Uu|RQrHt*= z?8F-OjeI-VsCI#O#YN#FtpjvLz9UF(aD%EXC4!lMTS3MfV)1_;)gAHoYTT+ z&UCXLr{J25E5i3uPw|6L;S^4)yjG#f@i=g3;Mop?VN}1<5u>$CXjOhLCVu^i8Q%|L zVwx$c>g{1NPF<&=TTarfdu5EP(*hK;KS3rZbko2pYb0M7w3gDK7YrU#t3C}hXqRS> zlWMv=MHv^rE2VY8o4z%m76N)}Kgwu7@@L2XW`fKO~do_BHcTD@>4L;kGoqZGe2r#KI zcVhbT+u69X-%NY&D6-*lB8_)n$o$7=Tu!)q(3FVBj77Hr5y%vyp8Zc8UoVH7{f2Pi zvMih-F&5V!TtnM+W`km~gkY-G2#ngl#+tiej%_s^oD`FTcQ<|G{Z8RbY-@>4t&To@ zZR`k2sRL+vITv%2(>RfmIdrJQTzJM?j&2!>LwV!|0nDd%Am;IWA5&#Ha6(wfyvqlg3r}_+AE@$@#Np^{w<<+f8I=kKrWO@$6XM z-#l*D4bEz~4}G8a(VRA(rMKoJ?K4#&snQPQMe{*nx5{3Umg7YaAF-uT^F)|;mqycv z*Yj--wC<&ABxGpo=q+F%*3bTF%(3Zj=;Zk@pYiQG4nO|a%IT%%W5d`Mbn6Jl)26HF zv<*6-IPW}p{>==OXEnpYf;~1bD>E=guMKZr-d%O+dIOJIRVO+NKjYEvAv{0Po|6sF zp!t8Gs`7(P5!*n$CtYP{IelkFUURgf)0S3UD;Iw5 zoWp+jltZqEjv^7?J~2v8reu9-6qUQjA=7;VtL}+oU6e0vTzrlu+nVAgp(lALKNDU! zK8EkM29SBgg1o!ZiZ*+O@kmEA{nKc{M14HQe*HWeg)7$Ls}+%WVAE0TnX?u%X6C_$ zq`lN3J&r^cD?)^{8AzPf0QKeq;iYZiVfoF`ZB4Wn(J65nA4y817YUO63xt=Fr_d;a`xb*T&j#wHSDxfnX_+C!K;R~L@AalnY>!L7uDr2Y3L zrp;<43BO%UnHSsGo%h~Drp-O*;rWF{U->tfy%XVZ|La(?R6W-wX_mL}+~xu*YH$`T4;V7r z7eBAEj8bF@Tus4Yk_qWilm#)DF>q$~NAgE+K4e69!tqnb;9AK=KEvP$2^%EIL5VzE zb?YomvpY;u78hgknE(i_`3tv~mqT2uI6OHyiELrM!<6t>WXof9uvn4@LB_*Ofyi{0 znZK5Bc@v4-=+idymOY?H1VZ*)&ne=q^@-&s$I#YzQ(U>M24}U#vQL8CXbZEAi0$?O z)#x;spFT{EA9p9|d|qc>C1o1-nnArjzcxP`4fS9B1v+*ng0au;!%4d|2w%JzHc9ds zWy4?Otk`K1KE9lMesKbJ_berU4gC1CdJGj@QKS33FO%b!{UBUUhlK51%6quFN$lRirfGqJywiPy<19s)DE$ejgqLB@Gfc?%P_z1{-e2S!$OO+AHp^Bzme(FrNFfD zG9>HBlIq$eFy^@<`BPT|Q?_k^1+&h>jOivkb4MP|qY8-f=NXgnl3=DYSNKwY53}mm zPjdccC8RiJgZAf9pl(wMi{CZF0_mGDamQKM615%9HV=SZ%te~4UrNnQ57AYc14Kv5 zpG|&eO4eS!szO$RCBf`{hPd!u1K>alO&FVX@3tX@D`HK zqie{aS!W=+)Ez8lE`~!r-yuhBD-p3Wq7y44n5S76>4$qGbgfyCwagn|Qs})GM0N`x z-$Q{Es{!O~i-d&!FJL<41j)}R>}sA4_N|sg=3)S$rERosN;X}mv67R95??ka8s{m!?IDW0`*o=fsN6A$Zt0Xx9>ZN(Q9`a&v!^-mj==gu0G7O zeqD0Cy`3m$Iy2uNFDFM&JZCdI>uJ%?LpFV1Ptwnobto<`ATd(TB&g{PdtASQOp5zU z!rGNdMLz$V+Dl-^EJeEd!)@F;REP7&Hj;KdOPb)Uj)vV5=wa25ck@lqfMjx(116k? zLI%%E=Xw0?OYvu-88>l!9}X;b#C!8UabxL4ZhG`O{E?o^X=hL59M?N=1}fh8nDfQU zO3w7*9ed%8CJ{F4?`i9Y;v1O;qE0CcOnuDL%nNT@;Oj6iL_FW~MBn8^g{e;EAxCSQlT03uV`!oLm7bsnJZ; zt;VqW;iGA1oC#gDOPmn#8j|V1oZTQ?Ozl<}k#Ng*u=wgPnAp=vx)Rbrr7@LR-`Igm zrz)ZQPjug=2hxF(WTKU&O`LW+CO*1JyDu%Yem&?*U%$LV6dlcI)r4;}^P>$; z8x*At?}t&!X+XFqyn{&tgUZr8em&87OqA2hnBNA`||KR3e}fjVtIWD3&z4PYeh8^~xBv$oN*(d)DXC#ydb-&_mk z#Ae@OwYA$}>&AKD8|q22%lF}_z81`Q?}6uw&f++&Z}jT#5A;v04(=9nqJ?w(P_LPH zFZW2XS3lX%4{BRs^!~lX<9-(yhYd5Ur5}^?W(mU8(yL$_u>{2+QxMzu8qOO&5=h1O z3bZ#j2n?pL7TBzd7C6~|gf6weaADu!5AjE2G1=J-FnxCgzCj#w!utIJ#s%PC7G-XCMrsaZMo&xXco*mOR$Z zQB-(aREk`c7eLx}1>&TkOa3f4MPh1)!MHV*h)8r1|0CylhoJ`i1rA1c8PhN4*F&AL zP+(L!8=?aj2~-v-Ls3Hr;FT@FHeV!{Pi*C)2d8pQQAwOb@MUhMhYwy6-$-->&g7=F zG`a3*&L{=-q2_fb%&+^4qopM2{nWK+vRWQZgx^tR?=UWi=QBZ?!R%1(RFJwlmkcY` zg8E-;5Nj5<`BzvAOT_`Q=G}t5V|BpmOg8*GRs(%=uMp>|8D#z2(WHNAC&>D$vLX7( zL~Z3taCvncUX1gCn|qTu^J(R{eq$0B-08&0dr&&xtdh*KGGv=*C{-J!N&OalgrqUk zFztK_E)X5&-$hr`Fu8s7S41_<+|fWKip6Mz!&aJPu0tMH?BTg|e&BbljJS_;gv|=xHH%33k5)W0q{k=-M`Lq8&vmcOpyO{x zgZ+6EV*4VU)A{!d@op1F1zM7j<}7IbHwWU}wPCZ|1=2V_f;?IElKrnLp4OEv!;6A9 z7<*waF0rhl8-%0j1g}+ex3wJKAMB)aUpkY4Nijs!xth>){=(<6HblxK9I`@QxE|i5(gHW7%#uC^2FCead(^2w_HlChv19slJ zfEw?2LGn>!xISK0AmQmMI2bA+80+&#Fy_n@T5_$1%`JQ?Xj*mo)>)~i4@M8NtHY2L5k*pz$OpiMP?{8 zX&FPOWxQb8huwmdc^3tdALa`rI+vns#20GgB_xp*GV~SKPj@}1)%(Qx^7pAeB8c^} zIdQX+-p>rP`HkLDx&*&cHVVwgj2GChQW2buIW5S! zVdVV|4MdyL;!eaR)E&|dNTcp5(&AuM|kZ;DyXh(V@`dnA}>Rn zsDZdD4g7F~d@SPk#WkiRag`i|p6Y}X>t`_!|GI&+^AqOU@K~6dE={b$LUFkR&vD-1 zPm|Uy#rVoic)@D}CY%^X?)M}*RHhDl9h%5tKP4!#YZ9oeauS4~lAy6kLvU`iiXb)P zups{-D+tI{5NH)lfUy;O-~bm7D(mi2U*TAg_6cOVm5a!JK_z*4NJwH;%-73udEP_9J5a>MTrn@f?>-+k|$@q%hVw6R*CH z#t*UA@uj^j{Cm#Zw}jtDzA=`1ABgTsQ?||SBNab9nr>R~h-S(hhf!~5 z5$4cKQ2J-a`_{T)>-9*cGGsp~)|tgh3RZ)}(jst(ZpB|6pRs9{5<;6SH{K38!YbiO9>sK%7!^ZQaOEA93)8FbX6(eGgC5v6_XM_8k8t)0^_=7VTb%AK2Xx(L z$f-JC;lwWaa+aBzcuLb1HT-0-j?ez825LjynITC3>n>0}$HJqAF32@mO}j@uBz7X1 zIAmo{qnCamLuN$PC@AT@KF`P-mYHt2_ zLr(j8Ck^tq#)$b3IgJNH0!eRmytuyr=ihf1i2YQ88S;~uF>z;b)P@M`{~pipEs;pZ zj{(&i-PS+*jfHQ|P6g2;{P%0~h>g?C1acn58cyZjGg@JzfZNqJ(7lH1FmsJ5C-)-& z`z4TzD0k!b#LD9t@a43Q40F@%Rh)a21^xZA95hNzh+3}_XnWiv zri=YZTDLOw&mN?=tL&J+&AdNw%S6&ycnNfNxm4fqi>FPGx6ta?Q`9@Aovxasf`-~f zH0o&)KHXwXD_j4NZ_7p?a8(XTes%&b>~O{lTMvV#&%@F5Z7Xmvj6q=hE)NZEd&Z)TXO*wtZ>z{x?rDJLm&>Dq{sA0mJu|~U9&u5R zYq&7I49+{ipR*ODa`Md`oS}I+r(v*#e(Y0a8euk*vOkCj&)tHRch$&4$tQ$!3Sc}X zuCVvFn3F@=9;B(zo|N5uO*>ND(WBTGlU?obLR%g;D(w_I{mpEqn(sGqKdxbA8 z_~JpTJQ7G?xGH-)?IJop$bcJi=LI4ca$xnNS?tv2NapNJA)e;B{q`A_xbt!xhKy*@ z$6;dh!-+<^>Z7a8_!55ZRAB(eGfu-E;~pmFR}5%{<`au4d2G2!E%Dp(!e*)KJDXc) z5XJcUwNuS+q_;-n8h%z>`Jx(Msk4}?T8}B`n(#!?2;a1ii_Qr~n+p*;vlKO83=W(}&m;xr>hd?VBV!LII>Q4` zH3Vbm40k4Q>RM=-U<|5tPNaNOXI1T+v8W+6LdWTk!?4l8JOi&CR2>NQ`_@5zalxcD z^&wQ=v4aKTo#0u%114{J%s{mZJ&;mG|N5}(-ua0_51qtuue=#nu7xL+J%+nw(32p&qFH4IsBv*TLoMFJR%n zJG{$c5^2~BB(HipQxwdTn`SkU2AR7=;-MJMb|%cPPg5`={u%xj=Q%n>vUsh_gkC*7 zli(WON4cbt{yfifKGe_C@X3uB(p-r}@lEXQ+)7T#B!)}>@`c=1)P_Ch4hW>uuM3Q& z?!w)>T_8WO8tg2}C|~D=X&JVVa9W#eQ2I@VW|WcNhgE@by!+?571J7|!pMCpzA~ zAQI%jv~QmTqJJWA?7UTMM8F^VWy%YXR=!UTHNOYj;!(^d5>85zYiV)MJ4S)lve;ur z4zD>#Pj8neciWU{Sycc8?5<-9w`xGv(XHg}GGn;??G5Y=ISa`1jn0-fLfi>CfyJm? z!8}VpaNhR{r)C#W59xhO!8cbjAutT`W!-NdCVy5ep%Y8Qh_c8gFeC)z-v1=0Zj5K2&b!K*)K$>q_L_{RxIeM)?PlWU zj$@59R8hs!LU{jsGShZUf@t!7npA$SZD-^F6)!`;af=k3iHiis=C=ad4aWqF_&3Fk zQ)OV_>q5x&YvVJz{{+gSrckom1GgQu24U!WJfX-Nkk>7ktBOfCdXX=kh6;7^zi8ubm>D~VdIbAG zM63X8GvR%LD%6dc0xHp02`jUWmQ6cLzrVVLlU6ukY6xPg+zw0}{!XJtMCs+=ZR`(` z{q$zAIHSF+h4y?@XRkS`&e^D^EG#<`LaXdbNs-iR21k_0g?bOvA1H<0Ar5T3rYS`F zdO~Z+D~Ql26&P4=6)eeWgC;)HlAFiu=|D;wt?C%T8+_J2CXVk#E%}Vv zxz*S`XD8OASMohw#Fu8`n1A>d9+hZjucc9&4nF7{wLOLeemp>&$|ULgLz~!5k9sJ^ zo@INA_2}!4rMy?Ohj#8X=6BTJY^?%=-d~F;w;_bB{34Ev6JF2C1q_D5Y7#Omj=eQD^_zIIQ_Zc6LprUtHYi z`%peZr?{W_wnLtr8zaH~6sg9;LDpnW=UuEz5a6lpk(m6;93S;uWSfV(amuq!T=T-6 z-jv@7J|hmW>U9Olk79{vmLB$Ay3a{m+l3phdZMq7EY`P|Vt{%Ro^<&S)rGlK<69b? zrT&g~{f?%lhmX)fZ-GsC&?3~aQm1!rR#Tr9>NNMmFUCWEAH6;%Paq$8i!of1#;wnM z$|)V&$B8jZI33wU^c?j=;BmNKU>K}JhOdND+o$RDzcCw_-0Vs=^rso6gTrjyhjx;8 z$ADakZ>L*C?=d>}y{L=IUPh^3p88)bA%|7>v(FRfF;Xw`*l!og=*8kldb{}=bLZP4 zHp-=*&$Nld83A}Qq1~Xkh;J-L) zOjzDbbJWB^`%)hHKGud@8jPj|qjj0M9p^}?Ll-Hy_K!Jis6gCm{3&sL&;D1sod#Sr zWP)xLlJ*%#t1d6>qg(V=k^EE%Dn7Y~Xtc~Dk>`fk(qHq)qaP*JjXQckt}p^TmNkNA za}SA?9tEa}so}I=F!+7;w7rbsz=t6dqt8YR`)xpQ!OTTk-Koz3V*cQ z@RZ$stO$G~ro*_{$yB{D9`z(Q^Dfh0)TpkXMyIT0x{4*pv@%67xts<1<)caY_KW1# z?yo#E&YhMAUZ;=7{1Ymb|3|n;XEI~udxBn1$!+&mIGK_J&L6VKy6>|=V)u18@i~WF zo>&Ls`kLgE=yPVk;vfCvn!r%mFj98pIh``+66&UT5al5$67=*0#s~bRnrn0E<+p>x z;It#yzMTo~7V-R?Cz6^h5MwW2(qwvnRZ{&NGg8zqL7GK=fM)DpqF3ops(J!w1v=CJ z%>Gfet`ckh5tACv4Q4_P<6JJ;@JL(< zw{7{{VBK`~VoL)R`FWD=>93<9%lkmV)dt4>I0lA~d5`zLcwjEnP|wRl^byYvF&S&f z9``*-%e|k|&fRi!+<+2s?{}o<-KEHugNJOM3zpD;Vn>>mlTF4inZwj<9gVuCb&QJP zWIF2bRZ_!VV?^X1;#{}qv^MV*S+OgTI3{bs4#9J{zpR?)fsHVaZd@WU{r+UM z(ND_TO^M>m9Gd>3h5W9c4g#z1q9$?Q^u*4$^w^;v}?`*&F*$6tk;F}Bbl)5 zpedn0ufv|B6`<0w9_Rgg#ukVrz*gP!FxArx{IuVYy6zj~_uCwhxV?`=I|6As(F_y) z4^ah`98$RXA;f6(!M@&IHapzjfVa&O@?%jQz45^fk1AZnO=qj9MZ6q_1;59D{jPk5 z`4?T|>;pb52k|flR#i%oV^;p?vH2-c3UVd;U1UJ}M?ajMS3=8-n#n)1nu%QAL2tk# z{;ePZMT4aohb1*+Rl+!!^yDp_({P1W0T@4jWs$Vd9HA8Zj%87D!IQFzOoo?WAxE_NE~JaDMQA0 zHFocKKZ4yisL8cn%&W|Uu+}Wd{qY*U5_h`#aJP*}Of@raL9cq@ZBQ5HiP*h^Sb@+r-dTCIM z{<_1!luMfM>0hE?l!^ric042_&S&71&jJu%8ia);7e#vyV8rkOJof4VRI9v$n9^U2 z;ps@25W}CVZuF9PJr&w5l8$9%<&5I=KeYXmEDkzc#Unb;Se5!V>(}AYBrvp*MBZFP z_b!`GZU23s&sN#u#_fb+k0`F?S+5Jv*r2FOItffK!1bUH1|=%Orno+8@N+wyzFQ@j zkogSQnn<|uZ2}azOor=%bEJHd8f`jfPHWU1@an_Ytf|jsB3C*Hd4eCb#eOu{Y+^y; zrVG9;8Rk7jr?ELk2cOjW(@~aG`g$m}m+L~w*cyx~ z7=tk%DtFxB`cemQ~oByJXd(Kv@KojY-<;wheokbz@u1YqK4Myh`9L-h_(EMv#v z69d4%<)=AG7bOgl--@m`y6B9wMMBfzZz%nJEzSG1hU$e&(44snxHoee&bKi{@j7qZ zaJ?lJins|o0S8>$|94!>>&rSo62G0zDuBR z_cK*wCUGK*)3I^Edn}$FNbYPcq{-!PaND2}WU8No$Z=`#<1qW-PnI% z4JS1mL*LEX%$$6-m>T&w^7%AbCMqBd)O~K_l5_j9RWTSHZ`EPp%x5@KFdhAmJYruk zvE+10A$8^bJ4T)NP%}}@rskj-9=Vc$eofnO*@J$J;rDgZg*R}HXcgnNY6}`+FG@Du z7INiLuzIQ+Ih5uM5nJoQ=4CiInA?+vldr#EYT7aFQHg3Ldh!F4{#n;`TRxJ#%wa8^o)v6Kk zzG-Y_vPgBwy&l}$`WcUF8u6^{{m3^U5Ptrq*WVP8pN@%WUhQVxUV9Lwic`rM{&#EB z{Esw#-op2R@4}HP2AHB@SaIYoPCSr>C(EZ|WX>^8TX!`UEq6i>h5z`x-*^}ulmlvy z>tWaZZ6KU-1&*gy3lyI{ho=_}$+L|^q(*x&t`J@%oAZGz9@tMm8A{;HO~zpM|)XE`nG)kmqR_F$CgLl%%@5bbOZ z6;pT~f2b6FQZ*Hmzs=p zjCNg&@1CotKKtAO%ERC_j48H?7y(k|8&(30=nJfZ$84Q+gu8=mE2t6~xald>p z&b(xb{l6Ny`JIv6;-i1D;_MHakTU|wn?m6412ci5%X+~Sg9+s9a|?1)FCLO-n4ntK zQ=4Oa<}-HA03G!q6lVzJG1?^sgT2eqeu!sSO&O1#CGvQ3ax6MnyQ5|8RaA-|2K__V zVeNwwP}Gb-k+$RP+}Wmd$H_Xzt}za3Osb(W*a4z5Ey3pg9Oy1FrIGJyIYq%!&d~WY zcdSvDvyiVs*3KPgg_Xn1#%5;8mtcr=42Q}u=3t-E0)8ojut8)1^I}<|x~!T0HuPq* zGpf7ef~O0vlf-}>>~*VLRQ<~5v2HEHjEFIKZ-x@Lpvse5S(1#g5_@Q( zq6?klHVvHjJf{D#bA=sBLCoX0lho(lY@6unqj*I&3nv}nyPT+pTVw*!R24DNkwX*x z^ECg043*gMgEsQdIC8@ol4h&@<2M?RG~RhC*o8k>%jBHJ^>v@;7u*67mgrbdX`lu9my--SE8E2QQF z2hxY!X#Xoq`nu^peOh%Cy|-6jifJD{ZEnT~NAFjrCLtk|J;AF4b<9bc3xC86ZH*Zkfr zi6TqiR`;&IO#EkCkh#h(c>BLa7-}O$zeG>MPec**EN)^@xB%or6iM4qFZr{%h~A~8 zcz@Y+%ujJana8`Zp{tjhyuJZXE&BqJRi9w`k05YV{6xP851^};J2n*iStoc&aa1f%Ax4TNfR>P?l!f)X^W~;^=c-E%s-!6!jhoV!s~Vin13e z3IFdHBkC1_h94y{%4{}$u&EB6v=*Yg^avfSnoSkCDv%HyAnRqL*k*?mY}@gMQ#q8& zdEMB@=}NS5vku3;tFG#Y^cOF>9@XQ^) zgX8H8&Uz*2IA7TkaJ66ra4jSZT)mYmqE4;s6M zus&51-RqV!)&HJj+`1>syz4@C^tV2uVEP%oWUQH~$zf>hFoLo9kMW0a5B_|xksB+k z#u--p#-df4urBTk$;ta*+d&YHlfbUi8LcuNDTc< zaBcKwR9ShP-rV9Mj5#u2STG-`&&q1{g*MVpo92TN)RNyrnn0rK;lS2sXcJ|HqZe%e zQ{@}D?`s|Swd^1lM5m+G2P0haO_lj2A4L1>Mv+gQxwwxG;U}YEPVA&Jr(EAgV;-x) zsfZEMG<=rS_4vZ5-4QU+&L0FlsVG|B!u(xu45dAHR`sk(Az$})|i8A;RRT-C=IT1V&F5W8%C#oAb)DY;C$bHSm|I3S4;mgM*Sa2 z0TBah+{Y%~E(Kc;bLL+I3s$YaaAr&_{rYk*)zVl^uZ%rR^UU>V&b2Nyo%ax4${mFr z`4V7JG7eS`_QQ>mCB)gJlYJ)h1J3=tM|=#n((OI%!uD(>5M5qG&a{}|w9sF4!q=@r zlf*cZomfwz`~4xrN*w5uDe!7A50W;%B8%m$V0YmlRDTW^*!azbO<{eoY4iekrSu03 zVw;K4b_Y^%)E9N5W;1R7bZ|lY6TA?t$mhX5!P@)}g!}8zm|Qhjwa63Z=iU{_-SmeC z276#xNIt!{Z>Y z{2jF4-46d{-Gz)N#^gp>3i+_opwX z*|SSL9YBZGB~M!aqf!&i;lS)+!59txjcBqPI4}9Yd$^RqwXqWRBy56(Viur1@ibZ% zmEos|&Da&LgcGUOqGO}sSE!vwH$czirW#_)XM%7ymfmDGa%y&`;6S^IWF9i^v-Cs%X zg+C-sH=A~7eb`*x1|XKN{^s?0%5kg*T9C1b;8Dbn_#zCKAg(27b=MTAS(@f zh4D^KF3F+DhT=#Vz-r(!Pa<$ugrt z=2n*^%yF8`uYpqR;N<2xmlj5ldmlb9Len-n^U7OtcI$06bh3mnYFM33YntNj4~(M4vn*H>Fa@iMIiOb&2U5?MfPJqQ)Xo;b>iIlJ$y*85_c()BWj*AE@!#=D zFF+{XMT$-=vAK0mkADN}qA9aJqh+8odW?y{WE~ZpKYkGF-Sm*@G^96~<3GNV%`E*# zUWL_@%Yg)}`X0lSyJa9tZbO8NJA_4N!nW6~FmB#;(pI^b{BrbyW4?-Dd3!At7VoCw zH%3s^>zOchv;oE+KZalT^y9f@8?o8r98#%+{A~9Ro^%$+d!NRk)$Pf2TW~Nvalb$_OiFL#1)>6DfXtSfFKQCs6xd4AG&t!GvFnMK^2^RxE#S9jco{CnxZ;z~FLW z-}FIhf9(gU7~@U1VGON)t|{CX!SjOhXVF`aB~j^t1f3c_iz&I%KxRe}qQ7?uIX^xF z94da2udod?Bjv&R&?|^}o=$eHTF*9HOJX8_x4LxE3Jbn0X1Lf9T0FCYro`^FNmwia zCndgt!}x4c-O~>%POpQzCze4x-?KE8^B}q>&e87ZYCL9UfwuPUs9fa2=Bqf;rBhv) z^%r^G-h>Toxd%s<-XBG;SaeVn$)g45-;ymZciD+^ms)R*awbtZB6O3$humv4 z1g$W4a%LOv>uX#H=A+f<)j1sIhmAv(f4qzKNGGRk@dIb@Z_Phj0^vyD6PR7J9iDS5 zVg5V*yQk+0(H#wNYria1to}jbe@mkA=C7EV`aeZy9!SOahH;TSq+}~C6iO6jy>s3X ziAthXX;Eokl*+e~?3BGMkwUVhgvypX=bb1@M52wf(n3jlQTfg9&->ps=FXY(p6B^| zd~vCa2BnYwhz%XLaQRQq(CZ7rDYt8on-%OxpG(>jaeD$)dFMh;l?4$agHJd+y@x6+ zPDA~d_4M|MQfg?n8dWPZX=t(^1YFz?X(qK$t)(H%-~~2KeZmg`VuM#906yNh+6_Zggk~P z{k;DwdhV7L`Ay72ea@9>HkYPS|CFfdj9;|qXsJzS?|DjVji~6;PwxDtYqT!u4ZVBF zh5oxQPuc!^5c|>)K5cO)_R^7b{)lnpT=*>-bjT5M_k5*?7Z0NJsPVkTB6~c0{U9&5 zZXOoD5s@q3Zxf}$B#6D{4?Z`PKqA-@l0VI3zIb245}CfT+9oGwVy<^xB4S3QYy#W!|r0WQ!*wWY~@D<96-aE zcABts5=n|UNgwzb(1O;fU~ehLjnc~4;?{<}E+?_F^*m;!3Ny5re!6bz0FLP`rrTI}|b>w4cyw&ZKk2RD>q#EgmDvHoLl`1NzJ*)N9b!S}4xs(RKbyMa|P z90AW`A42JC#E^T^=p|Q9+6CtHB(+hv^7nRL^>8siyrc&EW4d_h<3o8_2}8_Xe-{Jm zUtwRFC%%dti=X5*@k!@lUQuT;Huc(I_L7T&Q(!7;9!RBGOSaG?)A{tM;On?Cz)}4( zC&&rkpF~6bD|u4%jzlh;1*&qcHfA zHila>m>nDgcLUqVrOPtJM6>{phx_moBQ>yeNG5*izKh&RJLF%=;*Q`tOg1s*724%^ z?VV-VIMa?-UmcAfQ`K;cX$@^CUPe>AeX0LkJuV~kDUE10A<`a4xEH#IVN97n_qs-d zVQQw4GyQrn;z$ofSf2xT;hl2G&m_oycazn*jO=`a%dEc2A2NK#VPe}VoKc$2qpQn~ zvy&F@hKCzJfr3K~Z5?Tbr9TvD;>q{8F;@Z|T`RF|T@+d@IDzv|dZG9D=U8w|2D=aG z2|edZtkYgWtHgdd30rgdhaQLiQ_zIyE_v;O&TO|sY~hJZ{EzHb1PSy{aSF>+0(02N73@L z|1b--QIC3nr|*pfrbr>psO*L0`yW7U_AUJT*N<0bEO~ji<@ooq6TZwcM%UHXaY&*B zSN=;CRjcmOoXRMidT=vwRXil{3SW|SneF7S`B`%3NHMr5X@RBd9xz^00GV$GVd9?O zWc9i<;+j8;1}>WjrzTav?&E2=*{}@RglVYgnTfueUg6Leam=|#VbJ8c6kfF#Lg;%r zcKA4J_%awrDw2M~?^uDkq;`q)2(!(wxTpAWPAv9h7xU6_$8pHsW#S+76lq+tjqW#W zp^jZoxIdpKF!9`WsvdIPCR6$o8G2?Txy?z!snz-5<);ZH1}kCR8o_NoW-bwZv?ofl zf8cPv2`KxO;?^}Uc*%8N(LMGeYFaFT141^zz``0Py7a-7Lo%dGEf0#Mx=3gFEr{j2 zVYQnTWE#Xkw8M0;J6(vy9__e&-cxR|;Y)#qU4u5~xv2yeK7OTc~)w64-^mI4M~68$k#G&ur0n#&hPG^t$Yi5 z-TsAhl~i!n5-(nI;a+;?TN-WOkw{N$GsNXzH{lZfMU^vrM?m>O4rNL#o99XqzbFcI^(5;!rNy=YcCUNFz`ta6M z653TwrhT7G+yy*SLU|A9yf0*Bk3EI#fvpe}Hw4Vb%c~C@#r|nAG9MW(Koq{x(kq<@CL^JHef^Q|1OTY?z1Vg$&oq z2w?YLg#T7`lQ)G=iScF=bW^*^YRHz7ty9m!(K<&6UpR|MZ1IDXCl2sRaJcxto=mqN zd<6}C>2Rqa7=os}#r7{ZF~uqx@(O0a{r1c7FU|@&l!WhCJ_We%&M+x+1Zc0&fU^>2 zusF;aQn${hx^rH_h;zQAc==8mFhb5|OG`V=aF3;Pp2vyney<|6KP!Q)H3!F^Eig*% zJXdo!ADut+QjP8Jfxl)8qZ%b3yf_GK-a8WGm%>PvIZisq&qKq$Mj9gi1}S0N$b_Ix z0_#`IEB4;!-TzKP9l_^kZMKETy}C}*e!hjPPXeGKE(3BsHj=UgQ`qrtIVhB8z}cYqf12#``;QWX@#y7aEzC+P zg-IqxA3zMcWc9=rl@PDh_Diulf}fgkH? z!aHo*jX$zJbGGB`v1nfsuao(Wm)yDmg7TBd?g|wWyCwzxHV$Jxww(ga5`W^m?3v*1 zNrye>){#{Y=Wru!WofzNTyldKoOiNsi0y+=Qam|||}^-#75n4!z}N z9&Y4S1M>I@sj+#f3>f0~Xff7Jc>#5*d(gOUPJ5`#MZ(StlHebdN50otlhBn< zIc2k#)JFR`vH9#v|B5(zVRZ#=Ke`Ifrd42BF~PNmv#@mXU7RTS0HZaw;i7GRy!n+8 zxP7A{>YY3QlV5+r_;WjP#1kbTXX0@0WjSj0twd7Ako(d^i7kQYV&I}bJ`9r@yv4O z)QwQC)O0k;Ot_As`W^VZ+Y&SEQn*Xi?ex)M7rLQ!J(0O-&$P;2M(2V6T(k2ENLx&z z-qK5PiQvxA+`Iu)L>p0Sel3}_C>phu)S18jdR%??Y+Nx?iLpB3iF0{@a~%Ij_})ip z_Orz>PW~5UAK!$Be+7`f`lT>L*BPn}pFqO=Z!jfCft-FSN!K3vK}PNIXLc0!a7OB9 zXwmn5c+6pj;QjfE)~*`d?NQ3GMAn&ZsF$WU^}--_*d@>z#?v=02oWpZgIt;^^V#5w z;09iRe&7Cq#~mehoG=TzHGKvnxndms_*wz3zMlc_6sv(7sS3e6%wX@#hoJ3g0cP_M zq8gj74Nfuuho#2gH~1b>+60GejT;G6{Y4|?q==e;tHwMHJ@7Q7h*-PH~FH>kg0c#I?mF z{*^5EQDGQ;vQ8PD)5|gVz-}_)O*JZBUyAbkb%k#AL>%G~%@5Jk#^B6-tem$8c{TeC zs1`-i_b<-So$mFxI?oi+MLMt){9$9>5)zdyqUP!bv@z6!$fTK(y2+Xl5=g5Y4SRJtj?{$lCgIP6A^vDwT zoqdavdbM~cTi~W`=tsByDWcdP159)MT`ni+J8o=bzl zbCad3#@`la%z4fPmv>=CxCVZ+93|{WBLxrIJ~XypPtM-cXRaB|Va!58U}D}1lHKk{ zysi~;?_`uoq;V!ueeTE{`&fxuzsCvB-VL~JQ2?)YCJ%oOjKxHEYk>u2g9UwoxU?q< zMNifX{Jt2hUowfwOd1fb$vF~eK8)(jT8T;5jc_>^g_8T8koGat@ofEK`t8$Q68?Gt zN~Y}Om6KKQ+MXZ0PF?{&`*bQ+Hhjf0_p3B|js>bG)Zsi=PcHAr6p;UQ0LEt266F_j z8HqmzB;oc8;*`9LmTk7eVJjcusy`3$=S&0qqpOVf%^zVfAINKnQZVhAH5g4$!%J&p z$a=_;zL%9=0(ND*ttR$i3{>~LfnYF5Qv<5ePkB1lyy5Us*f~s}{TL?>7vnl%9m`l2N*m(M;O&J77zo=B z{d4XSKB|yat6B$-LX61evcJ*}Vb<;&H7NA4kK&NgN9pbHvD9Y8emqcr6Mrn2t4327hg z$;i#^fT_zo$$~?+q>JnmdRyD+_Vtg+@!|>s0}EkSuoU=C$Y747nNcO)jK-I*gi*%R z;HPr~t2F*BYh@e=$zBw+R=d)d4}dlmrSemoUSW5rKKVPd7<82~;AqMerhkblhjbRZBXkIC9KcgDhy-e$V)mqdB1w%5 z^OMzKqCCvWG0vD=`8F~L7g3w`1sL6iJp7bWMez-FskCng$$R>_BEtzCh}7aZFDo9qBG*h za$6rsk$sPvNt9nbtWPf?u`4CW^P|3CyG~$el}f-y#dM;y{yCR?@*8bP7)LMNxl2qQ z{6_s>Zg?_NAMa^&qVL6D#Oj77+?#31I%xT@tJW8=>tqMn?HziofZZNzw(`Q%NlihB5VfHy0M*EJ1djCiEtuOQg=v(?LyMOwF1Ic ztYpodSHWqAXh<$O#M%gK`ghkN;Gv3;OKA=zK@m4GqAM9BCVQdV;TR^<`ZO*~%NIB! zg&14*fYvQDC-%`goaYQbl&XnBo&GKK+BSE3WTqb7PbG-OK{py+wTznWX%hUl&ut=R z<>3s~*;MSfnyQ{WkGe(eTwH7+iO_3;5ucnOu>U!|?jH-Yw8VnD{FpEY_{gfM{DQ}m zRUx%;FAP=P2RCQ^2MhKZLP^kKI&#W5%>MI_SIjxjE1!wLRJRO%%o#_1bgwx-v|bLM z9O>rNLg&zyP13aeXeJ5ts-p(ax04tW#tjn*pCA1rCwGM~MVeOB{Fgf|kSV66KR=@F zfvYIB=e4-e&d@VAR7R7v z&B|gOV#cwm$F#_u#fNeI-wpV+Qpf|ww&Jt;Z$hp!1oOlD@M`-UYQFQS;x_G-P>@w_&#eI)(+BTH z$bXQ{22xC9nK5X{y(8|U3a^!DET_YZBdIczfzhihYm|Ffu*mE3Lt_qXN2Bxe#dDJP(jT$J;70|}wXtM2zB3TeKjo9@w z5c6ac8R762RhO;9=xry+nThTA+Rz*SSbS#uw->|cHCE&<-vnn3dLh28o;iIbgB}gl zqSp+vfNhH+2M-w1H;Gr#&f-3A;9P?lTTfH=jS0@wRuekydSDr2L*G@Zk*qLTQu;xQ zxbGVxl1*-6oT3*H3*td$z0;v86Ti{yCzFZZybki+Q!xu&o{Jh%VBCQ{|e;_>tVXI44e)R0H33;Ax?1S zW?emi@o#sdiHjzy<8}*5eRYs`R-t$16_dsiE7|91uc8;i`(gA5b6D}m3P%0C0ptEnBQ`zdLUv3M zBv#yk?CqySal5U;3Lon`J>GE>7cQu4==$5CXcl{mE|4XwK!%Z0=U3Ke@BNhJ7VjTF znzM9RMSMwsFi)(i^q7%IM*RsU>1_|l+Yc`wS;3oJ8}=MtmqcUSkA+N(9l(Lf9iHfiyg=<64ZvxCg$ALF7FfZdmEU(@o~^i1&wk%84XqYAmd7)q_Xl zr$BR{CA@A5oSTL!E6j%I5lntsyDO-=yWkZKBDKc90-8pUX#q)jS zevlNL?z>E6^LElxgJIxwYYB+_{(*gm1}+}60Opq{W1y!dwx=0`x^o@1Wai;2m8s~P zp@6f0Wub&#CQP_nNY&cDLa$jh`Ti%7RXuQ@ozr%iRX7j}&)yz~%9-&(*Uk-odq06- za-P{9n$LVa%#pa{Aw*_h2_a9S$y3Qb>b|v>TU_eT1n#tj;SZd)AD=HcrZ|4*Z){a1GR?WiS1i3;Gj81C_2;JE%#wI zX9&`nh9D>V0BXu_!u@x9;PL!UcJ#|_P<^ruf)_dhcjLL>T%8EScJ zg5UBYsCh6RPW`73=VYdW`*weUUlIjr!xq8{jT|@y-@$G~J~*f_q-U`cNQIUNPC^;3 z_f01AbW1$VHQ2)~XgFzc?m;8!UjNF~mL|~8T17a2*;B6d&?VgYA(o4=Y(?>#+5BYP zo0zlX8R}g;jhFk>@W(B4(suJQZocn>buVM^l=EROyQP-SkbZ-K6iJ%Az>uo?2Ep22 zLD{Jho{COD(EA;bA6yA1mYPGxEl>Dzb}PK7xe42)q{%L~dt}zk1)$>=N%Wl;kxP00 z#5I<3U&5>DgKuw8W91a0edRnI6~2eQV}t2Hu@)-GkED?D2z`d7;p^JH7%htCT3e;i zBY#lfsvcrq-Yw!~rrzR*H#G9b4*i%oawn4TRNR`b1rs|25A%4~x6JBLS4;3`FyxsRGUSVP_UMd^J>(S5g zR;e1@{j(lTdQO7=lzC9Nv==;PDbNIvf$Z+BaAT2#Ao&^#*&~vmUR_;ai@B4!&`Pi@ z{Q{FlBntOlG5ILI0A|@Yn4jh!h~m^m+zU?^+AJ`3NB6En%@URl8mT~vFsHpc)`2-P zFP^AAG!k76Uqkc9#PB00_waHp`t(j|4);C&CGT7ofU!CL=wr_@qYy~U5Uy&;tQUv7FOc?8Q9d5Ktg#(7WKu+%im)!eQ$f!rtV@p55!SglX zc%upe`xJ&ndlmb`!=D#u7<(GS`d zmXCpJH{y+|$Gqg_c#eG+4|-vC*pT{<-pzgn)>kWWv8gdOtIZ>Udrrc2%VVt0a1|kE z{{kL8*iTO>En)6OT0;EM#c(Ni7;L+n0PFp0=$Q}O(f?K}d9ms^%=HW-+gp+RE|4X@ zUK!;4tA%vIs4nWWb_W&tXQOLt7pg2;&HZd0N-8hiCx!a!VAJnNrti#Q=I+$cy^JEuee`41a3*X zQWy1?#D5gR;*Vi)%q17tJOA85xoA&qJ6*J8P0MVk&uQn+Q@fUorEP zw8)|SkM!>Id2}G>DD&}$1ZdAHhez_0z(z?y$lKOzkuGo?Ot+9h;a=X~IgCtuyqH_+XiH*~+PLh%Hkv+C zlMe3H<^G+&FWi^O#4|bw4jJBnRN;5HV%9eZk&>sIUT>nc9rbjcc_QI&Jma@q+0PGc za>S8c`?#_dQ5f$bIBAW#xEMV-IP$#|{aWw9SndYVmM_5?Cmjrx-A3ai&!DEU5!!59 zkHJ^>V3CR%H~aE^?!Mm*5^H&cbSO+`tX=Mtl>G{X@7_!F2Ae?lL?lTG&R|YhN0QHd zz5=WIA;b@Shm&go{0k=%HMc7yc=j_qn3so92j+6Wwa=5etP;PCd4ng9mQuAvo}#Lh zjo9V6l-5<1!R@EgAZJz}GU6*iL&z5%ev;2i>@x{f#C3x%RRNVPx zIqf;EiK=^Rn8f(wG$QaNY3^tihx^Z=268<_;zI?w@hFHS7Ibp#SzkJ~B$}>P+yrYU zY$i{Zxsvl%^Ktz@MBPJBWy)oI7@mX8CkaOOISr@N!!X9*mpX@;Oz zfR1_-i1PK#WOQ^e40}3~>zVSF>1gUDMZ<5vn7(mj=3{*ldOQeZzOE$IvEPZ@3KwcG zFfH8cOi<-=1-&nD(G~sdz;A9D-u!-ypI|SzpM*@KTG16U!FwaUvsDa-`~Sk$OAIxg zX@$k56z}a&2hZl?7&*fP1LZ9RztCO0KDPxg&FI257>zMW9e6U@A6GT1(Ym(VoQ8xQ z^QvK__-UI8xj11fOu3u|E5{!oEhn_Z)o~#r$0=Kxl;-KMWk~^zu3QO98%nsU*)dGc z3KMACQwRLbHDG*EpT3&54EgOm9b=XcS>xt`g@z^s{ivtOQfo=l`*HYNZ6$^damVtX z&eSR{3yICme-J^KO`3NJ7nzxB~T+=KFsE6`q6xMuq0VB?q!x26ft zjzWL3>iG-$Xy?Mg4rEy-o zzz3Z^1nnhqaNpTMjPY_s=l2gW;pYQvemfWTJn@1I{qeL}(FYD}xeS?mirD{-bg{>_ zB(V!CmO|%|sc% z1$OEG#_ag{xM98>bKUnO@%z;d!yQ_IR%jZY zN*Yj%(w~Evl|7?a%_%3Lw9E@XJ-EqF{oHM6w^Xn`T0BbOv?f3@&e)9 zcQZK&PDHd{nENegVU#N=H4K(U`Fr+gxMDZ{_wfNHPu+?4o=Rgztqpp9ID`u?TGGrZ zv9w^c2wDopz_KMrAXhz`x%gc0*ZECh|L-!eINyT9#{a<{uaC50g%wFWtRa5b(1F3{ zIC30BFl1s1jd~SGr$oF!*N_C-5-^UIl?KymHfK?*bT5%fsG*~cqqt&U4{}4UpM(So z8St|K#E)CeOwex@7;GXOCh3IU`-^a;Cy#D9vB-=bj&>*VnVg;z(6nU@D-&@C{)T9< zTK7M)x~;+Nh$&L6p!$5|`I>`p?bu;PT=hAZ;F=Ndx{2RbeMR}D{r%l>U)P#4)o?pc_Y zqQmAt=$gkl*zkNY8crUMonCA4wSx!;-xr|y9D!AGMGSk)4vRNw@#JO^fqn1g6@NQVlwLiYWxOY#4@r}tuy>%B%cXcD47LIWB+f>+F`<_$_ed^Jl zf{0FrE>X7(MUyF!^k>O8YMeS2WI8X=5YO2}XIUCZEe}AQ97$e%hZp{}*v!w?*W@kw zWO$Rhb12)J51%qA1PAPAm90Ajeua?XIJbou9>^pQl|B(;H78o)l1e)lJg0y5Qi`EV zII;dIB41rVq<=+{>(=SwwEQoe^n&N&JMp`T$-qVk|2zh+dFPVe4?^Ffej2EKss|ro z4%aqiR9lSE&CMttHkTh-TncL@nS#^n=2Aagylc5td%|ot+m%x1{c_(^7?OL-$g$3{z^q!moi$MXKqZE zgsp<1!9QTC?o#SE+ZVpn{e$UYi$5? zgEl-9W`OjRGqy|^hX?G6uAPt_Lr-kTLbEj;I6Ue;9o{|?C7%C8QI5W7#shs($eQcq zh~0XaW_AWTM@8VI#fs#KvI!iRE&&gX^kL@$S4^C;nU(cA52@aIEu{JtA7S+0%T6HlZ+<<0Q%^P{LU*u_sD(_)jVy@rw0hy(NB#dL7`Bh*7ot>G8A?AgrD{*A z>FOtY1*bWWqV+H6sip(;;U;NDw5*sOe=LDg=O>bhO+3z7@f9Njf?(0yWPxkhMok|X zfTLVEE=^IU+d2c$=K3cL(p*QA7d!L&{*A&#Gq2NI3bhb<*ND@5;{Xn|ML6$Y7d%>e znpJMlgum?rnBG*!4n*wTPkQU6g;*RTTb7PCmA#Xx7 zOg-!gIdApYnR$=F%vq8Cu^C|ADrVuU=5e(1Y!$xM6}X0bRp`~*30QLK2;TDDjC(e2 z!Uq;F(0AoNYr*LLD z1g52UlVki@^sdOq{nba%U+psNifDtoYU)fI*@A(OZer(!IlRiu1YXrTN_gg}qT&5H zyn=~3tr@-?$7Si#ky2UYpLZ9%GG!F%wO`=O79`P>o|$AC6Mz#NqG-U=qbT`#2i2rY zkpA|fLF%#Swr&UB{B;x?Ex+t`)_ABMcNa$1+p{uuekkW8oM(i7p%WWj>D#%9 z^l#97+H&Y7&Fo2nC8Z^B*vKA-WVnH7l|JS&5ttTr7=J&p#@j>p(*CqpywthfIPt1K zy|g!zal1K`wv`8Qb5fcFp5r^(9e1%MWiXY+P-)HIX&BaKo(!B-1aAl@slNr#qKGsK)@2bvR9*Jt)Ac!0Ys~(oWpHP>WaX zoXMMpE5NtTL@0<}PP(lkNCf*;6xtSn<&k^w;4))uNO{LuwConTQ~zOH)F%3Elq6Fn zS%@|oo9U+!=c((4@AT8_T2cC~b!a_p2Q}Yv9T$z=fb)iW(0kphP;d8qJU(eGCU07S z4-3Mua^*BS&GiDzoShB1jrZV|;Tni4`3H7#I&_HLDqcO;p4X3>!fT{E@Y*K5xHIE0 zYrRT}UH57e``;sdc0)-zjJ{|Jkufy{rDSlVcDz`Bx;t{>*ngB^60U=MOXsPLSEAv+lu$&hrW5371cw} zm+6xQ+X5k`s+EZ9z3FD(8T>SXTV(e;n@=%W#an3`^Bel#@ax~+5!_}+*xgrmu=`av zvJqQWv3?^~LfL_6 z57kz&C*E`H&aNY@>nKAApZtcMaH;cTTQBM= zD@6H=i)lsu2kzp8&%+6H0#VTzpVy_GwVq@3r zVuy4GvAfs)WW6erSk>?6V3qt$)Lr`<mN&W-c_!ZF8Bim^nm{Yd}E2!1` zgQ)P|Xlk&0CFR4yFy76F7KmO@ajga|Z_GoB4G;JkEq?rb`5b=!a{*vU}Xjs4OfVFy%W3q z);3l{!4xc+Vwfzi%2=4l(0BWXpwppDnyWCDX4Xw13KH@}J~R%;ozO(1PGfqdDntB{ z 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


WrLoO`yCsX(y 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 z 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

#$NJJEE?9<1NcjDCLo;QVSGD(WqO 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%0AsYo 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�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 literal 0 HcmV?d00001 diff --git a/tessdata/newdiff.asccodes b/tessdata/newdiff.asccodes new file mode 100755 index 0000000000..95ce309ec4 --- /dev/null +++ b/tessdata/newdiff.asccodes @@ -0,0 +1,51 @@ +a a 304 0 +a c 300 0 +a g 310 0 +a r 260 0 +b s 000 1 +b u 263 0 +b x 374 0 +b w 272 0 +c p 265 0 +c r 246 0 +c t 277 0 +d g 276 0 +e a 305 0 +E a 334 0 +e g 311 0 +e q 323 0 +g D 343 0 +g m 363 0 +g s 357 0 +h d 362 0 +i a 325 0 +i l 322 0 +l d 055 0 +n s 267 0 +o a 306 0 +o b 000 2 +o l 000 3 +p d 273 0 +p g 327 0 +p m 376 0 +p p 274 0 +r g 247 0 +s b 000 4 +s c 275 0 +s p 000 5 +t b 040 0 +t l 274 0 +T M 264 0 +u l 000 6 +u u 317 0 +v t 000 7 +w t 000 8 +1 e 341 0 +1 h 370 0 +1 q 367 0 +3 e 320 0 +3 q 365 0 +3 s 354 0 +5 e 330 0 +5 s 353 0 +7 e 340 0 diff --git a/tessdata/normproto b/tessdata/normproto new file mode 100755 index 0000000000..0b2cc5fc51 --- /dev/null +++ b/tessdata/normproto @@ -0,0 +1,1179 @@ +4 +linear essential -0.250000 0.750000 +linear non-essential 0.000000 1.000000 +linear essential 0.000000 1.000000 +linear non-essential 0.000000 1.000000 + +0 2 +significant elliptical 288 + 0.318846 0.290495 0.195702 0.142236 + 0.000090 0.000282 0.000033 0.000233 +significant elliptical 348 + 0.345446 0.319894 0.213860 0.149429 + 0.000121 0.000359 0.000076 0.000142 + +1 2 +significant elliptical 392 + 0.304545 0.207693 0.232139 0.099862 + 0.000324 0.000385 0.000122 0.000262 +significant elliptical 244 + 0.350724 0.169451 0.212060 0.081235 + 0.000171 0.000159 0.000121 0.000260 + +2 6 +significant elliptical 153 + 0.310424 0.275265 0.215929 0.131091 + 0.000025 0.000883 0.000047 0.000170 +significant elliptical 26 + 0.294140 0.277187 0.210862 0.133591 + 0.000017 0.000334 0.000022 0.000099 +significant elliptical 69 + 0.296168 0.339358 0.194093 0.146740 + 0.000062 0.000246 0.000012 0.000067 +significant elliptical 24 + 0.362177 0.308165 0.243823 0.149779 + 0.000038 0.001627 0.000131 0.000180 +significant elliptical 200 + 0.325442 0.281881 0.226315 0.134555 + 0.000039 0.000978 0.000046 0.000141 +significant elliptical 158 + 0.341765 0.303741 0.233599 0.139534 + 0.000073 0.001316 0.000058 0.000118 + +3 2 +significant elliptical 283 + 0.311447 0.279358 0.209289 0.130291 + 0.000092 0.001479 0.000105 0.000239 +significant elliptical 352 + 0.340944 0.289556 0.227974 0.133208 + 0.000139 0.001152 0.000056 0.000141 + +4 1 +significant elliptical 636 + 0.306944 0.247093 0.190616 0.136465 + 0.000554 0.001132 0.000190 0.000184 + +5 2 +significant elliptical 329 + 0.321523 0.301852 0.208950 0.133222 + 0.000123 0.001405 0.000063 0.000163 +significant elliptical 307 + 0.350979 0.307068 0.231493 0.141976 + 0.000149 0.000906 0.000102 0.000148 + +6 3 +significant elliptical 91 + 0.281702 0.258208 0.189777 0.127966 + 0.000057 0.000399 0.000021 0.000053 +significant elliptical 313 + 0.314844 0.295526 0.202147 0.136239 + 0.000109 0.000660 0.000066 0.000132 +significant elliptical 229 + 0.345528 0.326737 0.217373 0.144567 + 0.000149 0.000685 0.000066 0.000102 + +7 4 +significant elliptical 149 + 0.431994 0.244414 0.224243 0.129643 + 0.000084 0.000096 0.000064 0.000108 +significant elliptical 224 + 0.402071 0.229203 0.218777 0.130081 + 0.000089 0.000179 0.000061 0.000178 +significant elliptical 183 + 0.371363 0.201152 0.208206 0.113405 + 0.000109 0.000205 0.000032 0.000191 +significant elliptical 71 + 0.376610 0.240061 0.187744 0.130601 + 0.000102 0.000133 0.000025 0.000068 + +8 2 +significant elliptical 235 + 0.310908 0.304590 0.200339 0.133824 + 0.000079 0.000666 0.000030 0.000121 +significant elliptical 391 + 0.338318 0.334376 0.216574 0.142083 + 0.000128 0.000873 0.000119 0.000141 + +9 1 +significant elliptical 640 + 0.346514 0.300717 0.206569 0.138103 + 0.000485 0.001384 0.000125 0.000141 + +A 4 +significant elliptical 60 + 0.295317 0.291135 0.197958 0.173142 + 0.000084 0.001030 0.000062 0.000247 +significant elliptical 309 + 0.269117 0.289609 0.210508 0.172803 + 0.000108 0.002740 0.000081 0.000471 +significant elliptical 96 + 0.223682 0.303950 0.194928 0.167332 + 0.000147 0.000433 0.000070 0.000068 +significant elliptical 171 + 0.262423 0.265798 0.186835 0.160956 + 0.000150 0.000873 0.000062 0.000300 + +B 3 +significant elliptical 237 + 0.331420 0.345272 0.216151 0.156055 + 0.000074 0.002468 0.000084 0.000321 +significant elliptical 272 + 0.306253 0.329640 0.202478 0.147694 + 0.000075 0.001864 0.000036 0.000370 +significant elliptical 130 + 0.356162 0.407484 0.235516 0.176441 + 0.000109 0.001776 0.000108 0.000296 + +C 3 +significant elliptical 273 + 0.362437 0.319370 0.242108 0.185750 + 0.000204 0.000858 0.000152 0.000267 +significant elliptical 47 + 0.399123 0.362039 0.256187 0.206893 + 0.000121 0.000761 0.000050 0.000112 +significant elliptical 313 + 0.320724 0.288399 0.210263 0.165915 + 0.000130 0.001137 0.000093 0.000600 + +D 5 +significant elliptical 99 + 0.351165 0.410284 0.244188 0.209579 + 0.000054 0.000118 0.000019 0.000091 +significant elliptical 20 + 0.357471 0.375001 0.228367 0.200333 + 0.000026 0.000074 0.000012 0.000024 +significant elliptical 34 + 0.374195 0.424893 0.256904 0.216720 + 0.000064 0.000540 0.000107 0.000090 +significant elliptical 270 + 0.331887 0.351728 0.217483 0.183960 + 0.000095 0.001013 0.000089 0.000501 +significant elliptical 216 + 0.307222 0.329480 0.204058 0.169656 + 0.000049 0.000669 0.000050 0.000392 + +E 6 +significant elliptical 263 + 0.336000 0.322877 0.229263 0.148080 + 0.000079 0.004662 0.000052 0.000481 +significant elliptical 58 + 0.356175 0.398808 0.240245 0.170828 + 0.000010 0.006082 0.000019 0.000435 +significant elliptical 25 + 0.368631 0.388224 0.245500 0.168056 + 0.000022 0.007474 0.000023 0.000446 +significant elliptical 16 + 0.359910 0.432941 0.256521 0.179215 + 0.000013 0.001808 0.000007 0.000101 +significant elliptical 196 + 0.314755 0.319304 0.211461 0.141241 + 0.000050 0.005128 0.000056 0.000635 +significant elliptical 48 + 0.300947 0.397229 0.197197 0.157860 + 0.000059 0.001248 0.000026 0.000168 + +F 4 +significant elliptical 35 + 0.389726 0.217505 0.199265 0.122331 + 0.000055 0.000048 0.000034 0.000092 +significant elliptical 280 + 0.372756 0.250254 0.201145 0.136829 + 0.000185 0.002175 0.000081 0.000517 +significant elliptical 104 + 0.334166 0.314517 0.199804 0.152319 + 0.000056 0.000924 0.000054 0.000241 +significant elliptical 193 + 0.409500 0.325012 0.226656 0.166058 + 0.000153 0.002850 0.000152 0.000297 + +G 4 +significant elliptical 126 + 0.334257 0.354859 0.221870 0.187992 + 0.000119 0.000389 0.000128 0.000519 +significant elliptical 301 + 0.307960 0.345713 0.197583 0.168543 + 0.000086 0.001279 0.000056 0.000561 +significant elliptical 21 + 0.331917 0.400075 0.207798 0.195615 + 0.000054 0.000313 0.000069 0.000043 +significant elliptical 191 + 0.360213 0.411850 0.236862 0.205326 + 0.000213 0.000524 0.000067 0.000097 + +H 2 +significant elliptical 438 + 0.323232 0.337900 0.207226 0.181311 + 0.000309 0.002471 0.000150 0.000731 +significant elliptical 128 + 0.359868 0.457742 0.250487 0.217008 + 0.000184 0.000990 0.000103 0.000162 + +I 4 +significant elliptical 446 + 0.322057 0.185283 0.218852 0.088663 + 0.000169 0.001594 0.000193 0.000877 +significant elliptical 53 + 0.356535 0.175547 0.226608 0.085736 + 0.000064 0.000566 0.000062 0.000401 +significant elliptical 116 + 0.356625 0.220825 0.256705 0.107650 + 0.000102 0.000283 0.000047 0.000289 +significant elliptical 22 + 0.384536 0.227324 0.269152 0.121914 + 0.000118 0.000125 0.000083 0.000156 + +J 7 +significant elliptical 17 + 0.389382 0.214380 0.259519 0.109430 + 0.000542 0.001663 0.000481 0.001308 +significant elliptical 78 + 0.273468 0.210507 0.261378 0.101218 + 0.000275 0.001177 0.000190 0.000648 +significant elliptical 86 + 0.308849 0.262402 0.209846 0.160450 + 0.000116 0.000970 0.000057 0.000642 +significant elliptical 68 + 0.322921 0.262029 0.232657 0.143799 + 0.000128 0.001955 0.000117 0.000887 +significant elliptical 75 + 0.336729 0.273457 0.242610 0.149696 + 0.000230 0.002835 0.000046 0.001711 +significant elliptical 207 + 0.281798 0.228827 0.205787 0.138667 + 0.000083 0.001209 0.000031 0.000527 +significant elliptical 42 + 0.255344 0.205205 0.203199 0.127943 + 0.000043 0.000501 0.000064 0.000679 + +K 2 +significant elliptical 133 + 0.361854 0.427578 0.256950 0.199465 + 0.000201 0.002140 0.000086 0.000139 +significant elliptical 502 + 0.324520 0.323561 0.220265 0.164182 + 0.000248 0.003542 0.000152 0.000598 + +L 3 +significant elliptical 155 + 0.292148 0.290278 0.249502 0.166207 + 0.000155 0.001431 0.000094 0.000539 +significant elliptical 22 + 0.233466 0.177916 0.197641 0.099307 + 0.000025 0.000072 0.000041 0.000103 +significant elliptical 461 + 0.264958 0.227055 0.215267 0.126636 + 0.000160 0.002218 0.000086 0.000973 + +M 1 +significant elliptical 633 + 0.305688 0.439220 0.206972 0.227820 + 0.000380 0.007449 0.000287 0.001094 + +N 2 +significant elliptical 483 + 0.325608 0.351061 0.200671 0.184343 + 0.000242 0.002271 0.000134 0.000696 +significant elliptical 134 + 0.377818 0.427549 0.230610 0.226265 + 0.000141 0.001021 0.000102 0.000215 + +O 4 +significant elliptical 66 + 0.331905 0.367717 0.222514 0.206667 + 0.000034 0.000210 0.000014 0.000196 +significant elliptical 153 + 0.324999 0.325504 0.205302 0.176982 + 0.000080 0.000854 0.000030 0.000758 +significant elliptical 174 + 0.306470 0.307732 0.193817 0.168686 + 0.000058 0.000159 0.000024 0.000272 +significant elliptical 247 + 0.356705 0.381356 0.232853 0.209108 + 0.000164 0.000409 0.000073 0.000221 + +P 6 +significant elliptical 197 + 0.391887 0.273217 0.196445 0.156036 + 0.000151 0.000425 0.000128 0.000229 +significant elliptical 147 + 0.404152 0.336509 0.228668 0.174680 + 0.000215 0.000687 0.000110 0.000220 +significant elliptical 108 + 0.365895 0.236510 0.184232 0.133395 + 0.000049 0.000326 0.000052 0.000205 +significant elliptical 116 + 0.351031 0.311655 0.202745 0.162444 + 0.000093 0.000277 0.000031 0.000119 +significant elliptical 22 + 0.349532 0.242767 0.178135 0.132613 + 0.000058 0.000424 0.000030 0.000228 +significant elliptical 36 + 0.323145 0.294749 0.199192 0.154595 + 0.000039 0.000117 0.000025 0.000051 + +Q 6 +significant elliptical 145 + 0.247434 0.376216 0.231609 0.174751 + 0.000216 0.000397 0.000069 0.000192 +significant elliptical 213 + 0.293870 0.442806 0.262442 0.209334 + 0.000227 0.002002 0.000119 0.000208 +significant elliptical 58 + 0.295001 0.375931 0.202084 0.183128 + 0.000126 0.003478 0.000023 0.000870 +significant elliptical 103 + 0.319993 0.349013 0.208729 0.168949 + 0.000087 0.004217 0.000029 0.001194 +significant elliptical 67 + 0.334749 0.356339 0.226434 0.188035 + 0.000094 0.000879 0.000056 0.000605 +significant elliptical 39 + 0.243414 0.507446 0.292352 0.203621 + 0.000073 0.000939 0.000272 0.000084 + +R 2 +significant elliptical 303 + 0.344237 0.370775 0.227930 0.174130 + 0.000192 0.002895 0.000191 0.000347 +significant elliptical 322 + 0.309842 0.323667 0.204315 0.152571 + 0.000165 0.001731 0.000072 0.000426 + +S 3 +significant elliptical 29 + 0.386699 0.367816 0.249920 0.164740 + 0.000063 0.004082 0.000177 0.000462 +significant elliptical 200 + 0.353651 0.340052 0.238898 0.153711 + 0.000095 0.003130 0.000071 0.000414 +significant elliptical 391 + 0.321460 0.306240 0.211646 0.138986 + 0.000204 0.001845 0.000169 0.000394 + +T 2 +significant elliptical 340 + 0.373288 0.252468 0.207323 0.134260 + 0.000417 0.003913 0.000044 0.000906 +significant elliptical 299 + 0.428225 0.283477 0.234075 0.159355 + 0.000242 0.003124 0.000177 0.000464 + +U 5 +significant elliptical 141 + 0.384893 0.393738 0.243227 0.209320 + 0.000157 0.000420 0.000079 0.000157 +significant elliptical 70 + 0.305733 0.286175 0.196826 0.150917 + 0.000043 0.000116 0.000022 0.000388 +significant elliptical 37 + 0.336843 0.308984 0.199727 0.162204 + 0.000015 0.000583 0.000010 0.000310 +significant elliptical 160 + 0.322979 0.313158 0.209934 0.181585 + 0.000053 0.000406 0.000076 0.000700 +significant elliptical 216 + 0.353749 0.344881 0.215413 0.187599 + 0.000103 0.000435 0.000072 0.000270 + +V 8 +significant elliptical 79 + 0.377781 0.266237 0.189623 0.159885 + 0.000013 0.000639 0.000035 0.000476 +significant elliptical 122 + 0.362693 0.245200 0.188026 0.146523 + 0.000034 0.000652 0.000037 0.000655 +significant elliptical 28 + 0.386884 0.249268 0.194369 0.165236 + 0.000031 0.000158 0.000025 0.000401 +significant elliptical 57 + 0.345545 0.241030 0.180924 0.140658 + 0.000027 0.000392 0.000035 0.000626 +significant elliptical 174 + 0.398155 0.286342 0.192975 0.168610 + 0.000071 0.000250 0.000059 0.000128 +significant elliptical 58 + 0.423963 0.289968 0.202626 0.173805 + 0.000077 0.000263 0.000036 0.000093 +significant elliptical 22 + 0.481122 0.330803 0.224107 0.189034 + 0.000090 0.000232 0.000037 0.000083 +significant elliptical 97 + 0.452654 0.317746 0.214176 0.178190 + 0.000079 0.000263 0.000020 0.000087 + +W 4 +significant elliptical 155 + 0.430206 0.491588 0.220413 0.238618 + 0.000399 0.001052 0.000081 0.000289 +significant elliptical 234 + 0.340371 0.371117 0.192385 0.199310 + 0.000130 0.004933 0.000104 0.001401 +significant elliptical 59 + 0.390083 0.452136 0.199475 0.237455 + 0.000039 0.000414 0.000070 0.000759 +significant elliptical 169 + 0.368663 0.429648 0.194138 0.228796 + 0.000040 0.001544 0.000114 0.000934 + +X 4 +significant elliptical 405 + 0.314408 0.309117 0.223767 0.162894 + 0.000126 0.003023 0.000068 0.000620 +significant elliptical 28 + 0.384005 0.399214 0.275533 0.190603 + 0.000055 0.000896 0.000055 0.000214 +significant elliptical 108 + 0.354567 0.402094 0.261455 0.193293 + 0.000099 0.001357 0.000056 0.000130 +significant elliptical 96 + 0.341968 0.319510 0.238352 0.177133 + 0.000064 0.001362 0.000045 0.000257 + +Y 2 +significant elliptical 246 + 0.418942 0.295175 0.236138 0.163411 + 0.000223 0.001797 0.000276 0.000143 +significant elliptical 386 + 0.379923 0.246897 0.205108 0.141586 + 0.000295 0.001904 0.000170 0.000490 + +Z 4 +significant elliptical 272 + 0.334833 0.318358 0.242311 0.158239 + 0.000095 0.002135 0.000069 0.000397 +significant elliptical 73 + 0.360911 0.374730 0.260379 0.178154 + 0.000064 0.001297 0.000090 0.000180 +significant elliptical 199 + 0.312178 0.279416 0.220893 0.134996 + 0.000074 0.002950 0.000055 0.000443 +significant elliptical 86 + 0.303930 0.334053 0.204256 0.151017 + 0.000051 0.000500 0.000012 0.000101 + +a 1 +significant elliptical 637 + 0.233354 0.251547 0.161230 0.142984 + 0.000199 0.001258 0.000069 0.000415 + +& 3 +significant elliptical 262 + 0.315604 0.398104 0.218209 0.179905 + 0.000128 0.006317 0.000091 0.000399 +significant elliptical 226 + 0.284468 0.314591 0.205687 0.153445 + 0.000141 0.003791 0.000067 0.000566 +significant elliptical 137 + 0.273864 0.340313 0.181402 0.174428 + 0.000088 0.002015 0.000030 0.000588 + +* 5 +significant elliptical 103 + 0.450644 0.132755 0.114076 0.116726 + 0.000056 0.000208 0.000148 0.000179 +significant elliptical 71 + 0.419567 0.137041 0.115418 0.116413 + 0.000135 0.000063 0.000045 0.000048 +significant elliptical 100 + 0.518593 0.108533 0.091930 0.087753 + 0.000208 0.000549 0.000054 0.000126 +significant elliptical 225 + 0.481850 0.157880 0.122050 0.123796 + 0.000100 0.000796 0.000122 0.000161 +significant elliptical 127 + 0.511726 0.164415 0.122738 0.124260 + 0.000083 0.001016 0.000118 0.000173 + +@ 7 +significant elliptical 30 + 0.317959 0.429413 0.228978 0.221312 + 0.000042 0.040083 0.000111 0.000471 +significant elliptical 270 + 0.322822 0.458187 0.189195 0.169200 + 0.000074 0.006241 0.000061 0.000130 +significant elliptical 197 + 0.350188 0.501287 0.204643 0.186594 + 0.000103 0.010684 0.000043 0.000177 +significant elliptical 45 + 0.358445 0.322309 0.251813 0.235711 + 0.000426 0.010659 0.000245 0.000482 +significant elliptical 16 + 0.386341 0.633730 0.228163 0.214108 + 0.000060 0.000866 0.000060 0.000148 +significant elliptical 44 + 0.366754 0.585792 0.212737 0.196277 + 0.000048 0.008805 0.000023 0.000047 +significant elliptical 31 + 0.342910 0.662457 0.239315 0.229741 + 0.000088 0.001355 0.000082 0.000204 + +b 2 +significant elliptical 384 + 0.283991 0.288488 0.193843 0.147596 + 0.000125 0.000583 0.000072 0.000294 +significant elliptical 250 + 0.312248 0.309609 0.218169 0.150781 + 0.000157 0.000558 0.000112 0.000353 + +\ 3 +significant elliptical 272 + 0.363305 0.172220 0.226357 0.102810 + 0.000179 0.000063 0.000064 0.000462 +significant elliptical 131 + 0.310157 0.144095 0.193624 0.075187 + 0.000038 0.000016 0.000027 0.000270 +significant elliptical 227 + 0.326673 0.155965 0.211055 0.078384 + 0.000094 0.000082 0.000101 0.000393 + +! 4 +significant elliptical 160 + 0.346216 0.165118 0.225065 0.065635 + 0.000039 0.000066 0.000038 0.000194 +significant elliptical 56 + 0.366786 0.172613 0.235305 0.074989 + 0.000059 0.000106 0.000061 0.000213 +significant elliptical 270 + 0.303881 0.152900 0.199710 0.058633 + 0.000118 0.000101 0.000046 0.000108 +significant elliptical 143 + 0.327683 0.158261 0.210570 0.064868 + 0.000032 0.000087 0.000063 0.000179 + +c 1 +significant elliptical 639 + 0.248247 0.222283 0.160367 0.134508 + 0.000156 0.000444 0.000037 0.000268 + +: 2 +significant elliptical 291 + 0.216809 0.103314 0.152953 0.057817 + 0.000069 0.000357 0.000065 0.000163 +significant elliptical 342 + 0.242681 0.110191 0.173392 0.064314 + 0.000101 0.000263 0.000115 0.000130 + +, 5 +significant elliptical 294 + 0.021151 0.080123 0.095117 0.062497 + 0.000098 0.000099 0.000125 0.000127 +significant elliptical 85 + 0.049466 0.081748 0.095288 0.069758 + 0.000098 0.000072 0.000084 0.000156 +significant elliptical 50 + 0.039684 0.057450 0.066739 0.049777 + 0.000092 0.000030 0.000046 0.000038 +significant elliptical 30 + -0.022895 0.090486 0.103946 0.065098 + 0.000044 0.000134 0.000096 0.000148 +significant elliptical 84 + -0.003030 0.080111 0.094504 0.058128 + 0.000034 0.000113 0.000097 0.000099 + +d 3 +significant elliptical 20 + 0.340370 0.340991 0.236804 0.174992 + 0.000054 0.000985 0.000050 0.000194 +significant elliptical 388 + 0.309519 0.313876 0.211977 0.160371 + 0.000137 0.000976 0.000134 0.000214 +significant elliptical 226 + 0.276437 0.281996 0.188853 0.146486 + 0.000139 0.000463 0.000026 0.000242 + +$ 6 +significant elliptical 43 + 0.399100 0.329827 0.246828 0.136272 + 0.000358 0.004925 0.000106 0.000071 +significant elliptical 193 + 0.342356 0.330095 0.246215 0.134128 + 0.000394 0.005918 0.000258 0.000216 +significant elliptical 203 + 0.329310 0.230339 0.199154 0.113864 + 0.000250 0.001755 0.000096 0.000150 +significant elliptical 40 + 0.321435 0.381088 0.241895 0.138996 + 0.000020 0.001780 0.000009 0.000102 +significant elliptical 113 + 0.310198 0.340346 0.228925 0.136503 + 0.000095 0.003308 0.000039 0.000205 +significant elliptical 48 + 0.296014 0.373516 0.217431 0.137046 + 0.000067 0.003154 0.000055 0.000082 + +. 2 +significant elliptical 21 + 0.124395 0.058629 0.062725 0.061468 + 0.000281 0.000070 0.000078 0.000099 +significant elliptical 618 + 0.078244 0.051716 0.054854 0.054216 + 0.000200 0.000082 0.000086 0.000144 + +e 1 +significant elliptical 625 + 0.244195 0.244558 0.154216 0.132900 + 0.000174 0.000806 0.000051 0.000211 + += 3 +significant elliptical 42 + 0.201548 0.225105 0.114890 0.166300 + 0.000348 0.000820 0.000115 0.000163 +significant elliptical 38 + 0.267091 0.223469 0.126144 0.180550 + 0.000046 0.001845 0.000022 0.000105 +significant elliptical 475 + 0.261742 0.219701 0.100742 0.157173 + 0.000221 0.000493 0.000087 0.000160 + +f 11 +significant elliptical 39 + 0.271675 0.280558 0.303388 0.163255 + 0.000087 0.000163 0.000197 0.000112 +significant elliptical 70 + 0.317847 0.228550 0.213079 0.122138 + 0.000037 0.001338 0.000067 0.000504 +significant elliptical 23 + 0.359566 0.260323 0.239966 0.131342 + 0.000019 0.001076 0.000048 0.000434 +significant elliptical 21 + 0.345946 0.275043 0.236450 0.139564 + 0.000013 0.000988 0.000011 0.000341 +significant elliptical 28 + 0.336082 0.280577 0.227341 0.143115 + 0.000009 0.000355 0.000015 0.000294 +significant elliptical 67 + 0.333286 0.184281 0.204272 0.097052 + 0.000035 0.000661 0.000027 0.000309 +significant elliptical 19 + 0.372940 0.238719 0.235006 0.106585 + 0.000014 0.000192 0.000013 0.000056 +significant elliptical 86 + 0.364220 0.191834 0.215868 0.102239 + 0.000084 0.000308 0.000053 0.000266 +significant elliptical 133 + 0.364672 0.186794 0.194268 0.101954 + 0.000197 0.000203 0.000042 0.000342 +significant elliptical 107 + 0.401069 0.212153 0.234294 0.108049 + 0.000226 0.000617 0.000152 0.000248 +significant elliptical 41 + 0.341583 0.271438 0.283773 0.144464 + 0.000228 0.000219 0.000099 0.000281 + +g 1 +significant elliptical 604 + 0.168422 0.329618 0.210773 0.146473 + 0.000703 0.002074 0.000156 0.000325 + +` 8 +significant elliptical 48 + 0.580671 0.056521 0.059270 0.057491 + 0.000133 0.000072 0.000105 0.000140 +significant elliptical 74 + 0.584962 0.081105 0.093365 0.060219 + 0.000132 0.000051 0.000074 0.000060 +significant elliptical 40 + 0.559296 0.101706 0.116034 0.073287 + 0.000089 0.000026 0.000035 0.000036 +significant elliptical 100 + 0.480698 0.072876 0.090075 0.050723 + 0.000232 0.000047 0.000076 0.000069 +significant elliptical 86 + 0.543431 0.054620 0.061542 0.049971 + 0.000134 0.000064 0.000115 0.000098 +significant elliptical 21 + 0.554305 0.072779 0.084105 0.057668 + 0.000042 0.000008 0.000018 0.000060 +significant elliptical 65 + 0.523189 0.069279 0.082231 0.057370 + 0.000084 0.000019 0.000030 0.000090 +significant elliptical 40 + 0.526960 0.093796 0.108069 0.066044 + 0.000083 0.000069 0.000058 0.000038 + +> 4 +significant elliptical 98 + 0.281632 0.222804 0.157402 0.164141 + 0.000062 0.000102 0.000039 0.000068 +significant elliptical 174 + 0.235604 0.181000 0.131518 0.134828 + 0.000075 0.000162 0.000043 0.000117 +significant elliptical 287 + 0.256385 0.202272 0.143844 0.148978 + 0.000080 0.000140 0.000042 0.000098 +significant elliptical 80 + 0.315982 0.177652 0.193077 0.105489 + 0.000132 0.000029 0.000047 0.000034 + +h 5 +significant elliptical 91 + 0.333944 0.305505 0.231239 0.152469 + 0.000130 0.000664 0.000120 0.000088 +significant elliptical 193 + 0.287168 0.259506 0.188408 0.141111 + 0.000129 0.000192 0.000042 0.000166 +significant elliptical 88 + 0.250656 0.289334 0.190826 0.148177 + 0.000063 0.000507 0.000020 0.000088 +significant elliptical 55 + 0.267381 0.314258 0.204407 0.160264 + 0.000021 0.000419 0.000029 0.000163 +significant elliptical 208 + 0.298389 0.313250 0.218687 0.155580 + 0.000154 0.000721 0.000095 0.000281 + +# 1 +significant elliptical 633 + 0.333119 0.306886 0.205136 0.144635 + 0.000439 0.001878 0.000371 0.000298 + +i 3 +significant elliptical 59 + 0.290579 0.241577 0.228792 0.119944 + 0.000069 0.000074 0.000039 0.000117 +significant elliptical 127 + 0.294744 0.166868 0.199790 0.064340 + 0.000223 0.000268 0.000067 0.000144 +significant elliptical 445 + 0.340434 0.177919 0.213800 0.072450 + 0.000267 0.000610 0.000242 0.000264 + +j 6 +significant elliptical 76 + 0.193618 0.219655 0.249009 0.102132 + 0.000128 0.000096 0.000028 0.000224 +significant elliptical 154 + 0.246919 0.247668 0.283461 0.108393 + 0.000120 0.000708 0.000069 0.000663 +significant elliptical 254 + 0.248374 0.214245 0.253480 0.091694 + 0.000301 0.000779 0.000099 0.000639 +significant elliptical 67 + 0.212235 0.228824 0.276882 0.090111 + 0.000152 0.000165 0.000051 0.000332 +significant elliptical 22 + 0.224574 0.257417 0.305783 0.128845 + 0.000120 0.000105 0.000064 0.000212 +significant elliptical 47 + 0.288986 0.238297 0.286240 0.102478 + 0.000355 0.000745 0.000381 0.000716 + +k 2 +significant elliptical 499 + 0.291232 0.279034 0.205081 0.138877 + 0.000312 0.001797 0.000153 0.000365 +significant elliptical 138 + 0.340772 0.292329 0.235794 0.145799 + 0.000310 0.001498 0.000117 0.000201 + +l 9 +significant elliptical 26 + 0.410063 0.185063 0.254191 0.079622 + 0.000088 0.000083 0.000117 0.000083 +significant elliptical 126 + 0.372518 0.187673 0.243312 0.076234 + 0.000145 0.000315 0.000098 0.000144 +significant elliptical 84 + 0.336687 0.198974 0.241152 0.081915 + 0.000125 0.000511 0.000076 0.000386 +significant elliptical 112 + 0.350419 0.160249 0.212562 0.070170 + 0.000142 0.000056 0.000046 0.000143 +significant elliptical 27 + 0.317483 0.172352 0.221586 0.071099 + 0.000031 0.000084 0.000019 0.000120 +significant elliptical 76 + 0.322225 0.147454 0.203912 0.061106 + 0.000033 0.000028 0.000028 0.000127 +significant elliptical 121 + 0.296051 0.158236 0.205481 0.064890 + 0.000082 0.000177 0.000071 0.000160 +significant elliptical 31 + 0.303626 0.238079 0.244653 0.120279 + 0.000029 0.000169 0.000024 0.000159 +significant elliptical 34 + 0.287216 0.235816 0.235344 0.122200 + 0.000035 0.000038 0.000011 0.000078 + +{ 7 +significant elliptical 29 + 0.398345 0.203188 0.267112 0.090245 + 0.000260 0.000086 0.000102 0.000121 +significant elliptical 173 + 0.291204 0.184886 0.240086 0.073775 + 0.000117 0.000167 0.000142 0.000245 +significant elliptical 81 + 0.264736 0.172055 0.227416 0.063600 + 0.000049 0.000095 0.000057 0.000217 +significant elliptical 62 + 0.302109 0.210209 0.269688 0.093870 + 0.000160 0.000111 0.000139 0.000166 +significant elliptical 86 + 0.342628 0.204155 0.267689 0.088644 + 0.000245 0.000116 0.000143 0.000161 +significant elliptical 71 + 0.334882 0.170782 0.227213 0.066081 + 0.000304 0.000042 0.000043 0.000143 +significant elliptical 137 + 0.263040 0.227921 0.290832 0.095109 + 0.000292 0.000212 0.000184 0.000418 + +< 3 +significant elliptical 335 + 0.250990 0.189502 0.138358 0.140789 + 0.000106 0.000193 0.000067 0.000115 +significant elliptical 227 + 0.279632 0.216093 0.154837 0.159516 + 0.000130 0.000210 0.000067 0.000175 +significant elliptical 77 + 0.333171 0.185913 0.192668 0.114284 + 0.000203 0.000126 0.000065 0.000129 + +( 6 +significant elliptical 165 + 0.290294 0.188119 0.247744 0.082120 + 0.000161 0.000107 0.000184 0.000108 +significant elliptical 108 + 0.260563 0.187189 0.227544 0.094273 + 0.000104 0.000187 0.000065 0.000227 +significant elliptical 149 + 0.332481 0.165451 0.218673 0.076262 + 0.000269 0.000073 0.000102 0.000119 +significant elliptical 90 + 0.336951 0.195401 0.258480 0.086412 + 0.000197 0.000064 0.000058 0.000154 +significant elliptical 97 + 0.389052 0.188168 0.245315 0.088715 + 0.000408 0.000156 0.000272 0.000184 +significant elliptical 22 + 0.461612 0.197903 0.261322 0.089354 + 0.000293 0.000121 0.000166 0.000140 + +[ 2 +significant elliptical 99 + 0.357152 0.203227 0.254760 0.091556 + 0.000384 0.000227 0.000134 0.000335 +significant elliptical 452 + 0.297166 0.210485 0.268712 0.084354 + 0.000607 0.000342 0.000408 0.000318 + +m 5 +significant elliptical 29 + 0.233528 0.296680 0.184001 0.211086 + 0.000130 0.010952 0.000046 0.000869 +significant elliptical 128 + 0.259874 0.373509 0.159451 0.231394 + 0.000025 0.000638 0.000020 0.000287 +significant elliptical 240 + 0.241860 0.359991 0.160016 0.215954 + 0.000044 0.001232 0.000051 0.000746 +significant elliptical 66 + 0.211034 0.409874 0.158716 0.222350 + 0.000031 0.000431 0.000015 0.000190 +significant elliptical 153 + 0.225164 0.372966 0.160210 0.204914 + 0.000015 0.002240 0.000052 0.001039 + +- 5 +significant elliptical 51 + 0.227232 0.062052 0.045462 0.078213 + 0.000038 0.000032 0.000056 0.000048 +significant elliptical 173 + 0.248704 0.061441 0.043531 0.078484 + 0.000052 0.000020 0.000026 0.000037 +significant elliptical 109 + 0.240713 0.076562 0.062603 0.093625 + 0.000063 0.000025 0.000031 0.000052 +significant elliptical 193 + 0.273098 0.076724 0.050509 0.099693 + 0.000092 0.000272 0.000080 0.000589 +significant elliptical 111 + 0.314401 0.068818 0.045223 0.089966 + 0.000256 0.000442 0.000123 0.000891 + +n 1 +significant elliptical 636 + 0.239332 0.257083 0.162513 0.154298 + 0.000227 0.000781 0.000070 0.000200 + +o 2 +significant elliptical 70 + 0.270632 0.254284 0.164075 0.155057 + 0.000034 0.000348 0.000029 0.000202 +significant elliptical 566 + 0.244845 0.245970 0.156272 0.144596 + 0.000082 0.000305 0.000037 0.000233 + +p 3 +significant elliptical 267 + 0.183653 0.316133 0.209733 0.162461 + 0.000151 0.000617 0.000058 0.000225 +significant elliptical 231 + 0.198747 0.277798 0.186358 0.147656 + 0.000119 0.000735 0.000050 0.000306 +significant elliptical 100 + 0.229948 0.292703 0.199403 0.158464 + 0.000177 0.000821 0.000154 0.000273 + +% 5 +significant elliptical 21 + 0.358987 0.307843 0.237889 0.195256 + 0.000074 0.008177 0.000077 0.001547 +significant elliptical 97 + 0.327262 0.261495 0.224972 0.159225 + 0.000164 0.004804 0.000046 0.001582 +significant elliptical 109 + 0.361352 0.545608 0.208876 0.205846 + 0.000118 0.002669 0.000051 0.000185 +significant elliptical 235 + 0.338706 0.467290 0.198261 0.185091 + 0.000067 0.006953 0.000047 0.000602 +significant elliptical 178 + 0.314620 0.434499 0.189620 0.178236 + 0.000085 0.002948 0.000062 0.000493 + ++ 1 +significant elliptical 625 + 0.262680 0.173990 0.135041 0.130994 + 0.000395 0.000232 0.000152 0.000139 + +q 3 +significant elliptical 35 + 0.246861 0.277601 0.193582 0.149681 + 0.000184 0.000990 0.000141 0.000285 +significant elliptical 161 + 0.209216 0.268783 0.186384 0.139683 + 0.000089 0.000608 0.000038 0.000280 +significant elliptical 330 + 0.181605 0.308012 0.205872 0.151479 + 0.000177 0.000623 0.000115 0.000240 + +? 3 +significant elliptical 377 + 0.360372 0.208830 0.205193 0.103750 + 0.000202 0.000373 0.000110 0.000223 +significant elliptical 78 + 0.367927 0.241648 0.174797 0.117292 + 0.000092 0.000058 0.000017 0.000030 +significant elliptical 169 + 0.410893 0.243463 0.224818 0.102440 + 0.000238 0.000520 0.000053 0.000091 + +' 4 +significant elliptical 169 + 0.570494 0.071590 0.090491 0.051550 + 0.000181 0.000125 0.000195 0.000171 +significant elliptical 60 + 0.608460 0.063110 0.079688 0.045543 + 0.000174 0.000056 0.000077 0.000095 +significant elliptical 202 + 0.530821 0.063473 0.077768 0.049788 + 0.000150 0.000166 0.000174 0.000301 +significant elliptical 204 + 0.487237 0.077494 0.094782 0.058817 + 0.000227 0.000037 0.000068 0.000079 + +r 4 +significant elliptical 53 + 0.273045 0.227264 0.185012 0.153642 + 0.000033 0.001142 0.000019 0.000615 +significant elliptical 352 + 0.265015 0.173308 0.164367 0.115196 + 0.000085 0.001569 0.000075 0.000715 +significant elliptical 193 + 0.289524 0.156183 0.162701 0.113876 + 0.000043 0.000392 0.000065 0.000403 +significant elliptical 34 + 0.306912 0.163010 0.169389 0.125540 + 0.000045 0.000221 0.000047 0.000297 + +} 5 +significant elliptical 172 + 0.250847 0.222965 0.283932 0.092085 + 0.000282 0.000237 0.000245 0.000385 +significant elliptical 201 + 0.270419 0.177619 0.232381 0.068713 + 0.000186 0.000158 0.000061 0.000273 +significant elliptical 118 + 0.302050 0.198324 0.259131 0.080625 + 0.000146 0.000076 0.000056 0.000213 +significant elliptical 71 + 0.329161 0.174648 0.228855 0.070712 + 0.000141 0.000035 0.000034 0.000115 +significant elliptical 62 + 0.353735 0.202930 0.263197 0.084049 + 0.000481 0.000958 0.000112 0.000415 + +) 7 +significant elliptical 52 + 0.341344 0.194430 0.259021 0.086611 + 0.000144 0.000060 0.000084 0.000152 +significant elliptical 144 + 0.331478 0.169722 0.225193 0.076676 + 0.000185 0.000054 0.000060 0.000142 +significant elliptical 115 + 0.289713 0.174047 0.230087 0.075603 + 0.000189 0.000083 0.000132 0.000099 +significant elliptical 92 + 0.275550 0.197463 0.260401 0.086004 + 0.000096 0.000090 0.000153 0.000074 +significant elliptical 83 + 0.305625 0.195463 0.257930 0.085290 + 0.000078 0.000051 0.000052 0.000140 +significant elliptical 124 + 0.250793 0.187394 0.227722 0.095209 + 0.000104 0.000124 0.000042 0.000136 +significant elliptical 29 + 0.387880 0.192209 0.256911 0.082400 + 0.000130 0.000063 0.000096 0.000110 + +] 5 +significant elliptical 41 + 0.427015 0.219709 0.280652 0.087408 + 0.000386 0.000148 0.000126 0.000241 +significant elliptical 86 + 0.365446 0.204988 0.260486 0.086787 + 0.000191 0.000184 0.000152 0.000286 +significant elliptical 16 + 0.396721 0.182680 0.237607 0.068525 + 0.000204 0.000279 0.000197 0.000211 +significant elliptical 297 + 0.306760 0.213303 0.270200 0.087420 + 0.000444 0.000221 0.000363 0.000283 +significant elliptical 112 + 0.270863 0.190238 0.244829 0.074670 + 0.000216 0.000159 0.000079 0.000256 + +s 1 +significant elliptical 637 + 0.244088 0.228902 0.161654 0.120423 + 0.000133 0.001159 0.000058 0.000308 + +; 2 +significant elliptical 21 + 0.260456 0.131133 0.194874 0.075360 + 0.000182 0.000390 0.000176 0.000430 +significant elliptical 539 + 0.165605 0.131213 0.197472 0.068188 + 0.000641 0.000441 0.000240 0.000256 + +/ 6 +significant elliptical 85 + 0.267252 0.177119 0.230006 0.110530 + 0.000185 0.000079 0.000077 0.000494 +significant elliptical 220 + 0.342780 0.171268 0.222184 0.107073 + 0.000089 0.000096 0.000056 0.000713 +significant elliptical 28 + 0.314043 0.174559 0.230352 0.102568 + 0.000091 0.000028 0.000076 0.000233 +significant elliptical 73 + 0.316247 0.156535 0.201314 0.103781 + 0.000082 0.000059 0.000034 0.000349 +significant elliptical 82 + 0.322551 0.194536 0.248478 0.129960 + 0.000183 0.000117 0.000298 0.000531 +significant elliptical 128 + 0.378140 0.180570 0.238258 0.105054 + 0.000181 0.000116 0.000138 0.000446 + +t 2 +significant elliptical 583 + 0.291134 0.190937 0.193078 0.100389 + 0.000255 0.000828 0.000088 0.000366 +significant elliptical 57 + 0.339090 0.174326 0.194378 0.084102 + 0.000116 0.000414 0.000141 0.000429 + +~ 2 +significant elliptical 320 + 0.261140 0.116162 0.058461 0.148148 + 0.000519 0.000070 0.000080 0.000103 +significant elliptical 80 + 0.581353 0.086597 0.053718 0.109318 + 0.000745 0.000033 0.000079 0.000052 + +u 3 +significant elliptical 405 + 0.234926 0.241295 0.156408 0.146973 + 0.000069 0.000508 0.000036 0.000156 +significant elliptical 201 + 0.255415 0.263480 0.167510 0.161020 + 0.000073 0.000390 0.000027 0.000149 +significant elliptical 24 + 0.210606 0.219534 0.146129 0.137077 + 0.000018 0.000192 0.000048 0.000186 + +_ 9 +significant elliptical 83 + -0.176219 0.107712 0.037824 0.150783 + 0.000758 0.000075 0.000143 0.000129 +significant elliptical 124 + -0.089273 0.120124 0.050328 0.166633 + 0.000095 0.000170 0.000033 0.000369 +significant elliptical 55 + -0.095009 0.126274 0.033823 0.178707 + 0.000038 0.000188 0.000021 0.000395 +significant elliptical 60 + -0.074176 0.104458 0.033514 0.146840 + 0.000036 0.000068 0.000023 0.000147 +significant elliptical 70 + -0.056322 0.109460 0.037122 0.153553 + 0.000015 0.000079 0.000022 0.000160 +significant elliptical 74 + -0.049055 0.109308 0.045872 0.151519 + 0.000022 0.000102 0.000021 0.000219 +significant elliptical 97 + -0.065350 0.112271 0.044407 0.156304 + 0.000026 0.000295 0.000028 0.000619 +significant elliptical 23 + -0.067418 0.125733 0.064109 0.172536 + 0.000035 0.000325 0.000013 0.000714 +significant elliptical 50 + -0.030036 0.114855 0.047891 0.159488 + 0.000113 0.000180 0.000127 0.000301 + +^ 6 +significant elliptical 79 + 0.558222 0.076427 0.054828 0.090347 + 0.000597 0.000045 0.000079 0.000087 +significant elliptical 34 + 0.501872 0.133686 0.091306 0.135354 + 0.000193 0.000020 0.000050 0.000041 +significant elliptical 39 + 0.506849 0.164867 0.121266 0.132542 + 0.000188 0.000116 0.000086 0.000071 +significant elliptical 108 + 0.472142 0.163577 0.119919 0.129286 + 0.000077 0.000097 0.000045 0.000079 +significant elliptical 94 + 0.430134 0.142877 0.114053 0.115289 + 0.000174 0.000085 0.000047 0.000079 +significant elliptical 40 + 0.367571 0.198585 0.139560 0.135962 + 0.000096 0.000018 0.000020 0.000018 + +v 1 +significant elliptical 630 + 0.292634 0.203086 0.148552 0.134961 + 0.000463 0.000705 0.000077 0.000328 + +w 1 +significant elliptical 634 + 0.272351 0.300050 0.154160 0.193189 + 0.000368 0.002315 0.000122 0.000537 + +x 1 +significant elliptical 634 + 0.241075 0.236781 0.172612 0.148433 + 0.000178 0.001371 0.000093 0.000463 + +y 1 +significant elliptical 525 + 0.209765 0.255096 0.210389 0.144287 + 0.000440 0.001435 0.000201 0.000460 + +z 1 +significant elliptical 616 + 0.237871 0.226094 0.169354 0.129572 + 0.000197 0.000977 0.000087 0.000235 diff --git a/tessdata/pffmtable b/tessdata/pffmtable new file mode 100755 index 0000000000..be615c8b15 --- /dev/null +++ b/tessdata/pffmtable @@ -0,0 +1,92 @@ +! 27 +# 44 +$ 45 +% 46 +& 46 +' 7 +( 28 +) 27 +* 14 ++ 26 +, 10 +- 9 +. 5 +/ 26 +0 53 +1 28 +2 50 +3 47 +4 34 +5 50 +6 46 +7 37 +8 50 +9 47 +: 10 +; 17 +< 29 += 29 +> 31 +? 32 +@ 54 +A 45 +B 52 +C 47 +D 57 +E 49 +F 41 +G 58 +H 56 +I 26 +J 33 +K 51 +L 34 +M 55 +N 53 +O 56 +P 44 +Q 60 +R 53 +S 49 +T 34 +U 55 +V 44 +W 49 +X 47 +Y 38 +Z 46 +[ 35 +\ 26 +] 35 +^ 18 +_ 17 +` 8 +a 39 +b 51 +c 36 +d 51 +e 35 +f 29 +g 56 +h 49 +i 24 +j 34 +k 45 +l 27 +m 59 +n 42 +o 43 +p 50 +q 49 +r 24 +s 35 +t 28 +u 43 +v 33 +w 40 +x 37 +y 41 +z 36 +{ 34 +} 34 +~ 18 diff --git a/tessdata/soptable.cls b/tessdata/soptable.cls new file mode 100755 index 0000000000..833f598e2c --- /dev/null +++ b/tessdata/soptable.cls @@ -0,0 +1,6544 @@ +!1 ! +B 0.000000 20 64.005249 57.521229 60.611259 118.432755 3.812411 33.310555 45.922043 ++ 1.611265 1.986372 11.780315 9.845653 5.785399 1.626407 9.161409 +B 0.000000 18 0.041902 72.882736 60.488358 116.439392 3.261841 31.351467 54.353531 ++ 2.829066 1.779686 4.010518 6.066471 1.268941 0.955935 12.218236 +B 0.000000 1 32.680893 63.734104 35.594570 112.730598 9.530541 30.306316 17.547375 ++ 1.353523 1.307659 7.895417 7.135591 2.537544 1.155637 8.823147 +A 0.000000 180 62.079082 66.383209 96.301903 ++ 4.767420 11.910351 7.809275 +A 0.000000 179 102.330330 119.694664 15.214146 ++ 34.684856 9.821125 6.328176 +L 1.000000 199 29.430170 70.497284 85.841728 116.536598 ++ 2.086412 2.554899 6.711692 15.364473 +L 0.000000 192 96.893311 58.511299 87.570068 111.719582 ++ 2.804138 2.345820 10.066628 18.790897 +L 0.000000 178 60.813358 65.373863 133.653061 102.132111 ++ 6.171493 2.291151 6.796940 16.818260 +L 0.000000 142 126.326439 63.296074 3.189127 94.442337 ++ 6.027892 3.945562 5.317891 13.685312 +L 0.000000 129 94.356041 56.746902 15.788860 95.896057 ++ 6.563445 4.073596 5.726952 16.635715 +L 0.000000 122 30.143476 70.368103 13.499565 93.054291 ++ 5.806498 4.840635 5.033850 13.784767 +L 0.000000 111 61.193565 64.764519 25.967413 97.698395 ++ 6.731588 4.131020 7.098120 15.924765 +L 0.000000 65 123.627510 64.022629 43.112522 84.461197 ++ 6.765898 5.389733 13.039365 15.538884 +L 0.000000 2 105.982399 55.597656 3.981614 72.735725 ++ 5.369670 3.294510 6.185365 14.291052 +L 0.000000 1 122.439980 62.764889 68.031075 70.823334 ++ 5.369670 3.294510 6.185365 14.291052 +S 0.000000 149 94.374069 49.661911 65.803268 72.434273 55.806671 67.238960 ++ 21.097900 21.207048 8.920137 8.324635 14.419332 6.523633 +S 0.000000 33 98.721558 0.506279 70.305038 72.446854 0.756544 121.370300 ++ 14.443996 0.319258 4.360436 3.223060 1.182804 7.701436 +S 0.000000 7 122.199402 0.968744 15.967284 81.553398 10.591908 23.478754 ++ 8.826340 0.861821 8.314072 5.696407 9.063377 6.463427 +S 0.000000 5 98.933891 0.655430 71.797798 73.858223 23.813831 92.240677 ++ 9.926503 0.321238 0.859499 5.266938 4.816579 6.544582 +S 0.000000 4 67.741585 0.578889 75.022469 64.750900 5.043105 81.109215 ++ 4.679764 0.257436 4.285591 0.915272 4.435626 8.844058 +S 0.000000 1 97.192062 27.315863 96.186096 73.626740 51.976124 89.135071 ++ 11.794901 4.593360 4.802827 4.685262 6.263472 7.215427 + +74 1 +3 98f8 +1 8af8 +4 9af8 +3 9ef8 +2 94f8 +2 91f8 +1 9cf8 +1 81f8 +2 9f78 +6 99f8 +2 9df8 +3 97f8 +2 96f8 +3 90f8 +3 9bf8 +3 8bf8 +1 93f8 +1 95f8 +1 86f8 +1 9db9 +2 83f8 +1 8ef8 +27 8ff8 +14 9ff8 +2 8f78 +12 87f8 +2 40ff8 +3 8df8 +1 40ef8 +2 9fb8 +1 97b8 +14 81e3 +1 87fc +1 80e3 +2 81a3 +12 10ff8 +1 816f8 +3 81ff8 +4 107f8 +1 126f8 +3 106f8 +2 10af8 +1 10bf8 +1 85f8 +1 8068 +1 20ff1 +1 87b8 +1 89f8 +1 a7f8 +4 8078 +3 8178 +2 8478 +1 88f8 +1 212f8 +1 8a78 +1 20178 +2 20078 +1 8878 +1 201f8 +1 82f8 +1 8cf8 +1 200f8 +1 101f8 +2 11ef8 +1 103f8 +4 100f8 +1 104f8 +1 8163 +1 10ef8 +1 400e8 +1 8678 +1 daf8 +1 41ff8 +1 100ff8 + +"1 "'` +B 0.000000 53 32.917751 66.893135 136.424866 104.747765 14.502829 35.261982 28.648586 ++ 2.026971 1.710088 4.620895 8.883481 10.482641 3.017868 5.813509 +B 0.000000 49 96.033394 64.949280 92.338272 82.948761 54.349091 35.888378 39.549973 ++ 1.194687 1.874189 4.708661 13.641004 27.168390 11.923002 10.968621 +B 0.000000 1 64.374168 59.960468 108.029213 108.838829 8.104903 28.665337 19.031677 ++ 1.610829 1.792139 4.279703 11.262242 17.951962 7.470435 8.391066 +C 0.000000 2 61.409584 95.827957 7.632182 ++ 2.000000 2.000000 4.000000 +C 0.000000 1 68.037643 112.339325 13.546643 ++ 2.000000 2.000000 4.000000 +A 0.000000 116 63.089207 82.851768 107.243385 ++ 8.142894 10.135328 11.562074 +A 0.000000 4 42.675938 108.137352 110.023766 ++ 1.092904 0.233276 1.731847 +L 0.000000 162 29.969873 73.596947 105.248466 109.739548 ++ 2.456082 4.562919 10.082978 11.632596 +L 0.000000 136 96.373451 56.374901 109.865898 104.738678 ++ 2.870304 3.928957 7.187507 14.587605 +L 0.000000 70 62.992245 65.369034 130.455597 93.288078 ++ 6.843008 2.463189 8.537724 19.246178 +L 0.000000 43 102.262703 63.954960 92.662773 87.398216 ++ 5.150534 6.681442 8.784020 17.696766 +L 0.000000 19 26.234577 63.073391 98.364716 81.468620 ++ 1.261471 1.207681 3.075357 5.945698 +L 0.000000 15 126.251526 64.976913 89.162689 107.552681 ++ 3.204662 1.066590 2.895852 18.090792 +L 0.000000 8 126.511879 65.243004 45.813858 73.548447 ++ 3.067617 1.424386 2.107601 3.418300 +S 0.000000 145 75.311569 60.252693 65.184814 66.081261 61.769352 64.841568 ++ 23.659084 15.606576 7.380055 8.272152 9.452324 9.066730 +S 0.000000 14 109.702713 1.810798 69.139412 77.717117 35.715065 85.079994 ++ 15.578835 2.910484 5.540183 8.195443 5.402968 12.464760 +S 0.000000 2 59.191170 43.175125 32.253544 57.296333 48.607910 39.530525 ++ 16.004377 6.874191 5.332610 6.969996 6.639952 9.380550 +S 0.000000 2 88.636627 77.899170 35.085110 69.907829 73.522491 38.634216 ++ 16.004377 6.874191 5.332610 6.969996 6.639952 9.380550 +S 0.000000 1 21.150835 0.497736 21.644159 53.344753 43.557396 11.072624 ++ 16.004377 6.874191 5.332610 6.969996 6.639952 9.380550 +S 0.000000 1 128.000000 104.640076 64.468620 88.290337 74.419304 56.773701 ++ 16.004377 6.874191 5.332610 6.969996 6.639952 9.380550 + +41 1 +38 41a0 +20 43a0 +1 40a0 +1 44a0 +3 42a0 +5 46a0 +7 66a0 +1 62a0 +2 51a0 +8 53a0 +3 81a0 +1 4420 +2 51a1 +3 51c1 +4 4383 +1 100a4 +10 4183 +1 40a3 +1 438b +1 43a3 +1 41a3 +1 43c2 +1 418b +1 401a0 +1 806a0 +1 100a0 +1 206a0 +2 87a0 +3 47a0 +1 85a0 +2 4120 +5 4583 +3 4083 +10 4d83 +3 4f83 +3 4983 +1 20983 +8 83a0 +1 4783 +2 4c83 +1 4193 + +#1 # +B 1.000000 200 71.780418 35.569164 25.199982 98.619873 16.422028 24.666708 53.385433 ++ 1.332086 5.887717 5.889737 7.602705 4.376776 4.337299 7.097179 +B 1.000000 200 96.013191 56.264626 14.567207 69.115692 65.855446 69.682861 66.196533 ++ 2.536978 4.595855 7.480374 3.311084 27.151493 31.930305 12.339417 +B 1.000000 200 124.097610 92.017067 62.197090 75.739311 35.302826 22.330013 53.798351 ++ 3.051237 3.547547 4.916439 5.491346 8.697236 13.356896 10.210320 +B 1.000000 200 48.448883 50.142044 104.675957 106.164467 12.126544 36.129093 57.959892 ++ 3.093846 2.710504 7.041606 4.671948 2.689128 4.583696 7.475208 +B 1.000000 200 60.235619 38.701756 65.129356 74.662582 37.231163 20.870979 55.688644 ++ 3.074290 3.953526 5.034546 5.584909 9.291034 11.178568 8.716244 +B 1.000000 200 113.087608 80.664711 22.130527 107.092339 11.317513 35.990520 56.016106 ++ 3.117528 2.600755 5.689172 3.234806 1.940226 2.590454 5.810111 +B 1.000000 200 32.513309 74.935883 111.955048 68.792107 66.981415 76.452736 64.452599 ++ 1.092054 3.390979 7.571812 3.473806 21.487085 28.210394 9.699892 +B 0.000000 199 6.916679 95.619423 101.699730 99.971710 15.287798 25.223421 50.652611 ++ 2.224856 5.392128 6.794118 8.895538 4.767903 4.167789 6.794169 +C 0.000000 193 65.553329 63.031071 19.434345 ++ 1.518856 5.060821 6.221398 +S 0.000000 197 65.503014 65.722191 73.537514 65.647865 65.249687 64.209984 ++ 3.898895 4.433608 6.260215 3.353769 4.428040 4.756278 +S 0.000000 3 64.202377 79.646606 94.168541 64.940369 68.038368 80.287842 ++ 4.540500 3.609126 2.858878 2.832558 1.571311 3.284168 + +4 1 +189 3ff +1 37f +3 5ff +7 2ff + +$1 $ +B 1.000000 400 63.757893 52.474613 48.275486 63.924374 35.620052 88.495590 87.255516 ++ 2.219936 4.282555 5.384981 6.722664 22.176849 22.304157 14.926564 +B 1.000000 400 1.415084 77.424759 86.307747 69.373657 30.100054 86.091515 78.988914 ++ 2.087108 5.300478 8.704967 11.018600 18.724031 25.634615 12.595261 +B 0.000000 334 111.848259 72.480453 -2.793824 109.816849 8.466534 32.518211 31.505753 ++ 2.661370 4.274125 4.406165 5.145746 3.421825 2.733102 6.145999 +B 0.000000 327 81.065109 52.040508 -1.640572 108.052460 9.637445 34.361176 31.275534 ++ 2.498991 4.169073 4.278265 5.586042 4.647019 5.424204 9.161527 +B 0.000000 171 18.975332 78.798500 127.152679 109.688889 8.693463 34.857944 24.075378 ++ 2.877942 4.935247 6.340425 7.486902 8.198008 7.337857 6.039721 +B 0.000000 170 45.745937 57.685108 127.893448 111.174980 7.748262 31.834290 25.554502 ++ 2.776468 4.834202 5.681841 6.965476 4.472526 5.249751 6.366097 +C 0.000000 319 72.194366 32.624142 22.274458 ++ 2.051180 3.458103 5.669805 +C 0.000000 160 58.319630 90.358994 17.887806 ++ 2.391423 3.113743 4.276769 +C 0.000000 158 59.305393 114.049187 16.937840 ++ 4.467888 3.971383 4.075617 +L 0.000000 183 53.000160 76.444527 68.518120 80.120384 ++ 2.940598 5.196254 6.948206 14.452828 +L 0.000000 140 117.529259 54.601276 60.076981 79.183418 ++ 3.865152 5.296862 7.631669 13.974800 +L 0.000000 74 54.211468 75.358780 131.081940 73.390182 ++ 2.949212 1.933429 8.916071 5.060909 +L 0.000000 42 76.318916 55.501476 130.279205 74.738655 ++ 2.238478 3.978876 8.665147 7.453161 +L 0.000000 28 117.026497 53.652988 0.359181 79.766960 ++ 2.061982 1.387179 3.935222 6.376854 +L 0.000000 24 13.309243 75.866409 0.782961 75.467133 ++ 1.974284 2.385188 4.751681 6.675047 +L 0.000000 13 66.152496 65.193222 31.508623 70.519150 ++ 5.051671 4.712892 3.648978 4.120340 +L 0.000000 6 121.398460 72.022453 88.314133 68.578773 ++ 0.748798 1.000938 3.514334 2.913505 +L 0.000000 2 4.941011 61.551262 105.120956 65.288330 ++ 2.728772 3.236452 6.001413 7.628430 +S 0.000000 396 66.272125 65.789886 38.040054 68.810516 64.488365 53.091976 ++ 6.015302 14.011927 13.351356 3.074974 6.479433 14.870383 +S 0.000000 4 61.934837 92.047562 95.819405 71.852386 67.840302 109.322723 ++ 1.216926 3.062648 1.083283 1.214267 0.627104 1.475752 + +148 1 +40 4063f +12 4863f +2 6063f +1 4163f +7 4023f +1 4003f +4 5063f +5 4061f +1 40e0f +1 4001f +1 4043f +1 4121f +1 5043f +1 4843f +1 40e2f +1 5141f +6 408ff +3 40aff +1 404ef +4 402df +4 400df +2 412df +5 400ef +12 400cf +2 40eff +9 406ff +2 410df +6 40aef +2 408f7 +1 404ff +1 410cf +4 40eef +3 406ef +6 400ff +5 402cf +3 402ef +1 40ae7 +2 408cf +4 402ff +2 416df +2 426ef +1 426cf +2 406df +6 404cf +2 416cf +1 412ff +1 40ecf +1 422ff +1 406fb +1 400d3 +1 400e7 +1 400d7 +2 404eb +1 404f7 +1 402e7 +1 410d3 +2 404e3 +1 400fb +1 448fb +1 402cb +1 410e3 +1 406e3 +1 404df +1 402f7 +1 444d3 +2 402fb +1 414cb +1 40ccb +1 404db +1 402c7 +1 420df +1 404d7 +1 404fb +1 402d3 +1 406cb +1 408e3 +1 424f7 +1 40cef +1 416c7 +1 40eeb +1 412cf +1 440cb +1 40ac7 +1 422c7 +3 404c7 +2 440c3 +1 420c7 +1 444c3 +1 416cb +1 456cb +1 442c7 +1 420cf +1 400cb +1 404cb +1 406cf +1 404c3 +12 4034f +4 40b4f +38 4014f +3 4014b +2 44143 +4 42347 +2 40143 +1 40343 +1 4074f +1 44b4b +2 40347 +1 4494b +2 42747 +1 44543 +2 40943 +3 42147 +1 4034b +1 42b47 +1 44b43 +1 42f47 +1 4054f +4 4016f +5 4096f +13 4094f +1 4086f +1 40b6f +5 4114f +8 4015f +2 4115b +3 8015f +4 4115f +1 44173 +3 4017f +1 4417b +1 40147 +1 4017b +1 4515b +1 8016f +2 41143 +2 43147 +1 42947 +1 4104f +1 4414b +3 44943 +1 4314f +1 42163 +1 45143 +1 4514b +1 44903 +1 42143 +2 43143 +3 40947 + +%1 % +B 0.000000 60 72.702492 51.724373 71.218567 66.211548 101.636101 0.466845 112.317917 ++ 0.718449 4.829939 11.862422 1.763211 13.053645 0.294665 18.232014 +B 0.000000 60 32.904747 70.762146 138.154938 107.974907 7.413080 33.734741 62.097881 ++ 0.877964 2.977327 2.295249 4.346498 1.601868 1.098072 9.312941 +B 0.000000 15 127.346756 86.669395 54.794147 85.870247 34.121941 43.451653 73.994476 ++ 0.685359 1.343028 4.394920 2.328104 3.838725 7.949286 4.201117 +B 0.000000 15 81.585716 52.267368 33.893913 83.892731 42.533215 35.406456 58.381401 ++ 0.492817 1.270299 4.289475 3.816056 8.415298 7.273956 6.336056 +B 0.000000 10 18.429241 75.289444 92.232033 81.262405 51.694641 42.621468 60.199478 ++ 1.173511 2.323315 5.229602 2.912778 9.938715 12.706806 7.094656 +B 0.000000 9 64.353043 41.949039 69.836815 83.795265 38.311443 34.717419 68.693176 ++ 0.267320 1.893033 4.321119 2.581785 7.220465 8.370083 5.658684 +B 0.000000 2 117.529999 76.662689 84.088394 105.143715 2.771288 30.516354 43.302032 ++ 0.682613 2.260382 6.083143 2.736723 7.160171 5.855890 7.808948 +B 0.000000 2 7.364782 88.888718 80.687195 93.458115 27.219799 15.756346 98.895462 ++ 0.682613 2.260382 5.069286 2.736723 7.160171 5.855890 7.808948 +B 0.000000 1 80.814133 27.007004 61.409775 108.697258 8.017445 36.852703 14.815441 ++ 0.682613 2.260382 5.069286 2.736723 7.160171 5.855890 7.808948 +B 0.000000 1 46.480942 78.591820 57.101963 107.485825 6.065760 29.517086 17.334299 ++ 0.682613 2.260382 5.069286 2.736723 7.160171 5.855890 7.808948 +B 0.000000 1 71.556648 39.788380 47.366936 95.651222 26.321863 20.490765 106.206688 ++ 0.682613 2.260382 5.069286 2.736723 7.160171 5.855890 7.808948 +B 0.000000 1 32.181034 75.095444 53.338779 96.844521 21.516352 32.316708 10.818478 ++ 0.682613 2.260382 5.069286 2.736723 7.160171 5.855890 7.808948 +B 0.000000 1 38.965034 76.384880 86.618095 102.984917 6.302010 36.736439 26.139830 ++ 0.682613 2.260382 5.069286 2.736723 7.160171 5.855890 7.808948 +B 0.000000 1 96.489365 65.346039 28.281099 68.387222 71.945526 90.329887 40.896194 ++ 0.682613 2.260382 5.069286 2.736723 7.160171 5.855890 7.808948 +B 0.000000 1 107.874611 73.372108 64.073677 106.827408 2.606960 29.625576 27.243799 ++ 0.682613 2.260382 5.069286 2.736723 7.160171 5.855890 7.808948 +B 0.000000 1 96.752007 63.538063 15.716076 85.359848 38.157230 37.430893 84.093941 ++ 0.682613 2.260382 5.069286 2.736723 7.160171 5.855890 7.808948 +B 0.000000 1 62.168449 56.435844 75.182060 76.162506 47.485115 0.603288 44.823120 ++ 0.682613 2.260382 5.069286 2.736723 7.160171 5.855890 7.808948 +B 0.000000 1 32.959705 67.172867 102.884949 79.488098 51.964588 61.309872 45.320354 ++ 0.682613 2.260382 5.069286 2.736723 7.160171 5.855890 7.808948 +C 0.000000 196 87.757469 33.484589 36.486294 ++ 9.026154 7.206942 9.776224 +C 0.000000 182 44.059406 99.273140 36.455677 ++ 6.631586 5.675539 10.473967 +A 0.000000 117 39.988430 42.145397 66.998108 ++ 10.192586 8.032516 5.731821 +A 0.000000 2 109.738327 121.881004 34.315750 ++ 10.192586 6.693762 5.731821 +A 0.000000 1 35.431862 121.023369 100.135658 ++ 10.192586 6.693762 5.731821 +L 0.000000 182 20.234632 68.605057 65.265778 123.767220 ++ 4.388373 3.895850 6.239277 21.479467 +L 0.000000 161 86.213554 57.726051 65.248619 122.635735 ++ 3.549580 4.182896 7.194473 11.155860 +L 0.000000 108 76.429947 77.888443 60.170063 85.107697 ++ 12.706413 13.387260 16.813847 20.054466 +L 0.000000 86 122.883698 63.938198 4.389029 108.263260 ++ 6.426983 3.919092 3.627388 18.298521 +L 0.000000 83 56.729519 64.501167 129.201294 111.938850 ++ 5.960134 4.010108 6.789066 15.938410 +L 0.000000 65 30.115345 111.594490 38.178707 77.612328 ++ 3.531445 10.477723 9.383040 10.528790 +L 0.000000 43 125.132202 88.566246 4.280533 80.203934 ++ 5.303766 9.253276 4.086534 20.306862 +L 0.000000 35 125.082077 41.462341 67.607437 76.725449 ++ 6.497273 4.524006 9.998478 10.719562 +L 0.000000 28 62.301895 45.836197 124.638161 81.047539 ++ 4.740659 6.378730 6.332509 15.927987 +L 0.000000 26 30.315647 60.320133 94.605827 74.877930 ++ 4.094895 7.405896 5.537346 9.219893 +L 0.000000 23 95.734032 24.861710 96.412453 74.433220 ++ 3.007564 4.587984 5.816247 7.345078 +L 0.000000 22 92.690964 73.417374 36.689964 80.910927 ++ 7.952679 9.317863 6.236247 16.857399 +L 0.000000 6 73.741623 58.799637 70.449753 116.916435 ++ 1.268464 5.042543 4.938092 14.871120 +L 0.000000 2 25.375757 81.037315 91.776314 76.199600 ++ 4.929045 6.006733 6.986472 12.586694 +L 0.000000 1 7.316662 97.741066 25.011549 81.612129 ++ 4.929045 6.006733 6.986472 12.586694 +L 0.000000 1 88.546700 48.906712 35.000549 77.729248 ++ 4.929045 6.006733 6.986472 12.586694 +S 0.000000 111 65.962334 40.677032 77.330132 64.679764 48.131207 75.048538 ++ 11.875165 32.061302 10.027500 3.720654 22.930979 17.972803 +S 0.000000 59 128.000000 8.066235 69.962517 81.812561 58.797569 84.243614 ++ 0.001000 9.442021 6.254001 3.353652 3.404517 4.150948 +S 0.000000 8 52.404823 125.686325 35.861328 57.546665 75.666473 50.091816 ++ 10.056108 6.121408 3.724666 2.705703 3.258295 3.452263 +S 0.000000 7 66.871925 59.215614 24.827606 64.603767 63.085159 0.907635 ++ 2.695360 3.766287 3.780344 1.565666 2.280106 0.806101 +S 0.000000 6 68.013634 69.831207 128.000000 64.665993 65.095947 128.000000 ++ 7.134042 16.306585 0.001000 1.603715 5.286144 0.001000 +S 0.000000 2 86.573776 18.746975 39.168121 72.139366 54.487000 55.713314 ++ 6.352335 13.539521 4.757502 2.589878 7.432008 4.178285 +S 0.000000 2 0.397388 105.180603 49.031040 55.588631 68.235924 60.861103 ++ 6.352335 13.539521 4.757502 2.589878 7.432008 4.178285 +S 0.000000 1 65.008240 0.214330 28.608702 67.438713 57.457230 37.967861 ++ 6.352335 13.539521 4.757502 2.589878 7.432008 4.178285 +S 0.000000 1 128.000000 20.772209 47.526871 67.017273 56.456413 61.294243 ++ 6.352335 13.539521 4.757502 2.589878 7.432008 4.178285 +S 0.000000 1 128.000000 3.836183 0.203989 80.281288 16.380184 22.980471 ++ 6.352335 13.539521 4.757502 2.589878 7.432008 4.178285 +S 0.000000 1 31.726650 128.000000 94.831177 54.212475 95.668846 122.816071 ++ 6.352335 13.539521 4.757502 2.589878 7.432008 4.178285 + +100 2 +1 469c0000 80 +7 29c0000 80 +3 29c0000 800 +1 129c1800 88 +1 28c0030 1004 +1 28d0010 1004 +1 429c0000 88 +4 20c000c 200 +1 420c000c 201 +1 a9c0000 81 +1 559c0000 80 +2 29c0000 88 +1 29c4000 88 +2 c003c 400 +1 90c000c 202 +5 20c003c 400 +1 a0c000c 200 +1 28c0030 4000 +1 420c000c 200 +1 29c0000 8c +1 29c0000 81 +1 429c0000 80 +14 d9c0000 80 +1 d9c0000 81 +1 119c0000 802 +1 1940000 80 +1 2d9c0000 80 +4 9940000 80 +1 5940000 80 +1 259c0000 80 +1 11940000 800 +1 1f9c0000 81 +1 6f9c0000 82 +3 4d9c0000 80 +1 859c0000 82 +6 1d9c0000 80 +3 d940000 80 +1 459c0000 80 +3 85940000 80 +1 bbb80000 a6 +1 df9c0200 85 +1 f59c0000 83 +2 59c0000 80 +2 ff9c0000 80 +1 8f9c0000 81 +1 ff9c0000 83 +1 ad9c0000 80 +1 dd9c0000 83 +1 ef9c0000 80 +1 2f9c0000 82 +2 2f9c0000 83 +1 cf9c0000 84 +3 f9c0000 80 +1 1f9c0000 84 +1 5d9c0000 80 +2 f940000 80 +1 a99c0000 82 +2 99c0000 80 +1 6d9c0000 82 +1 1940000 800 +1 ad9c0000 83 +1 1f9c0000 80 +1 6f9c0000 85 +1 3f9c0000 81 +1 8db80000 82 +1 cfd40000 87 +1 cf9c0000 80 +1 3f9c0000 83 +1 9f9c0000 84 +1 f79c0000 85 +1 4f9c0000 80 +1 c10c8080 2013 +1 5d8c0040 82 +1 7d9c0000 81 +1 ef9c0000 84 +1 228e0400 8044 +1 7f9c0000 81 +1 10c2080 2011 +1 f79c0000 20007 +1 e99c0000 80 +1 2d9c0000 83 +2 8d9c0000 80 +1 d9c0000 82 +1 6d9c0000 80 +1 d9c0100 80 +1 4d9c0000 82 +1 79c0000 80 +10 118c0003 100 +7 338c0003 100 +7 18c0003 100 +13 138c0003 100 +2 138c0003 104 +1 218c0003 10000 +9 38c0003 100 +1 1880003 100 +1 38c0043 100 +3 238c0003 104 +1 318c0003 100 +3 338c0003 104 +2 38c0003 101 + +&1 & +B 1.000000 400 1.769900 95.378227 37.913578 99.394669 15.483581 23.088934 66.032753 ++ 2.179321 4.034113 6.395760 11.999109 7.517179 11.208960 21.944414 +B 1.000000 400 57.928349 39.938034 81.293625 107.386726 11.648932 32.817337 54.824482 ++ 3.105267 4.943439 8.339809 5.293695 4.624905 3.907837 12.078005 +B 0.000000 399 97.054329 75.280838 3.959608 108.362785 9.070305 32.798832 45.377728 ++ 0.976586 4.337253 3.011638 4.847668 2.844659 3.038387 11.538806 +B 0.000000 383 10.316792 77.891098 76.643471 62.211922 41.388752 11.371058 75.004021 ++ 3.908411 3.942716 5.053528 10.974621 23.871981 13.649494 15.038361 +B 0.000000 17 16.859518 91.823341 107.455650 101.808769 16.310375 33.526371 62.511761 ++ 0.662504 1.389329 2.411850 3.892734 3.167619 2.604382 5.138044 +C 1.000000 400 54.172848 38.117916 40.929733 ++ 4.406622 3.418796 10.910159 +C 0.000000 320 63.673759 110.392273 26.816572 ++ 4.601273 5.546567 4.523160 +C 0.000000 17 75.993713 75.387497 38.858311 ++ 1.256557 2.698415 1.385175 +L 0.000000 20 3.037512 60.160683 4.730303 70.116608 ++ 0.835668 5.264192 2.563785 5.864570 +L 0.000000 20 42.288597 65.761841 72.429314 67.937424 ++ 1.018120 1.089069 1.925194 2.094218 +L 0.000000 15 21.444195 96.072189 62.283264 66.809654 ++ 0.332007 2.554489 2.341492 0.746965 +L 0.000000 3 60.686195 67.645905 113.595055 68.236481 ++ 0.083948 1.119920 2.549423 2.176132 +L 0.000000 2 78.250885 38.496170 76.896629 68.050964 ++ 0.567436 2.506917 2.344974 2.720471 +S 0.000000 223 20.748497 31.176178 54.890240 65.923859 59.787624 51.210503 ++ 10.120691 11.324469 6.165760 3.107170 4.377853 5.101822 +S 0.000000 135 22.990271 39.485134 79.867393 59.407043 61.065636 68.786835 ++ 6.095737 17.737057 4.251659 4.909535 2.060074 9.279860 +S 0.000000 40 1.764504 59.089230 98.021378 58.959236 57.680214 98.529312 ++ 2.244428 3.604089 3.526631 1.880227 1.648564 4.344600 +S 0.000000 2 25.291315 50.749039 22.820040 67.028000 72.188347 23.361437 ++ 6.153618 10.888538 4.648016 3.298977 2.695497 6.242094 + +18 1 +49 202f +19 222f +1 232f +6 212f +3 282f +1 1002f +1 1012f +145 206f +106 406f +1 416b +2 416f +8 446f +8 816f +30 806f +2 906f +11 40f7 +6 44f7 +1 456f + +'1 '" +B 0.000000 135 65.247971 58.984913 106.501114 106.699768 11.713119 33.368660 36.245907 ++ 3.235197 1.618732 5.036223 6.697288 5.193398 6.732397 8.668591 +A 0.000000 77 52.479107 81.915787 102.097420 ++ 8.490204 7.126872 15.403316 +L 0.000000 186 26.125938 71.638252 104.521606 92.364746 ++ 4.765816 2.212979 14.773415 19.723982 +L 0.000000 174 105.846901 59.714100 104.787773 91.608986 ++ 13.339334 4.265795 26.808899 18.622189 +L 0.000000 132 60.825531 64.811050 131.604050 89.091187 ++ 6.399921 2.638608 9.153120 17.590307 +L 0.000000 16 88.976730 56.398670 96.590385 118.472427 ++ 1.185636 2.028556 13.337788 6.296843 +L 0.000000 9 8.066439 65.458908 88.618225 78.221825 ++ 6.516279 1.751802 6.452028 17.894356 +L 0.000000 6 103.045128 58.783184 116.790573 83.058632 ++ 4.596146 1.275193 3.645932 8.053367 +L 0.000000 5 32.073074 74.856552 117.865089 76.049187 ++ 1.388092 1.327382 1.408385 8.323574 +S 1.000000 204 94.786659 60.010098 57.117470 68.364891 61.000404 62.124905 ++ 18.324696 29.844168 25.286516 5.546808 22.398418 37.328625 + +23 1 +34 21e +16 23e +13 20e +1 21a +15 215 +2 21f +44 21d +2 31d +6 25d +7 205 +3 219 +6 29d +2 217 +2 20a +2 207 +1 25e +2 20f +2 24e +29 20d +9 209 +3 201 +2 30d +1 305 + +(1 (` +B 1.000000 207 127.661018 72.829208 51.959213 112.804817 6.130986 32.442429 121.919624 ++ 1.610764 2.977527 3.592935 3.831924 1.156854 0.663137 8.824937 +B 0.000000 2 48.280495 78.062439 140.000000 106.165024 11.284923 27.318691 12.666225 ++ 1.610764 2.977527 3.592935 3.831924 1.156854 0.460512 8.824937 +L 0.000000 26 23.805696 65.150475 95.034645 71.619804 ++ 0.898691 2.665612 4.943768 4.240093 +L 0.000000 15 33.070206 67.100319 50.715790 70.187309 ++ 2.722514 1.368528 16.531031 3.323933 +L 0.000000 12 78.635033 68.227249 127.225632 87.563545 ++ 4.818888 3.795030 14.196296 16.260994 +L 0.000000 10 113.779884 67.448532 -19.922207 84.292877 ++ 1.479203 3.359662 6.301350 14.948366 +S 0.000000 167 62.085178 21.178179 65.195663 64.606056 53.942150 65.352150 ++ 10.308704 21.997496 8.283433 5.463855 4.977063 16.776699 +S 0.000000 40 78.962646 0.464763 65.510384 67.336929 14.447095 72.537300 ++ 8.494372 0.304111 9.198198 3.921777 8.019630 31.767397 + +14 1 +134 41 +9 51 +8 61 +12 49 +22 85 +14 81 +1 95 +1 8d +1 45 +1 71 +1 59 +1 69 +1 87 +1 83 + +)1 ) +B 1.000000 207 63.762260 57.604500 52.623043 112.666443 6.183340 32.486603 120.901871 ++ 1.612627 3.243293 4.180657 4.219477 1.183234 0.811905 8.802439 +L 0.000000 23 98.604706 62.478001 68.761559 75.091301 ++ 3.115773 2.673977 18.056223 6.013084 +L 0.000000 23 87.651207 64.936249 10.262928 71.922371 ++ 0.536370 1.189086 2.808139 3.890592 +L 0.000000 21 14.373419 63.898582 -16.893942 84.694557 ++ 3.784270 5.341216 9.897423 15.356453 +L 0.000000 15 51.706387 61.206699 129.819855 87.813927 ++ 2.356802 2.315979 7.231856 15.067945 +S 1.000000 207 65.491631 108.608017 64.879562 64.253960 80.691277 65.176537 ++ 13.865183 21.006952 7.833254 4.324200 20.876392 24.352365 + +9 1 +137 21 +16 23 +10 31 +12 29 +20 25 +3 2d +4 2b +2 39 +3 33 + +*1 * +B 0.000000 197 67.387177 45.696438 88.635506 97.004318 20.549053 33.132118 36.271816 ++ 2.722196 2.931146 15.932130 7.847195 11.146944 13.468978 6.325923 +B 0.000000 184 44.280064 54.656631 119.848877 98.818619 18.155893 31.695602 36.463184 ++ 2.653637 2.034827 11.633392 13.451281 7.370694 8.055589 11.522698 +B 0.000000 173 125.379883 84.124199 91.289009 97.210594 20.685204 34.977505 35.632927 ++ 2.863267 2.588150 13.114137 10.616257 12.417014 11.713616 6.057251 +B 0.000000 166 19.968300 76.478630 116.541260 96.327660 21.266388 33.956692 33.908615 ++ 1.957619 1.972492 10.958135 9.039450 10.408520 14.143635 7.068118 +B 0.000000 120 96.028809 64.777550 56.809639 97.577972 19.505840 34.199673 41.028206 ++ 1.023405 1.441266 13.605963 6.438075 5.832481 4.554849 5.494289 +B 0.000000 60 107.427208 75.389816 73.948494 95.294060 24.113192 31.709957 35.988144 ++ 1.519182 1.322400 7.171587 9.113683 14.153857 4.916488 4.654840 +B 0.000000 60 85.823761 55.067036 76.275742 85.604294 34.662750 37.810883 35.701252 ++ 1.437839 2.220204 6.302518 10.352435 24.151270 9.608008 7.988138 +B 0.000000 24 124.911240 87.507545 59.451660 97.861870 18.878767 37.519672 34.618595 ++ 1.066783 1.334939 1.759257 7.002640 5.723701 7.855723 8.210592 +B 0.000000 3 22.766794 74.316971 122.632271 65.351959 107.274185 73.769585 34.252087 ++ 0.136994 1.063279 1.758473 3.958699 14.656945 4.984944 5.457543 +B 0.000000 3 22.687609 73.639328 119.621468 67.043289 92.879005 23.810131 30.672483 ++ 0.604961 0.628315 0.339778 1.279761 14.641242 16.708050 4.241342 +C 0.000000 1 56.873287 114.662773 6.558966 ++ 2.000000 2.000000 4.000000 +C 0.000000 1 73.947166 74.505127 7.465426 ++ 2.000000 2.000000 4.000000 +A 0.000000 8 124.073715 121.345634 108.606827 ++ 17.813745 3.605340 3.830722 +L 0.000000 18 56.167110 74.523872 133.978668 81.873886 ++ 2.344275 1.851446 5.209478 3.602555 +L 0.000000 15 119.910522 56.345692 80.701569 80.598633 ++ 1.188395 1.929232 6.981928 4.971448 +L 0.000000 7 10.365947 74.861862 74.924606 76.484528 ++ 1.435173 1.735930 10.627545 4.782844 +L 0.000000 5 73.074036 56.075787 134.988297 73.706963 ++ 2.670759 1.247813 1.901062 5.175903 +L 0.000000 3 31.420795 85.954109 104.765091 78.141846 ++ 4.081337 1.993535 9.827012 6.905942 +L 0.000000 2 102.733841 48.978134 88.633163 94.990997 ++ 2.343988 1.751591 6.909405 5.087739 +L 0.000000 1 94.052376 46.550930 116.915222 75.591461 ++ 2.343988 1.751591 6.909405 5.087739 +S 1.000000 200 63.138992 65.161842 63.020428 56.866207 63.800228 63.367786 ++ 10.719078 12.742775 3.768673 10.800917 3.156570 5.393810 + +35 1 +90 10001f +24 10009b +1 12001b +2 102017 +1 14001e +1 142016 +1 100017 +46 10006f +1 10500d +2 10804f +1 100165 +1 102067 +2 100067 +3 100267 +1 10ac47 +1 100167 +1 110165 +1 10402f +2 106005 +1 106025 +1 1a7002 +1 102007 +1 10b007 +1 108007 +1 114005 +1 125003 +2 107005 +2 115005 +1 104005 +1 102065 +1 106007 +1 10a005 +1 110025 +1 106027 +1 10a045 + ++1 + +B 1.000000 200 79.420723 50.361393 38.278347 101.084679 16.603939 31.154814 66.691521 ++ 0.825323 2.120786 5.642311 3.417524 1.945465 2.251870 7.405800 +B 1.000000 200 49.312176 52.702477 83.251358 100.502892 17.286957 34.134171 66.358788 ++ 0.731159 1.975188 7.749459 2.872129 1.788612 2.906186 5.585153 +B 1.000000 200 15.295288 80.416557 79.486877 100.869438 16.713589 30.696753 67.042282 ++ 1.164947 2.165697 7.450608 3.856524 3.784103 2.375234 5.485160 +B 1.000000 200 113.257996 77.840324 34.014549 101.093880 16.784124 33.721828 66.197861 ++ 0.832649 2.033843 6.479857 3.167571 2.319700 2.015274 5.518560 +L 0.000000 1 43.820614 64.138008 87.176353 85.582741 ++ 3.000000 8.000000 8.000000 32.000000 +S 0.000000 198 63.283543 65.529419 55.045242 64.863281 64.558685 73.189270 ++ 24.045494 26.776758 5.377308 6.580789 4.425867 6.560078 +S 0.000000 2 61.184067 76.152260 35.144226 61.289265 62.826065 19.254808 ++ 11.596011 12.913173 5.377308 3.808326 3.073519 5.466732 + +3 1 +198 2f +1 5f +1 4f + +,1 , +B 0.000000 142 64.537949 59.096947 -3.658900 105.840141 12.562458 33.706047 37.409039 ++ 2.714293 1.607178 4.293151 13.683870 8.709389 7.072926 7.817279 +B 0.000000 1 113.992928 69.962807 0.768940 108.756432 3.814529 34.098042 22.305206 ++ 2.714293 1.607178 4.293151 7.918906 6.048186 4.911754 7.817279 +A 0.000000 125 49.407085 74.164825 5.141765 ++ 6.535633 11.095516 3.179369 +L 0.000000 228 25.412725 70.749672 0.554370 99.435364 ++ 4.417593 2.408163 6.149816 21.227579 +L 0.000000 225 96.530083 59.102531 3.629136 101.537605 ++ 9.205162 2.401191 8.843036 20.993820 +L 0.000000 188 62.123108 64.402573 28.505396 95.491051 ++ 6.796550 2.383424 6.644981 20.741386 +L 0.000000 59 5.194554 65.449379 -22.086563 85.784683 ++ 8.663555 2.083616 3.844467 18.441389 +L 0.000000 8 113.863861 60.599754 4.341672 91.890556 ++ 6.879754 1.184599 3.772843 11.564111 +S 1.000000 259 93.803520 52.005608 62.844429 69.064636 56.915226 69.157455 ++ 16.745472 31.561787 29.761110 6.447505 25.979591 31.268833 + +28 1 +75 13c +26 17c +9 11c +40 139 +3 13d +21 119 +1 149 +12 129 +8 109 +11 179 +3 12d +5 131 +2 169 +6 15c +2 11d +3 1b1 +9 111 +1 191 +2 1b9 +3 141 +3 151 +3 101 +2 121 +2 171 +4 159 +1 1f9 +1 199 +1 13e + +-1 - +B 0.000000 1 96.400230 64.115753 43.774364 121.437874 3.582798 31.939747 30.086756 ++ 2.000000 4.000000 4.000000 20.000000 20.000000 20.000000 8.000000 +A 0.000000 202 0.125693 80.991966 53.263374 ++ 3.141706 13.854100 4.321684 +L 0.000000 202 63.917660 65.627724 63.863136 107.457657 ++ 1.570510 4.341974 5.715289 11.228134 +L 0.000000 200 127.967125 65.172356 42.768833 108.756378 ++ 1.884227 2.644419 4.181104 14.002970 +L 0.000000 99 32.771687 84.051315 52.798584 90.063759 ++ 6.483339 5.640245 4.458272 14.570005 +L 0.000000 98 94.823624 46.377815 54.377426 92.719589 ++ 7.114461 6.087997 5.021265 16.996984 +S 0.000000 191 63.226185 64.855141 59.187042 63.489136 64.750725 58.063244 ++ 16.983227 11.303350 11.813718 11.377839 4.777973 13.292861 +S 0.000000 4 3.314805 64.506378 64.428581 11.091066 64.579689 64.367020 ++ 2.821479 0.270443 0.192021 10.767456 0.220723 0.215950 +S 0.000000 3 11.065296 38.296429 25.602531 40.570492 57.727207 32.493282 ++ 2.497861 0.351785 0.952618 0.307171 0.861278 0.234211 +S 0.000000 2 0.703657 50.752747 41.357773 40.505859 67.343979 48.723228 ++ 6.490676 2.823925 3.663135 6.852053 1.466679 3.842515 +S 0.000000 1 108.527481 27.510113 93.902878 88.461113 49.896519 96.292877 ++ 6.490676 2.823925 3.663135 6.852053 1.466679 3.842515 +S 0.000000 1 114.546806 86.076355 18.997795 88.990921 68.418282 24.563784 ++ 6.490676 2.823925 3.663135 6.852053 1.466679 3.842515 +S 0.000000 1 63.028595 67.593918 0.223784 64.690948 67.333549 0.588501 ++ 6.490676 2.823925 3.663135 6.852053 1.466679 3.842515 + +15 1 +69 4e +70 7e +24 6e +23 5e +3 10e +1 41e +2 be +1 56 +2 46 +2 22e +1 4d +2 8e +1 81e +1 5a +1 100e + +.1 . +A 1.000000 248 14.898916 121.463341 15.306340 ++ 34.718567 7.221146 3.595671 +L 0.000000 122 63.856510 65.574883 27.846304 89.175240 ++ 6.469222 2.020466 5.998548 16.509554 +L 0.000000 120 94.255737 57.413151 16.607862 87.967140 ++ 7.439978 2.162988 5.225257 14.483957 +L 0.000000 118 30.551205 73.188019 14.042198 90.145920 ++ 4.649730 2.463299 4.820208 16.458424 +L 0.000000 98 127.327705 65.309685 2.721646 86.609535 ++ 4.592943 2.814596 4.322944 16.416399 +L 0.000000 3 46.655781 71.624283 25.698103 92.105896 ++ 2.217208 2.826505 4.369421 1.406807 +S 0.000000 231 62.761314 64.775948 62.404198 63.366348 64.680511 61.270115 ++ 18.909386 15.890218 8.895386 9.908590 10.759688 11.296729 +S 0.000000 9 58.862164 67.858154 95.861267 61.442425 64.476006 94.572762 ++ 9.943202 8.888189 4.804637 4.711574 7.314241 2.880430 +S 0.000000 5 24.603836 63.491150 66.063332 35.147682 64.356163 66.729347 ++ 10.145714 2.388282 3.032114 7.478976 0.156762 4.427326 +S 0.000000 3 71.147369 14.400313 69.013145 68.612961 42.824783 73.044594 ++ 17.937428 9.582438 2.986989 2.782108 6.545814 6.134344 + +29 1 +3 53 +5 57 +17 4b +46 41 +13 51 +23 43 +8 49 +9 55 +10 5d +42 5f +1 75 +7 4f +4 5b +12 45 +8 47 +7 59 +14 4d +3 85 +2 83 +1 8b +2 20f +1 81 +1 89 +4 10f +1 11f +1 71 +1 213 +1 77 +1 87 + +/1 / +A 1.000000 200 46.726498 36.555374 50.615284 ++ 3.643897 6.017420 6.606476 +L 1.000000 200 87.524010 60.023106 53.705910 123.758850 ++ 1.843281 1.837095 21.668907 25.640997 +L 1.000000 200 23.609364 69.914833 48.368210 124.081551 ++ 1.819703 1.904038 9.955535 8.943349 +L 0.000000 193 120.575371 64.500977 -35.775707 118.323135 ++ 7.415007 1.720142 4.060833 24.168186 +L 0.000000 189 56.999939 65.505699 135.239639 115.056000 ++ 6.290235 2.497538 8.493941 23.560547 +S 0.000000 189 61.996330 56.567226 74.258179 63.642582 57.850513 71.056778 ++ 12.666356 31.963678 9.305552 3.134483 27.084574 9.304347 +S 0.000000 5 64.687454 128.000000 69.557396 62.322525 115.519501 65.994812 ++ 4.746367 0.001000 3.965414 2.436738 6.064809 2.662883 +S 0.000000 3 68.874763 74.018082 128.000000 63.818039 66.322334 128.000000 ++ 2.075386 15.279862 0.001000 1.330986 7.323642 0.001000 +S 0.000000 2 90.852852 1.206486 91.200638 71.112251 30.973810 106.534821 ++ 5.792350 13.972421 3.907014 2.300736 10.732393 3.041745 +S 0.000000 1 98.342796 0.920138 64.596664 74.520119 37.497040 87.506798 ++ 5.792350 13.972421 3.907014 2.300736 10.732393 3.041745 + +8 1 +181 3f +6 2f +3 5f +3 87 +1 21f +2 117 +2 37 +2 4f + +01 0o +C 1.000000 204 65.443657 64.911972 71.789330 ++ 1.096369 5.245805 9.905252 +L 0.000000 20 30.490431 91.634338 62.440331 69.652344 ++ 3.185317 2.726466 10.379773 6.924863 +L 0.000000 18 92.943909 39.309948 77.540466 67.992142 ++ 4.082936 2.483702 13.658937 3.445555 +L 0.000000 10 127.274796 63.865803 3.404732 70.951233 ++ 5.551590 4.390501 4.133164 4.823240 +L 0.000000 4 62.942997 66.577850 125.731682 66.773018 ++ 2.243795 2.926396 7.170950 1.378849 +S 0.000000 203 64.345139 64.574554 64.020416 64.363922 64.732986 62.440926 ++ 3.118469 6.252738 3.150025 2.104194 3.647523 3.640234 +S 0.000000 1 57.540237 71.876587 75.957611 62.131870 68.675461 74.406708 ++ 2.598724 3.015401 2.187517 1.753495 2.110835 2.772265 + +10 1 +160 21 +7 29 +15 23 +2 31 +1 45 +11 25 +1 39 +2 2d +4 27 +1 33 + +11 1l +B 1.000000 204 66.009773 54.850014 62.488228 87.045807 9.637583 27.073484 97.987251 ++ 3.460301 4.788383 4.892464 15.622623 2.704778 4.435328 19.271038 +B 0.000000 123 3.684168 79.359367 53.802174 105.798111 7.401834 27.509636 86.313629 ++ 3.036848 1.807917 5.475383 7.193706 2.931496 2.614546 12.829051 +B 0.000000 48 44.364120 62.451279 111.403694 111.743088 8.449205 33.429287 29.073931 ++ 1.440689 7.471102 5.909161 5.779646 1.917046 4.484986 7.850305 +B 0.000000 2 96.157211 67.321114 2.168291 113.146881 3.113133 33.476044 37.309235 ++ 2.645946 3.199589 4.539704 8.320694 2.517774 3.215117 11.759915 +L 0.000000 201 31.679005 76.712654 70.626640 114.310295 ++ 2.098028 3.443281 11.537759 18.029325 +L 0.000000 198 95.860733 61.233936 56.999432 90.869835 ++ 2.163868 4.133218 12.789112 12.205950 +L 0.000000 124 0.748730 66.422340 2.555645 112.395683 ++ 1.947150 3.117495 2.102409 21.459122 +L 0.000000 111 72.457870 61.249180 127.508110 94.406784 ++ 2.542600 3.245677 9.448014 19.455441 +L 0.000000 14 119.812012 59.026188 89.208794 83.693092 ++ 1.879756 0.307067 3.373134 2.705875 +S 0.000000 97 51.275787 60.944565 53.611416 56.034935 59.219772 51.701962 ++ 9.063824 24.109196 8.927049 2.997615 7.581006 12.949498 +S 0.000000 59 87.104431 128.000000 33.753597 65.703407 82.515755 9.393921 ++ 6.045363 0.001000 5.146061 1.896561 4.561212 10.770341 +S 0.000000 26 56.225433 127.885826 58.787403 59.378189 80.154839 65.735199 ++ 6.106666 0.396566 7.501747 2.557824 6.610920 14.362458 +S 0.000000 12 110.816612 127.256470 19.429132 68.948471 45.734917 3.372754 ++ 7.398993 1.690261 4.739276 2.301270 10.038724 4.031080 +S 0.000000 10 107.490334 98.710014 22.722826 68.985306 43.584049 18.059902 ++ 10.303163 11.539957 4.951231 2.921867 5.626542 8.334496 + +22 1 +45 273 +1 267 +1 263 +3 253 +41 2f3 +16 4b1 +6 5b1 +28 435 +7 535 +1 475 +3 1035 +3 2035 +5 20b1 +7 10b1 +2 10b5 +2 20b5 +1 5b5 +3 2d3 +26 8f3 +1 27b +1 2fb +1 2e3 + +21 2 +B 1.000000 201 0.366996 77.691170 40.121925 78.125465 41.895142 1.962537 86.066101 ++ 1.609591 3.455543 7.986717 2.802062 9.878490 5.180982 15.882639 +B 1.000000 201 63.351463 58.068138 75.243584 72.907227 31.193167 1.788466 116.267548 ++ 1.675175 5.534683 6.399627 7.512290 10.163117 7.289217 15.731024 +B 0.000000 1 96.571861 63.143925 0.784300 121.796661 3.031327 32.189182 33.538277 ++ 1.386453 3.361574 6.215451 4.531152 7.939918 3.263638 14.483278 +L 1.000000 201 0.033414 64.298096 2.170575 110.445908 ++ 0.915024 2.694438 1.616340 14.106071 +L 0.000000 157 79.862167 59.713055 52.801388 89.708076 ++ 2.831678 4.790684 9.131303 16.046009 +L 0.000000 129 15.761054 73.888741 49.883163 82.546570 ++ 2.509003 3.422866 8.588465 13.324942 +L 0.000000 119 65.141663 75.633385 23.101381 80.100273 ++ 3.427530 1.900157 7.741635 7.854553 +L 0.000000 24 6.430458 55.141293 104.906288 72.673958 ++ 1.572124 1.564250 12.709573 3.796712 +L 0.000000 14 62.263985 66.937370 121.063400 71.340111 ++ 5.705109 5.646492 8.548909 3.981741 +S 1.000000 201 57.310905 81.769020 88.347008 58.660851 71.307922 82.689331 ++ 5.887548 16.636705 10.430968 3.889091 7.172461 14.213943 + +23 1 +27 23b +13 22b +21 21b +4 29b +5 20b +1 3bb +1 23f +2 33b +5 2bb +2 2ab +47 27b +6 2fb +18 26b +33 25b +1 35b +2 24b +3 37b +3 36b +1 31b +1 2cb +2 3db +2 2db +1 3fb + +31 3 +B 1.000000 204 62.733475 65.538322 93.460243 78.923454 24.695372 19.982420 67.667450 ++ 1.640888 3.159190 7.361122 5.731709 5.760390 8.650510 11.126741 +B 1.000000 204 62.518082 64.638077 38.465408 76.594894 26.345972 42.123203 91.011017 ++ 1.707239 2.463725 2.176027 4.714409 7.152610 7.032063 15.584165 +B 1.000000 204 1.512276 87.994637 74.551689 106.006203 11.406301 32.183228 44.396530 ++ 1.925664 2.172538 10.050337 5.081652 3.723022 3.102560 8.572049 +L 0.000000 72 59.336029 55.752022 20.743500 72.989319 ++ 4.939338 4.188530 4.539185 6.695642 +L 0.000000 41 0.832856 61.115635 2.500925 78.755638 ++ 3.561104 3.591257 2.196497 7.899208 +L 0.000000 38 6.421627 55.743851 106.514938 71.070595 ++ 2.023701 2.543011 8.010391 4.519025 +L 0.000000 26 117.179970 65.996094 60.491299 68.595459 ++ 1.354007 2.135678 4.162508 3.912361 +L 0.000000 24 76.215294 64.416458 86.315887 69.562035 ++ 1.126295 1.626135 3.851122 4.045342 +L 0.000000 10 62.415859 67.436195 128.951859 72.487610 ++ 4.163362 5.168416 7.087633 5.060684 +S 1.000000 204 65.327133 115.184761 60.098049 63.511250 71.100227 67.999229 ++ 5.470638 11.855494 9.447325 4.109515 5.591525 16.574953 + +27 1 +2 237 +76 207 +16 227 +27 20f +5 21f +5 22f +4 23f +12 217 +5 307 +3 247 +1 327 +2 30f +6 28f +9 287 +2 257 +3 297 +7 24f +4 25f +1 2af +3 27f +3 29f +1 2cf +1 2b7 +1 37f +3 26f +1 277 +1 347 + +41 4 +B 0.000000 199 84.111320 57.078552 25.243114 98.015556 16.771154 42.536533 59.906361 ++ 1.651358 2.993484 2.873858 7.902381 5.749362 19.265596 13.552229 +B 0.000000 197 3.094712 88.090576 76.354568 108.274658 6.791703 28.386999 57.439716 ++ 2.275959 1.776554 6.259202 6.851969 1.906122 2.642387 7.642813 +B 0.000000 118 116.872246 84.238632 23.012966 109.554962 9.635605 35.343304 33.666145 ++ 2.026420 3.135637 2.690923 6.692975 4.417264 5.537215 5.070548 +B 0.000000 39 127.735779 89.317963 22.939070 94.733513 19.172499 35.510296 31.240568 ++ 1.408584 1.858537 1.459496 10.935258 7.637326 3.642623 6.644166 +B 0.000000 8 47.972881 76.241783 121.509666 108.058815 4.109679 33.822392 22.864700 ++ 0.619244 4.870156 7.650138 9.242293 1.620774 1.072993 3.682993 +B 0.000000 5 48.807148 67.095245 97.701988 106.384430 4.470064 34.595173 23.282743 ++ 0.591916 4.524353 4.159610 5.087483 1.173716 1.391314 1.754761 +B 0.000000 1 125.796410 85.927788 24.389816 85.992882 27.902187 58.848324 17.524242 ++ 1.365692 3.109968 4.182205 7.254215 3.094858 3.378358 5.439668 +B 0.000000 1 80.759125 61.313667 25.680670 70.909363 49.696266 115.385849 26.007401 ++ 1.365692 3.109968 4.182205 7.254215 3.094858 3.378358 5.439668 +C 1.000000 200 63.229519 66.230331 30.289942 ++ 1.879259 5.716874 8.847868 +L 0.000000 192 83.914169 55.641682 91.697296 95.060257 ++ 2.977046 10.627006 13.971595 12.314361 +L 0.000000 181 31.634295 85.028618 90.828842 76.687187 ++ 1.991326 2.744702 7.251179 7.867118 +L 0.000000 82 126.348900 53.476219 34.116398 75.339302 ++ 1.120864 2.246949 3.701640 5.412409 +L 0.000000 8 126.378624 74.245155 2.085293 70.134712 ++ 1.825621 1.363639 1.179408 2.505762 +S 0.000000 140 44.998619 117.152397 73.752708 66.399521 68.361237 69.734764 ++ 7.054893 15.448744 7.079320 3.925218 4.657491 9.270427 +S 0.000000 46 44.285259 123.430847 100.144249 67.379700 69.729164 85.208519 ++ 5.548732 6.017478 5.994272 2.113696 4.047546 4.389698 +S 0.000000 13 56.005150 71.783836 18.300507 69.763672 77.570297 18.887737 ++ 3.172995 3.377434 3.871163 1.164404 3.090322 3.999659 +S 0.000000 1 42.545826 115.808685 29.141222 65.778297 88.781525 38.109406 ++ 4.935698 6.707735 5.039891 2.401106 3.931786 4.362525 + +35 1 +17 2f0b +1 2f43 +10 270b +2 330b +4 370b +2 230b +1 3f0b +1 3f09 +1 278a +1 2709 +30 2f07 +25 2707 +2 8707 +11 2f03 +8 8f07 +20 2703 +1 8f03 +2 8703 +3 2307 +28 4707 +2 4527 +2 4727 +5 2b07 +2 2b03 +2 2303 +3 4f07 +1 4f03 +1 4701 +1 2713 +1 10103 +3 4517 +2 4307 +3 4717 +1 4537 +1 2d07 + +51 5 +B 1.000000 200 61.210869 60.414009 44.996590 63.103981 50.278545 35.708595 114.377151 ++ 1.718640 1.943502 2.870689 8.491181 23.464378 7.641634 14.872016 +B 1.000000 200 1.111999 76.345909 96.683189 74.098511 58.369541 75.411606 81.551086 ++ 1.671147 3.384450 7.826042 4.675973 15.033785 26.701540 11.187689 +B 0.000000 2 32.689884 63.660042 124.046150 111.898758 3.834485 31.237671 31.376644 ++ 1.694893 2.663976 5.348366 6.583577 17.293716 17.171587 13.029852 +L 0.000000 187 64.500443 69.497757 130.172852 85.254448 ++ 1.008084 3.570575 8.062130 9.526338 +L 0.000000 155 2.366718 71.960922 108.770233 73.941208 ++ 3.351424 2.532158 8.234741 8.018671 +L 0.000000 126 126.826530 58.092266 67.096779 76.948601 ++ 7.169940 2.348108 8.212796 12.099283 +L 0.000000 74 59.438034 55.733932 26.433929 76.894768 ++ 2.700081 1.955422 4.919174 7.195668 +L 0.000000 54 53.785603 71.931534 92.057236 80.844131 ++ 4.285209 3.254877 7.738161 15.586662 +L 0.000000 48 92.456558 45.467991 93.953995 69.163605 ++ 2.858292 2.365348 5.880444 2.484999 +L 0.000000 45 1.822922 61.097763 3.279848 74.211029 ++ 2.163204 3.922364 2.447758 6.002989 +L 0.000000 1 110.074532 70.077477 70.163643 83.875572 ++ 2.873495 2.700128 6.358140 8.402695 +L 0.000000 1 74.229485 68.471748 25.693626 66.867172 ++ 2.873495 2.700128 6.358140 8.402695 +S 1.000000 200 77.421951 78.633102 43.467968 57.523808 72.026108 60.956718 ++ 10.110652 26.147068 11.925796 3.548957 6.603191 13.015491 + +50 1 +24 103b +13 101b +2 106b +6 105b +3 109b +1 1017 +5 10bb +12 107b +7 100b +7 111b +11 104b +1 10cb +6 102b +20 113b +2 10ab +2 11bb +1 139b +6 117b +4 112b +3 133b +4 10db +2 115b +1 11d7 +3 123b +4 12bb +7 127b +7 10fb +1 12db +2 10eb +1 128b +2 122b +3 129b +2 108b +1 1203 +2 11fb +4 12fb +1 120b +2 121b +2 1293 +1 16db +1 12eb +1 10b3 +2 125b +1 1053 +1 1033 +1 12a3 +2 12b3 +1 12f3 +1 1a3b +1 1223 + +61 6 +B 0.000000 164 1.228640 76.070976 96.442993 77.375511 44.980793 40.129192 88.071663 ++ 1.439309 7.091578 8.922007 8.696263 20.591427 33.041878 17.547029 +B 0.000000 36 0.818107 73.502625 90.553604 61.937122 101.586517 126.713631 84.993439 ++ 1.231028 2.408208 4.538750 1.889076 16.970827 5.256459 11.191558 +C 1.000000 200 65.287361 43.610443 52.020252 ++ 1.337579 3.117970 9.067019 +C 0.000000 1 64.444588 88.991631 27.348124 ++ 1.337579 3.117970 9.067019 +L 0.000000 92 65.420174 68.912102 110.273582 74.447067 ++ 6.267898 4.490405 22.379967 10.074186 +L 0.000000 63 4.156340 73.421585 110.019608 74.176109 ++ 8.965200 2.804563 10.599170 5.975577 +L 0.000000 15 124.245728 65.055214 7.059605 71.547455 ++ 6.679305 6.324702 4.705396 3.312526 +L 0.000000 10 94.413651 41.415028 64.340515 76.055054 ++ 2.812512 2.065167 9.267376 6.594245 +L 0.000000 6 55.411816 75.576576 84.663818 77.445023 ++ 1.483267 1.314856 6.993772 8.104406 +S 1.000000 200 23.120113 61.897499 36.763107 61.464077 66.201889 44.665051 ++ 17.037012 9.489800 12.096966 5.441370 7.996422 11.328998 + +24 1 +43 215 +20 235 +62 205 +5 275 +16 225 +3 315 +1 245 +10 206 +2 256 +3 285 +1 2e6 +10 226 +5 216 +1 20d +2 295 +1 276 +5 236 +1 266 +1 2a6 +3 255 +2 335 +1 395 +1 2c5 +1 2a5 + +71 7 +B 1.000000 201 68.355995 55.908260 79.467102 87.358902 24.954891 6.627673 116.016258 ++ 1.849197 3.602070 8.319350 2.577822 2.966717 5.722599 19.303696 +B 0.000000 38 118.784042 70.835487 58.145630 122.153770 3.535312 32.335533 61.203476 ++ 1.202331 4.189528 2.954317 2.495761 0.646851 0.647756 6.258915 +B 0.000000 1 32.811882 59.523941 137.453323 107.524239 5.731015 29.596058 25.226919 ++ 1.525764 3.895799 5.636834 2.011064 1.806784 3.086214 12.781305 +L 0.000000 200 64.093262 55.779152 130.222183 113.227211 ++ 1.022138 7.458420 8.100091 20.725008 +L 0.000000 198 23.929220 77.183327 67.067146 114.729462 ++ 2.091221 5.014352 13.444718 16.447018 +L 0.000000 174 88.778862 64.165825 59.423752 102.616432 ++ 2.416591 4.827378 11.859543 15.934664 +L 0.000000 64 0.269445 54.615093 114.446663 79.701790 ++ 2.300049 3.037386 5.336898 8.865816 +S 0.000000 150 123.761536 95.884750 5.564672 85.418755 16.332100 1.375934 ++ 9.106649 21.236334 8.814024 3.958646 9.589396 5.078979 +S 0.000000 48 125.625336 50.762218 122.776466 86.522758 60.376965 124.164970 ++ 6.009474 11.926980 8.649642 2.674277 5.214284 8.485798 +S 0.000000 2 120.024185 50.178001 26.961212 82.955193 31.765911 25.502449 ++ 6.298385 16.581657 7.276527 3.316461 7.401840 4.171134 +S 0.000000 1 123.415581 90.519348 73.925003 79.565903 66.523758 71.868607 ++ 6.298385 16.581657 7.276527 3.316461 7.401840 4.171134 + +17 1 +96 b9 +22 f9 +1 439 +3 9b +15 99 +9 bb +5 15b +2 ab +1 8b +15 17b +3 13b +3 159 +2 239 +18 179 +1 b1 +1 fd +4 139 + +81 8 +B 1.000000 200 62.541607 42.315842 70.818733 103.874023 12.669112 33.901688 50.136814 ++ 1.781486 3.445422 6.342821 5.907326 4.298331 4.190469 9.774439 +B 1.000000 200 1.267009 88.763199 71.800964 103.445793 13.556808 33.079441 50.692093 ++ 1.605154 2.214154 6.740967 5.408937 4.584993 4.256919 11.847423 +C 1.000000 200 64.369774 36.792362 45.821983 ++ 1.546361 2.146518 7.453160 +C 0.000000 137 66.036003 92.963844 37.118824 ++ 1.688466 7.387461 6.058814 +C 0.000000 63 68.297966 113.519928 40.549656 ++ 3.809777 2.844204 4.947568 +L 0.000000 25 127.672470 64.071884 3.061007 73.719200 ++ 4.547411 4.258028 3.114196 5.797208 +L 0.000000 13 61.901890 67.774200 132.440231 75.092125 ++ 4.887723 5.422467 5.235678 8.385038 +L 0.000000 1 38.127625 84.724213 51.052650 64.377457 ++ 3.759663 4.189715 3.699157 7.091123 +S 1.000000 200 55.872017 64.630287 65.232956 65.361450 64.659904 64.829765 ++ 5.414144 9.398854 12.204275 2.190376 5.515365 9.260412 + +8 1 +12 12f +117 10f +7 14f +45 117 +12 137 +5 157 +1 18f +1 177 + +91 9g +B 1.000000 200 65.326584 55.053711 37.883667 74.502647 54.793804 59.619804 89.496925 ++ 1.660141 6.226900 2.940191 10.055049 27.200647 40.926956 17.348257 +B 0.000000 1 0.062071 94.565071 70.631088 96.120316 9.289005 32.893616 24.561346 ++ 1.660141 6.226900 2.940191 10.055049 27.200647 40.926956 17.348257 +B 0.000000 1 16.456202 84.337524 113.529846 117.580704 4.274961 32.536903 18.895056 ++ 1.660141 6.226900 2.940191 10.055049 27.200647 40.926956 17.348257 +B 0.000000 1 112.130608 73.977760 33.391029 96.583221 6.494504 32.499195 19.539434 ++ 1.660141 6.226900 2.940191 10.055049 27.200647 40.926956 17.348257 +C 1.000000 200 65.515640 89.014442 51.006935 ++ 1.378231 7.776123 9.423361 +L 0.000000 63 5.095337 63.365803 7.261839 73.245544 ++ 4.876937 6.194809 8.184632 9.139166 +L 0.000000 40 123.376892 59.378281 51.523880 73.725960 ++ 4.049532 4.263789 6.629168 8.294138 +L 0.000000 29 58.801483 55.925789 27.909086 74.757370 ++ 3.957518 1.380517 3.991960 6.198274 +L 0.000000 25 77.692970 58.191204 24.424997 78.756424 ++ 1.069743 2.902693 1.812171 7.762351 +L 0.000000 11 63.445930 63.075001 130.980789 68.891220 ++ 3.000776 3.800809 8.131477 2.170972 +L 0.000000 3 30.365570 89.424232 72.245094 65.754608 ++ 1.733960 1.877425 8.288995 1.817127 +L 0.000000 3 69.578148 57.797642 17.842201 67.975700 ++ 0.516234 3.105954 2.190682 2.720962 +L 0.000000 1 12.768035 61.196270 24.353735 117.434135 ++ 2.334698 3.090448 4.839142 4.927581 +S 1.000000 200 105.804100 68.142082 39.206886 67.228477 63.548634 47.413181 ++ 17.608194 10.286920 18.396107 5.523131 7.423532 14.223130 + +27 1 +40 2031 +17 2051 +2 2811 +71 2011 +2 2231 +3 20b1 +1 2851 +7 2131 +7 20d1 +1 2271 +3 2251 +1 24f1 +1 2211 +16 2091 +1 2035 +5 2071 +1 2411 +1 20d3 +1 22d1 +12 2111 +1 2331 +1 3011 +1 2139 +1 2311 +1 2171 +1 2751 +1 2151 + +:1 : +A 1.000000 200 9.351406 121.505402 83.644173 ++ 33.429165 10.324297 3.772259 +A 0.000000 191 127.377205 121.569832 15.604745 ++ 34.614933 7.088521 3.407418 +L 0.000000 126 63.202023 66.516296 95.239975 91.481430 ++ 6.394967 4.841265 2.696808 16.567959 +L 0.000000 120 95.034576 57.568443 84.843048 87.255226 ++ 7.951179 5.044607 5.087521 14.499930 +L 0.000000 113 30.918570 73.845718 82.741028 86.895210 ++ 4.619862 3.286209 5.777840 15.275126 +L 0.000000 109 95.341385 56.390694 16.031446 87.294518 ++ 7.037479 2.837863 6.162959 14.729472 +L 0.000000 108 30.720234 72.262032 13.801159 87.099648 ++ 7.929480 3.342194 5.463101 15.855967 +L 0.000000 103 126.730278 63.924194 2.637724 89.072708 ++ 4.209045 2.797457 2.312335 15.848318 +L 0.000000 91 63.225788 65.502151 26.932598 87.598160 ++ 4.911147 2.363634 5.471043 14.328959 +L 0.000000 84 127.356644 65.384926 70.921448 88.992706 ++ 4.242314 2.600970 5.179913 15.207298 +L 0.000000 1 118.900238 53.955383 64.350304 128.000000 ++ 5.158767 3.030807 4.300368 15.289128 +S 0.000000 195 62.694435 64.848763 65.810005 63.528122 64.910789 64.146744 ++ 23.962784 14.904617 13.582285 8.243465 8.609654 11.773851 +S 0.000000 3 61.471897 0.249103 44.128433 62.539764 13.962007 39.284927 ++ 5.494121 0.177912 17.937706 2.530202 14.014460 17.968565 +S 0.000000 2 44.882870 123.228699 65.215218 55.440384 100.755173 59.323524 ++ 8.525123 6.299213 13.684923 4.127415 9.996693 13.890054 + +111 1 +1 853 +2 80f +6 87b +3 b5f +2 8d7 +2 b87 +1 a13 +1 9ab +1 a2b +2 9d7 +2 9eb +1 8ab +1 a3b +2 987 +1 9c7 +1 90f +1 a5f +4 883 +4 807 +1 ab3 +1 c9f +2 bdf +2 13ff +2 827 +1 887 +3 863 +2 a7b +2 8ef +3 81f +2 83f +4 9f7 +1 8bf +1 847 +1 9d3 +3 9ef +3 aab +2 bbf +22 bff +4 aff +1 8c7 +2 b9f +3 bf7 +3 957 +2 9fb +2 bab +1 bdb +1 b6f +1 8ff +1 87f +1 93f +4 8df +3 a1d +1 a1f +1 af7 +1 aaf +1 8e7 +1 b67 +1 89f +1 8f7 +1 80d +1 b0b +1 abf +1 b2b +2 a9f +1 873 +1 bf3 +1 93b +1 85f +1 96f +1 8af +1 b73 +1 b97 +2 84b +1 9e3 +1 943 +3 86f +5 803 +1 b57 +1 a1b +1 be7 +1 b8f +1 a77 +1 9c3 +1 9cf +1 98b +1 843 +1 907 +1 a0b +1 893 +4 813 +1 ae3 +1 101f +1 a6b +1 9a3 +1 a03 +1 b23 +1 83b +1 a27 +1 805 +1 90b +2 801 +1 23ff +2 903 +1 a09 +1 809 +1 833 +2 823 +1 91f +1 200f +2 877 +2 92f + +;1 ; +B 0.000000 142 64.739197 57.565929 -5.554220 105.045052 13.118443 33.954735 36.816658 ++ 2.505254 2.780560 5.817530 9.659440 6.503871 3.833364 7.812295 +A 0.000000 147 126.654564 122.307579 82.975883 ++ 20.255537 5.733072 4.815958 +A 0.000000 62 51.785740 83.941971 10.726496 ++ 6.798558 5.986316 6.419576 +A 0.000000 49 62.906799 118.134598 84.831497 ++ 6.548546 6.460461 3.888792 +L 0.000000 163 26.308163 71.519318 1.138934 96.253471 ++ 5.384140 2.911125 9.824449 20.070658 +L 0.000000 146 63.112961 65.526360 28.949686 96.336632 ++ 5.202074 2.154694 8.900419 17.244740 +L 0.000000 140 96.852356 59.360958 7.198184 93.290138 ++ 8.658330 2.277796 14.763997 19.214609 +L 0.000000 136 127.338409 66.515953 72.334984 92.340752 ++ 11.069538 5.717049 6.463275 12.950511 +L 0.000000 134 93.218620 59.044460 85.798393 93.125061 ++ 4.736876 3.998597 4.838893 15.721684 +L 0.000000 126 62.116734 67.963257 95.886040 93.639557 ++ 5.625843 3.772661 3.984136 15.105917 +L 0.000000 123 30.627474 74.160286 83.659103 90.501297 ++ 7.089418 3.878398 5.520018 14.554517 +L 0.000000 60 119.125954 61.995201 -9.816473 82.004311 ++ 5.261906 3.109286 7.556039 11.779619 +L 0.000000 24 13.650417 66.361458 -21.531523 95.692970 ++ 2.717817 2.214771 5.138346 21.956083 +S 0.000000 113 100.653465 65.252655 73.931931 68.089951 69.558296 81.747017 ++ 17.938562 30.877563 14.673541 5.594598 11.093388 20.003712 +S 0.000000 66 84.196686 127.827553 81.791016 68.843361 120.802429 71.881729 ++ 12.351664 4.981687 13.649169 4.980305 9.491731 12.720121 +S 0.000000 15 102.149506 83.006790 28.266644 63.619274 66.994774 38.599838 ++ 12.753816 18.489523 4.569617 4.533717 7.299246 6.133689 +S 0.000000 1 71.762802 0.199345 80.246941 57.044308 40.904453 86.050446 ++ 14.348014 13.774190 10.205822 4.759523 8.678489 10.915092 +S 0.000000 1 70.452545 128.000000 52.192902 66.866669 128.000000 2.921373 ++ 14.348014 13.774190 10.205822 4.759523 8.678489 10.915092 + +130 1 +1 4276 +2 43f6 +2 4076 +2 4af6 +3 48f6 +5 4876 +3 4c76 +3 44f6 +1 49f6 +1 204f6 +2 4b76 +1 4a76 +1 41f6 +3 4ff6 +3 4376 +1 4ef6 +1 45f6 +1 2776 +2 4bf6 +1 40f6 +1 4e76 +1 21f6 +1 2ff3 +10 27f9 +2 26f3 +1 2ff7 +11 27f3 +3 2773 +1 37f7 +3 27b9 +1 2033 +3 37f3 +2 25f3 +1 25e7 +1 2673 +1 2ef9 +1 27f7 +1 2fb3 +1 27bd +1 2fb9 +3 37f9 +2 27b7 +3 27b3 +3 27e3 +1 2433 +4 47f6 +1 2576 +1 4f83 +1 4db3 +1 4573 +1 4723 +1 53b3 +1 57b3 +1 4576 +3 47f3 +1 4ff3 +2 57f3 +1 24f3 +1 2733 +1 2ef6 +1 37fd +2 27fc +2 23f9 +1 27ec +1 37fc +1 2d59 +1 103fc +1 2073 +1 25dc +1 8913 +1 3b89 +1 8803 +1 9b13 +1 8853 +1 27d9 +1 8f99 +1 8ab3 +1 8fb3 +1 8013 +1 3903 +1 8fe9 +1 95a3 +1 8da3 +1 9ec9 +1 2fa9 +1 8f83 +1 8fa9 +1 9f89 +1 3fe9 +1 2213 +1 2ab3 +2 2913 +1 2c93 +1 2413 +1 2109 +1 2633 +1 2113 +1 2483 +1 2513 +1 20b9 +1 2b13 +1 3883 +2 2593 +2 22d3 +1 2919 +1 20c3 +1 2103 +1 5159 +1 47f9 +1 3149 +1 40d3 +1 40f3 +1 40c9 +1 4243 +2 2753 +1 4053 +1 42d9 +1 4193 +1 26f9 +1 47c3 +1 4063 +2 2053 +1 22c3 +1 35f3 +1 21e3 +1 2813 +1 2f99 +1 23e3 +1 23d9 +1 2223 + +<1 < +B 1.000000 200 0.515020 81.154800 56.245872 85.263153 36.580261 33.577835 116.231071 ++ 0.517597 2.216462 5.247228 1.470966 3.556452 4.747992 12.955986 +L 1.000000 200 119.252251 64.063217 30.243101 127.747124 ++ 0.771740 2.570670 3.900261 8.738476 +L 1.000000 200 73.162231 65.622459 85.632011 123.798882 ++ 1.039728 3.013141 8.098700 7.573296 +L 0.000000 190 10.597973 75.076691 74.531303 100.677414 ++ 1.051623 2.796646 6.745660 14.507954 +L 0.000000 180 54.011543 75.054573 38.491024 104.884285 ++ 0.730749 2.182925 4.545578 11.876400 +S 1.000000 200 72.158836 70.744225 62.255936 65.129829 70.437714 65.079529 ++ 10.171926 20.340382 9.018165 2.878774 5.145015 6.819506 + +3 1 +180 3f +10 2f +10 27 + +=1 = +B 0.000000 11 96.593887 71.694122 53.626980 113.375328 3.997582 32.348595 31.840921 ++ 0.215693 7.709425 17.512051 5.635925 1.908081 2.156935 7.756507 +B 0.000000 8 32.520779 57.988121 52.321384 110.432083 4.847179 32.926266 33.066891 ++ 0.201853 7.893218 3.943527 2.651334 1.722338 2.521195 9.763376 +B 0.000000 6 32.456017 67.199745 86.149101 121.031097 2.761187 32.068996 38.516460 ++ 0.175710 2.476570 9.375994 2.070801 0.224740 0.438292 2.707765 +A 0.000000 196 127.998848 57.278496 41.847874 ++ 1.995317 6.694685 4.514893 +A 0.000000 190 0.156678 57.185215 77.509125 ++ 1.563682 7.201234 8.191625 +L 0.000000 197 127.981544 64.979088 33.721497 116.977310 ++ 1.177131 7.517466 5.387760 23.330963 +L 0.000000 196 0.113448 65.054764 69.162949 116.103287 ++ 1.483968 6.024610 5.212936 22.137287 +L 0.000000 196 64.041054 65.541115 85.505127 118.167755 ++ 1.378772 4.086012 8.063151 15.505498 +L 0.000000 195 64.005341 66.027939 49.950108 116.892609 ++ 1.744413 7.278917 4.869066 24.232700 +L 0.000000 139 94.942696 35.863277 78.586655 95.696327 ++ 8.097963 3.190517 6.767209 14.791348 +L 0.000000 137 29.068762 95.045486 76.148529 97.949059 ++ 6.592730 3.299838 5.910019 15.368039 +L 0.000000 129 29.762098 95.209938 41.299671 96.298958 ++ 7.007225 3.306789 7.261426 15.352708 +L 0.000000 127 94.149643 35.717056 43.016411 95.969643 ++ 7.366994 3.089817 5.313863 16.523289 +L 0.000000 1 23.151447 93.123322 36.034042 96.180031 ++ 4.108951 2.779236 5.820833 12.078882 +S 0.000000 194 60.483040 64.107269 50.668686 62.325958 64.417366 50.195057 ++ 30.942358 15.572992 15.739038 13.506615 5.496104 15.793804 +S 0.000000 4 1.176189 64.355164 64.464691 5.766901 64.316864 64.817047 ++ 1.253236 0.062887 0.352695 9.078446 0.193912 0.071625 +S 0.000000 1 104.172157 128.000000 30.809809 75.046425 90.235329 43.069099 ++ 13.519267 6.520190 8.045867 11.292530 2.005326 7.932714 +S 0.000000 1 62.268608 0.493789 72.524414 64.366821 41.857754 64.958031 ++ 13.519267 6.520190 8.045867 11.292530 2.005326 7.932714 + +43 1 +68 5ff8 +1 4db9 +6 57f8 +1 5fd1 +7 53f8 +16 41f8 +5 51f8 +10 5df8 +6 4df8 +7 43f8 +4 59f8 +1 5ff9 +8 45f8 +3 9ff8 +11 5bf8 +10 4ff8 +1 12bf8 +1 52eb +1 49e9 +1 4466 +1 5fb9 +9 47f8 +1 43a9 +1 87f8 +1 4fec +1 49d9 +1 53e9 +1 4772 +1 53d8 +1 5d6c +1 59ec +1 44fa +1 49f8 +1 49fc +1 4f6c +1 53fa +1 4bf8 +1 4ff9 +1 46fa +2 55f8 +1 4ff2 +1 217a9 +1 58fa + +>1 > +B 1.000000 200 64.542274 49.426830 56.741634 85.103127 36.837086 34.120186 116.101791 ++ 0.870261 2.708642 5.425422 1.643863 2.937977 8.251997 12.424066 +L 1.000000 200 54.749832 68.091736 82.503441 125.215866 ++ 0.782269 2.732099 7.753098 11.699929 +L 1.000000 200 9.496292 65.777473 29.245029 126.544853 ++ 1.275450 5.115372 4.129728 16.666008 +L 0.000000 188 74.421829 55.651421 38.728149 100.658600 ++ 0.993552 2.751440 6.003137 13.708177 +L 0.000000 183 117.943169 55.421238 75.343582 104.469009 ++ 0.741301 2.956351 6.133693 13.663843 +S 0.000000 185 66.319664 57.434643 63.802059 65.001289 58.107983 63.006638 ++ 12.896584 10.110854 14.366110 3.065215 3.837105 13.877687 +S 0.000000 15 66.811920 13.882116 64.998199 66.641930 47.933926 62.874432 ++ 4.784781 7.626664 6.142734 2.018421 1.511564 3.547790 + +7 1 +180 3f +3 27 +7 47 +2 57 +5 4f +2 2f +1 5f + +?1 ? +B 0.000000 195 73.728569 63.054024 95.134529 68.952759 36.648262 10.314394 95.258942 ++ 4.716786 4.323405 5.950318 4.877034 7.144903 13.716969 14.850121 +B 0.000000 85 113.407959 73.355118 57.954952 111.637146 6.427322 32.278397 27.956448 ++ 2.976469 3.788556 6.204677 4.464639 2.369969 1.810617 7.359671 +B 0.000000 13 121.244843 74.833961 43.856529 105.004211 9.448465 30.880554 70.797920 ++ 1.057935 3.934217 3.946387 5.423072 1.955652 2.796867 7.341393 +B 0.000000 5 65.800407 64.730476 111.724571 87.482376 18.456684 0.638916 71.389320 ++ 0.212235 0.569809 2.111853 2.774896 1.904685 0.366814 6.560619 +B 0.000000 1 72.458397 61.321789 34.247322 96.648170 17.727625 45.500492 18.650026 ++ 1.756526 2.594389 4.553309 4.181701 3.343802 4.672817 9.027951 +B 0.000000 1 112.192162 66.264610 43.959221 107.964935 8.749546 37.486534 24.654079 ++ 1.756526 2.594389 4.553309 4.181701 3.343802 4.672817 9.027951 +A 0.000000 184 111.942871 121.519150 15.267513 ++ 37.399963 8.807236 3.116121 +L 0.000000 135 31.018539 69.900223 14.244343 96.428566 ++ 6.493922 6.823693 5.656098 15.262012 +L 0.000000 132 94.893921 55.695126 15.538651 94.385551 ++ 6.135560 5.772672 4.756172 14.257762 +L 0.000000 129 125.755272 61.592548 3.744099 97.345497 ++ 7.032510 5.818135 3.400379 14.085710 +L 0.000000 123 61.841667 63.552971 26.573492 95.160408 ++ 7.615647 6.186320 6.226742 15.820618 +L 0.000000 114 87.122429 66.388809 77.219803 87.278496 ++ 4.870653 2.895450 7.508984 16.060585 +L 0.000000 76 19.099115 75.834160 69.233208 86.876289 ++ 5.122041 3.705558 6.870023 12.299377 +L 0.000000 50 64.177582 64.975151 134.248611 71.529541 ++ 4.175207 3.502509 7.526443 5.163004 +L 0.000000 7 5.971188 56.225727 110.557518 81.361359 ++ 1.215958 0.614899 3.234390 2.183530 +L 0.000000 3 57.196556 67.718353 137.684052 99.572906 ++ 0.903677 8.496975 1.111254 5.008586 +L 0.000000 1 28.715969 90.017113 82.217346 65.269569 ++ 4.253665 4.868468 4.923335 11.126799 +S 0.000000 141 124.573112 110.553024 30.914684 77.193893 63.488846 30.006723 ++ 7.770325 11.997872 9.679010 5.345648 9.667531 11.126576 +S 0.000000 35 122.802605 88.917732 67.996193 78.635361 72.543388 69.707062 ++ 5.974566 14.062636 8.585676 3.879162 4.331668 10.485872 +S 0.000000 15 126.372101 73.530556 98.927483 81.017212 60.995899 94.128006 ++ 3.162760 9.057278 5.215297 2.780039 3.970400 4.846237 +S 0.000000 6 123.980637 95.270805 0.491001 74.875404 29.064159 0.459787 ++ 4.847346 6.710359 0.260926 1.954634 3.824126 0.294700 +S 0.000000 1 128.000000 34.633369 30.883720 61.641342 33.516800 38.282520 ++ 5.114985 10.457036 5.935227 3.489871 5.045617 6.688346 +S 0.000000 1 128.000000 0.585118 22.354849 84.796814 13.359494 55.354103 ++ 5.114985 10.457036 5.935227 3.489871 5.045617 6.688346 +S 0.000000 1 115.675713 54.642624 128.000000 75.708664 62.120575 128.000000 ++ 5.114985 10.457036 5.935227 3.489871 5.045617 6.688346 + +119 1 +1 20dc3 +1 200c3 +1 24843 +1 20243 +2 20c43 +1 24343 +1 204c3 +2 209c3 +1 24443 +1 20543 +1 22043 +1 20143 +1 24ac3 +2 20cc3 +11 207c3 +1 249c3 +1 247c3 +1 205c3 +1 20a43 +1 22741 +2 202c1 +1 206c1 +1 233c1 +1 330c1 +2 223c1 +1 20541 +1 22041 +1 226c1 +5 217c1 +2 230c1 +1 220c3 +1 23741 +4 207c1 +2 21441 +1 22341 +1 20241 +9 20fc3 +1 203c3 +3 1007c1 +2 21fc3 +1 225c3 +1 213c1 +1 1003c1 +1 20f43 +1 20ec3 +1 22fc3 +10 21fc1 +1 20b43 +1 206c3 +2 227c1 +9 20fc1 +2 40ec3 +1 41fc3 +2 417c3 +6 40fc3 +3 407c3 +1 42bc3 +1 40bc3 +2 403c3 +1 427c3 +1 40fe3 +2 42005 +2 43005 +5 40005 +1 43015 +1 100fc1 +1 22803 +1 40003 +1 247c1 +1 41005 +1 219c1 +1 21941 +1 23dc1 +1 21b41 +1 22b41 +3 21dc1 +1 23d41 +3 21bc1 +1 21ec1 +4 21841 +1 239c1 +2 22fc1 +1 22e43 +1 20041 +1 20043 +2 20d43 +1 21cc1 +1 20843 +1 23b41 +1 22843 +1 23ec1 +1 218c1 +1 23a43 +1 23043 +1 229c3 +1 22a43 +1 81a48 +1 208805 +1 83141 +1 83f41 +1 801005 +1 41801 +1 81c48 +1 81741 +1 49848 +1 82bc3 +1 838c8 +3 81fc1 +1 81941 +2 83fc1 +1 401cc8 +1 83a41 +1 838c1 +1 22dc1 +1 23fc1 +1 28fc1 +1 21f41 +1 102fc1 +1 41e43 + +@1 @ +B 0.000000 171 122.506691 64.055138 60.663807 55.827244 105.254448 122.132660 126.694412 ++ 1.624622 1.584089 4.876163 21.286682 46.094810 18.578215 7.891772 +B 0.000000 16 122.480240 69.397827 24.127602 70.406723 73.130440 41.424675 95.086739 ++ 0.949343 2.183903 3.548000 1.851177 6.292750 4.979185 9.755862 +B 0.000000 11 121.333351 104.125870 13.559196 101.518875 12.935726 25.002901 45.148594 ++ 0.465650 2.637249 4.857139 11.779023 7.348403 12.020745 7.948229 +B 0.000000 4 48.151863 33.292000 112.585777 96.532455 9.702400 32.355816 14.495278 ++ 0.090145 2.633908 4.908624 0.077453 0.308371 0.358891 0.908822 +B 0.000000 1 80.490517 43.253983 8.752691 118.339890 4.960888 32.568470 17.425446 ++ 0.782440 2.168791 4.547482 5.502512 11.183768 7.291915 5.794978 +B 0.000000 1 105.736565 65.778702 86.906479 112.987587 9.139418 30.365551 39.428696 ++ 0.782440 2.168791 4.547482 5.502512 11.183768 7.291915 5.794978 +B 0.000000 1 119.209946 66.488579 43.384975 86.682953 15.717191 128.000000 96.980911 ++ 0.782440 2.168791 4.547482 5.502512 11.183768 7.291915 5.794978 +B 0.000000 1 0.950925 93.768433 74.397057 110.827095 3.493780 34.179661 28.337599 ++ 0.782440 2.168791 4.547482 5.502512 11.183768 7.291915 5.794978 +C 0.000000 196 65.853279 53.833649 46.134869 ++ 4.824965 5.993156 9.719930 +C 0.000000 14 64.456467 99.374176 41.704739 ++ 1.170322 3.220735 2.073760 +C 0.000000 8 64.299622 52.621799 107.917786 ++ 1.489631 2.336215 4.889957 +C 0.000000 4 50.645920 63.286179 7.432443 ++ 1.360467 3.812028 0.401186 +A 0.000000 1 72.542274 116.319534 63.417599 ++ 8.000000 8.000000 4.000000 +A 0.000000 1 49.264912 81.658699 39.488049 ++ 8.000000 8.000000 4.000000 +L 0.000000 32 34.126404 92.689133 74.569023 71.601562 ++ 0.880360 6.016873 6.023826 21.252184 +L 0.000000 15 127.268135 74.911781 34.010036 72.230125 ++ 0.930544 1.924152 6.536073 5.078243 +L 0.000000 6 124.972694 66.222282 0.732794 74.212646 ++ 1.474759 1.029746 7.621875 5.017943 +L 0.000000 3 76.511513 68.095268 81.393524 69.002663 ++ 3.255760 0.442325 4.706336 3.610877 +L 0.000000 3 66.370277 65.705086 128.629745 70.307289 ++ 0.665643 2.038288 0.528231 4.008475 +L 0.000000 1 89.712730 54.940895 97.313507 102.082672 ++ 1.409779 1.856326 4.745972 5.203329 +L 0.000000 1 40.971252 76.646286 110.594902 81.649734 ++ 1.409779 1.856326 4.745972 5.203329 +L 0.000000 1 60.053192 74.939583 12.366617 67.240082 ++ 1.409779 1.856326 4.745972 5.203329 +L 0.000000 1 24.557510 71.054459 37.371780 119.238365 ++ 1.409779 1.856326 4.745972 5.203329 +L 0.000000 1 90.820862 57.246223 35.153969 69.295013 ++ 1.409779 1.856326 4.745972 5.203329 +L 0.000000 1 118.025490 53.491390 0.822920 68.497246 ++ 1.409779 1.856326 4.745972 5.203329 +L 0.000000 1 3.017707 71.181992 92.476112 87.990242 ++ 1.409779 1.856326 4.745972 5.203329 +S 0.000000 198 65.967476 64.096825 69.385712 66.140793 62.130402 65.744484 ++ 12.071730 7.270923 11.187209 2.517528 2.388455 9.255417 +S 0.000000 1 97.248604 62.023312 108.433823 78.314735 61.572781 104.636093 ++ 8.383145 7.270923 6.474079 2.097940 1.990379 6.427372 +S 0.000000 1 67.545525 40.209602 64.830978 67.340813 42.253262 69.776711 ++ 8.383145 7.270923 6.474079 2.097940 1.990379 6.427372 + +27 1 +1 4000b02 +1 400cb02 +1 401c302 +2 4004302 +1 a1ad020 +6 400c302 +1 4004b02 +1 4008b02 +2 400c102 +1 402c101 +1 4204302 +2 4008101 +1 4024101 +3 4044101 +1 4004111 +144 4000101 +1 5004101 +1 4010101 +7 4004101 +3 4014101 +1 4000181 +8 4000504 +3 4000104 +4 4000109 +1 4000001 +1 10c12000 +1 4000041 + +A1 A +B 0.000000 208 96.316063 63.893112 22.618700 72.181854 20.233335 34.691311 105.981575 ++ 0.744736 3.175496 3.910997 15.645009 8.980779 8.039799 18.373259 +B 0.000000 71 56.802372 40.335400 61.244160 103.850266 7.073497 27.483257 84.876694 ++ 5.652114 5.189891 11.149862 12.380412 3.843987 5.320408 20.327572 +B 0.000000 40 6.291943 99.778046 54.910435 112.685272 4.249992 30.274515 72.276741 ++ 3.706388 7.145343 2.520241 6.370249 1.412957 1.557335 12.773904 +B 0.000000 6 48.959095 76.910027 101.103752 112.086761 3.134143 33.854645 32.140625 ++ 0.580933 2.516895 3.924657 5.209663 1.266183 0.988570 5.540562 +B 0.000000 1 48.427467 96.406242 136.526047 105.553131 11.237039 26.920885 12.554969 ++ 2.614153 4.506907 5.376440 9.249458 3.189945 3.641536 14.253824 +B 0.000000 1 96.237152 66.021057 21.709019 32.371456 48.286743 29.907265 66.514366 ++ 2.614153 4.506907 5.376440 9.249458 3.189945 3.641536 14.253824 +B 0.000000 1 96.990273 30.171822 1.255657 107.760979 8.572666 37.738800 24.824615 ++ 2.614153 4.506907 5.376440 9.249458 3.189945 3.641536 14.253824 +B 0.000000 1 96.283730 93.430687 1.400948 114.151123 4.491150 32.072880 28.574284 ++ 2.614153 4.506907 5.376440 9.249458 3.189945 3.641536 14.253824 +C 1.000000 209 65.803818 71.588715 30.791933 ++ 6.044244 5.630033 6.313878 +L 0.000000 207 39.100563 89.963997 69.613792 125.055702 ++ 3.622388 7.188418 6.259457 12.391061 +L 0.000000 189 88.361511 43.502689 69.148071 121.676567 ++ 1.548054 3.327184 8.932471 19.591127 +L 0.000000 14 81.713173 62.583176 79.459099 127.439568 ++ 0.433772 1.304176 2.679372 1.404095 +L 0.000000 2 0.713078 85.202316 4.013545 66.084656 ++ 1.666828 3.207772 5.460852 6.662910 +L 0.000000 2 1.729427 64.016129 38.568779 66.686203 ++ 1.666828 3.207772 5.460852 6.662910 +L 0.000000 1 42.249836 77.937729 100.905426 74.333069 ++ 1.666828 3.207772 5.460852 6.662910 +L 0.000000 1 62.139999 57.449760 114.645081 68.369476 ++ 1.666828 3.207772 5.460852 6.662910 +L 0.000000 1 81.833656 80.961197 109.546906 89.935555 ++ 1.666828 3.207772 5.460852 6.662910 +S 0.000000 169 31.484734 68.363113 59.055912 56.873096 65.160614 55.953476 ++ 15.284576 11.667940 4.103404 6.413703 2.396883 7.847456 +S 0.000000 20 42.939911 123.505997 64.190010 63.112179 86.586922 93.747276 ++ 3.974175 7.064520 2.108468 1.697289 1.748275 3.657987 +S 0.000000 15 16.852478 128.000000 61.540859 47.690670 102.619225 126.168594 ++ 7.218469 0.001000 3.064914 2.137982 3.109987 2.511612 +S 0.000000 5 16.187675 128.000000 106.043633 49.611691 77.396278 123.067024 ++ 8.416251 0.001000 1.167688 2.669141 1.850451 2.712623 + +18 1 +37 20703 +12 20707 +1 20722 +1 28703 +104 20701 +2 22701 +1 24501 +20 40701 +1 20781 +7 20705 +2 21701 +3 10030f +1 11030f +13 80b07 +1 10031f +1 80907 +1 8030f +1 20745 + +B1 B +B 1.000000 211 1.403663 95.805351 74.702324 103.592613 12.892900 32.987144 47.689575 ++ 1.724000 3.032807 6.674688 7.819651 6.209797 3.075579 9.238050 +B 0.000000 133 63.816994 34.570019 70.221817 95.479027 4.859993 32.318901 98.103905 ++ 2.209327 3.288670 7.734440 11.101601 1.245648 1.608899 24.678705 +B 0.000000 1 96.318504 54.125053 3.144890 107.893852 2.757953 34.022343 38.005268 ++ 1.782552 3.160738 7.204564 7.340823 2.675199 2.342239 11.759852 +C 1.000000 211 67.532166 99.406837 47.308079 ++ 4.460047 7.936177 6.472407 +C 1.000000 211 66.959282 40.832428 54.326817 ++ 2.462936 3.572337 6.918605 +L 0.000000 209 95.546753 37.334927 69.710060 107.853210 ++ 2.499515 3.912666 11.519524 17.524904 +L 0.000000 208 63.777424 58.485146 133.775208 90.154671 ++ 1.266997 4.298501 7.443225 12.377235 +L 0.000000 196 1.080878 60.779232 3.553115 94.493317 ++ 2.092072 5.769166 2.188927 12.480210 +S 1.000000 211 59.831955 65.262459 57.823692 64.395554 66.252701 60.732136 ++ 3.591205 14.267024 3.625319 2.389421 9.819136 4.517374 + +8 1 +118 1fb +2 1db +10 17b +73 1f9 +1 1b9 +4 179 +2 1bb +1 17f + +C1 Cc +B 1.000000 213 127.727112 69.809525 68.698418 63.042671 32.208012 35.019615 128.000000 ++ 1.552980 2.960287 4.843616 7.655548 12.732700 6.556470 0.001000 +B 0.000000 23 30.500452 89.960777 136.118744 107.540283 8.394455 32.690823 27.638937 ++ 1.354841 4.670982 6.466877 4.406224 2.100674 1.472641 4.939666 +B 0.000000 2 48.764900 43.202774 124.850357 101.327370 8.356269 33.985245 15.041504 ++ 1.453910 3.426386 5.655247 5.663701 6.180573 4.014556 2.058694 +B 0.000000 1 0.178500 104.142128 118.061165 115.504555 6.460431 32.209927 25.397881 ++ 1.453910 3.426386 5.655247 5.663701 6.180573 4.014556 2.058694 +B 0.000000 1 112.472908 91.096222 13.652380 120.540131 3.539090 32.864220 20.666195 ++ 1.453910 3.426386 5.655247 5.663701 6.180573 4.014556 2.058694 +L 0.000000 18 65.267624 72.636337 137.602310 80.095535 ++ 4.622468 8.278273 6.940357 10.919986 +L 0.000000 13 68.903297 79.644485 25.555418 73.936935 ++ 1.233095 3.566484 5.492428 2.710044 +L 0.000000 9 126.090141 66.875763 5.349281 74.491577 ++ 1.071400 4.242560 2.351363 8.658561 +L 0.000000 6 33.210884 48.228733 70.126770 68.072556 ++ 1.589808 1.547420 6.603779 3.152846 +L 0.000000 1 2.313071 74.094574 130.549194 66.159554 ++ 1.776088 4.063756 5.057800 6.360359 +S 0.000000 122 67.629616 48.271019 69.292404 64.870308 60.108387 65.948067 ++ 7.181920 9.192354 12.528640 3.096859 4.118094 9.535118 +S 0.000000 87 68.867981 5.089407 81.157494 66.542175 58.748302 79.174759 ++ 5.953879 6.108608 9.715478 1.992645 6.203818 8.636871 +S 0.000000 4 64.040199 1.982716 101.431412 65.534790 51.948372 104.522255 ++ 2.998433 2.308934 2.766057 1.434392 0.717879 3.628434 + +25 1 +4 403 +105 401 +5 441 +2 501 +1 461 +1 481 +1 411 +1 405 +1 421 +1 883 +17 803 +40 801 +1 807 +12 821 +1 809 +1 921 +2 901 +4 841 +3 1001 +1 1041 +2 8a1 +4 881 +1 4c1 +1 961 +1 a01 + +D1 D +B 0.000000 124 63.725456 29.240408 69.735573 98.078758 4.309765 32.417637 96.065536 ++ 2.001985 4.466132 7.859338 7.375380 0.882824 1.494936 16.426075 +B 0.000000 3 96.668015 39.816582 2.207380 105.110886 5.344478 34.506676 33.582340 ++ 0.328255 3.006864 0.953788 1.783364 1.780691 0.277335 8.266916 +B 0.000000 3 32.283714 43.094425 139.509384 107.630524 5.166416 30.382307 33.205372 ++ 0.059810 5.272151 0.693828 1.387663 1.089351 0.442519 5.958887 +B 0.000000 2 64.594559 33.641876 65.235405 114.589676 3.314239 33.518677 34.798187 ++ 0.796683 4.248383 3.278826 3.515469 1.250955 0.738263 10.217293 +B 0.000000 1 32.214050 58.512325 138.457855 96.106598 9.891491 32.743149 20.467108 ++ 0.796683 4.248383 2.732355 3.515469 1.250955 0.738263 10.217293 +B 0.000000 1 64.123116 36.915024 24.327473 102.932098 11.464586 39.894230 14.508495 ++ 0.796683 4.248383 2.732355 3.515469 1.250955 0.738263 10.217293 +C 1.000000 205 66.983566 68.272964 91.106674 ++ 3.014141 4.729416 11.437462 +L 0.000000 202 95.496803 32.928974 69.801003 109.442352 ++ 2.371451 3.649918 6.655528 15.853985 +L 0.000000 186 1.175881 56.426819 3.655718 84.606247 ++ 1.912279 7.293790 2.222452 10.197557 +L 0.000000 182 63.407139 55.454166 132.576294 84.412376 ++ 1.127162 4.529580 7.787099 11.291574 +L 0.000000 1 30.632139 99.745369 67.413292 72.802345 ++ 1.403338 4.133480 5.185275 11.566928 +S 1.000000 205 62.853710 61.980221 63.940147 63.384159 65.720207 63.476105 ++ 3.970921 13.480095 4.679900 2.572918 9.905648 5.085258 + +14 1 +86 bc1 +11 ac1 +1 b68 +2 b41 +1 8c1 +73 bc0 +4 ac0 +1 bc8 +1 9c0 +1 fc0 +1 9d5 +18 9c1 +3 ac3 +2 9c5 + +E1 E +B 1.000000 208 0.621546 68.466873 39.886723 61.427719 39.579971 41.489647 106.648239 ++ 1.863834 2.795044 2.569830 7.257325 9.399488 12.137126 20.963348 +B 0.000000 206 0.321815 70.675499 99.803192 61.443638 47.657448 22.407839 98.232826 ++ 1.797525 3.836799 7.652798 6.337375 14.034439 12.042994 22.186701 +B 0.000000 130 63.727360 34.363403 70.129875 92.562408 5.205521 32.117378 101.668121 ++ 1.929142 4.331925 7.182187 9.865731 1.258508 1.754761 21.067539 +B 0.000000 5 32.502869 59.963596 134.832245 117.822739 4.510786 33.437969 33.285454 ++ 0.207256 6.635684 5.674180 7.668483 3.288104 2.272352 9.625632 +B 0.000000 2 96.239334 55.961525 2.035296 122.069427 2.170134 32.041275 43.396461 ++ 1.449439 4.399863 5.769749 7.479840 6.995135 7.051808 16.851479 +C 0.000000 2 70.535126 107.419189 59.296257 ++ 2.000000 2.000000 4.000000 +L 0.000000 206 0.010874 64.496658 2.013095 113.887611 ++ 2.209294 7.131270 1.521673 23.133926 +L 0.000000 204 95.485390 38.135506 69.416565 110.297737 ++ 2.582448 4.258591 7.954324 15.714702 +L 0.000000 201 64.076279 63.641823 133.732773 112.112831 ++ 1.335239 7.723197 7.156462 16.732857 +L 0.000000 93 63.654114 74.653572 21.340683 90.343544 ++ 1.790622 4.153300 2.668988 11.107650 +L 0.000000 82 1.756448 70.962715 111.522789 90.723465 ++ 1.059597 1.560074 5.032974 9.865894 +L 0.000000 78 63.173588 71.947174 80.947220 82.914848 ++ 2.123588 2.401040 6.291822 8.052228 +L 0.000000 78 1.726019 69.248764 58.578114 84.758827 ++ 1.220919 1.852264 3.289860 8.044182 +L 0.000000 3 30.193922 90.419579 87.184395 73.237129 ++ 3.059808 8.108371 8.601010 4.974157 +L 0.000000 1 87.962929 38.516815 43.179867 67.309097 ++ 1.364254 3.430180 4.675755 9.010175 +S 0.000000 206 68.495033 25.492044 63.611816 60.290329 62.454517 61.155697 ++ 9.316971 17.635366 7.842354 4.290982 11.328997 8.062267 +S 0.000000 2 114.252594 53.237236 74.921906 56.353523 62.303787 71.441406 ++ 7.764142 17.635366 6.535295 4.290982 11.328997 6.718555 + +22 1 +93 81c7 +14 83c7 +3 85c7 +1 8187 +1 85cf +2 9fcb +68 9fc3 +2 9fc7 +2 9dc3 +1 8fc3 +2 9bc3 +2 97c3 +1 87c3 +1 81d7 +2 80cf +1 a1c7 +4 80c7 +1 8397 +1 c147 +2 121e5 +3 8147 +1 88c7 + +F1 F +B 1.000000 206 115.301155 64.790070 40.196339 86.612831 25.795191 44.101723 94.240715 ++ 3.066037 3.573278 4.252824 13.558330 17.920603 13.213746 20.968071 +B 0.000000 202 121.727669 73.625572 99.819145 63.419048 56.594772 18.551991 105.468399 ++ 2.978142 3.424048 7.121233 5.845968 28.597666 28.384043 20.761259 +B 0.000000 129 63.758106 35.553059 69.545395 92.898392 5.430004 32.409382 103.048729 ++ 1.849876 2.877703 6.548615 12.393125 1.596354 1.331320 21.808432 +B 0.000000 4 118.529373 84.708405 59.072334 90.904106 17.673323 51.688282 40.018673 ++ 0.337701 1.058946 1.037020 4.753842 4.257622 6.630455 0.955953 +B 0.000000 2 96.683136 53.956264 2.130140 115.241287 4.253969 34.068897 28.603588 ++ 2.057939 2.733494 4.562722 8.621436 9.021046 8.715953 12.087753 +B 0.000000 2 32.179672 55.954041 136.227737 115.950554 4.339798 30.906315 29.935238 ++ 2.057939 2.733494 4.562722 8.621436 9.021046 8.715953 12.087753 +B 0.000000 1 64.425217 38.457798 49.498699 110.232040 2.918109 31.152859 34.521049 ++ 2.057939 2.733494 4.562722 8.621436 9.021046 8.715953 12.087753 +C 0.000000 4 71.548744 84.474884 28.010422 ++ 0.968694 0.758647 1.930772 +L 0.000000 205 64.161484 64.552757 133.983597 116.738495 ++ 0.879226 4.167897 7.261358 19.703308 +L 0.000000 204 95.470161 39.634975 69.619270 107.987831 ++ 2.672341 3.416074 8.830455 18.071455 +L 0.000000 94 1.447367 72.036743 113.783005 89.082626 ++ 4.064810 4.044471 5.984875 11.430065 +L 0.000000 78 1.865354 69.055367 58.600273 81.072380 ++ 1.355587 1.997366 3.075839 6.875823 +L 0.000000 76 62.719486 71.735924 80.271080 81.333977 ++ 0.979289 1.780055 2.965509 6.540665 +L 0.000000 59 0.176032 51.527431 3.372638 70.107887 ++ 1.538591 2.173438 1.839187 3.389806 +L 0.000000 9 32.930145 81.339554 76.431839 65.253937 ++ 1.414256 0.311082 1.564924 0.996107 +L 0.000000 1 36.734104 61.811520 37.096832 64.654968 ++ 1.397529 2.088322 4.117707 7.407101 +S 0.000000 196 104.535675 3.043463 100.887169 77.856400 57.924084 111.755852 ++ 13.280718 10.772495 7.431593 9.852968 13.244255 7.485463 +S 0.000000 10 99.459114 39.706944 102.099709 68.095032 65.925156 100.103699 ++ 5.163695 4.287267 1.919487 3.373934 2.604921 3.028552 + +19 1 +50 10307 +38 12307 +12 10707 +3 12707 +6 22307 +4 2238d +67 11f03 +2 11b03 +2 11703 +1 11f23 +1 11d43 +4 10f03 +2 11f07 +1 11d07 +1 10627 +2 10317 +8 16307 +1 14307 +1 18307 + +G1 G +B 1.000000 201 3.155039 65.388573 69.906143 48.781979 65.739189 10.368743 127.904854 ++ 2.402769 2.106894 5.090991 17.412178 20.408934 8.509735 2.396605 +B 0.000000 70 98.817596 84.783295 3.150196 106.276611 11.234840 35.258343 32.714401 ++ 1.099743 3.264868 1.175280 5.107905 4.882269 4.298710 4.700566 +B 0.000000 68 121.673164 104.092865 45.409603 112.640350 7.603958 35.000187 41.084705 ++ 2.286614 3.432420 5.955954 3.624750 1.555457 2.907631 6.817499 +B 0.000000 58 31.638321 85.591133 131.801025 106.624352 7.604753 33.383568 27.084452 ++ 1.165284 6.159425 9.152044 3.533690 1.607331 2.967026 6.013548 +B 0.000000 2 48.385101 45.462772 121.264793 107.355331 6.065534 32.237602 16.488253 ++ 1.692780 3.653115 5.343567 4.130409 6.740546 4.170795 4.550116 +L 0.000000 9 65.380295 72.175674 125.699203 79.970131 ++ 1.535570 5.301487 12.156755 6.351846 +L 0.000000 5 126.902908 69.860405 4.083447 78.308800 ++ 1.369892 3.562361 1.595201 2.749864 +L 0.000000 5 28.471205 100.422447 37.550423 65.144913 ++ 1.294281 0.894858 0.703224 0.919594 +L 0.000000 4 33.974949 44.824013 68.147476 70.147186 ++ 0.905616 1.526078 3.626369 4.990133 +L 0.000000 3 61.603054 82.057838 59.878174 66.265457 ++ 0.248550 0.274646 0.601810 0.802332 +L 0.000000 1 72.644020 74.625763 27.390293 73.671394 ++ 1.070782 2.311886 3.736672 3.162754 +L 0.000000 1 21.596205 48.346550 93.977356 66.700989 ++ 1.070782 2.311886 3.736672 3.162754 +S 1.000000 201 45.260078 64.544365 43.624138 72.968933 58.365204 54.207195 ++ 5.498255 10.369446 12.351891 3.611915 4.347557 12.529223 + +19 1 +18 1009 +2 1049 +34 100d +30 1001 +29 1005 +6 1021 +2 1209 +1 120d +62 1003 +2 1013 +5 1083 +1 1041 +2 1101 +1 1403 +1 1109 +2 1045 +1 1825 +1 1121 +1 1025 + +H1 H +B 0.000000 198 96.412590 63.801083 35.308739 63.739048 40.785267 35.321903 121.755928 ++ 0.394981 2.670604 3.299278 5.002866 38.132866 5.394542 16.504023 +B 0.000000 183 32.463150 66.934761 105.091072 63.994980 32.304104 35.214775 121.191284 ++ 0.664328 2.795970 6.562027 4.865881 19.513763 5.097589 22.014385 +B 0.000000 125 63.790821 27.569605 68.868172 95.236519 4.855917 32.454262 98.683578 ++ 1.973171 4.486774 8.804080 10.250587 1.236695 1.606544 24.852764 +B 0.000000 124 127.649124 102.847214 67.931717 95.692833 4.967741 32.759823 99.167282 ++ 1.985641 4.821537 7.422298 10.791989 1.202401 1.649653 24.142128 +B 0.000000 15 32.498192 64.946915 89.408974 49.740730 96.517365 34.239952 67.438263 ++ 0.967742 0.990842 1.971600 2.967390 19.767395 2.809150 4.332727 +B 0.000000 7 96.324730 66.432022 2.158522 109.897263 7.387269 32.026718 33.879166 ++ 0.451413 18.655928 0.696371 5.596089 4.965043 2.179477 6.943583 +B 0.000000 3 32.435863 87.752075 136.858398 113.351486 4.735431 31.922161 28.720814 ++ 0.188414 3.270437 2.221512 3.856429 1.077827 1.471062 3.340962 +B 0.000000 2 32.017426 66.040588 115.289261 89.781311 30.906784 31.200253 30.089607 ++ 0.888467 5.384584 3.577845 5.454192 8.007089 2.809331 9.620093 +B 0.000000 2 32.686142 41.834709 137.164886 114.542282 4.711346 33.887077 27.931469 ++ 0.888467 5.384584 3.577845 5.454192 8.007089 2.809331 9.620093 +B 0.000000 1 32.188480 65.441200 115.513275 95.486526 24.736149 52.426640 37.314587 ++ 0.888467 5.384584 3.577845 5.454192 8.007089 2.809331 9.620093 +C 0.000000 3 65.009193 89.090492 30.256342 ++ 0.746414 0.550840 1.859532 +C 0.000000 3 65.905960 34.854500 36.710323 ++ 0.702741 1.047042 2.234225 +L 0.000000 197 95.481071 32.287575 67.922989 107.656006 ++ 2.202286 3.762518 10.715261 17.277712 +L 0.000000 196 31.437145 98.036591 67.420631 107.305534 ++ 2.754176 3.961678 7.272979 15.082175 +L 0.000000 58 64.859253 65.770607 82.130363 75.750053 ++ 1.360030 1.487346 2.216424 5.856421 +L 0.000000 57 1.140238 64.227295 59.893917 76.285522 ++ 1.530444 2.482161 2.363244 5.265131 +L 0.000000 1 125.981941 82.068138 4.471076 67.177208 ++ 1.679317 2.733816 3.853519 10.150455 +L 0.000000 1 101.096672 25.492315 108.230591 75.279045 ++ 1.679317 2.733816 3.853519 10.150455 +S 1.000000 201 71.232788 64.251358 62.628044 61.543720 63.652512 61.929913 ++ 10.779164 4.532114 6.043279 2.919323 4.093980 4.706204 + +23 1 +91 4300f +2 4200f +13 4301d +1 43e2c +1 43cac +1 53cac +2 4101d +50 4f003 +1 4d00f +4 47003 +1 4d00b +1 4f00b +17 43003 +3 4b003 +1 43007 +1 42007 +1 4f007 +4 4302f +1 4314f +2 4304f +1 4310f +1 4100f +1 6200f + +I1 I +B 0.000000 145 63.731911 50.608379 69.293427 88.539345 6.384907 32.967434 108.139603 ++ 1.875577 3.547762 5.741463 13.234223 3.100652 1.661539 25.397213 +B 0.000000 144 127.711418 79.949707 68.526306 89.478310 6.297720 32.807446 107.706009 ++ 2.260533 3.162815 14.373590 13.083604 3.022631 1.654224 24.793398 +B 0.000000 3 96.404533 68.510651 1.376882 111.968254 5.125764 34.443886 31.017952 ++ 0.300463 2.126298 0.974554 2.644329 0.980213 1.203509 3.850338 +B 0.000000 1 32.177345 59.219418 140.000000 109.282249 6.749842 35.888580 29.238388 ++ 1.353273 2.262140 4.549241 9.654052 2.367832 1.506424 10.965275 +A 0.000000 78 62.216644 54.121761 67.083206 ++ 3.864152 5.910217 2.270337 +A 0.000000 1 65.750832 105.742950 135.248840 ++ 3.864152 5.910217 2.270337 +L 0.000000 221 63.679565 65.302299 132.601944 110.572998 ++ 4.071386 3.839251 19.082695 14.901368 +L 0.000000 220 127.403717 65.148651 2.951462 110.426422 ++ 4.053876 1.882257 2.014740 14.843809 +L 0.000000 217 95.664886 57.148285 68.111755 109.177727 ++ 2.145334 2.995365 9.522577 16.647415 +L 0.000000 216 31.594912 73.328217 68.070778 109.012009 ++ 2.618171 2.947455 8.750565 14.859745 +L 0.000000 1 116.529991 63.374027 125.495941 115.766502 ++ 2.774549 2.067518 5.173566 13.593350 +L 0.000000 1 27.118732 69.521736 130.575958 88.908096 ++ 2.774549 2.067518 5.173566 13.593350 +L 0.000000 1 101.299072 50.659184 103.721306 73.821968 ++ 2.774549 2.067518 5.173566 13.593350 +S 0.000000 218 64.688828 65.169502 66.226593 64.528122 64.646881 66.438980 ++ 4.905847 14.731775 6.848619 2.388548 7.748479 9.255993 +S 0.000000 4 72.637054 29.809931 76.356125 66.352112 41.498940 76.632225 ++ 1.663736 9.392118 4.754256 1.520117 3.072491 5.958482 + +17 1 +131 23c3 +2 22c3 +69 23d0 +1 2350 +1 21c2 +1 22d1 +1 23d1 +1 21d2 +4 43d0 +1 22c1 +1 2390 +1 23c7 +4 21c3 +1 2347 +1 32c7 +1 23cb +1 2fe3 + +J1 Jj +B 1.000000 200 56.069103 60.115685 60.757832 80.257034 17.110163 62.074753 115.375275 ++ 3.856066 5.081961 8.463440 5.998817 8.823739 12.296677 14.521312 +B 0.000000 119 123.463753 86.187920 86.707657 109.335449 6.507830 36.183376 69.433945 ++ 2.098660 2.979004 8.111422 6.490946 2.802080 4.111717 14.210709 +B 0.000000 3 32.603413 65.158775 130.431381 107.178894 7.572381 34.670113 22.436266 ++ 0.154477 4.386211 8.352679 1.667101 2.897305 3.235367 3.603629 +L 0.000000 197 95.177612 69.152847 72.785500 100.396828 ++ 2.231338 6.347124 7.609317 13.992295 +L 0.000000 197 30.681868 84.195541 67.763878 100.533134 ++ 2.349552 4.525193 6.143397 10.344693 +L 0.000000 118 64.327988 70.580315 132.648575 91.138275 ++ 2.200659 3.635358 7.906227 9.169740 +L 0.000000 26 127.089302 61.369274 2.329300 74.028656 ++ 3.134659 2.854444 2.041913 6.503720 +S 0.000000 80 1.319654 127.829491 127.967506 53.124184 74.577415 127.985962 ++ 4.851962 4.328771 0.718627 3.015551 7.120522 0.310444 +S 0.000000 60 58.294743 124.197296 103.307671 63.220585 58.429710 121.677696 ++ 6.632097 7.057037 3.822524 2.163220 4.322543 5.753354 +S 0.000000 34 52.138687 94.018448 122.215286 75.495003 44.306946 117.486565 ++ 5.520212 5.310378 4.757266 4.583292 3.811070 8.627700 +S 0.000000 16 60.834652 80.444954 90.028534 66.221779 59.485443 94.981796 ++ 5.649368 16.733793 2.851010 2.589207 5.186578 4.437696 +S 0.000000 6 57.009125 88.496002 74.043129 77.269638 48.355637 62.753590 ++ 1.950110 4.355215 4.734673 3.554889 2.176146 6.203824 +S 0.000000 4 66.233742 83.209351 99.891769 65.376205 56.972855 111.875519 ++ 2.415316 11.959734 3.173860 1.443104 2.411601 3.177782 + +16 1 +31 23b +5 83b +1 21f +2 233 +1 87b +65 99 +15 d9 +46 13b +8 17b +1 11f +3 12b +1 13f +4 103b +15 43b +1 47b +1 171 + +K1 K +B 1.000000 202 0.922808 90.205666 69.819870 98.538910 17.831490 37.196934 124.547310 ++ 1.809845 3.615227 4.939116 3.566799 2.612902 2.589558 15.039881 +B 1.000000 202 32.512516 61.288742 112.719391 81.117508 39.380966 72.201408 82.967949 ++ 1.176430 5.791445 5.365693 19.616121 15.490292 21.233660 26.971098 +B 1.000000 202 96.317924 60.322105 26.303289 77.491112 41.068104 9.409250 100.920959 ++ 0.483165 3.180555 3.167563 5.877237 13.628953 10.229129 20.309250 +B 0.000000 125 63.665829 28.101809 68.789024 93.169586 5.165390 32.483521 101.146713 ++ 1.968598 5.501067 10.474546 11.474735 1.313885 1.389120 25.193336 +B 0.000000 2 96.744781 89.227005 0.726551 114.291435 4.341167 33.829990 30.058640 ++ 1.235603 4.130129 4.883508 7.140604 6.180622 8.860367 15.217919 +B 0.000000 2 32.127388 64.021317 139.743729 114.883156 4.562090 33.929520 29.344612 ++ 1.235603 5.947386 4.883508 7.140604 6.180622 8.860367 15.217919 +B 0.000000 2 96.512207 41.266251 3.322836 112.998741 5.040458 33.625957 31.082695 ++ 1.235603 4.130129 4.883508 7.140604 6.180622 8.860367 15.217919 +C 0.000000 20 62.941532 91.768616 20.139555 ++ 0.835638 2.205178 2.175555 +L 0.000000 199 95.557297 33.250511 68.264366 108.306381 ++ 2.166877 4.827687 5.130848 14.162503 +L 0.000000 139 44.556602 84.226784 43.758373 88.209618 ++ 2.615451 3.650286 5.627411 9.351332 +L 0.000000 112 108.925652 72.490486 29.813316 73.151039 ++ 1.461333 5.197793 4.018307 4.710346 +L 0.000000 43 80.606422 67.258804 108.121254 79.152534 ++ 1.421365 2.335194 2.929753 19.728493 +L 0.000000 33 14.459178 81.462906 108.963364 74.568954 ++ 1.150234 3.765849 4.505030 4.946226 +L 0.000000 3 62.714447 51.395077 119.426331 68.572258 ++ 0.975884 0.699995 1.595845 1.025290 +L 0.000000 1 94.160393 31.337336 95.567833 72.494217 ++ 1.551782 3.167021 3.464389 8.439351 +S 1.000000 202 63.351295 36.179790 55.685787 66.728104 48.215099 59.414413 ++ 11.263636 16.140781 8.776192 2.130862 10.287426 13.439376 + +29 1 +22 870f +24 830f +4 850f +7 818f +4 838f +3 878f +2 858f +1 a18f +2 a38f +1 848f +38 8f07 +6 8507 +27 8707 +1 8e0f +2 8107 +1 8307 +1 c407 +1 8b07 +1 8d07 +5 950f +13 910f +13 930f +16 810f +1 9b0f +1 832f +1 816f +2 811f +1 934f +1 8d0f + +L1 L +B 1.000000 203 10.503797 71.129082 60.208942 90.260048 21.377247 14.802641 127.779961 ++ 1.639296 4.071436 4.173936 6.873081 4.095449 7.333723 4.147608 +B 0.000000 127 63.697701 34.828148 68.765869 93.654839 5.412713 32.473740 101.435722 ++ 2.028054 3.993610 7.714600 12.496235 1.814422 1.802564 22.179710 +B 0.000000 4 96.648392 67.031151 1.590984 113.340179 3.374545 31.140820 39.300346 ++ 0.269735 10.155424 0.878007 5.209890 1.325606 0.815593 10.686944 +B 0.000000 1 32.704029 43.930107 139.190643 110.040901 4.629250 34.925438 25.830778 ++ 1.312362 6.073490 3.826926 7.498833 2.411826 3.317293 9.159514 +L 0.000000 202 0.129830 65.269577 1.464747 113.419167 ++ 0.878898 4.286120 1.831553 14.969489 +L 0.000000 199 95.618576 39.716553 68.256912 107.391014 ++ 2.217308 4.407716 9.910080 14.485023 +L 0.000000 198 31.798206 56.673519 71.846214 102.373215 ++ 3.117137 3.510159 8.276672 12.995839 +L 0.000000 83 63.415001 75.060165 20.792707 89.207458 ++ 1.624740 3.956342 2.266922 9.334229 +L 0.000000 52 64.285347 51.086418 128.765533 69.530365 ++ 1.354086 3.219578 9.081452 3.258969 +S 0.000000 201 21.435600 0.662108 16.575356 51.219257 68.247253 1.532655 ++ 13.314156 2.321880 7.227098 7.665976 19.659939 5.748249 +S 0.000000 2 17.397543 12.913129 15.317608 63.540520 79.012192 15.930958 ++ 13.314156 0.933111 7.227098 7.665976 19.659939 3.326533 + +17 1 +40 373 +2 3f3 +1 367 +3 333 +64 273 +8 2f3 +2 353 +2 573 +69 2f1 +4 271 +1 2b1 +1 2d1 +1 2d3 +1 2f5 +1 27b +2 377 +1 233 + +M1 M +B 1.000000 207 96.402687 81.319336 37.982403 72.308357 87.515556 102.453293 93.182587 ++ 0.592211 3.699741 6.852403 4.016215 16.957676 25.838169 22.669588 +B 1.000000 207 32.380962 66.814651 102.800270 79.979500 52.018478 41.313801 116.010216 ++ 0.496791 2.248808 6.332316 2.751557 10.137780 12.572024 20.575209 +B 1.000000 207 96.360397 43.599045 37.794094 73.260925 82.143127 10.562322 91.939491 ++ 0.524336 5.963651 6.763668 2.838024 15.540998 16.181522 24.561419 +B 0.000000 130 63.383480 20.458080 69.141510 95.791496 4.955610 32.860500 97.641418 ++ 1.946652 7.623501 7.155935 10.589010 1.371004 1.977043 21.645630 +B 0.000000 129 127.713455 110.517174 67.979668 94.910164 4.947242 32.289989 100.119858 ++ 2.000744 8.208732 9.101014 11.033073 1.130194 1.442399 29.877314 +B 0.000000 3 96.574265 103.453766 1.225816 112.620819 4.787362 34.498066 28.182579 ++ 0.268359 3.022474 0.462000 3.725561 0.450635 1.405447 4.407545 +B 0.000000 1 96.805321 27.080820 0.901076 109.860420 6.206563 35.566555 25.493589 ++ 0.926792 5.127818 5.647745 4.733867 7.496834 9.902767 16.942312 +C 0.000000 1 45.872948 65.978081 6.937196 ++ 2.000000 2.000000 4.000000 +L 0.000000 204 95.322906 24.946621 68.564819 108.006424 ++ 2.223368 6.098341 11.056107 17.472424 +L 0.000000 202 31.442953 105.577644 66.909714 107.963974 ++ 3.340013 6.434025 8.734090 16.792114 +L 0.000000 169 39.211292 53.486229 89.936989 93.446640 ++ 2.660688 3.128756 6.793083 9.697290 +L 0.000000 168 94.973091 90.947632 47.051918 75.057327 ++ 2.717964 3.786453 6.850090 6.150179 +L 0.000000 165 87.526741 78.502441 91.743042 96.988144 ++ 2.108618 3.640857 6.318028 11.280887 +L 0.000000 165 23.319162 78.376518 44.224972 89.898766 ++ 1.923135 2.376561 5.105190 9.167065 +L 0.000000 157 103.286934 47.735264 45.837780 85.363434 ++ 2.851790 3.372018 7.061068 11.853833 +L 0.000000 151 31.028793 35.213253 48.018692 75.988487 ++ 2.529224 6.219945 8.412397 5.817601 +S 0.000000 206 65.477654 64.199188 61.920620 62.159191 67.541016 62.915169 ++ 7.368952 5.417107 3.707662 2.715301 4.486103 5.705567 +S 0.000000 1 59.958992 85.682816 52.622051 61.777477 73.098694 48.037025 ++ 6.140793 4.514256 3.707662 2.715301 4.486103 3.962199 + +34 1 +13 18b1f +1 1831f +1 18a1f +3 10b1f +1 1cf1f +1 18f1f +19 1031f +1 1011f +56 1ff07 +6 17f07 +1 1ff0f +1 1d707 +6 13f07 +1 15f07 +1 13d17 +1 17f0f +1 1b707 +1 13707 +1 13e8f +1 17f17 +1 1bf07 +2 1f707 +56 1ff1f +11 17f1f +2 1ff3f +1 17f3f +1 1ef1f +1 1ef5f +1 1fd1f +3 1771f +7 1f71f +2 1f51f +1 1f61f +1 2ff1f + +N1 N +B 1.000000 206 96.405304 57.759102 35.220852 77.809174 52.917107 2.817539 118.367554 ++ 0.674943 4.078711 8.961642 5.126726 29.183546 6.220316 17.471992 +B 1.000000 206 32.380764 74.049255 101.694054 77.196594 54.128292 2.832371 117.095528 ++ 0.537282 2.607702 7.902534 4.452274 28.994640 8.368559 17.092716 +B 0.000000 126 124.660713 101.064873 81.977806 111.731392 4.601391 34.769215 70.975861 ++ 1.864745 3.861554 9.901540 4.759022 1.207017 1.544035 14.257932 +B 0.000000 126 63.704712 27.304585 69.610748 95.341461 5.061816 32.782341 100.480026 ++ 1.720401 4.073384 6.492799 10.095707 1.019425 1.532166 16.881691 +B 0.000000 1 125.752914 104.314590 102.580116 82.278214 3.968675 41.814995 34.274128 ++ 1.176956 3.485391 6.958188 4.253951 4.970026 3.949632 14.047361 +L 0.000000 205 30.890526 97.570198 61.788120 116.020790 ++ 1.946277 3.091000 11.282987 16.546560 +L 0.000000 204 95.383606 32.267727 66.903671 106.530777 ++ 2.576127 3.427806 9.452257 17.149954 +L 0.000000 204 108.523605 68.013168 44.852917 106.484657 ++ 1.957334 4.978343 10.567242 18.243019 +L 0.000000 199 44.213978 64.079834 92.549324 100.094383 ++ 2.311935 4.858807 7.980399 16.930664 +L 0.000000 168 31.054127 43.415668 50.325954 80.884277 ++ 2.337368 4.551823 7.933669 7.802095 +L 0.000000 148 94.840393 86.706856 88.955368 80.318970 ++ 2.748110 4.279626 6.535573 9.019087 +L 0.000000 1 101.660728 23.408854 106.180687 70.899933 ++ 2.177079 3.427147 6.742916 11.666628 +S 0.000000 163 71.654655 60.160355 44.650108 67.617653 61.985069 40.178410 ++ 9.173604 6.065896 9.329595 3.875578 4.146365 8.388556 +S 0.000000 43 71.456413 53.055115 65.674980 67.431015 63.067131 69.911819 ++ 5.551356 4.077484 3.166984 2.374385 3.599844 5.996542 + +20 1 +19 27ef +4 23ef +19 21ef +1 216f +66 17e3 +6 15e3 +1 17e7 +4 13e3 +3 11e3 +41 17ef +1 17fb +4 16ef +1 176f +3 12ef +21 13ef +1 11ef +8 15ef +1 1faf +1 13cf +1 13af + +O1 Oo +B 0.000000 1 80.855591 33.755211 23.499428 106.341064 6.405560 34.770401 15.527055 ++ 2.000000 4.000000 4.000000 20.000000 20.000000 20.000000 8.000000 +C 1.000000 208 65.297829 68.971672 103.309731 ++ 1.082438 6.024603 13.005810 +L 0.000000 3 96.955147 23.365103 75.354973 68.787613 ++ 1.893455 0.756982 3.720397 1.301407 +L 0.000000 2 126.582230 63.482170 3.464843 64.951241 ++ 1.893455 0.756982 3.720397 1.301407 +L 0.000000 1 33.774731 109.490723 81.145309 67.383995 ++ 1.893455 0.756982 3.720397 1.301407 +S 0.000000 200 64.529205 64.644661 64.080574 64.584656 64.659744 63.002102 ++ 2.516138 2.448725 3.039648 1.750610 1.738716 3.385557 +S 0.000000 5 65.449142 63.788948 37.967308 65.445114 63.873741 35.403297 ++ 1.897149 2.195265 3.701819 1.574273 1.579138 2.549920 +S 0.000000 2 65.389008 62.854656 49.841103 65.260483 63.192635 49.046978 ++ 1.996965 2.321995 2.730437 1.516557 1.658927 2.254577 +S 0.000000 1 64.582314 65.979256 90.841286 66.017258 65.999939 90.134064 ++ 1.996965 2.321995 2.730437 1.516557 1.658927 2.254577 + +8 1 +193 22 +1 23 +2 2a +5 42 +1 102 +2 82 +3 26 +1 32 + +P1 Pp +B 0.000000 200 115.200249 66.196686 40.791283 95.455109 20.757116 25.623564 91.694710 ++ 3.092536 5.491959 6.147311 13.474126 15.915850 11.257828 18.192921 +B 0.000000 121 63.865883 36.749531 68.927856 93.114891 5.320520 32.250664 101.655235 ++ 2.045873 2.569396 6.179256 9.437428 1.494004 1.681998 10.968609 +B 0.000000 2 96.535538 54.427124 2.476098 114.898590 4.129652 34.252285 29.338924 ++ 2.569204 3.816561 6.163284 9.397229 4.584735 6.329746 13.064688 +B 0.000000 2 64.387505 36.544682 72.953217 123.774185 1.844595 32.418621 44.549831 ++ 2.569204 3.816561 6.163284 9.397229 4.584735 6.329746 13.064688 +B 0.000000 1 32.475121 63.864410 140.000000 108.141068 5.085681 29.486134 25.486736 ++ 2.569204 3.816561 6.163284 9.397229 4.584735 6.329746 13.064688 +B 0.000000 1 117.132065 65.375252 83.863075 62.452129 48.075977 128.000000 128.000000 ++ 2.569204 3.816561 6.163284 9.397229 4.584735 6.329746 13.064688 +C 0.000000 200 68.858208 96.705765 48.065876 ++ 4.079711 9.242568 6.039643 +L 0.000000 198 63.431522 59.975327 133.766434 94.087807 ++ 2.743745 9.865398 7.747847 11.079084 +L 0.000000 196 95.612350 39.568039 70.326042 109.172592 ++ 2.144424 3.774794 7.342549 14.160185 +L 0.000000 117 4.534685 70.760124 61.421600 79.133743 ++ 2.033498 4.549158 8.352631 9.104604 +L 0.000000 68 0.528305 52.411335 3.678777 70.228943 ++ 1.647998 2.476103 2.542956 3.577323 +L 0.000000 1 100.820755 36.143684 104.691689 73.056450 ++ 1.647413 3.525989 6.190556 9.480299 +L 0.000000 1 32.820057 52.193089 70.155869 91.961540 ++ 1.647413 3.525989 6.190556 9.480299 +S 1.000000 201 123.857620 35.958355 115.134850 71.260078 62.260490 101.520233 ++ 13.461586 11.194516 10.808880 8.372733 8.929010 10.689560 + +15 1 +44 25c3 +29 21c3 +17 27c3 +2 26c3 +2 2543 +2 24c3 +73 23c1 +2 23c9 +4 21c1 +1 2351 +21 23c3 +1 21c7 +1 23c7 +1 2cc3 +1 31a2 + +Q1 Q +B 0.000000 196 126.671883 93.581284 12.375675 92.649178 28.818764 19.739758 58.070087 ++ 1.687200 7.992828 12.892457 14.641164 21.636248 13.055047 18.774822 +B 0.000000 132 94.698166 69.051239 -11.293993 110.588852 7.042349 33.841473 40.853527 ++ 1.918222 14.019607 14.157640 4.397501 2.064634 2.347951 6.590279 +B 0.000000 43 70.711540 42.013027 7.107338 96.693962 18.411970 48.942688 53.683975 ++ 2.430771 5.053236 3.163735 6.407182 5.699237 7.286918 9.593804 +B 0.000000 18 64.295982 27.475599 2.388128 98.458420 18.064516 36.109425 73.603661 ++ 0.363283 1.089276 1.770146 2.799878 2.149716 3.944280 4.021183 +B 0.000000 15 81.526459 48.374252 -1.619545 104.356018 6.663435 33.436306 23.239080 ++ 1.044949 3.620834 2.093310 4.535473 1.460323 1.899997 2.725607 +C 1.000000 201 65.245903 69.966576 100.852524 ++ 1.203003 6.316038 13.214554 +L 0.000000 70 123.106819 70.833733 -5.514468 84.258553 ++ 3.415844 10.655759 12.976923 13.569614 +L 0.000000 1 29.857773 102.539803 64.312744 86.795532 ++ 3.415844 7.399832 12.976923 13.569614 +L 0.000000 1 58.741367 74.828156 127.251251 64.539932 ++ 3.415844 7.399832 12.976923 13.569614 +L 0.000000 1 91.018692 30.876600 92.555710 66.548737 ++ 3.415844 7.399832 12.976923 13.569614 +S 0.000000 182 78.505989 68.385262 57.236965 68.482170 65.875145 54.620438 ++ 12.876821 4.388086 4.961297 4.977904 2.622931 4.405883 +S 0.000000 12 81.264030 60.912075 92.504784 64.671516 64.346878 90.119049 ++ 2.855279 3.230215 3.562971 1.632845 2.083990 4.136690 +S 0.000000 7 80.283073 63.492565 74.451668 69.693512 62.705688 68.081688 ++ 2.062692 2.639199 2.153079 2.974016 1.511574 2.696329 + +21 1 +36 427 +4 467 +50 423 +46 461 +10 463 +1 561 +1 623 +4 1023 +1 1063 +4 422 +1 462 +1 4a3 +5 471 +6 421 +10 431 +1 86b +6 42b +9 82b +2 827 +2 102b +1 465 + +R1 R +B 0.000000 202 96.382858 63.248280 30.925798 73.341545 38.717113 13.209373 104.708977 ++ 0.438597 1.894194 3.475022 3.868474 19.042379 15.616225 21.069502 +B 0.000000 202 2.334869 93.441170 54.335045 102.427177 12.207522 37.752010 78.188232 ++ 2.451605 2.843181 7.641466 8.599174 4.956017 3.848588 20.629154 +B 0.000000 128 63.760597 30.401415 69.888100 93.370331 5.136676 32.406567 100.031631 ++ 1.950062 4.962067 8.571724 12.144820 1.504801 2.541912 24.440317 +B 0.000000 3 95.855209 62.710384 66.237068 61.991150 124.077507 29.368160 128.000000 ++ 0.553698 0.581907 0.512077 0.432998 5.547243 2.134044 0.001000 +B 0.000000 1 32.200596 50.129978 120.666946 109.501709 4.110616 30.005169 29.369476 ++ 1.348491 2.570337 4.374523 5.755332 5.694289 5.767467 13.960839 +B 0.000000 1 96.060074 39.282505 2.907217 112.357994 3.822078 32.533020 34.198513 ++ 1.348491 2.570337 4.374523 5.755332 5.694289 5.767467 13.960839 +C 0.000000 202 65.537636 97.869759 49.826363 ++ 4.610379 8.105873 6.247357 +A 0.000000 2 32.552097 128.000000 -15.569940 ++ 8.000000 8.000000 4.000000 +L 0.000000 202 95.527298 34.917274 70.304245 107.931160 ++ 2.113482 4.347855 9.992728 17.326302 +L 0.000000 184 63.602734 56.635418 134.008179 86.679901 ++ 0.989322 4.433349 7.585451 10.695612 +L 0.000000 67 109.876114 71.909081 31.529078 73.498642 ++ 1.383192 3.089033 5.268137 6.566919 +L 0.000000 35 35.531555 93.309456 42.267723 75.003815 ++ 3.593707 2.059659 9.005785 16.402670 +L 0.000000 4 1.593768 61.965149 55.991592 68.934769 ++ 2.062087 1.370405 1.100420 1.486065 +L 0.000000 3 32.827698 50.870155 71.303741 113.654366 ++ 0.372427 1.634847 2.522250 2.856658 +L 0.000000 2 64.904228 44.743938 -15.932044 128.000000 ++ 1.681929 3.940874 4.944945 7.905763 +L 0.000000 2 0.326696 44.730202 -15.507660 128.000000 ++ 1.681929 3.940874 4.944945 7.905763 +L 0.000000 1 27.543579 89.028976 85.439018 68.420074 ++ 1.681929 2.736718 4.944945 7.905763 +S 1.000000 205 95.558525 52.339520 61.621346 65.949333 59.353848 58.353600 ++ 16.830473 14.095015 4.965788 5.422380 12.300901 7.372688 + +22 1 +1 20557 +50 20747 +7 20547 +46 20347 +11 20147 +1 20647 +44 20343 +26 20b43 +1 20a47 +1 20b47 +3 20b41 +3 21343 +1 21143 +1 20767 +1 20247 +1 2c3c7 +1 2c7c7 +1 30747 +1 22d0e +1 22f0e +2 20f47 +1 2270e + +S1 Ss +B 0.000000 202 1.188716 71.936638 97.869438 69.783844 45.163380 113.178917 106.203384 ++ 1.860239 10.965161 19.036720 15.682381 14.261122 44.025043 38.340115 +B 0.000000 196 62.884800 59.027657 43.401836 68.146942 54.192783 104.615128 118.819527 ++ 5.004175 6.430125 4.433028 10.304248 18.661673 24.606012 36.533443 +B 0.000000 51 96.242401 47.988190 1.467312 106.276489 6.955690 33.869240 24.496185 ++ 0.885401 3.740575 1.187112 5.260326 2.928065 2.384933 5.188639 +B 0.000000 37 32.451897 80.351814 130.594620 107.641464 6.945059 33.792454 24.294846 ++ 0.897735 5.638843 9.043820 6.097647 2.253736 1.708581 4.474109 +B 0.000000 6 62.655930 39.097492 59.717915 92.445625 24.882990 48.052345 38.820137 ++ 0.230754 1.031670 1.128824 2.306403 2.249761 3.511749 3.612220 +C 0.000000 7 64.864426 36.672012 38.464634 ++ 0.751268 1.547131 1.428359 +C 0.000000 5 67.897339 82.793564 35.601429 ++ 0.979596 2.106680 0.719119 +A 0.000000 2 120.591827 96.577530 35.977310 ++ 8.000000 8.000000 4.000000 +A 0.000000 1 109.674515 110.916519 83.493172 ++ 8.000000 8.000000 4.000000 +A 0.000000 1 32.596500 104.885567 -27.085205 ++ 8.000000 8.000000 4.000000 +L 0.000000 165 54.216469 71.710327 82.085159 87.442589 ++ 4.603840 3.575758 8.115684 17.896193 +L 0.000000 163 118.279327 60.748459 62.150974 90.747322 ++ 3.674863 3.776781 7.177567 17.688887 +L 0.000000 56 63.518280 71.638771 132.499496 81.064018 ++ 1.855175 4.607435 10.184869 7.046924 +L 0.000000 49 126.866806 60.193394 2.431904 84.423607 ++ 3.470381 3.912618 3.041456 10.604158 +L 0.000000 6 64.729591 65.996353 20.502205 65.443459 ++ 4.910754 4.763772 2.771997 0.854918 +L 0.000000 4 58.923359 57.870079 38.514221 103.553802 ++ 4.039128 5.258115 10.315651 11.278562 +L 0.000000 4 118.685860 73.994278 125.471031 70.084534 ++ 1.745782 4.172299 4.163522 4.073532 +L 0.000000 2 103.805222 51.608109 40.969109 92.008751 ++ 3.210317 4.095630 6.538678 9.920453 +L 0.000000 1 23.014330 75.161858 -27.679291 128.000000 ++ 3.210317 4.095630 6.538678 9.920453 +L 0.000000 1 87.448387 74.562881 -27.372467 128.000000 ++ 3.210317 4.095630 6.538678 9.920453 +S 1.000000 204 61.990479 67.645592 40.799133 62.984558 66.008972 54.474781 ++ 9.033048 5.727123 11.791913 3.398157 4.192152 13.478928 + +55 1 +4 10080f +6 101c07 +5 100c0f +5 10040f +6 100c0b +2 10040b +3 101807 +14 102c03 +3 101407 +1 10080b +1 101035 +1 100435 +3 101075 +2 102c0b +4 103403 +1 103843 +1 101047 +1 10000f +1 128080 +1 128d80 +1 103031 +2 103803 +1 10002f +60 100c03 +7 100403 +12 100803 +1 100003 +2 104c03 +1 102803 +1 106c03 +3 102c07 +5 100c07 +1 10280f +1 103c07 +9 101c03 +2 102c0f +1 105c03 +1 113c07 +1 100807 +1 103c0b +2 101c0b +7 103c03 +3 101403 +1 100407 +2 10200b +1 104c0f +1 1c1e0b +1 101007 +2 102403 +1 110c03 +1 113c03 +1 109c03 +1 10ac03 +1 107c03 +1 111c03 + +T1 T +B 1.000000 211 69.617630 46.055031 76.093651 87.114655 15.299176 11.813261 119.700821 ++ 1.807599 3.384090 10.559005 11.415777 6.903582 9.715784 12.700701 +B 1.000000 211 121.654396 80.482437 75.822304 89.451454 13.969547 55.954903 114.442856 ++ 1.667965 4.280772 9.532109 10.772770 8.690506 20.627390 14.893758 +B 0.000000 4 32.377087 69.336014 127.240318 107.874916 2.985333 32.260117 38.454193 ++ 0.176061 3.188700 9.667073 9.493845 1.296774 0.910310 6.085696 +B 0.000000 2 32.080910 53.852470 130.361618 108.274780 4.634187 30.363949 37.261097 ++ 1.217209 3.617854 8.803223 10.560798 3.427317 9.271862 10.521124 +B 0.000000 2 96.589897 66.371590 0.243470 110.058937 4.587788 34.875168 31.177534 ++ 1.217209 3.617854 8.803223 10.560798 3.427317 9.271862 10.521124 +B 0.000000 1 0.817821 101.040337 95.739349 117.467468 4.943347 33.333427 24.884239 ++ 1.217209 3.617854 8.803223 10.560798 3.427317 9.271862 10.521124 +L 0.000000 210 31.445425 71.660416 63.989929 104.255089 ++ 3.096652 4.692239 9.314801 10.696708 +L 0.000000 207 64.113808 63.806103 134.626328 116.605873 ++ 1.032931 7.458431 7.450264 16.641773 +L 0.000000 206 95.800949 54.967129 65.911362 104.015785 ++ 3.673573 3.229089 6.930004 12.303946 +L 0.000000 64 0.247939 64.618324 2.455063 73.858032 ++ 1.284828 1.957960 2.228759 4.426506 +S 0.000000 167 116.034904 60.776981 67.376373 75.842911 65.662827 68.617699 ++ 11.710878 10.297946 4.178986 10.422496 3.541834 7.810457 +S 0.000000 37 119.717003 0.604624 66.378723 83.005562 62.837440 125.012619 ++ 10.682659 0.276746 4.646001 5.411488 4.680458 5.876487 +S 0.000000 4 112.447289 0.437475 55.640350 76.631142 54.339638 91.711899 ++ 6.693209 0.262848 4.033526 1.213251 2.675933 5.207195 +S 0.000000 3 128.000000 18.744410 74.785072 88.717690 69.790504 99.954277 ++ 0.001000 8.884405 2.241919 2.790701 1.450672 4.439412 + +15 1 +56 7c3 +1 7e3 +3 6c3 +2 7c7 +1 783 +1 74f +98 5c3 +3 21c3 +1 547 +36 9c3 +1 943 +1 5d3 +4 11c3 +2 4c3 +1 55b + +U1 Uu +B 0.000000 195 32.448677 66.364861 76.812241 65.319199 61.786823 36.603638 127.987221 ++ 1.046082 2.918569 8.143518 1.125942 10.635363 10.725098 0.754035 +B 0.000000 122 123.954758 100.230377 89.547325 112.385368 5.194806 34.890137 64.549232 ++ 1.918142 4.886857 9.896957 4.007516 1.236522 2.929662 14.531374 +B 0.000000 113 67.029938 28.245663 91.160530 112.449623 5.175857 30.128643 61.219463 ++ 1.885147 5.309309 10.937681 5.511119 1.403028 2.809291 10.331465 +B 0.000000 8 32.255493 65.680229 115.280243 94.910904 22.307491 34.400269 28.936478 ++ 0.482476 2.421824 2.831276 6.082849 8.768800 3.301986 8.223076 +B 0.000000 1 32.863663 81.739517 116.355347 114.134514 5.832016 31.199417 20.941633 ++ 1.222784 3.783230 7.613045 4.181857 5.067788 3.447139 6.774806 +B 0.000000 1 62.481289 29.118490 113.835396 90.678879 9.018719 14.173676 39.993587 ++ 1.222784 3.783230 7.613045 4.181857 5.067788 3.447139 6.774806 +C 0.000000 8 66.510811 62.211639 65.727562 ++ 1.129926 1.378038 1.150475 +L 0.000000 200 30.495995 95.650536 72.650490 97.312927 ++ 1.995693 6.260341 8.151268 13.660302 +L 0.000000 196 96.379700 31.518175 76.339966 92.210297 ++ 2.834743 5.826471 11.328707 11.207279 +L 0.000000 186 31.833464 47.176308 79.052528 90.794518 ++ 3.209248 5.356290 11.481705 12.435221 +L 0.000000 181 94.926010 84.804573 79.267570 93.089584 ++ 2.486971 5.242095 8.823127 13.167201 +L 0.000000 5 127.774879 64.984131 2.132859 70.170563 ++ 1.219791 3.491782 1.596171 2.512160 +L 0.000000 2 63.299110 42.352188 138.685699 64.393387 ++ 1.975778 4.510735 7.893472 8.908222 +L 0.000000 1 68.646416 70.093498 25.693298 66.493210 ++ 1.975778 4.510735 7.893472 8.908222 +S 0.000000 180 60.520065 45.381889 61.589668 62.947083 63.044842 60.186810 ++ 15.243749 20.329092 7.214071 6.618123 7.308066 5.541662 +S 0.000000 20 71.211243 32.637840 61.104839 66.333717 29.038000 68.856628 ++ 5.443220 9.369777 4.881378 1.772218 2.808933 6.042761 +S 0.000000 3 57.873905 30.786051 48.147430 64.929733 68.922050 37.729652 ++ 4.832115 3.921777 5.343809 2.349290 3.035033 3.796375 + +29 1 +68 4787 +6 4387 +5 41ce +1 414e +1 41cc +9 4783 +1 41de +66 4781 +1 6781 +3 4581 +4 4f81 +3 4381 +1 4685 +1 4703 +1 4e81 +2 5787 +14 8787 +3 8687 +1 8487 +1 8187 +1 87a3 +3 4587 +1 4383 +1 4687 +1 10187 +1 10787 +1 4707 +1 10387 +1 4385 + +V1 Vv +B 0.000000 180 32.450447 66.586342 99.754372 78.491943 54.725708 43.428650 125.292145 ++ 0.579839 3.475478 7.812943 1.687337 5.699518 18.099239 5.444583 +B 0.000000 79 71.873894 35.799404 81.212799 114.696716 4.032866 30.716780 68.256714 ++ 3.294900 6.579791 9.204849 6.549133 1.120226 1.587764 19.083385 +B 0.000000 70 118.093575 89.282845 82.148529 116.234322 3.065981 33.498417 60.826054 ++ 2.014150 14.639586 14.547703 5.265099 1.269894 0.574979 16.929647 +B 0.000000 16 32.281704 66.613007 80.855583 74.491409 65.369644 26.082855 82.317337 ++ 0.869336 0.951522 2.391179 3.406343 14.125274 8.020597 5.637372 +B 0.000000 4 32.409000 60.175255 116.534698 102.989845 13.789852 30.935995 32.343460 ++ 0.117204 4.309233 1.323477 6.555231 8.016945 1.449196 8.566526 +B 0.000000 2 114.049721 95.542496 110.649597 120.845352 3.857045 33.068642 33.371498 ++ 1.375086 4.475202 6.167004 4.474324 5.968767 5.893430 9.401773 +B 0.000000 1 112.633362 46.402237 23.490421 111.242622 3.299136 34.014736 25.918737 ++ 1.375086 4.475202 6.167004 4.474324 5.968767 5.893430 9.401773 +B 0.000000 1 32.730854 37.993519 140.000000 116.982353 5.422155 32.685329 33.192520 ++ 1.375086 4.475202 6.167004 4.474324 5.968767 5.893430 9.401773 +C 0.000000 4 65.712410 81.743279 40.423134 ++ 0.872252 2.223712 1.859943 +L 0.000000 193 23.896536 83.214859 62.650642 125.317688 ++ 2.517157 6.048098 5.860932 19.467518 +L 0.000000 181 38.129688 54.730171 85.307846 88.641838 ++ 2.915381 7.618084 7.539616 9.778925 +L 0.000000 181 103.560555 43.785088 64.055779 122.321930 ++ 3.441795 4.181841 7.589634 12.854869 +L 0.000000 162 87.944252 75.009392 84.748772 89.518272 ++ 2.524493 6.432214 7.638472 12.262012 +L 0.000000 19 95.487999 29.150770 63.271736 120.519844 ++ 0.643591 3.119128 9.344585 19.435310 +L 0.000000 3 28.466835 93.366524 98.012070 69.024467 ++ 0.696869 1.949669 1.358367 3.930207 +L 0.000000 2 18.397514 57.992760 42.816742 93.387253 ++ 1.788177 4.132973 5.801989 8.727941 +L 0.000000 2 61.561584 48.041908 114.358177 66.184456 ++ 1.788177 4.132973 5.801989 8.727941 +S 0.000000 100 114.059296 42.328957 58.791412 84.958412 67.627495 59.627094 ++ 9.859082 20.651695 5.178342 2.492852 3.315331 8.175756 +S 0.000000 58 74.203865 56.638237 61.698948 70.589294 63.682888 63.294159 ++ 9.437443 10.833864 6.577875 3.100785 2.382781 7.149182 +S 0.000000 20 68.762260 18.561647 60.772446 68.573387 34.928360 96.997261 ++ 2.485662 8.088118 3.814926 1.367648 1.871849 6.707728 +S 0.000000 15 101.504585 0.524211 57.752987 83.747520 17.792648 122.407608 ++ 6.343030 0.336403 4.285561 2.201013 3.277636 4.946648 +S 0.000000 5 103.636642 0.450019 117.591408 82.430496 50.680161 126.602829 ++ 5.493546 0.284722 5.465314 1.221298 2.191226 1.738692 +S 0.000000 1 59.625607 78.953644 41.502132 66.070389 64.737923 37.941105 ++ 6.723752 7.677832 4.345968 2.076719 2.607764 5.232770 +S 0.000000 1 84.105667 79.459808 60.985065 75.009033 75.352974 50.305267 ++ 6.723752 7.677832 4.345968 2.076719 2.607764 5.232770 + +32 1 +11 21e03 +25 21e07 +16 21e01 +10 21e05 +1 30b12 +7 20a0a +1 20a0c +1 21e0e +3 20a0e +3 20a08 +1 20b16 +1 20b12 +1 20e0e +1 30b14 +56 41e01 +1 41a01 +20 81e01 +1 805c01 +1 45c01 +1 405c01 +8 20e07 +8 20e05 +2 203603 +7 103607 +1 20b427 +6 103603 +1 203467 +1 102603 +1 203407 +1 109c07 +1 20e01 +1 20e81 + +W1 Ww +B 1.000000 202 96.320259 61.519367 28.629677 77.875336 58.640560 41.586662 105.791039 ++ 0.697591 6.317729 6.367503 2.731264 14.325352 14.732762 21.810978 +B 0.000000 197 32.541073 47.171371 100.055931 73.890648 71.814621 42.263626 100.688889 ++ 0.935476 4.261868 8.312105 3.000686 14.918360 43.127045 28.009933 +B 0.000000 195 32.427525 86.431778 100.386520 75.772453 66.319069 36.932667 102.287231 ++ 0.565180 6.080570 10.373857 2.898170 10.253114 18.176300 29.418575 +B 0.000000 48 67.959457 22.935959 80.847130 113.885918 4.374584 30.586878 66.690704 ++ 1.850095 10.141331 8.474865 5.756567 1.130702 1.364421 16.752451 +B 0.000000 43 121.150322 100.522339 78.007790 116.726654 3.227468 33.497147 56.931488 ++ 2.953305 6.884431 9.272946 4.833237 0.924988 0.654195 8.603382 +B 0.000000 5 32.469700 64.490555 116.385170 96.242348 20.732803 30.668287 25.319330 ++ 0.349351 0.690141 2.024914 0.603404 1.939721 3.847011 4.718565 +B 0.000000 5 32.504696 49.421402 140.000000 112.548279 5.400838 33.905781 29.891809 ++ 0.301016 20.302383 0.001000 4.530951 1.158882 2.018806 3.581755 +C 0.000000 4 76.313965 83.840714 17.987429 ++ 1.073407 3.778325 1.047562 +C 0.000000 3 65.654442 90.690529 34.074097 ++ 0.518498 1.283760 1.098922 +C 0.000000 1 64.417397 57.321793 8.142881 ++ 0.795953 2.531043 1.073242 +C 0.000000 1 58.227470 92.280113 21.372824 ++ 0.795953 2.531043 1.073242 +L 1.000000 202 25.641335 99.884491 63.664566 124.218239 ++ 2.482559 6.232855 10.285339 22.101025 +L 0.000000 201 100.818092 25.556528 66.426041 119.430443 ++ 2.618532 6.405405 9.397027 16.144991 +L 0.000000 164 36.632069 36.904556 92.370506 76.372200 ++ 2.554851 6.063678 7.281353 6.651093 +L 0.000000 156 101.444038 71.681816 41.005917 78.581627 ++ 2.855622 4.144624 6.107867 10.069719 +L 0.000000 146 36.884853 77.978386 91.803154 77.748611 ++ 3.460312 5.010828 4.338405 8.129366 +L 0.000000 122 24.299231 52.901421 40.868122 85.738655 ++ 2.510130 4.391317 5.075523 8.594226 +L 0.000000 119 89.728996 92.401833 90.761833 81.349609 ++ 2.571187 6.073613 5.817420 9.944326 +L 0.000000 82 89.536476 50.421421 93.443649 84.020874 ++ 2.684904 4.868265 2.786913 7.912714 +L 0.000000 1 99.047676 30.295692 36.910961 71.930862 ++ 2.542624 5.212882 5.101822 8.737996 +S 0.000000 161 82.334145 59.952595 60.746361 75.952660 58.824348 61.371601 ++ 11.557420 6.732090 4.816116 6.137715 3.916366 6.498829 +S 0.000000 20 70.571274 39.098740 58.778049 68.336731 43.090202 43.804409 ++ 3.213827 4.415861 9.158152 1.322325 1.073952 3.361606 +S 0.000000 19 90.429802 17.133827 60.548012 81.656532 39.183262 45.084415 ++ 8.380417 7.893133 2.204453 2.199413 3.135913 2.564263 +S 0.000000 1 91.372894 58.695118 35.953331 72.298531 61.743057 48.233368 ++ 6.863660 5.973023 3.544810 3.219817 2.708744 3.593764 +S 0.000000 1 85.381233 15.369962 51.949780 80.177757 36.690598 32.402733 ++ 6.863660 5.973023 3.544810 3.219817 2.708744 3.593764 + +64 1 +1 107807 +4 10180f +1 12381f +2 105817 +1 10380f +2 123807 +1 121807 +2 12581f +1 125817 +1 10580f +1 13180f +1 107817 +2 127807 +1 123817 +1 12380f +1 127a1f +5 101817 +1 10189b +1 101cb9 +1 101931 +5 10181f +1 1018b9 +1 801929 +1 101929 +1 101893 +53 17f807 +1 16b807 +1 137807 +3 16f807 +1 15f807 +1 13d807 +6 11f807 +16 27f807 +1 2fe80f +3 27d807 +2 115807 +17 10f807 +7 10b807 +3 12b807 +1 11d807 +1 12781f +1 11b807 +1 11f817 +1 13d817 +5 117807 +5 117817 +1 12f807 +1 13f857 +1 11f847 +1 13f817 +6 41f80f +2 47f81f +3 43f81f +5 43f80f +1 46f80f +1 43784f +1 103f85f +1 42781f +2 10b80f +1 10f80f +2 12b817 +1 12f847 +1 139807 +1 17f80f + +X1 Xx +B 0.000000 199 63.441895 37.940659 68.935318 103.117088 14.436301 32.181629 123.740028 ++ 1.789178 4.035739 5.117345 2.793677 1.949163 2.134255 10.127218 +B 0.000000 199 0.089180 92.921532 67.394783 103.169769 14.178935 33.533218 122.749229 ++ 1.975453 3.494328 5.059707 2.970582 1.773047 2.113807 8.519472 +B 0.000000 187 96.397812 62.300308 20.449444 83.686348 34.661690 34.771366 93.436966 ++ 0.461770 3.661850 4.684320 12.634250 19.594866 6.131474 20.639027 +B 0.000000 183 32.400982 68.593391 117.813576 84.424309 33.636116 33.512074 85.885056 ++ 0.508115 4.005278 5.303451 11.791836 12.876834 12.583853 23.512835 +B 0.000000 16 32.354271 66.986031 95.786026 57.842247 94.001564 31.793812 47.023327 ++ 0.570469 0.789400 1.251089 2.091203 10.550331 5.351053 3.968829 +B 0.000000 12 96.475594 65.587105 22.971172 53.565063 88.438141 26.919016 50.086590 ++ 0.297102 0.630559 1.533975 3.210857 4.134339 2.311650 3.659693 +B 0.000000 4 32.550503 47.587280 140.000000 112.182022 4.637251 34.287346 29.299910 ++ 0.133696 6.390588 0.001000 3.883593 0.878287 1.726325 2.765863 +B 0.000000 3 96.587524 93.634201 1.329960 115.936272 3.917478 33.577332 32.058479 ++ 0.233041 5.359047 0.582836 3.743755 0.725922 1.198979 4.134332 +C 0.000000 4 67.060333 93.446625 20.044735 ++ 0.729285 1.366308 2.028282 +A 0.000000 2 32.303947 128.000000 -5.838327 ++ 8.000000 8.000000 4.000000 +L 0.000000 59 41.903446 87.559105 43.786972 78.733551 ++ 2.762954 3.017034 7.193882 7.652588 +L 0.000000 53 83.798347 41.932095 39.459366 78.268417 ++ 2.241021 2.857794 3.342522 5.724767 +L 0.000000 35 105.676865 44.946884 92.447273 73.785339 ++ 1.871354 3.811701 6.521281 5.954125 +L 0.000000 28 20.621510 86.846573 94.599884 73.576691 ++ 1.946466 2.544159 5.586980 3.672610 +L 0.000000 1 112.919037 63.410732 -0.976793 91.345085 ++ 2.205449 2.898851 5.661166 5.751023 +L 0.000000 1 80.440079 63.166965 4.284106 91.486778 ++ 2.205449 2.898851 5.661166 5.751023 +L 0.000000 1 16.266388 65.984360 -0.526348 91.471451 ++ 2.205449 2.898851 5.661166 5.751023 +L 0.000000 1 63.472546 40.974293 140.000000 64.551750 ++ 2.205449 2.898851 5.661166 5.751023 +L 0.000000 1 127.176292 85.868263 4.145823 64.740150 ++ 2.205449 2.898851 5.661166 5.751023 +L 0.000000 1 48.268940 65.859261 4.374274 90.604935 ++ 2.205449 2.898851 5.661166 5.751023 +L 0.000000 1 64.446236 112.522881 -13.582910 128.000000 ++ 2.205449 2.898851 5.661166 5.751023 +L 0.000000 1 0.219698 112.859238 -13.595899 128.000000 ++ 2.205449 2.898851 5.661166 5.751023 +S 1.000000 200 63.292152 65.324570 62.604851 64.836815 65.008881 56.278671 ++ 4.836371 5.967857 11.135469 2.609001 4.493628 14.247530 + +26 1 +49 40000f +26 40040f +15 40100f +13 40200f +3 40300f +5 400017 +8 400033 +2 40012b +1 402033 +1 401017 +1 40112b +1 40010f +1 400817 +11 40280f +13 40140f +26 40080f +14 400c0f +2 40048f +1 40108f +1 42100f +1 40004f +1 44044f +1 49c200 +1 40044f +1 400c4f +1 70060f + +Y1 Yy +B 1.000000 205 69.642204 44.362354 63.470695 107.761040 8.158669 36.385777 105.386139 ++ 1.944087 4.178293 5.145462 10.013327 2.299987 4.130992 20.309855 +B 1.000000 205 122.254509 83.570526 60.998512 109.816086 7.811713 28.845612 104.065407 ++ 2.276255 5.619423 4.450990 10.299380 2.865698 3.895854 22.030848 +B 1.000000 205 32.407879 67.747971 111.629692 82.892220 38.760551 34.896545 100.951614 ++ 0.576576 3.395314 7.782160 7.647441 10.610886 5.783079 19.418104 +B 0.000000 4 32.239555 39.297852 140.000000 110.609947 4.588174 34.273697 31.057976 ++ 0.130537 1.543749 0.001000 1.102964 0.855755 1.575650 2.135783 +B 0.000000 1 5.111273 31.708612 37.859783 100.861267 13.399154 41.863438 19.185705 ++ 1.207840 3.684195 4.344903 6.681599 3.715961 3.511942 15.164559 +B 0.000000 1 96.612068 69.309906 2.207775 110.792419 3.572656 34.998672 29.633627 ++ 1.207840 3.684195 4.344903 6.681599 3.715961 3.511942 15.164559 +A 0.000000 1 72.122482 102.937317 77.472351 ++ 8.000000 8.000000 4.000000 +A 0.000000 1 19.532059 90.564034 40.514301 ++ 8.000000 8.000000 4.000000 +L 0.000000 194 20.926275 86.966171 92.315666 85.724640 ++ 1.560187 4.932343 8.280481 10.447074 +L 0.000000 158 106.607712 43.643135 89.463257 83.627846 ++ 1.639304 3.668783 12.240396 9.155657 +L 0.000000 55 0.144292 66.492981 1.721044 71.851555 ++ 1.174731 1.554222 2.314557 4.531511 +L 0.000000 18 44.292213 53.064194 107.077423 73.093681 ++ 0.718503 8.360185 10.839523 28.318903 +L 0.000000 5 64.271385 45.264233 138.601395 66.141144 ++ 1.599782 1.789857 1.322830 1.090261 +L 0.000000 4 84.349266 75.412689 106.930359 67.750488 ++ 0.232225 1.352329 1.390888 1.331707 +L 0.000000 2 26.563833 66.326111 48.809772 66.156342 ++ 1.154122 3.022600 4.899366 6.170154 +L 0.000000 1 44.742775 31.120823 33.533699 71.009621 ++ 1.154122 3.022600 4.899366 6.170154 +L 0.000000 1 86.552612 23.749001 39.955353 122.901054 ++ 1.154122 3.022600 4.899366 6.170154 +L 0.000000 1 91.984245 25.024860 87.531517 105.807121 ++ 1.154122 3.022600 4.899366 6.170154 +L 0.000000 1 64.520576 35.349819 40.268795 128.000000 ++ 1.154122 3.022600 4.899366 6.170154 +L 0.000000 1 122.055023 31.531044 64.108139 116.615288 ++ 1.154122 3.022600 4.899366 6.170154 +L 0.000000 1 5.863672 27.843702 23.871761 106.517067 ++ 1.154122 3.022600 4.899366 6.170154 +L 0.000000 1 0.910495 35.859016 39.998158 128.000000 ++ 1.154122 3.022600 4.899366 6.170154 +S 0.000000 165 109.663445 59.531147 59.564640 66.441628 66.236145 52.757542 ++ 14.548238 12.587041 6.080742 4.643904 3.317239 14.638562 +S 0.000000 27 123.792953 4.660630 65.228966 69.945267 49.310947 116.891533 ++ 8.144866 8.665133 10.199141 2.756315 5.408697 17.119234 +S 0.000000 11 116.154778 0.515648 97.009521 65.020500 74.044777 128.000000 ++ 6.048635 0.300945 3.991299 2.923981 2.788128 0.001000 +S 0.000000 2 128.000000 0.186755 87.411682 72.861542 59.111469 122.644302 ++ 9.128087 6.003696 4.659050 3.288271 3.537538 8.842640 + +24 1 +38 400707 +4 400607 +67 400307 +7 400507 +14 400b07 +3 402307 +4 800107 +17 800307 +1 2000107 +1 2002107 +20 400107 +1 40030f +3 400907 +5 401707 +1 40032f +1 40070f +3 800007 +1 1004207 +6 1000107 +3 1000307 +2 800207 +1 804007 +1 13f8bd7 +1 40010f + +Z1 Zz +B 1.000000 200 63.084858 48.834873 81.954590 80.173630 30.037321 2.037663 121.804665 ++ 1.541456 6.023522 8.591763 5.608208 14.620169 3.984088 17.590174 +B 1.000000 200 0.461409 81.400078 52.113541 80.955826 34.292923 1.505721 121.217918 ++ 1.610144 3.770284 4.619421 6.720671 29.929998 3.601678 23.062780 +L 0.000000 199 63.919987 66.813538 132.668976 108.181870 ++ 1.629956 5.667735 6.918536 17.587667 +L 0.000000 199 0.110300 63.858395 1.497825 114.500175 ++ 0.878822 8.190874 3.593548 20.979820 +L 0.000000 195 19.799486 76.338562 70.513649 114.482361 ++ 1.325688 5.075359 8.115180 17.536514 +L 0.000000 193 83.634224 52.382248 63.162643 112.925621 ++ 1.295875 7.253551 12.883206 17.756384 +L 0.000000 122 63.908829 74.997398 19.055262 78.085793 ++ 1.556447 3.641152 2.833400 8.157567 +L 0.000000 88 127.320686 55.233608 113.542488 77.076462 ++ 2.459943 3.204240 6.386688 6.577067 +S 1.000000 200 61.655777 63.180267 88.571419 63.233910 65.010651 93.291443 ++ 5.806026 10.251654 18.177458 3.218997 5.038483 13.527789 + +9 1 +40 17f +70 1ff +70 13f +6 1bf +1 13b +7 1df +4 1ef +1 1af +1 177 + +[1 [ +B 1.000000 200 127.611198 72.107880 48.972610 82.341957 4.927084 32.451332 123.392326 ++ 1.862434 1.751505 5.878459 8.199158 1.398745 1.529488 13.594073 +B 0.000000 2 64.641785 50.931305 57.752258 103.396561 3.023966 31.984627 48.406303 ++ 1.862434 1.751505 5.878459 8.199158 1.165621 1.529488 7.866940 +L 1.000000 200 63.630947 65.881218 133.393875 101.766197 ++ 3.108536 1.894703 6.844024 12.898200 +L 1.000000 200 127.451302 65.098953 -34.422504 106.991241 ++ 2.394147 1.702671 5.126449 12.778862 +L 0.000000 198 95.404762 51.587948 49.757099 119.429718 ++ 1.998755 2.332887 12.555111 23.489565 +L 0.000000 198 31.638351 65.884392 48.998062 110.482849 ++ 1.984798 2.455369 6.288793 11.418715 +L 0.000000 62 9.743550 71.213387 118.284866 84.325912 ++ 2.949486 1.664373 11.457412 9.058783 +L 0.000000 39 56.982235 71.154022 -21.856983 84.374893 ++ 3.364452 1.852523 6.095297 8.932294 +S 0.000000 199 63.657455 0.505349 67.945450 65.131561 46.592113 69.858322 ++ 6.822618 0.300706 7.764661 3.249407 8.257648 22.614334 +S 0.000000 1 62.602085 21.321474 72.759422 68.286728 46.460449 71.204674 ++ 5.685515 0.300706 5.392125 2.256532 8.257648 13.086998 + +9 1 +28 1fd +10 1bd +1 1ef +126 13d +31 17d +1 11d +1 23d +1 15d +1 16f + +\1 \ +A 1.000000 200 78.474701 39.531746 51.934616 ++ 4.832417 6.196556 10.415517 +L 1.000000 200 103.552940 60.345871 50.119671 126.585747 ++ 2.475607 2.403204 6.724149 6.526370 +L 1.000000 200 39.509335 70.616310 52.119446 126.589844 ++ 2.428753 3.636809 9.940395 11.444258 +S 0.000000 193 63.894230 64.188484 0.504943 64.043221 64.087914 0.522546 ++ 21.303850 19.159748 0.300117 4.195149 4.935612 0.283528 +S 0.000000 7 103.895172 43.120167 0.325872 74.802559 63.067837 0.551673 ++ 2.954525 4.076750 0.296662 1.045784 1.696193 0.301800 + +2 1 +193 f +7 17 + +]1 ] +B 1.000000 200 63.571487 58.438869 50.010590 82.105423 5.015509 32.826668 123.197601 ++ 1.831586 2.153426 5.975486 12.399308 1.307205 1.598902 16.412678 +B 0.000000 3 0.632815 80.905731 55.873093 119.362114 2.158618 32.216583 57.148651 ++ 0.078090 1.585083 0.548709 5.928654 0.488620 0.863059 2.771714 +B 0.000000 1 0.051752 81.704224 92.226685 109.788780 2.935578 31.006443 42.649334 ++ 0.954838 1.689802 3.262098 7.269642 0.788979 1.230980 5.343389 +L 1.000000 200 127.495132 64.398041 -35.019836 100.043663 ++ 2.340898 2.331560 4.630896 12.566772 +L 1.000000 200 63.799820 65.068436 132.923050 107.713341 ++ 2.782626 3.412839 7.088197 19.049490 +L 0.000000 199 95.781021 64.390892 50.673523 111.332504 ++ 2.070275 2.191806 14.099358 14.311509 +L 0.000000 199 31.389811 78.847275 48.716240 120.149818 ++ 1.983545 2.361078 14.268994 13.252702 +L 0.000000 59 73.682129 58.831570 -22.212595 84.364174 ++ 3.469853 1.651915 5.960519 9.398498 +L 0.000000 25 121.176521 59.518448 116.576599 81.762283 ++ 3.165910 1.083387 11.214920 10.334977 +S 0.000000 196 65.001595 128.000000 68.385521 63.115326 81.854958 71.098495 ++ 6.990565 0.001000 5.348503 2.232478 7.775255 19.202818 +S 0.000000 1 72.193336 119.028519 67.705666 65.353081 76.457939 65.269958 ++ 5.825471 0.001000 5.348503 2.232478 7.775255 13.335290 +S 0.000000 1 54.436668 113.778633 53.597797 63.864895 78.916130 56.460953 ++ 5.825471 0.001000 5.348503 2.232478 7.775255 13.335290 +S 0.000000 1 80.297157 112.095528 82.431488 65.094528 64.298180 79.842308 ++ 5.825471 0.001000 5.348503 2.232478 7.775255 13.335290 +S 0.000000 1 62.373657 97.719055 56.576065 66.716415 72.635315 58.615368 ++ 5.825471 0.001000 5.348503 2.232478 7.775255 13.335290 + +11 1 +16 3f9 +43 2f9 +9 379 +123 279 +1 259 +1 479 +1 879 +1 1079 +1 2079 +3 27b +1 23d + +^1 ^ +B 1.000000 200 96.288742 64.523209 97.434944 99.031334 17.026390 32.780235 79.931213 ++ 0.560395 1.619645 15.412834 3.704955 2.281463 2.671761 11.940050 +B 0.000000 7 48.562183 48.729557 120.303200 111.121208 6.495940 34.020660 19.745653 ++ 0.284446 3.596939 7.371547 8.523658 2.982918 2.262278 5.605773 +B 0.000000 6 15.875752 83.067459 126.878845 114.342468 4.942158 33.002056 25.707865 ++ 1.026152 2.373580 11.641595 5.901751 2.599442 1.981454 5.939803 +L 0.000000 194 46.071556 80.735176 116.009445 120.806015 ++ 1.991363 2.744026 15.252220 23.496756 +L 0.000000 191 82.905907 49.323208 116.646675 122.482086 ++ 1.830893 2.691353 15.620576 19.469791 +L 0.000000 135 113.744965 76.309982 101.476128 86.435844 ++ 2.127710 2.316786 15.198489 14.170253 +L 0.000000 100 14.184648 52.740379 100.840012 85.081924 ++ 2.734421 2.683494 13.168486 14.742293 +S 0.000000 199 59.671200 64.788048 59.520737 61.281151 64.352783 57.211735 ++ 22.060001 17.175133 13.427321 5.872659 8.933920 12.342134 +S 0.000000 1 77.948471 54.725662 23.082628 62.565048 68.908119 9.212225 ++ 15.319444 11.927175 9.324528 4.893882 5.170092 10.285110 + +18 1 +19 d9 +68 f9 +52 b9 +1 159 +41 99 +3 bd +3 e9 +1 d1 +1 f1 +1 91 +1 e1 +1 8b +2 eb +1 f7 +2 ab +1 db +1 f5 +1 fd + +_1 _ +B 0.000000 6 32.490204 54.072235 -35.788708 111.547096 3.192255 31.734289 38.487507 ++ 0.281225 10.108675 3.551733 3.867343 1.241193 1.287600 5.319109 +B 0.000000 3 96.392128 73.368576 -39.675045 109.221214 3.208603 30.933311 34.685753 ++ 0.102246 4.889478 0.141218 2.030238 0.269111 0.492629 2.357179 +A 0.000000 195 0.086997 54.120743 -38.950596 ++ 1.437120 7.899461 2.074569 +L 0.000000 199 0.139220 65.272530 -39.552589 118.211823 ++ 0.862009 7.757318 0.289954 17.920324 +L 0.000000 197 64.192719 65.529648 -35.418049 119.232140 ++ 0.707310 5.872394 4.215955 20.310246 +L 0.000000 163 28.771793 99.159393 -39.221249 99.354088 ++ 9.023366 5.756540 2.852888 15.541869 +L 0.000000 155 94.973297 31.733459 -38.271336 100.823166 ++ 7.622963 5.983656 2.769842 14.890718 +S 0.000000 178 63.640320 65.819046 52.990940 63.702057 64.843513 53.073822 ++ 31.793993 15.415807 16.545467 12.154965 4.764941 14.211832 +S 0.000000 17 2.606291 64.362709 64.431938 15.610415 64.497101 64.567711 ++ 1.340635 0.287867 0.337028 9.689609 0.276228 0.280435 +S 0.000000 3 32.026237 51.791603 8.587368 56.400043 61.855618 16.452736 ++ 1.080147 1.302049 1.015105 0.023165 1.199677 0.057894 +S 0.000000 1 64.788750 106.887772 64.549629 64.765282 75.957748 64.947723 ++ 8.166647 4.098445 5.046674 7.289246 1.594964 4.850054 +S 0.000000 1 63.612076 66.533333 0.048471 64.598442 66.137077 0.006695 ++ 8.166647 4.098445 5.046674 7.289246 1.594964 4.850054 + +14 1 +118 fc +22 bc +1 d6 +10 dc +17 17c +19 9c +2 fd +2 fa +2 89 +3 25c +1 cd +1 43c +1 f9 +1 81c + +`1 `" +B 0.000000 135 1.098677 71.787994 117.889961 107.268730 11.536766 33.725090 36.632938 ++ 3.372035 1.609314 3.458428 6.401232 5.830922 5.472333 8.273043 +A 0.000000 74 72.743584 84.195389 99.654274 ++ 11.945768 6.441480 13.801171 +L 0.000000 185 96.074287 58.687389 105.563431 95.194221 ++ 9.220844 3.114913 15.308997 20.992994 +L 0.000000 172 40.714180 71.475456 105.597748 94.349922 ++ 17.504314 5.113864 14.222587 18.715672 +L 0.000000 58 0.104070 65.995926 88.405586 87.262390 ++ 2.634854 1.712654 5.721299 17.135361 +L 0.000000 19 112.923378 63.326347 89.254639 84.976929 ++ 0.951610 1.589247 5.067267 9.915505 +L 0.000000 18 68.847733 64.875008 135.355423 79.724304 ++ 8.294603 2.287370 3.697350 13.242955 +L 0.000000 3 60.725952 60.283699 106.143303 71.064438 ++ 2.595556 0.411144 3.422511 6.026776 +L 0.000000 1 18.932302 70.982719 133.135925 67.459663 ++ 5.000737 1.771621 7.511596 14.338210 +L 0.000000 1 15.826595 68.230713 91.510361 91.212440 ++ 5.000737 1.771621 7.511596 14.338210 +L 0.000000 1 37.963005 77.118385 75.380638 110.614105 ++ 5.000737 1.771621 7.511596 14.338210 +S 0.000000 157 30.538834 55.315914 53.409931 60.064140 61.802975 58.960438 ++ 18.058334 22.452402 25.588451 4.067739 14.476027 33.630104 +S 0.000000 40 73.421913 71.875229 1.026964 65.979065 64.139290 0.557973 ++ 5.368919 11.837090 1.447202 2.750568 4.407380 0.261077 +S 0.000000 3 21.152515 122.415451 64.395355 55.169041 86.103218 75.309998 ++ 6.047012 5.715814 5.361319 2.815367 6.854795 11.745704 + +32 1 +35 100e +1 104e +3 108e +1 140e +7 81e +36 80d +6 85d +8 815 +26 81d +1 202e +4 80f +2 81f +14 805 +14 82d +2 817 +3 819 +2 85e +11 80e +1 2015 +1 82e +1 a0e +1 84e +1 86e +4 84d +6 809 +1 82f +3 801 +1 859 +1 909 +1 849 +1 200d +1 86d + +a1 a +B 0.000000 682 96.634384 69.807709 4.037755 106.040581 10.876374 35.282578 38.257000 ++ 0.908935 4.538177 4.871448 13.771496 15.306628 25.977404 10.356663 +B 0.000000 637 61.625439 55.131729 68.034592 70.947319 58.361610 4.388794 70.089432 ++ 1.420268 6.462424 6.760890 17.235083 25.171837 15.623380 20.289888 +B 0.000000 436 3.139750 88.942039 38.919056 107.331841 6.884592 26.775358 50.565556 ++ 2.080641 2.013467 5.591082 14.184313 4.022698 13.653090 12.949964 +B 0.000000 14 32.485561 82.781487 99.301003 105.170067 8.422134 35.040237 21.406908 ++ 0.278667 1.803126 0.789956 5.937760 1.656040 1.779775 4.490553 +B 0.000000 2 112.364464 84.578537 10.978918 117.823059 4.422937 31.748272 21.637928 ++ 1.134255 3.704298 3.240845 8.698502 9.250771 7.154522 9.021277 +B 0.000000 1 96.220558 63.515862 26.655439 70.232109 48.434074 10.813900 82.842461 ++ 1.134255 3.704298 3.240845 8.698502 9.250771 7.154522 9.021277 +C 0.000000 636 61.883495 33.717514 35.625050 ++ 1.555357 1.803981 5.919176 +C 0.000000 81 62.128689 70.501625 20.883886 ++ 4.101947 2.574032 6.602085 +C 0.000000 73 63.617352 56.353508 50.105377 ++ 1.105459 1.102154 2.401534 +A 0.000000 3 6.503144 99.993767 72.008400 ++ 4.693197 9.661753 2.939353 +A 0.000000 3 1.901995 100.659409 33.218559 ++ 2.206410 6.669840 0.464900 +A 0.000000 2 52.600075 102.414154 29.665913 ++ 3.449804 8.165796 1.702127 +A 0.000000 1 64.281593 99.641365 75.312332 ++ 3.449804 8.165796 1.702127 +L 0.000000 677 33.125664 87.559364 50.161301 94.640877 ++ 3.116289 6.892664 10.114411 13.950801 +L 0.000000 438 70.594978 58.576836 63.633289 79.825462 ++ 6.665764 7.677237 14.207269 14.062928 +L 0.000000 132 3.943527 63.058315 6.868098 77.010689 ++ 4.587914 6.490368 4.692971 12.476264 +L 0.000000 54 62.719185 65.601295 94.778114 73.396164 ++ 4.570516 4.991409 7.937749 19.987974 +L 0.000000 51 91.336922 43.790867 47.662090 81.499130 ++ 2.563714 8.743991 10.159565 21.951063 +L 0.000000 48 8.148092 57.219883 73.961151 73.586342 ++ 2.594041 4.121870 4.273428 24.152792 +L 0.000000 1 22.631805 100.490555 24.894539 128.000000 ++ 3.461766 4.389996 7.030742 12.305524 +L 0.000000 1 96.399025 58.187229 73.977455 128.000000 ++ 3.461766 4.389996 7.030742 12.305524 +L 0.000000 1 87.076950 94.797356 24.931730 128.000000 ++ 3.461766 4.389996 7.030742 12.305524 +L 0.000000 1 39.263775 73.602051 78.248764 101.713806 ++ 3.461766 4.389996 7.030742 12.305524 +S 1.000000 714 32.331802 68.046097 75.770744 65.644196 63.395660 71.737511 ++ 18.003790 10.599655 10.939954 3.388642 5.806613 9.739329 + +64 1 +141 806047 +85 802047 +33 80e047 +6 80a047 +4 81e047 +15 8020c7 +3 80a0c6 +2 80e046 +9 80a046 +1 808046 +4 80c047 +104 806043 +5 846047 +6 84e043 +4 856043 +16 816047 +41 802043 +41 8020c3 +2 87e600 +4 8000c7 +4 8020c2 +2 80a0c2 +14 846043 +1 80e0c3 +3 80a0c3 +7 800047 +14 804047 +1 840047 +1 844047 +2 842047 +2 814047 +1 84c047 +1 8220c3 +6 80a043 +3 8060c3 +1 84e047 +1 82a0c3 +1 84a043 +1 c7e600 +3 8060c7 +1 967800 +16 816043 +17 826105 +5 80210d +11 806105 +7 82210d +1 aa6905 +6 802105 +1 802115 +1 80211d +6 822105 +4 85e043 +12 80e043 +2 84e042 +3 80e042 +3 81e043 +1 856047 +1 85e042 +4 80a105 +9 82a105 +4 82e105 +1 82a10d +2 808047 +1 802022 + +b1 b +B 1.000000 245 17.990366 65.026787 107.830811 96.424393 22.399569 36.362938 69.632492 ++ 2.268455 3.803004 5.054114 7.945327 10.496049 14.967260 12.748492 +B 0.000000 139 65.202377 38.089653 82.431709 105.040184 4.600776 31.124300 76.123779 ++ 1.426724 3.624135 11.399935 11.441361 1.887777 1.644156 17.913580 +B 0.000000 116 95.456512 53.554913 2.827693 107.237457 9.278842 32.857723 31.128387 ++ 1.252589 2.980449 1.357435 6.326659 4.915345 3.693765 5.647889 +C 1.000000 245 67.289474 48.054840 60.618515 ++ 2.200248 2.299838 7.518516 +L 0.000000 240 95.933121 40.160614 66.159447 108.403633 ++ 2.170816 3.263478 6.299909 13.591332 +L 0.000000 48 126.559380 62.021477 3.631438 77.434807 ++ 2.797931 3.992475 2.636624 11.292583 +L 0.000000 35 62.621723 71.254135 92.801636 70.581116 ++ 4.736046 3.031178 3.560635 9.122393 +S 1.000000 245 20.425343 51.101940 35.998306 51.443581 62.774742 38.887375 ++ 11.568547 14.663874 4.717968 3.541607 6.737890 6.657362 + +19 1 +43 9f +15 bb +3 df +2 8f +12 b9 +41 9d +1 89 +11 bd +11 dd +19 99 +1 ab +3 f9 +2 fd +6 d9 +1 cf +63 9b +2 bf +7 db +2 fb + +c1 cC +B 1.000000 362 0.056252 69.624687 50.294857 62.464184 32.943665 40.885658 123.331596 ++ 1.754054 2.690603 1.250419 18.649288 21.929693 9.130493 12.082167 +B 0.000000 21 31.444508 82.213799 97.536049 107.547653 7.552710 34.033638 25.563108 ++ 1.155956 2.056193 0.780609 4.765981 0.936727 1.529965 4.717772 +C 0.000000 1 86.604416 70.738747 8.378542 ++ 2.000000 2.000000 4.000000 +L 0.000000 122 67.506004 68.127289 95.536285 82.658226 ++ 4.251999 6.419241 4.007284 9.723639 +L 0.000000 70 126.486542 67.105446 3.120451 75.761803 ++ 3.109309 3.179417 3.258505 14.834868 +L 0.000000 68 35.869152 57.138836 47.830410 72.418396 ++ 4.184420 2.977057 5.637128 6.269821 +L 0.000000 49 68.816429 75.238953 24.728590 74.945473 ++ 3.810659 1.526525 7.078124 6.022487 +L 0.000000 43 95.032288 41.951931 49.566765 70.641708 ++ 2.435784 1.284155 9.221992 5.138851 +L 0.000000 20 120.797226 74.787018 70.004395 76.362976 ++ 2.121871 1.229743 4.699326 6.907854 +L 0.000000 20 7.176084 64.469475 81.455399 69.558044 ++ 2.551194 1.900126 1.434512 3.959067 +L 0.000000 1 116.177277 53.613369 9.635715 87.038414 ++ 2.686253 2.215435 3.996810 6.623043 +S 1.000000 362 61.763359 28.746494 76.835373 64.087280 60.768566 76.100777 ++ 6.474957 17.087111 13.454288 3.259578 5.293751 15.040208 + +51 1 +17 803 +99 801 +56 809 +1 823 +22 841 +3 843 +26 811 +2 891 +11 821 +5 831 +4 941 +8 901 +5 849 +3 921 +13 881 +2 861 +2 9c1 +1 961 +16 829 +8 839 +12 819 +4 889 +3 8b1 +1 909 +1 851 +1 8c1 +1 8d1 +2 8b9 +1 8a9 +3 8a1 +2 a21 +1 a41 +3 a01 +1 a39 +1 ab9 +2 aa1 +1 a11 +1 a29 +4 a09 +1 8d9 +2 899 +1 879 +1 a79 +1 82d +1 ae1 +1 aa9 +1 919 +1 c81 +1 859 +1 a81 +1 8c9 + +d1 d +B 1.000000 325 48.839874 67.680145 109.132362 91.803047 25.243362 21.948544 70.629227 ++ 2.742206 4.810615 5.309293 14.768568 44.389519 10.769010 22.201870 +B 0.000000 246 96.898544 73.382088 4.303908 102.338081 13.438613 36.066944 35.571068 ++ 1.040984 5.537887 6.441049 12.931173 13.777465 30.654650 10.934532 +B 0.000000 231 1.841085 93.119102 57.240700 107.618599 4.708021 28.256588 72.475433 ++ 1.521243 3.419281 8.849481 16.132212 2.334780 11.973326 14.355907 +B 0.000000 1 32.686344 71.648010 124.778275 107.689972 9.464801 37.460968 16.080332 ++ 1.683631 4.241001 4.227732 9.101697 7.237504 9.620865 11.499818 +C 1.000000 325 62.011673 50.956341 62.363213 ++ 2.021428 3.540927 8.375170 +C 0.000000 7 69.327026 100.110947 9.648204 ++ 1.043653 1.412025 1.622104 +A 0.000000 1 87.497108 111.201485 -36.000423 ++ 8.000000 8.000000 4.000000 +L 0.000000 318 31.582354 90.971802 75.481651 113.212379 ++ 1.818422 2.111042 8.959855 14.337491 +L 0.000000 93 68.668678 61.377773 96.444008 73.085136 ++ 2.885983 2.763572 3.160134 7.548321 +L 0.000000 63 1.196667 70.751648 2.285902 81.785248 ++ 1.906925 5.476467 2.229991 10.970376 +L 0.000000 9 13.504256 61.846142 15.114764 71.749153 ++ 1.220364 1.196850 1.265544 4.429982 +L 0.000000 1 71.004387 75.679031 -25.506081 81.255257 ++ 1.837674 2.658797 3.772208 7.911804 +L 0.000000 1 115.752869 77.388908 -39.090504 128.000000 ++ 1.837674 2.658797 3.772208 7.911804 +L 0.000000 1 40.597477 83.462440 -35.056313 115.349899 ++ 1.837674 2.658797 3.772208 7.911804 +L 0.000000 1 93.875336 39.756371 53.303879 66.785316 ++ 1.837674 2.658797 3.772208 7.911804 +S 0.000000 319 13.607122 78.004532 95.189209 50.574841 65.749229 91.996239 ++ 13.361881 8.530048 12.409608 4.810889 5.682151 8.499449 +S 0.000000 6 21.596004 91.859985 62.105675 47.718124 74.858246 66.543198 ++ 6.078914 9.170251 2.407112 1.940753 4.845237 4.160451 + +28 1 +111 8097 +3 80b7 +3 82b5 +1 809f +18 8295 +2 8215 +1 8037 +66 8197 +26 8291 +10 8193 +7 8391 +2 10091 +30 8093 +2 8293 +2 10093 +1 10291 +1 c093 +10 8091 +1 10193 +1 8393 +9 8095 +2 8017 +3 8297 +1 8191 +5 8497 +4 8597 +1 b9d7 +2 8117 + +e1 e +B 0.000000 597 126.253052 71.377853 33.647259 68.896141 54.755417 26.355021 100.015984 ++ 1.996231 2.738014 3.369362 4.222009 20.709049 10.447855 10.138552 +B 0.000000 81 126.906822 70.344566 31.820656 61.779636 121.967781 16.650723 75.998955 ++ 3.262666 1.990832 1.964021 2.200702 7.767816 18.431414 13.361873 +B 0.000000 15 127.488487 93.906822 37.792957 96.205544 18.912064 31.140026 28.059896 ++ 1.569423 3.078306 1.943300 7.661791 8.227873 12.650833 5.864563 +B 0.000000 5 4.342581 72.163506 43.314663 60.976154 57.591267 36.993107 100.834099 ++ 0.385404 0.778417 0.927952 1.994986 2.557936 1.135794 3.039920 +B 0.000000 1 114.305107 66.393608 32.226128 83.712166 30.937389 20.309488 94.456932 ++ 1.593180 1.858013 1.793777 3.697357 6.719031 9.704028 7.326754 +B 0.000000 1 0.838946 65.972343 31.265524 67.744576 59.960846 24.814714 51.290455 ++ 1.593180 1.858013 1.793777 3.697357 6.719031 9.704028 7.326754 +B 0.000000 1 126.437553 83.718452 64.434418 112.113937 7.270122 31.038542 21.413626 ++ 1.593180 1.858013 1.793777 3.697357 6.719031 9.704028 7.326754 +C 0.000000 708 65.956825 73.225800 36.451252 ++ 2.107546 3.538565 8.219461 +C 0.000000 23 67.454552 34.463474 30.873987 ++ 1.815165 1.158023 3.270165 +A 0.000000 2 6.751656 91.453880 34.510162 ++ 8.000000 8.000000 4.000000 +A 0.000000 2 1.977459 88.020485 70.236542 ++ 8.000000 8.000000 4.000000 +A 0.000000 2 107.559189 125.750916 49.173664 ++ 8.000000 8.000000 4.000000 +A 0.000000 1 7.364477 77.314217 8.239825 ++ 8.000000 8.000000 4.000000 +L 0.000000 670 2.293456 70.599167 47.332439 89.636429 ++ 3.037926 2.440068 8.114447 15.406995 +L 0.000000 129 67.045242 64.338516 95.476585 72.146736 ++ 3.947526 4.524890 5.802786 4.873528 +L 0.000000 122 126.303101 65.858650 4.154473 75.076118 ++ 8.355043 6.776618 12.978202 24.985386 +L 0.000000 121 67.663864 74.984314 22.660913 72.257782 ++ 3.968055 4.452253 8.559233 22.171473 +L 0.000000 80 94.285439 40.220474 52.671150 73.253464 ++ 5.976016 8.084901 9.255851 23.375389 +L 0.000000 2 7.801173 69.053802 28.144707 100.360771 ++ 3.013783 3.117768 4.685917 8.146338 +L 0.000000 2 30.561628 89.890068 46.662121 71.625038 ++ 3.013783 3.117768 4.685917 8.146338 +L 0.000000 1 48.014771 73.328537 74.291664 91.344086 ++ 3.013783 3.117768 4.685917 8.146338 +L 0.000000 1 85.011421 75.892189 6.379504 101.328194 ++ 3.013783 3.117768 4.685917 8.146338 +L 0.000000 1 26.416862 92.529312 10.568156 96.344414 ++ 3.013783 3.117768 4.685917 8.146338 +S 0.000000 711 109.312294 47.757511 89.696724 53.859291 59.376591 77.070747 ++ 25.523083 13.972374 19.032227 6.422568 5.072157 14.288288 +S 0.000000 2 128.000000 57.056114 88.145088 87.387085 71.000427 88.309868 ++ 17.877789 8.085863 13.216823 4.460116 3.522331 9.922421 + +52 1 +26 802082 +2 826081 +7 822082 +34 812082 +2 800082 +1 806082 +5 80a082 +11 800184 +1 810082 +2 800884 +1 808184 +1 800084 +28 812081 +322 802081 +3 832082 +1 8020a0 +1 826082 +65 806081 +6 800180 +1 8a8180 +1 808180 +1 824180 +1 117e600 +1 820180 +1 107e600 +1 880180 +1 836082 +28 822081 +2 820081 +54 80a081 +8 800081 +12 82a081 +2 804081 +24 80e081 +17 816081 +1 832088 +5 832081 +1 832001 +2 812088 +1 82a088 +1 812008 +1 81a001 +5 836081 +1 8160c1 +1 810081 +10 81a081 +4 81e081 +2 83e081 +1 83a081 +1 828081 +1 82e081 +1 e2b090 + +f1 f +B 0.000000 262 125.754532 76.986137 108.050758 78.172554 30.678347 37.566750 56.834320 ++ 2.125733 3.982306 4.614683 13.602168 33.330780 25.840132 18.075987 +B 0.000000 261 124.390633 72.057648 53.306992 98.615929 9.984797 35.974678 72.832001 ++ 2.088817 3.045077 7.581539 13.720425 4.550086 3.721993 20.890560 +B 0.000000 254 64.498924 48.304989 47.325226 99.297340 6.654332 32.290424 69.983727 ++ 2.410710 4.326707 10.613959 15.214721 2.116083 3.233641 19.700087 +B 0.000000 138 51.533516 53.788422 106.524490 108.964561 8.159419 35.992008 30.835697 ++ 2.743288 6.690366 4.230460 5.917896 3.037456 3.885832 8.335803 +B 0.000000 1 96.329987 67.265594 -0.185615 109.310982 4.431582 30.028948 26.907585 ++ 2.342137 4.511114 6.002014 11.233276 8.212500 5.732922 16.750608 +B 0.000000 1 124.381294 69.828545 116.375282 63.298618 29.810312 128.000000 37.247505 ++ 2.342137 4.511114 6.002014 11.233276 8.212500 5.732922 16.750608 +B 0.000000 1 48.924652 73.594551 135.501816 96.743820 9.703168 32.411995 11.004587 ++ 2.342137 4.511114 6.002014 11.233276 8.212500 5.732922 16.750608 +L 0.000000 155 0.551864 61.584049 2.637279 93.494179 ++ 1.781925 2.979859 2.397443 13.396427 +L 0.000000 125 95.890663 53.413250 42.221340 72.920731 ++ 1.860221 4.768955 8.618095 13.080778 +L 0.000000 123 30.893002 68.601051 39.689194 74.016434 ++ 2.313856 3.904980 14.158802 6.367017 +L 0.000000 64 69.594681 69.307335 133.676788 74.994553 ++ 7.553374 5.476969 7.148921 9.504993 +L 0.000000 8 120.418320 74.651657 122.934395 66.633331 ++ 0.971741 1.338221 2.555497 2.437215 +L 0.000000 6 9.957903 74.300858 79.714409 72.172272 ++ 2.045495 1.047922 1.566218 3.572024 +L 0.000000 6 7.016652 74.512451 131.553177 75.571327 ++ 2.329101 0.800369 3.077994 4.006126 +L 0.000000 1 59.102592 76.161987 97.716370 68.502464 ++ 2.693673 2.772064 4.554240 6.513150 +S 0.000000 243 85.916298 28.249756 90.281776 56.462444 63.742599 104.147331 ++ 15.240814 39.897064 10.825356 7.641721 8.096473 16.410191 +S 0.000000 20 110.239021 4.475606 126.597404 64.828163 59.673550 128.000000 ++ 7.393876 4.689113 2.682951 2.008986 4.806369 0.001000 + +49 1 +23 808f +6 828f +3 818f +3 838f +1 918f +1 c18f +14 848f +1 841f +5 870f +4 850f +3 8707 +38 830f +16 8307 +2 8207 +8 810f +17 1020f +2 1070f +1 8007 +4 820f +1 8305 +2 800f +1 8205 +32 8087 +1 8c87 +1 898f +1 888f +8 8187 +1 8887 +1 8c8f +1 8a8f +5 8787 +4 8287 +3 8587 +10 8387 +1 83a6 +1 8383 +2 8583 +2 8783 +2 8d83 +1 8183 +1 8103 +1 1030f +1 814f +17 8487 +3 a087 +1 a487 +4 9087 +1 b487 +1 a187 + +g1 g9 +B 0.000000 249 127.651146 77.733521 30.917770 80.101326 53.441448 14.832881 86.952919 ++ 1.714936 4.986240 4.335292 7.873734 26.791376 13.105533 23.757050 +B 0.000000 236 67.161057 60.246170 -3.695716 66.189415 84.698906 70.044052 74.714729 ++ 3.238379 5.715706 3.492742 4.004981 26.044020 41.403816 13.215112 +B 0.000000 235 63.021259 39.462940 26.580885 107.755836 8.781215 31.084690 61.476051 ++ 1.578309 2.886018 6.530612 7.126719 3.920274 2.722258 12.484897 +B 0.000000 168 32.242970 78.540215 97.531448 107.373169 9.654013 33.207100 31.869112 ++ 0.812459 3.448273 1.182187 6.726159 4.610196 3.412496 6.496044 +B 0.000000 81 125.008453 93.533043 51.757778 107.815987 6.131124 36.165661 61.978573 ++ 1.651969 1.496806 4.205276 5.395257 1.933713 2.496640 12.292663 +B 0.000000 31 1.144635 86.661865 73.995346 104.651131 9.619892 38.194599 19.535984 ++ 1.692407 1.916887 6.068335 7.802878 6.395379 7.353463 6.732841 +B 0.000000 28 64.321167 36.465309 -3.697618 107.588249 7.765126 31.032999 26.560192 ++ 0.833922 2.596134 5.719634 7.364653 3.864319 2.636344 3.857978 +B 0.000000 13 60.157066 46.398369 18.632679 87.993187 19.297365 28.193361 76.752487 ++ 0.464289 2.120883 0.767177 2.532453 1.014783 1.374444 11.233676 +B 0.000000 4 65.836380 41.455097 10.268631 88.570305 24.853157 54.300652 54.372322 ++ 0.649907 0.676853 1.244420 3.710329 3.808734 9.561567 6.212423 +C 0.000000 251 63.333748 51.243820 60.365665 ++ 2.588331 4.618541 9.805267 +C 0.000000 238 63.376991 66.451874 41.537373 ++ 2.884214 1.348670 5.862376 +C 0.000000 164 63.006710 -14.165306 39.135033 ++ 2.983203 6.000309 6.156552 +C 0.000000 89 62.705437 -21.293003 58.922520 ++ 2.242512 1.724084 2.326550 +L 0.000000 236 31.209406 91.234367 36.534760 98.187386 ++ 1.536751 2.099469 7.726271 9.673767 +L 0.000000 165 59.534039 73.504303 16.142729 80.754189 ++ 4.172653 2.391455 4.317934 11.037558 +L 0.000000 113 61.151596 72.943855 96.920296 84.519165 ++ 1.459784 3.855072 2.133018 9.088217 +L 0.000000 89 60.147690 56.974171 -10.693418 76.162331 ++ 5.360846 3.020900 4.369530 5.889975 +L 0.000000 72 1.006815 64.188087 -36.503151 74.059471 ++ 4.602528 4.061914 3.705145 7.830706 +L 0.000000 68 4.273747 65.996307 23.668982 73.631012 ++ 6.398395 5.803428 17.762541 6.512495 +L 0.000000 47 92.609398 40.254833 6.269110 68.165428 ++ 1.240678 2.029654 8.423848 15.040085 +S 1.000000 489 75.506386 57.509968 56.002930 66.886299 61.861324 60.106842 ++ 20.720390 10.951065 8.481521 4.437459 5.026222 6.664767 + +106 1 +69 10221a +4 11221a +3 14221a +2 10021a +1 10222a +1 10a212 +1 102212 +31 10220a +31 11220a +1 13a202 +10 15220a +1 13220a +1 17220a +2 11a20a +11 10a202 +17 11a202 +2 102b08 +13 112202 +3 15a202 +1 10ab00 +2 132202 +12 102202 +1 12a202 +1 110212 +6 14220a +2 12220a +1 10a20a +1 16a20a +1 11020a +5 142202 +2 152202 +1 12ab00 +2 164c05 +3 108c45 +11 124c05 +3 140c25 +1 1e8c05 +3 10cc25 +1 1a4c05 +1 1e0c25 +1 12cc25 +2 1e4c25 +1 1a4c25 +3 128c05 +9 100c05 +1 1ecc25 +3 14cc25 +1 188c25 +1 108c25 +1 1acc25 +13 10cc05 +1 1a0c05 +2 1a0c25 +1 164c25 +11 12cc05 +2 144c25 +2 184c25 +23 104c05 +1 124c25 +1 160c25 +9 100c45 +6 180c05 +3 108c05 +4 104c45 +1 1c0c05 +2 100c65 +2 10cc45 +2 100a45 +2 120c05 +1 184c05 +1 1c8c05 +2 184a05 +1 144a45 +1 104a65 +1 144c45 +1 14cc05 +1 180c01 +1 180a05 +1 124a05 +1 1a4a05 +3 18cc05 +3 16cc05 +1 1a8c05 +1 168c05 +1 1c4c05 +3 1acc05 +7 101405 +8 101481 +4 121481 +1 101205 +1 101281 +7 10d405 +37 105405 +2 14d405 +5 185405 +1 12d405 +1 101445 +5 145405 +3 181405 +1 145445 +1 109405 +1 1c1405 +1 1a5405 +1 169405 +1 105445 +1 1ad405 + +h1 h +B 1.000000 375 17.053408 64.283623 109.362343 93.562294 25.182968 31.920677 76.943253 ++ 2.023435 3.886304 8.564184 10.535290 13.840322 16.621431 16.249052 +B 0.000000 366 96.199249 65.739960 42.354797 65.286087 66.701668 33.921215 121.008102 ++ 0.691249 1.619494 4.236667 3.739405 26.104898 21.174927 17.162443 +B 0.000000 295 64.218933 36.147198 73.378937 98.563057 4.131432 32.464657 92.748489 ++ 1.431882 4.013927 13.746710 9.439634 1.634877 1.621823 31.834223 +B 0.000000 223 3.990041 94.487137 37.292912 110.631348 6.508278 28.353682 49.658237 ++ 1.262020 3.584887 5.578913 17.044756 6.980771 13.178573 6.248053 +B 0.000000 6 96.447334 57.851307 3.078472 114.664932 4.541387 34.516087 30.065905 ++ 0.090734 6.799956 1.629618 6.493588 1.554409 1.462773 5.901596 +B 0.000000 4 64.363289 38.786293 47.166782 106.518753 4.224357 30.481621 36.738998 ++ 0.242561 1.496219 6.749115 2.833707 1.366652 0.643607 5.014353 +C 0.000000 9 65.648323 45.984200 42.172493 ++ 1.105428 1.920768 3.294447 +C 0.000000 1 32.940937 9.030984 7.861342 ++ 1.105428 1.920768 3.294447 +A 0.000000 1 32.008663 128.000000 6.500588 ++ 8.000000 8.000000 4.000000 +A 0.000000 1 32.393585 104.675201 52.728935 ++ 8.000000 8.000000 4.000000 +L 0.000000 363 95.867439 40.037506 70.425781 109.155930 ++ 2.103350 3.741857 8.149475 13.622331 +L 0.000000 197 33.471069 91.569260 46.989758 74.701706 ++ 2.652945 2.825107 5.301019 6.856849 +L 0.000000 143 96.333565 76.531273 40.990700 71.311150 ++ 4.594181 5.053168 5.582229 27.967363 +L 0.000000 66 29.430637 56.550873 37.895267 72.769768 ++ 3.916990 4.707615 7.484795 23.776678 +L 0.000000 42 66.207047 71.557983 91.277672 75.051346 ++ 6.366040 3.361437 5.080712 9.046875 +L 0.000000 10 28.206438 58.745670 109.555634 71.593887 ++ 0.433083 1.452892 0.702909 1.931736 +L 0.000000 8 0.789098 63.525604 3.873367 99.850449 ++ 1.364149 8.248268 1.192866 26.777725 +L 0.000000 3 8.724504 63.998810 71.960899 66.398117 ++ 1.328341 2.684719 2.916402 0.360793 +L 0.000000 1 64.951134 60.942902 5.890226 128.000000 ++ 2.539820 3.535280 3.461549 8.798864 +L 0.000000 1 92.441628 39.577412 36.407135 73.801315 ++ 2.539820 3.535280 3.461549 8.798864 +S 0.000000 363 48.975689 34.152214 28.683727 47.656647 65.577408 26.084085 ++ 11.892361 11.045789 9.307430 3.730862 14.948371 9.417382 +S 0.000000 9 35.307613 35.898983 57.762379 57.564255 70.183121 18.834646 ++ 4.641919 9.424307 4.402100 1.391737 2.255737 3.564046 +S 0.000000 3 43.472107 59.751987 66.514107 53.142769 82.213181 34.224640 ++ 6.191755 2.372938 1.873857 2.057621 5.922183 7.047631 + +48 1 +122 10040f +33 100c0f +38 100c07 +4 10000f +1 18000f +16 105c03 +9 107c03 +3 102c03 +1 104c03 +25 103c03 +18 101c03 +1 125c03 +1 106c03 +2 103403 +1 107403 +1 100c03 +1 121c03 +1 127c03 +6 10240f +3 110c45 +1 100c55 +6 103c07 +1 10045d +4 102c07 +10 101c07 +3 110c55 +1 101827 +1 11044d +1 107c07 +1 40040f +26 10140f +1 40140f +4 10340f +1 407c0f +1 100407 +1 102807 +1 104c07 +1 109c0f +9 20dc0f +1 150d07 +2 10102f +5 101c0f +1 10048f +1 10002f +1 10360f +1 10041f +1 100807 +1 10100f + +i1 i +B 0.000000 414 64.112602 53.477848 49.314213 94.909569 7.293959 31.907436 74.545677 ++ 1.095734 2.007004 6.004598 10.500868 4.386450 7.626174 20.634024 +B 0.000000 370 3.542384 74.967781 41.846706 107.867027 7.001092 27.295647 59.847118 ++ 2.597308 3.559407 3.805360 14.051576 3.539108 12.140543 11.980013 +B 0.000000 2 96.619568 68.249207 1.241157 109.747604 6.209314 35.896507 24.488922 ++ 1.538768 2.239407 4.087482 10.129454 2.997701 5.575384 12.156263 +B 0.000000 1 9.640382 73.969177 80.187759 103.049416 15.112804 26.400288 35.142746 ++ 1.538768 2.239407 4.087482 10.129454 2.997701 5.575384 12.156263 +A 1.000000 495 52.888416 119.223816 130.946381 ++ 31.699789 6.911944 5.131941 +A 0.000000 98 62.666775 66.294594 50.449402 ++ 4.332973 12.061362 3.901552 +L 0.000000 485 32.132259 72.370789 55.936600 108.693596 ++ 2.216798 2.156382 5.247004 13.544805 +L 0.000000 477 0.321145 64.508049 4.280775 114.709183 ++ 5.564048 2.775024 3.276134 18.544479 +L 0.000000 461 95.961464 57.551540 48.764179 95.005836 ++ 2.173393 2.353018 10.904577 13.926760 +L 0.000000 447 93.506012 60.378815 132.461319 94.786995 ++ 5.398254 4.725142 5.973123 13.197114 +L 0.000000 447 125.574928 65.569099 121.343933 97.632500 ++ 7.387447 4.421193 6.066446 11.756733 +L 0.000000 420 68.431442 61.727901 96.127815 88.417374 ++ 5.929232 4.112555 2.916823 15.422335 +L 0.000000 406 30.873444 72.266106 130.383469 96.297989 ++ 6.300227 4.225338 5.761370 12.236473 +L 0.000000 372 60.959328 67.337738 138.330719 99.291458 ++ 6.710296 3.419491 4.648610 13.149928 +L 0.000000 7 9.352525 68.345825 122.623833 86.121460 ++ 2.730322 1.205445 0.827684 5.493570 +L 0.000000 3 14.442334 69.582634 11.179526 89.269119 ++ 0.591235 0.486951 0.220948 3.859879 +S 0.000000 427 55.859959 60.203632 60.967003 59.548214 60.449837 59.946007 ++ 16.356907 22.030855 8.533275 3.755986 11.322739 11.497515 +S 0.000000 45 64.149612 66.571259 99.195229 64.356918 67.438911 107.433632 ++ 6.746810 15.132523 5.331877 3.442086 5.995644 7.216919 +S 0.000000 8 62.841015 128.000000 70.402161 63.055725 121.659950 70.435898 ++ 2.345517 0.001000 5.054791 1.906456 5.737311 2.606949 +S 0.000000 6 54.832588 128.000000 68.473633 62.080105 124.980286 83.999771 ++ 0.557012 0.001000 3.893299 1.370923 4.393544 4.484370 +S 0.000000 4 58.054382 122.806732 47.254959 67.839966 89.506828 45.904663 ++ 4.213006 5.426945 2.369146 3.498631 2.671601 4.568055 +S 0.000000 4 48.516022 128.000000 71.809128 59.486267 128.000000 123.266823 ++ 3.861477 0.001000 4.221131 0.699317 0.001000 3.213312 +S 0.000000 1 78.025406 128.000000 64.543106 70.454353 128.000000 56.057095 ++ 4.531604 6.486919 4.900587 2.445566 4.443686 5.597853 + +100 1 +3 113d3 +4 133d3 +4 135d3 +5 125d3 +11 137d3 +121 13fd3 +1 127d3 +6 117d3 +8 136d3 +1 112d3 +1 13793 +2 105d3 +1 121d3 +2 106d3 +1 103d3 +2 116d3 +5 11ed3 +1 123d3 +6 13bd3 +45 13ff0 +4 13df0 +1 11ff0 +3 137f0 +3 13bf0 +1 12bf0 +1 127f0 +5 83ff0 +5 43ff0 +1 117f0 +2 2037f0 +1 403ff0 +1 41df0 +1 437f0 +1 83df0 +1 42ff0 +1 2023f0 +1 2007f0 +1 11bf0 +1 136f0 +1 12ff0 +1 10bd1 +2 109d3 +2 10fd1 +39 12fd3 +4 11fd1 +53 11fd3 +1 119d3 +1 11793 +1 10ff1 +1 10bf1 +1 119f1 +1 12bd3 +1 101f1 +1 109d1 +9 13fd1 +3 11ff1 +1 10dd1 +4 13ff1 +2 13ad1 +1 11df1 +1 10bd3 +1 103ed3 +1 101b53 +4 23bd3 +6 23f53 +1 103fd3 +17 23fd3 +4 21f53 +1 21b53 +1 103f53 +1 22f53 +1 20fd3 +3 137f1 +3 137d1 +1 133f1 +1 135f1 +1 13bd1 +1 2bf53 +1 23b53 +7 21fd3 +2 2bb53 +4 13dd3 +1 13cd3 +5 11f93 +4 17dd3 +7 12dd3 +5 12ed3 +1 175d3 +1 13fd7 +1 13b93 +1 17cd1 +2 12fd1 +1 12cd3 +1 13ed7 +1 17cd3 +1 12f9b +2 13ed3 +1 11bd3 +1 13d93 +1 10fd3 + +j1 jJ +B 0.000000 199 61.528793 59.085030 23.965744 96.617485 8.885926 37.186367 92.110123 ++ 2.272508 2.233696 8.239694 16.709400 5.072748 10.125751 26.079069 +B 0.000000 3 0.640538 81.377953 46.811466 112.452789 3.183088 31.123877 42.501110 ++ 0.248748 3.191391 9.666026 3.210104 1.456407 1.122294 6.776017 +B 0.000000 1 32.176048 63.468468 95.025543 109.861229 3.999674 30.373646 27.333666 ++ 1.260628 2.712543 8.952860 9.959752 3.264578 4.077033 16.427544 +B 0.000000 1 0.054862 82.300323 10.712523 96.358643 7.124455 32.996605 32.197376 ++ 1.260628 2.712543 8.952860 9.959752 3.264578 4.077033 16.427544 +C 0.000000 1 61.021530 -22.794197 9.276594 ++ 2.000000 2.000000 4.000000 +A 0.000000 202 63.060520 118.421005 126.558182 ++ 32.648720 7.706487 5.379482 +A 0.000000 4 56.346561 59.070011 27.224762 ++ 4.040989 5.196107 2.158886 +L 0.000000 201 31.216629 80.848000 36.370861 105.814186 ++ 1.838750 5.255558 7.731458 14.022666 +L 0.000000 198 95.458107 66.549278 36.325836 98.955788 ++ 2.790352 4.929332 11.488276 12.479023 +L 0.000000 183 31.654255 80.262634 126.070305 102.537224 ++ 7.832517 6.351031 7.657389 15.514224 +L 0.000000 180 93.867332 68.039276 127.951218 106.241318 ++ 5.892817 4.360603 6.263627 13.448363 +L 0.000000 168 0.971327 63.772442 -34.614941 88.351906 ++ 5.619859 5.168362 3.114241 13.617110 +L 0.000000 167 126.517876 73.701096 116.926514 103.569305 ++ 8.212673 4.677376 6.487085 12.652061 +L 0.000000 153 62.357048 75.252823 136.409927 101.489441 ++ 5.697029 4.191172 4.317953 14.998769 +L 0.000000 149 65.882027 66.938721 95.999245 88.041451 ++ 7.886076 3.826744 3.631678 14.929428 +L 0.000000 72 69.441277 59.414227 -17.205986 77.494057 ++ 4.171077 2.049216 4.547675 8.404123 +L 0.000000 41 127.200066 57.949924 84.028397 86.805687 ++ 2.458067 6.205131 13.489450 13.096167 +L 0.000000 4 11.659056 77.511276 118.047722 80.706909 ++ 2.939747 4.076685 4.583562 9.634985 +L 0.000000 1 112.857208 57.087044 83.378387 73.902077 ++ 4.474807 4.089488 5.518339 12.271375 +L 0.000000 1 78.029648 62.177029 -39.611008 78.854683 ++ 4.474807 4.089488 5.518339 12.271375 +S 0.000000 201 57.578953 127.928467 83.479095 61.168438 90.967133 104.374176 ++ 10.396751 2.833249 14.678378 5.757582 13.384543 30.819323 +S 0.000000 1 77.773788 51.674030 80.163300 71.313622 64.980560 83.815643 ++ 10.396751 0.790708 14.678378 4.797985 13.384543 25.682768 +S 0.000000 1 47.598946 111.598167 97.652130 59.681343 73.171959 114.089561 ++ 10.396751 0.790708 14.678378 4.797985 13.384543 25.682768 + +72 1 +4 11fba1 +10 117fa1 +2 11fda1 +1 117da1 +5 11ffa1 +1 11d7a1 +1 1179a1 +1 115ea1 +2 11dfa1 +2 11efa1 +2 114fa1 +1 11cfa1 +1 11cba1 +1 104fa1 +1 114da5 +1 1147a1 +18 10ffa1 +1 10c9a1 +1 117ba1 +3 116fa1 +30 107fa1 +8 105fa1 +6 101fa1 +21 103fa1 +1 100981 +3 106fa1 +1 105ee1 +1 416fa3 +1 112fa1 +1 100fa1 +4 102fa1 +1 100ba1 +1 107be0 +1 107fe0 +1 108fa1 +3 10bfa1 +1 103fe0 +1 103da1 +1 1027a1 +1 200620 +1 103ba1 +1 121fa1 +2 107ba1 +7 10dfa1 +1 109fa1 +5 10b7a1 +1 1073a3 +2 1075a1 +1 1055a1 +1 1079a1 +2 106da1 +1 1041a1 +2 12fba1 +1 107fb1 +1 10efa1 +1 1067a1 +1 104ba1 +1 1059a1 +1 1051a1 +1 14ffa1 +6 10f7a1 +2 10fea1 +2 10f5a1 +1 10e7a1 +1 10d7a1 +4 1077a1 +1 101f2b +1 1057a1 +1 12f3a1 +1 1097a1 +1 1035a1 +1 1867a1 + +k1 k +B 1.000000 258 1.048409 85.227814 52.142273 97.014671 18.244719 37.376514 100.218575 ++ 1.802679 2.682998 1.755893 5.864843 4.340017 6.156920 18.714748 +B 1.000000 258 96.163902 62.062748 18.390760 76.699608 46.715630 16.154760 68.145935 ++ 0.897914 2.506232 4.317965 5.880265 23.460560 14.020399 14.288998 +B 0.000000 256 19.662975 64.514938 95.104530 79.074150 47.145206 11.893777 83.943314 ++ 2.245577 3.206969 6.547974 5.691977 26.344198 20.783344 25.364626 +B 0.000000 176 63.938965 37.311699 70.425026 99.957428 4.375862 32.689316 87.975441 ++ 1.347371 3.843448 10.462182 11.101024 1.282840 1.737017 18.648291 +B 0.000000 2 22.791851 59.240013 88.183075 65.921814 128.000000 0.337203 35.775856 ++ 1.479832 2.787976 5.441159 7.134527 8.654870 8.228895 18.474384 +B 0.000000 1 96.431252 48.157887 2.880630 109.332764 6.173319 36.242603 22.059855 ++ 1.479832 2.787976 5.441159 7.134527 8.654870 8.228895 18.474384 +C 0.000000 2 59.865662 75.706558 8.559233 ++ 2.000000 2.000000 4.000000 +L 0.000000 252 95.701256 40.907726 68.885803 110.081528 ++ 2.004199 2.898674 7.323682 14.852818 +L 0.000000 152 32.161968 56.150429 102.761597 70.749542 ++ 2.050048 1.872233 4.920319 4.291743 +L 0.000000 64 76.365471 71.992874 81.502274 88.025284 ++ 1.731379 2.760786 2.234143 13.644781 +L 0.000000 63 13.404819 81.755379 76.730652 68.847450 ++ 0.982466 1.500863 2.273736 3.819251 +L 0.000000 28 113.897125 71.454521 20.964296 84.060165 ++ 1.309630 1.359681 1.697742 6.128920 +L 0.000000 2 41.755997 83.739120 35.875916 75.560333 ++ 1.571890 2.078447 3.092103 7.909350 +S 1.000000 258 32.285519 18.015162 45.904049 52.694275 50.394524 27.487974 ++ 13.999010 15.921699 8.671932 3.865051 14.375871 8.386647 + +29 1 +41 208f +5 248f +30 258f +46 218f +2 20cf +2 209b +36 2087 +14 2387 +14 2187 +12 2287 +2 2487 +1 2787 +1 3087 +1 2407 +1 21af +2 250f +1 308f +1 200f +1 210f +1 2587 +12 2b8f +9 2f8f +2 298f +6 238f +9 278f +3 2d8f +1 290f +1 288f +1 228f + +l1 l1I +B 0.000000 241 2.343678 75.277504 58.185608 108.529327 5.171760 28.517654 76.188644 ++ 2.331573 3.111168 6.684707 12.256677 2.621181 9.661535 14.348058 +B 0.000000 225 63.992989 55.001587 73.875397 101.606239 3.948730 32.146542 90.191422 ++ 1.604488 1.990303 12.122506 8.496824 0.762042 1.809348 21.993793 +B 0.000000 40 63.908424 48.804455 65.257050 77.200989 10.015723 35.173077 118.044525 ++ 0.465067 1.312293 2.336390 6.435466 0.854960 1.672770 10.137127 +B 0.000000 1 45.573265 57.498325 140.000000 106.403877 7.802473 29.286209 18.459940 ++ 1.303622 1.965079 6.003022 8.382062 1.412728 2.713807 14.271115 +B 0.000000 1 112.825615 66.795601 9.331129 108.310860 7.892768 35.877342 15.829198 ++ 1.303622 1.965079 6.003022 8.382062 1.412728 2.713807 14.271115 +B 0.000000 1 0.420624 82.061752 19.773022 102.580956 10.113650 35.504654 24.127747 ++ 1.303622 1.965079 6.003022 8.382062 1.412728 2.713807 14.271115 +A 0.000000 87 62.224056 55.787571 63.665543 ++ 3.776769 7.291550 3.631655 +A 0.000000 1 32.819397 128.000000 -14.974708 ++ 3.776769 7.291550 3.026379 +A 0.000000 1 32.339844 128.000000 140.000000 ++ 3.776769 7.291550 3.026379 +L 0.000000 344 95.852585 58.073002 69.515266 109.934441 ++ 1.876206 1.845537 10.311707 18.597857 +L 0.000000 339 31.841707 72.502808 75.882401 114.645851 ++ 2.719798 2.186910 12.966450 21.782015 +L 0.000000 326 0.015056 64.952347 3.203863 112.252319 ++ 5.126899 1.746847 3.594377 15.063411 +L 0.000000 282 67.413605 62.400391 134.223404 91.957199 ++ 6.448859 3.144795 7.506403 15.382194 +L 0.000000 3 67.818054 70.569778 21.261703 66.050270 ++ 0.287679 0.896963 1.231201 2.716246 +L 0.000000 1 64.784676 76.341660 140.000000 128.000000 ++ 2.597406 1.905982 5.449776 10.482508 +L 0.000000 1 0.026212 67.007843 -15.035456 128.000000 ++ 2.597406 1.905982 5.449776 10.482508 +L 0.000000 1 64.688034 67.425064 -15.208798 128.000000 ++ 2.597406 1.905982 5.449776 10.482508 +L 0.000000 1 0.310051 75.922249 140.000000 128.000000 ++ 2.597406 1.905982 5.449776 10.482508 +S 0.000000 327 58.167351 64.191322 60.635162 60.670322 63.062920 59.282471 ++ 15.607806 20.412157 8.065735 5.148342 9.996111 11.597775 +S 0.000000 13 65.294579 63.625656 127.262993 65.811852 64.488632 128.000000 ++ 4.531289 6.337476 1.500118 1.525961 1.460507 0.001000 +S 0.000000 5 54.497932 59.802372 32.122040 65.277565 68.011330 17.876196 ++ 6.856279 6.290040 2.799801 2.175106 2.386585 5.471742 +S 0.000000 1 47.408100 53.106152 23.176626 53.335293 60.254086 29.500402 ++ 6.806621 9.879215 3.673788 2.425435 4.059062 5.045851 + +30 1 +34 40e05 +5 41e05 +1 40a05 +75 41e40 +2 41640 +1 40640 +2 41e41 +5 40e02 +1 41a41 +145 41e03 +14 40e03 +1 59e83 +13 41e02 +3 41e42 +2 101603 +10 41603 +2 100603 +2 40603 +1 41613 +1 101e03 +3 40e42 +1 41c02 +1 225f02 +1 41c03 +1 41e22 +3 83e03 +10 81e03 +4 41a03 +1 40a03 +1 40e0b + +m1 m +B 1.000000 329 32.247650 71.701202 95.645149 103.474243 12.210365 34.042175 43.199055 ++ 0.674149 5.550078 4.370537 10.850891 12.417317 21.390862 9.318017 +B 0.000000 305 96.179123 47.886593 43.251850 65.574211 75.450218 37.732536 118.037178 ++ 0.636999 4.451534 3.705390 2.745473 20.993040 19.959040 17.502636 +B 0.000000 302 96.325859 83.271652 41.995426 65.831406 75.352867 34.116798 117.941566 ++ 0.881710 2.425694 4.056652 2.860659 20.479439 22.800783 20.349237 +B 0.000000 246 64.306374 20.177149 50.088493 97.161118 6.306598 31.631927 71.747894 ++ 1.248247 5.425849 7.002369 9.808049 1.746056 7.599430 12.904522 +B 0.000000 212 33.101154 40.889389 96.392120 99.756508 14.978654 37.844566 29.180847 ++ 1.291811 5.019917 4.458634 15.714692 19.059504 40.563374 12.773856 +B 0.000000 181 3.768965 109.850960 37.123066 110.397041 6.588374 28.321896 49.769672 ++ 1.277228 5.769706 5.235720 14.255170 7.752278 12.525997 10.105548 +B 0.000000 24 96.397842 51.101177 3.785170 103.629929 15.047087 33.636929 29.254749 ++ 0.418741 10.194336 1.215107 10.590664 9.577668 3.482578 8.629766 +B 0.000000 22 96.500946 79.424026 3.455977 104.871964 15.306952 33.505386 29.530426 ++ 0.314129 4.990993 1.333624 13.293109 11.679757 6.533370 8.414232 +C 0.000000 27 78.960693 44.959766 38.241379 ++ 2.766120 2.072496 5.978018 +C 0.000000 24 52.520424 45.525970 37.187592 ++ 2.577206 2.426492 5.448834 +C 0.000000 1 17.223320 7.144403 9.440404 ++ 2.671663 2.249494 5.713426 +L 0.000000 323 95.775978 23.922913 49.110207 94.133369 ++ 2.425709 4.819026 6.047279 14.680464 +L 0.000000 319 32.959297 107.504341 49.080360 96.704605 ++ 3.294706 4.962397 6.087134 11.652185 +L 0.000000 293 96.484474 93.408768 40.861782 93.794182 ++ 2.075571 4.194040 3.181357 8.651260 +L 0.000000 286 31.251381 72.892754 40.468987 84.204834 ++ 4.370391 2.555718 6.594764 8.831623 +L 0.000000 283 31.133303 37.419296 41.283424 84.356087 ++ 3.678171 4.684803 5.559560 9.109868 +L 0.000000 272 96.068550 58.158081 43.185200 89.888618 ++ 2.080210 2.374868 3.425530 9.565449 +L 0.000000 28 62.952919 50.007515 96.141045 78.458389 ++ 3.114052 3.047482 1.783421 6.239651 +L 0.000000 6 127.852707 69.826408 4.764279 82.715981 ++ 1.127669 15.531241 0.807510 4.370671 +L 0.000000 1 17.608898 42.817455 60.742302 65.240280 ++ 2.166363 5.168477 3.690641 9.137647 +L 0.000000 1 102.720276 57.101402 59.472225 76.187958 ++ 2.166363 5.168477 3.690641 9.137647 +S 1.000000 329 78.954193 62.461876 58.107754 67.386589 64.561279 57.333450 ++ 13.922406 11.895398 8.570320 4.937733 4.795941 12.545390 + +62 1 +4 21b81f +91 21f83f +18 21f81f +2 23f82f +1 21383f +1 23782f +3 201bf9 +5 221be9 +1 2213e9 +1 241bb9 +1 2199bb +4 201bd9 +1 201b79 +1 221b69 +1 21701f +2 221bc9 +20 21f82f +5 23f807 +30 21f817 +44 21f807 +1 21e817 +8 21f80f +1 217807 +1 21e807 +2 20782f +1 21e82f +5 21e83f +11 20f83f +1 245a3d +1 201bc9 +2 23998b +1 261b89 +1 22f00f +1 241b49 +1 261b49 +5 20f82f +3 23f80f +1 207a4d +1 23d82f +1 23d80f +1 22f80f +1 21998b +1 25990b +1 29683f +1 20f80f +2 21d81f +7 21783f +1 211817 +1 20d80f +2 21f87f +1 21f85f +6 21b83f +2 21983f +2 21b03f +4 20783f +1 21fc1f +5 20f81f +1 21f03f +1 21781f +1 21683f +1 20781f +1 30f83f + +n1 n +B 0.000000 466 96.221016 65.880127 42.628666 65.540955 65.136169 33.499176 122.715332 ++ 1.132004 1.430161 4.663825 2.934986 22.520151 19.640965 17.533510 +B 0.000000 400 32.968647 58.053131 95.972595 100.174919 15.305019 40.088394 32.883911 ++ 1.180815 4.024724 4.998824 12.525853 14.394125 27.436941 14.175666 +B 0.000000 397 64.352592 35.415405 49.319931 94.919861 6.597249 31.089392 73.432671 ++ 1.528855 2.633651 6.620992 10.883787 2.490365 9.204193 14.170980 +B 0.000000 308 3.788176 94.678162 37.477440 110.367874 6.451895 28.009228 50.172577 ++ 1.700863 3.165289 4.700360 12.757430 4.534128 11.556332 10.007993 +B 0.000000 11 96.548447 56.588295 3.797270 110.835938 6.535447 33.390266 28.510080 ++ 0.346741 9.824404 1.315016 8.808938 4.969743 2.459353 5.900515 +C 0.000000 11 66.853699 48.050938 44.054729 ++ 1.034541 1.791348 2.388063 +L 0.000000 469 32.986012 92.346420 49.786724 95.596733 ++ 2.737354 2.743523 8.855719 11.648461 +L 0.000000 460 95.879715 39.456810 48.397202 94.515976 ++ 3.053056 1.622921 7.833940 13.162826 +L 0.000000 431 96.285805 77.676979 40.772461 93.600243 ++ 1.897019 2.830868 3.792268 9.043732 +L 0.000000 409 31.237309 53.438374 41.397446 83.912048 ++ 3.700517 2.871494 5.372264 13.815991 +L 0.000000 79 67.942726 63.339741 93.927467 81.592308 ++ 6.041812 5.791952 6.151927 13.886495 +L 0.000000 9 127.629562 69.964645 4.160713 102.403305 ++ 0.953988 6.555627 1.071502 24.962435 +L 0.000000 5 8.402470 64.557396 74.406082 66.538757 ++ 1.461749 0.488164 1.376527 0.752456 +S 1.000000 478 75.275635 61.261791 57.922523 66.591148 63.889500 60.558853 ++ 16.839825 9.160315 12.576528 4.832184 6.780993 9.935832 + +45 1 +198 23cf +5 238f +4 22c7 +43 23c7 +26 21cf +3 204f +6 20cf +1 208f +4 20c7 +3 24cd +1 24dc +9 22cf +36 23c3 +11 27c3 +8 27c1 +15 23c1 +1 2781 +2 22c3 +1 21c3 +1 21c1 +2 33c3 +1 33c1 +2 37c1 +10 23cd +1 23c5 +1 22cd +14 27c5 +1 26cd +1 2cec +5 2ce4 +1 24f4 +3 2cf4 +1 2476 +6 27cd +1 26c5 +1 218f +1 23cb +1 2745 +18 27cf +11 234f +11 21c7 +1 235f +1 2347 +1 278d +4 23df + +o1 oO +B 0.000000 1 48.721157 48.914364 82.159470 96.802322 9.854740 32.224262 16.247139 ++ 2.000000 4.000000 4.000000 20.000000 20.000000 20.000000 8.000000 +C 1.000000 522 65.176491 50.574867 71.372253 ++ 1.093939 2.221102 7.731008 +L 0.000000 46 30.340809 92.912086 48.916248 70.739357 ++ 1.896051 2.348481 4.659981 4.779337 +L 0.000000 31 95.099022 36.733894 50.512363 70.809631 ++ 2.123242 1.771747 5.481993 4.928589 +L 0.000000 24 127.686623 64.903557 4.603537 69.127708 ++ 4.655590 6.545293 4.979148 9.439497 +L 0.000000 24 65.939606 63.209984 96.154556 70.661377 ++ 2.558996 3.239114 1.686395 3.659979 +S 0.000000 520 64.971611 64.209259 64.967133 64.684814 64.250908 64.425659 ++ 3.732792 4.610924 5.073050 3.690159 3.407230 4.866238 +S 0.000000 1 65.932106 66.599991 97.950462 67.242455 67.293106 97.561317 ++ 3.110660 2.668358 2.935793 2.135508 1.971776 3.379331 +S 0.000000 1 60.559086 60.893379 83.013672 60.968304 64.408752 77.384758 ++ 3.110660 2.668358 2.935793 2.135508 1.971776 3.379331 + +11 1 +415 42 +25 4a +14 62 +31 46 +1 10a +14 52 +5 56 +10 66 +1 82 +1 43 +5 5a + +p1 pP +B 1.000000 281 113.668610 63.365593 -12.013303 92.946342 23.823753 24.401981 76.403076 ++ 3.274523 4.908048 3.730091 10.018318 12.878125 11.023065 15.970778 +B 0.000000 203 32.758511 58.996120 97.064568 103.490051 12.213902 36.100883 30.966005 ++ 1.144293 6.908731 4.594098 13.295761 9.875551 20.086079 10.277295 +B 0.000000 183 64.371414 35.948601 25.192762 97.735374 4.031087 32.523571 93.671112 ++ 0.502725 2.896266 11.386579 9.252836 1.602147 6.475560 23.524464 +B 0.000000 20 59.357109 43.259064 27.346952 96.078522 4.354187 31.859087 104.992325 ++ 0.382259 0.973787 2.605977 9.187222 0.519975 0.915610 5.494196 +B 0.000000 4 64.580566 37.601086 -10.453959 106.147591 5.876606 35.122307 28.307861 ++ 0.232749 0.928754 13.731899 4.232671 3.831953 7.033803 6.977174 +B 0.000000 1 96.505165 56.326801 -39.366089 110.451675 4.662979 35.117104 27.756784 ++ 1.107310 3.079080 5.964315 7.743126 4.216052 5.929400 10.798676 +C 1.000000 281 69.029579 48.853512 62.401875 ++ 2.702189 2.269634 8.060341 +L 0.000000 272 95.648239 39.859795 27.260086 109.315079 ++ 2.617003 4.171162 6.459060 13.505775 +L 0.000000 82 5.563634 69.826233 3.797882 71.503433 ++ 2.686394 3.350593 3.219569 5.115186 +L 0.000000 76 65.744965 60.028080 96.903992 86.008385 ++ 3.884145 6.392358 4.153298 12.657137 +L 0.000000 44 0.406992 53.417896 -37.716820 69.945839 ++ 2.070823 3.325603 3.129141 2.983233 +S 0.000000 189 106.694687 51.226105 92.877289 76.420837 63.465084 90.725258 ++ 9.270274 7.500899 9.934175 5.085934 8.305172 10.658025 +S 0.000000 78 128.000000 50.292076 116.134247 75.744202 66.281853 106.464615 ++ 0.001000 4.205852 4.510570 2.491904 2.936578 2.377683 +S 0.000000 12 95.601852 36.238338 65.422462 75.082169 55.670029 75.383278 ++ 5.161718 4.785439 2.121409 2.489213 3.301271 2.023532 +S 0.000000 2 115.743935 53.124840 62.453449 83.969971 61.361389 63.261742 ++ 4.810997 5.263738 3.876387 3.217245 4.847674 4.427634 + +42 1 +38 8c7 +8 cc7 +3 dc7 +3 9c7 +19 ac5 +1 a53 +26 8c3 +8 9c3 +6 ac3 +16 ac1 +1 853 +1 bc3 +5 8c1 +2 22c1 +1 a55 +1 20c3 +2 40c3 +5 9c1 +4 bc1 +3 8c5 +8 20c7 +1 847 +1 20c5 +1 ae5 +14 ec5 +2 9cb +2 dcb +13 ccb +1 fc9 +1 8cb +1 ec9 +2 bc5 +1 f45 +1 fc5 +27 10c7 +42 11c7 +2 13c7 +2 12c7 +3 1147 +1 13c5 +1 1047 +1 9d7 + +q1 q +B 1.000000 206 79.772049 64.268074 -9.609321 88.739868 29.272705 50.723915 73.556938 ++ 3.163984 4.911148 4.200903 12.214839 19.588566 37.586330 13.088472 +B 0.000000 116 32.082615 77.381355 97.518837 107.497932 9.099118 32.754143 31.927900 ++ 0.894565 4.075375 1.223115 6.262252 3.711506 2.311426 5.916044 +B 0.000000 84 0.095114 92.638412 23.388855 101.871361 4.368847 31.907959 81.772873 ++ 1.587672 4.407501 12.209359 12.290738 1.097739 1.687850 17.761709 +B 0.000000 1 96.360428 78.145699 -39.673397 115.406288 5.043202 32.085342 29.978075 ++ 1.882074 4.464674 5.644409 9.577341 5.381740 10.033630 12.255408 +C 1.000000 206 63.495903 50.684250 60.980583 ++ 2.082130 1.758004 6.644128 +L 0.000000 201 31.698822 91.516457 30.771542 112.350693 ++ 2.132968 3.833863 13.501166 17.016409 +L 0.000000 45 62.411362 71.787483 98.111481 76.388885 ++ 2.201067 4.592012 1.504415 9.375568 +L 0.000000 29 126.973145 60.956131 4.354155 71.134766 ++ 6.543126 3.857384 6.387291 8.268604 +L 0.000000 28 0.075855 78.597626 -37.474285 67.280647 ++ 1.499795 1.534953 2.563594 2.371734 +L 0.000000 1 92.578636 72.060036 -10.963511 70.710220 ++ 2.405090 3.294809 3.297543 7.326585 +S 1.000000 206 103.921883 80.237053 36.217796 75.124847 66.594902 37.212254 ++ 12.582181 12.865452 8.741820 5.215771 8.787661 5.840743 + +22 1 +38 437 +10 537 +3 4b7 +1 5b7 +1 417 +4 473 +34 433 +14 4b3 +1 413 +16 471 +3 415 +29 431 +13 475 +7 4b1 +10 435 +2 4f1 +1 43b +9 533 +4 571 +4 575 +1 4f5 +1 6f1 + +r1 r +B 1.000000 506 122.002579 72.926941 48.090542 89.780914 14.518917 38.591595 92.594551 ++ 2.938426 2.572754 3.483554 10.707658 5.121342 5.102671 14.899382 +B 0.000000 459 32.657837 69.528549 95.772980 101.868546 14.945996 40.399483 33.631317 ++ 1.005877 3.253453 3.633201 13.673152 19.430702 31.699657 6.815200 +B 0.000000 427 64.294777 46.381519 48.817322 91.159485 7.299184 31.879887 78.487122 ++ 1.306108 2.972785 5.359557 11.884686 2.271223 8.831758 14.122711 +B 0.000000 5 96.411652 59.748444 2.561397 110.935913 5.566938 33.462292 28.840586 ++ 0.279770 1.054564 0.598385 1.525962 0.611581 1.894871 4.037446 +B 0.000000 3 0.196486 87.796768 82.867180 101.944260 15.976243 32.874702 15.877481 ++ 0.253132 0.256867 2.018080 3.823622 4.755073 4.470547 2.283705 +B 0.000000 1 44.933479 51.740879 92.396118 113.814980 8.576086 31.650616 20.042040 ++ 0.978898 2.022084 2.172012 7.091278 4.179528 5.523233 7.960932 +C 0.000000 1 47.471291 6.281808 10.992682 ++ 2.000000 2.000000 4.000000 +L 0.000000 475 95.888336 50.729317 47.993542 93.634995 ++ 2.687573 2.959996 8.779004 13.831631 +L 0.000000 399 31.243256 65.463097 40.995464 81.575813 ++ 3.236072 2.172186 4.363714 9.258805 +L 0.000000 378 0.690796 59.829880 3.735456 86.650093 ++ 1.793777 2.693128 2.181944 7.102993 +L 0.000000 105 66.266983 63.627289 96.493263 85.895393 ++ 3.513777 5.935421 2.198691 17.741119 +L 0.000000 38 11.528427 72.003685 75.563522 90.948662 ++ 3.816601 1.590131 3.038433 10.105516 +S 0.000000 503 98.070297 1.910212 98.574722 68.082253 67.564285 117.064514 ++ 12.594517 7.830218 16.720295 5.332330 13.426648 12.452277 +S 0.000000 3 79.435776 35.100452 100.105499 67.009476 67.741318 107.866936 ++ 3.415127 3.287462 6.043008 2.187797 1.351813 6.932395 + +31 1 +78 1287 +9 1687 +230 1387 +2 1607 +3 1207 +10 1d83 +9 1183 +25 1583 +20 1d81 +5 1983 +1 1505 +7 1581 +3 1083 +2 1b87 +11 1785 +1 1107 +23 1307 +1 1987 +10 1787 +2 1587 +9 1087 +27 1187 +3 1585 +3 2785 +2 1685 +1 11a7 +3 118f +1 110f +3 1397 +1 138f +1 12c7 + +s1 sS +B 0.000000 437 63.418537 59.411243 32.020718 67.818420 59.635586 116.452217 86.324501 ++ 1.989567 2.143378 3.629008 4.037015 31.926275 19.027355 14.524844 +B 0.000000 416 2.098673 71.104973 70.898842 69.088875 52.006580 121.284569 75.792465 ++ 1.788093 7.186102 7.405542 9.841461 21.913267 32.791439 18.038359 +B 0.000000 22 96.559166 50.127930 1.658256 105.648811 6.900963 34.702797 22.546291 ++ 0.279173 3.998817 0.852693 4.346086 1.799275 1.723215 2.840059 +B 0.000000 21 1.880381 90.947548 58.610378 100.896797 16.086239 35.604748 35.625885 ++ 0.849221 1.609352 2.905384 6.480690 5.544969 5.787466 5.931922 +B 0.000000 14 32.340218 76.597122 98.354240 106.569466 5.619655 34.281345 25.356102 ++ 1.385907 1.938034 1.115605 1.949998 1.358767 2.136999 3.003940 +C 0.000000 27 66.777702 69.197594 22.481655 ++ 1.299646 1.183175 3.534296 +A 0.000000 1 3.836915 104.785194 71.303711 ++ 8.000000 8.000000 4.000000 +L 0.000000 389 118.006454 60.339039 44.281746 97.570656 ++ 6.188199 2.057002 3.617082 16.533052 +L 0.000000 342 54.060871 71.883324 59.221840 98.161926 ++ 5.152702 2.431321 4.370430 17.272837 +L 0.000000 215 62.858196 69.431694 98.339653 79.268524 ++ 2.881737 2.850909 2.074657 7.668166 +L 0.000000 158 127.920822 61.300671 2.145379 87.349884 ++ 2.145935 2.995036 1.863366 10.204938 +L 0.000000 71 57.558968 58.723652 24.200190 78.702507 ++ 5.312234 3.475300 4.830414 7.646974 +L 0.000000 47 120.769836 72.195206 77.262505 73.622009 ++ 3.735783 2.296990 5.813509 14.389694 +L 0.000000 3 5.930295 66.741867 84.937073 68.188820 ++ 1.312585 0.517553 0.421846 3.326469 +S 0.000000 430 62.671398 64.916328 40.987827 61.719166 63.907291 56.548470 ++ 9.086356 13.431835 8.931231 3.813514 6.367687 11.891753 +S 0.000000 8 61.070724 74.615036 67.457031 65.949509 63.866928 62.131641 ++ 6.544128 10.747093 3.907657 2.861400 2.422800 4.252401 + +75 1 +54 4783 +3 4d93 +3 4b87 +1 5997 +9 4f83 +1 4a87 +1 4197 +1 5513 +2 4593 +1 5197 +2 4997 +1 5917 +1 4c93 +5 40ad +3 44a9 +2 402d +1 482d +1 40a9 +1 84a9 +29 4583 +2 46a9 +1 48ad +1 40b9 +1 4029 +1 48a9 +69 4183 +20 4083 +3 4883 +1 5083 +10 4103 +8 4303 +2 5583 +5 5183 +83 4383 +8 4483 +3 5903 +1 4229 +2 40a3 +2 4023 +1 5c83 +4 5983 +1 4829 +8 4983 +1 4823 +1 5140 +1 4903 +1 4423 +4 5883 +4 5d83 +1 5003 +1 4803 +3 4a83 +7 5383 +7 4283 +9 4683 +1 4b83 +2 4387 +2 5283 +2 4e83 +4 5f83 +1 4787 +1 5e83 +1 5d03 +6 8783 +1 8583 +2 5783 +5 4703 +1 4503 +2 6183 +1 6303 +2 4003 +1 4c83 +1 4b03 +2 4d83 +1 4403 + +t1 t +B 0.000000 779 127.971481 74.788460 50.788601 79.108154 13.116846 32.266438 88.797104 ++ 2.014729 2.450627 2.860739 11.294030 9.758488 6.955990 17.620438 +B 0.000000 704 67.894249 50.896477 57.953945 109.527130 6.844956 29.050863 50.955151 ++ 2.097174 6.630794 4.566650 5.745431 4.958992 3.294984 11.922171 +B 0.000000 702 13.035578 73.834778 105.023308 104.166786 13.665397 29.933807 42.403179 ++ 3.268653 3.152887 6.111755 6.665466 3.223129 5.679813 10.094704 +B 0.000000 386 53.642784 52.023670 105.428108 110.873642 9.273686 34.410366 32.353291 ++ 2.515232 5.448553 5.522395 5.959321 3.885378 5.070544 6.692665 +B 0.000000 8 124.371063 69.210815 50.912434 65.797104 9.320175 3.767332 80.957939 ++ 0.408637 1.036428 1.243992 5.877431 0.868929 2.932947 4.288234 +B 0.000000 1 48.855816 64.360390 126.223228 108.922127 7.734501 28.236593 15.358098 ++ 2.060885 2.993080 2.987623 6.145307 3.275606 3.369755 8.531741 +L 0.000000 495 96.741814 53.677742 45.297375 74.639763 ++ 2.191667 3.304158 6.041317 8.436039 +L 0.000000 294 127.884209 67.071159 5.936949 79.569191 ++ 7.436816 4.190179 2.587068 12.761840 +L 0.000000 194 32.111805 68.936852 50.150490 71.001167 ++ 2.582550 3.955860 7.611604 4.202947 +L 0.000000 57 10.146840 73.780693 79.219521 77.695663 ++ 1.334716 0.670993 1.516759 4.007484 +L 0.000000 28 78.434608 59.620010 115.480362 94.941635 ++ 4.565620 2.286892 6.649556 13.726661 +L 0.000000 8 61.131317 75.799698 98.380814 64.466385 ++ 0.315236 1.000301 1.247136 0.278227 +L 0.000000 7 66.586929 76.918739 26.607550 67.548203 ++ 5.756320 3.922859 4.281005 1.507601 +L 0.000000 3 63.234932 65.280754 120.666641 69.990440 ++ 1.452045 0.400704 1.166538 4.213754 +S 0.000000 694 73.650818 28.929575 56.607044 51.344837 62.251610 47.840992 ++ 7.880009 25.677275 13.854081 6.353786 7.640216 21.978771 +S 0.000000 93 80.725990 1.745211 112.724709 56.113110 53.567924 124.302361 ++ 7.483100 5.835322 8.617117 3.490106 4.751278 7.984472 + +110 1 +3 504f +87 404f +67 400f +20 408f +4 480f +2 484f +2 488f +8 410f +2 500f +18 414f +17 4147 +55 4047 +23 41cf +4 4187 +28 40c7 +38 40cf +23 41c7 +1 41c3 +1 41cd +27 4087 +14 40cb +6 40c3 +1 60cb +1 6047 +1 44cb +11 404b +4 400b +1 600b +4 4083 +10 814f +3 804b +1 8087 +6 804f +2 808b +5 800b +16 80cf +2 8007 +2 814b +3 8147 +7 81cf +1 810f +1 80cb +3 80c3 +3 8103 +2 8107 +1 4041 +2 41cb +6 4043 +1 4183 +1 4003 +14 4145 +10 4107 +2 4547 +6 41c5 +4 40cd +1 414d +9 40c5 +4 45c5 +16 4045 +4 44c5 +1 44cd +1 454d +4 4445 +1 45e7 +1 404d +1 4545 +1 8156 +1 8154 +1 8487 +1 84c5 +1 4487 +1 8083 +1 8507 +2 8196 +1 9154 +2 81c1 +1 4112 +1 80c1 +1 44c1 +67 4007 +1 850f +1 858f +1 8503 +2 8187 +1 45c7 +1 81c7 +1 81d2 +1 8145 +1 8183 +1 810b +1 81c5 +1 81d0 +1 4141 +1 4285 +1 80c7 +1 82cf +12 4247 +6 4287 +1 42c7 +1 42cf +23 4207 +1 4387 +3 418f +1 424d +2 424f +1 52c5 +4 428f +1 420f +1 4245 +1 4307 + +u1 uU +B 1.000000 294 32.344082 65.632393 57.738041 66.019241 68.958389 33.863811 118.843895 ++ 1.047105 1.256166 4.015125 2.517415 23.686312 26.408573 15.641079 +B 0.000000 226 96.991684 74.152313 3.842182 106.302490 11.478492 35.698883 37.010895 ++ 1.080155 5.981758 6.544886 14.284612 20.989578 34.600903 13.201854 +B 0.000000 175 2.902221 94.382896 42.063622 107.393440 6.668392 26.576069 56.807888 ++ 1.468791 3.345255 3.855270 16.056234 4.503395 13.725299 11.455911 +B 0.000000 172 67.966064 36.909855 60.828533 109.499443 6.745671 28.819033 51.593761 ++ 1.783231 1.892945 5.365258 12.913818 2.675867 9.086118 9.889072 +L 0.000000 287 31.874620 91.849976 54.395554 105.917786 ++ 2.026851 2.635672 4.703421 12.109524 +L 0.000000 280 32.500431 54.920906 59.807896 96.624947 ++ 2.473065 2.940320 3.790594 9.194106 +L 0.000000 277 96.773216 39.572529 49.949902 94.824554 ++ 2.285942 2.126208 8.686955 14.856753 +L 0.000000 227 94.945831 76.665245 60.802868 85.979980 ++ 3.579784 3.148518 6.120341 14.141690 +L 0.000000 79 3.160909 66.757523 4.476694 82.439972 ++ 4.681103 7.381720 5.588319 13.029692 +L 0.000000 4 71.335892 65.640381 24.971199 66.709244 ++ 1.613819 1.200168 3.819045 0.905965 +L 0.000000 1 39.041630 56.339901 34.347469 70.041260 ++ 2.608626 3.179706 4.748529 9.564401 +L 0.000000 1 81.956070 65.752876 37.924316 66.774208 ++ 2.608626 3.179706 4.748529 9.564401 +S 1.000000 294 51.145863 67.028145 54.931042 61.224678 65.490959 58.159832 ++ 10.998899 8.259684 7.756257 3.967161 5.876524 9.034195 + +41 1 +72 10ff +5 10bf +40 107f +8 11fd +2 106f +7 103f +1 117d +5 105f +2 107b +48 10f3 +35 11f1 +8 11f3 +1 11d1 +3 1073 +1 10d3 +1 10d1 +1 10bb +9 10f1 +2 13f3 +1 12f1 +1 12f3 +1 10f7 +1 10c7 +1 10fd +6 11f5 +3 10fb +1 10f5 +2 11f9 +1 10af +1 113d +9 11ff +2 11bf +1 1171 +1 187f +2 117f +1 10cf +1 14df +2 10ef +3 10df +1 1177 +1 1077 + +v1 vV +B 1.000000 246 32.351791 67.549446 73.416939 77.736115 53.608814 33.660950 91.264488 ++ 0.829897 2.422852 7.343572 6.293621 16.851578 15.758283 14.525494 +B 0.000000 54 74.117958 43.466389 55.906147 116.746002 4.370049 30.707365 52.353569 ++ 3.691535 1.500730 4.360497 18.515509 12.004759 15.196943 11.508620 +B 0.000000 36 117.778824 89.465195 58.642544 112.584610 3.334337 34.187828 42.365448 ++ 0.494475 2.413677 4.819043 5.555067 0.721050 0.750953 11.085719 +B 0.000000 3 112.150887 61.671951 14.378730 107.655632 5.272738 34.661594 20.788509 ++ 0.098791 0.098255 0.567993 1.150143 0.205327 0.499051 0.808857 +A 0.000000 1 61.002758 123.181183 62.596966 ++ 8.000000 8.000000 4.000000 +L 0.000000 243 104.265236 47.726601 47.914917 122.827011 ++ 2.976522 3.034788 6.291406 9.898674 +L 0.000000 236 23.189365 81.491829 46.092026 124.764854 ++ 3.012936 4.943545 8.808163 21.422655 +L 0.000000 185 38.912258 58.766869 63.887802 84.253281 ++ 2.391207 3.187271 4.815437 7.640772 +L 0.000000 144 87.900345 73.591530 63.601368 82.361549 ++ 1.657567 7.322375 7.439950 11.529509 +L 0.000000 3 17.067984 72.629326 24.787277 77.580582 ++ 0.620785 0.896953 1.284040 4.092171 +L 0.000000 2 64.490273 51.342121 96.469971 67.859573 ++ 1.778718 2.748854 4.684488 7.923464 +L 0.000000 1 102.775436 41.851917 69.563179 67.295189 ++ 1.778718 2.748854 4.684488 7.923464 +S 0.000000 219 101.803955 50.536652 59.078362 79.754326 66.913239 58.480766 ++ 21.961138 15.553299 9.528308 7.285578 8.125271 14.999471 +S 0.000000 27 75.627853 17.609882 59.257557 70.722588 36.470707 93.063278 ++ 15.727227 11.856354 5.648488 5.259903 6.756680 7.909597 + +24 1 +12 1067 +6 11e3 +13 11e7 +5 10e3 +1 1165 +1 10e7 +8 1063 +2 1065 +88 11e1 +27 1061 +30 10e1 +7 1161 +20 21e1 +1 1071 +2 1461 +5 20e1 +1 1a83 +2 12a3 +4 10a3 +1 21a9 +1 20e9 +1 108b +1 1103 +7 11e5 + +w1 wW +B 0.000000 258 96.244667 62.874035 20.321590 81.131210 47.217316 39.469917 78.854225 ++ 0.956490 4.359797 5.382936 5.171886 11.733747 11.375168 19.628902 +B 0.000000 254 32.235878 83.200073 72.981903 76.156609 58.250320 29.683231 75.159027 ++ 0.753358 4.890212 6.991723 4.362833 11.442469 31.746386 23.911297 +B 0.000000 237 32.367302 50.738300 74.199432 76.325455 60.465927 53.281116 75.130440 ++ 0.603541 2.980417 6.515593 6.149244 17.045109 31.735970 25.432661 +B 0.000000 60 70.538437 28.579718 60.156021 116.077950 5.281207 30.244724 50.731026 ++ 2.213224 3.721112 5.328757 20.988091 14.474094 13.883965 9.119967 +B 0.000000 23 120.017982 97.746513 55.673767 115.039108 4.598456 34.005497 50.080906 ++ 2.124759 7.793733 7.548415 6.451763 4.094082 1.402961 2.237765 +B 0.000000 8 32.524090 53.044334 74.212624 62.387787 114.687187 8.992381 54.874939 ++ 1.040787 3.071027 4.358390 1.205125 9.094528 11.711775 6.496430 +B 0.000000 4 32.676048 45.776257 96.031685 116.311470 4.726443 34.602097 32.206829 ++ 0.606911 8.604010 2.195585 7.354876 2.051157 1.712397 4.874518 +B 0.000000 4 32.492718 88.064407 81.768318 66.788269 103.105453 86.845329 45.027599 ++ 0.209620 1.394430 1.505003 1.830020 5.527290 27.781788 0.526149 +B 0.000000 1 93.182465 95.793419 -35.557281 96.593727 12.846161 19.971483 32.719765 ++ 0.983699 4.111456 3.815609 4.003482 6.789504 13.842034 9.578647 +B 0.000000 1 113.661156 98.690155 31.249268 89.796089 11.653859 49.634819 48.604595 ++ 0.983699 4.111456 3.815609 4.003482 6.789504 13.842034 9.578647 +C 0.000000 12 51.946461 69.052498 30.216795 ++ 1.864524 2.011143 6.864695 +A 0.000000 1 32.659500 128.000000 -39.602077 ++ 8.000000 8.000000 4.000000 +A 0.000000 1 64.256409 97.375481 71.767464 ++ 8.000000 8.000000 4.000000 +A 0.000000 1 67.599831 73.957535 89.733208 ++ 8.000000 8.000000 4.000000 +L 0.000000 256 102.364853 31.720367 49.462517 117.929909 ++ 2.795068 3.902761 9.576177 17.109182 +L 0.000000 247 24.219999 96.027992 46.696129 123.087929 ++ 3.294052 3.884154 7.954340 19.808279 +L 0.000000 163 37.358707 41.680637 66.200500 76.515190 ++ 2.427262 3.647253 4.729640 5.385414 +L 0.000000 155 38.087978 76.095757 67.247368 82.717140 ++ 3.554387 4.659727 5.737158 11.805880 +L 0.000000 130 22.467167 55.817032 29.093262 82.357407 ++ 1.534722 2.986627 2.795464 8.040937 +L 0.000000 130 103.386314 71.270180 29.972193 81.320694 ++ 2.326857 3.447881 3.001942 10.927684 +L 0.000000 123 87.082146 53.930706 69.823486 86.938141 ++ 1.738102 2.540950 2.487126 9.007973 +L 0.000000 102 88.631332 88.751472 65.167099 78.350739 ++ 1.919436 8.923090 7.188677 7.842504 +L 0.000000 7 64.592690 45.706860 97.497307 76.077019 ++ 1.188854 2.482728 0.833187 1.490786 +L 0.000000 3 18.126627 88.196938 28.586210 81.444839 ++ 0.129926 0.791232 0.792193 1.913852 +L 0.000000 1 23.284918 40.668812 72.200211 72.517723 ++ 1.811868 3.208333 3.436969 7.627797 +L 0.000000 1 0.216702 88.224258 -39.172920 128.000000 ++ 1.811868 3.208333 3.436969 7.627797 +L 0.000000 1 24.313902 68.326645 61.968483 99.237137 ++ 1.811868 3.208333 3.436969 7.627797 +L 0.000000 1 103.545509 60.477512 64.629776 116.569572 ++ 1.811868 3.208333 3.436969 7.627797 +L 0.000000 1 55.407459 64.475159 86.435478 72.272972 ++ 1.811868 3.208333 3.436969 7.627797 +L 0.000000 1 36.131119 84.433517 93.277664 109.413429 ++ 1.811868 3.208333 3.436969 7.627797 +L 0.000000 1 87.317352 87.580116 -23.228130 96.369888 ++ 1.811868 3.208333 3.436969 7.627797 +L 0.000000 1 118.632698 100.085938 -35.065048 102.947517 ++ 1.811868 3.208333 3.436969 7.627797 +L 0.000000 1 96.950386 79.444633 93.485710 107.452049 ++ 1.811868 3.208333 3.436969 7.627797 +L 0.000000 1 41.511826 108.142067 -27.747995 95.900505 ++ 1.811868 3.208333 3.436969 7.627797 +L 0.000000 1 0.112735 83.342934 80.703979 128.000000 ++ 1.811868 3.208333 3.436969 7.627797 +L 0.000000 1 60.535061 98.224976 -16.396955 72.791870 ++ 1.811868 3.208333 3.436969 7.627797 +L 0.000000 1 64.922112 87.595474 -39.226452 128.000000 ++ 1.811868 3.208333 3.436969 7.627797 +S 1.000000 259 82.655106 57.457485 59.699615 74.458687 58.299721 58.629131 ++ 15.095680 13.395503 5.705947 5.495281 7.045991 9.142624 + +84 2 +1 1c00f 20 +3 20c007 20 +1 c01f 20 +7 1c007 20 +2 20c00f 20 +1 21c01f 20 +1 20c017 20 +2 21c00f 20 +30 c007 20 +1 21c017 20 +1 1c01f 20 +2 c00f 20 +2 21c007 20 +8 c017 20 +1 2c013 20 +1 c403 20 +33 3fc007 20 +4 37c007 20 +3 13c007 20 +13 3bc007 20 +10 1fc007 20 +1 1bc007 20 +1 1dc007 20 +3 cc007 20 +2 8c007 20 +6 4c007 20 +2 10c007 20 +1 33c007 20 +2 39c007 20 +4 23c007 20 +1 15c007 20 +2 3ec007 20 +9 7c007 20 +1 31c007 20 +1 29c007 20 +3 27c007 20 +1 6c007 20 +1 3ac007 20 +3 5c007 20 +3 3c007 20 +2 2c007 20 +2 bc007 20 +1 c207c907 3a +1 8c085 20 +1 4c085 20 +1 4c023 20 +1 cc023 20 +1 c0a1 20 +2 c023 20 +1 c085 20 +1 ec00f 20 +2 1ec00f 20 +1 1b400f 20 +2 1f400f 20 +1 8b400f 20 +1 9f400f 20 +2 fc00f 20 +12 1fc00f 20 +2 e400f 20 +1 1e4007 20 +1 1bc00f 20 +1 13c00f 20 +1 8f400f 20 +1 2c443 20 +1 23c023 20 +1 40c413 20 +2 40c403 20 +1 48c403 20 +2 1c023 20 +2 42c403 20 +1 9c007 20 +1 ac443 20 +1 8c453 20 +1 c44b 20 +1 60c403 20 +1 1c001000 20 +1 22800f 20 +1 132000f 20 +3 1fc017 20 +18 3fc00f 20 +2 3fc017 20 +1 1f421f 20 +1 201fe007 25 +1 3fc01f 20 + +x1 xX +B 1.000000 219 96.355125 63.247963 13.999931 80.767242 38.038086 32.896530 58.311264 ++ 0.648495 3.211487 4.348387 13.232990 39.852615 9.599280 11.786710 +B 1.000000 219 63.281193 43.896576 50.427814 100.435890 14.947788 33.005886 94.733398 ++ 1.725219 3.350940 1.937853 8.327360 3.091172 3.715927 9.013763 +B 1.000000 219 32.426559 67.982010 87.523476 81.285576 37.829376 36.022549 56.939945 ++ 1.066952 3.207928 3.490776 12.194942 25.930359 14.363316 15.430223 +B 1.000000 219 0.120843 87.344063 50.412525 99.837624 15.042207 32.749798 94.666710 ++ 1.953570 2.894601 2.170016 10.387034 3.444952 4.260798 8.196331 +L 0.000000 56 41.634029 83.133560 34.082119 82.225197 ++ 4.955372 3.254902 5.317792 9.340141 +L 0.000000 49 105.295883 47.801384 69.867119 81.000031 ++ 2.394077 2.874877 3.605093 6.277549 +L 0.000000 27 20.191656 83.828644 70.911194 77.420975 ++ 2.720726 2.080513 2.152343 6.373694 +L 0.000000 24 84.513008 46.774498 30.460661 76.886505 ++ 1.261602 1.112045 1.693888 5.484103 +L 0.000000 7 60.992157 57.252319 93.062233 68.772339 ++ 7.487716 7.914698 4.055992 1.183348 +L 0.000000 5 109.439568 73.686844 16.930790 65.588531 ++ 0.150955 0.276800 0.284585 0.284201 +L 0.000000 5 126.167953 79.185478 7.152898 69.921471 ++ 4.034204 0.546174 2.931940 5.486733 +L 0.000000 4 10.659056 54.046906 11.181263 67.811523 ++ 0.107136 1.087787 0.955513 1.471493 +S 1.000000 219 61.633579 65.729988 64.020096 63.968361 64.269897 58.627922 ++ 5.268442 8.911586 9.507389 4.257296 4.869193 12.371187 + +22 1 +9 104f +95 100f +15 108f +6 10cf +11 106f +27 101f +18 102f +1 112f +15 103f +1 114f +1 140f +1 110f +2 109f +1 141f +3 143f +1 119f +2 111f +1 113f +1 181f +3 180f +3 121f +2 120f + +y1 yY +B 1.000000 283 32.415024 68.630936 72.443581 77.288399 54.325455 32.787834 90.958382 ++ 0.970210 3.229774 4.166248 5.201997 18.640234 15.886462 20.905537 +B 0.000000 280 64.785919 44.860470 19.298120 97.303307 11.987974 43.143604 114.656982 ++ 1.512275 2.929702 5.232248 10.275928 3.303472 10.364510 16.806690 +B 0.000000 48 121.470619 81.913940 25.340366 104.757439 5.705889 29.079250 85.793701 ++ 1.963829 1.719275 13.738225 8.044033 1.853584 2.534754 20.601740 +B 0.000000 3 63.444374 57.162544 -8.412968 73.041412 31.706587 3.862012 92.434616 ++ 0.447283 0.748998 1.457158 1.102664 3.513500 4.554095 4.685905 +B 0.000000 1 112.227417 54.108624 -32.631241 106.405182 6.591868 34.772816 17.937624 ++ 1.032959 2.156937 4.925428 4.638646 4.200089 6.581387 12.579272 +L 1.000000 283 23.533085 77.417046 28.603615 116.623718 ++ 2.103441 4.148291 13.485330 19.390909 +L 0.000000 267 103.991615 45.996246 48.314362 97.017845 ++ 2.862732 4.962595 8.325036 14.568615 +L 0.000000 31 38.967869 56.994308 64.859489 68.340630 ++ 3.081553 4.419709 3.739399 3.283418 +L 0.000000 22 86.203888 69.404968 67.038414 70.100433 ++ 0.648402 1.558100 2.379541 2.704495 +L 0.000000 10 127.680367 55.989136 -33.697571 68.999611 ++ 2.095371 5.567131 2.721957 1.953260 +L 0.000000 3 99.258568 58.282597 28.358868 70.176903 ++ 0.174212 0.291156 1.753930 1.802400 +L 0.000000 1 73.895485 50.867226 -19.354822 82.276474 ++ 1.689669 3.238439 4.536214 6.296350 +S 0.000000 239 110.032219 24.139786 87.490227 66.722984 63.837524 97.504013 ++ 16.653784 18.140585 12.533397 2.796821 10.930485 12.480914 +S 0.000000 44 113.066910 47.678642 17.341673 65.565254 23.400368 35.758411 ++ 12.626781 18.619720 10.361648 2.699383 9.735518 11.735987 + +22 1 +24 1067 +7 1267 +5 1027 +8 2067 +1 10e7 +2 1227 +1 2267 +15 10e3 +154 1063 +17 1163 +4 11e3 +27 2063 +5 20e3 +1 10f3 +2 1423 +1 14a3 +2 10a3 +3 1023 +1 1863 +1 21a9 +1 20e9 +1 2069 + +z1 zZ +B 1.000000 200 63.243301 52.121666 59.530178 79.935303 30.346128 4.033818 87.103630 ++ 1.684175 3.355477 3.706079 7.616447 22.933512 5.111348 13.965971 +B 1.000000 200 0.341543 78.784569 41.086369 80.588898 31.978434 3.561236 90.315559 ++ 1.471563 3.366239 3.368631 5.923500 22.010273 4.605107 14.329727 +B 0.000000 8 96.554031 58.772877 1.685135 114.875473 3.754868 31.735287 34.765381 ++ 0.258663 8.609570 1.033601 3.920547 0.349075 0.665051 3.755219 +B 0.000000 4 32.527084 80.562675 98.582321 106.040672 6.775808 33.779240 26.100882 ++ 0.129466 1.910102 0.967637 5.037095 1.647294 1.412353 3.494154 +L 0.000000 195 64.004868 66.364014 98.246185 107.455406 ++ 2.228899 5.428569 2.230376 17.376507 +L 0.000000 192 127.946167 64.561234 1.850475 112.659782 ++ 1.375359 5.056271 1.749154 16.955082 +L 0.000000 186 19.997036 76.062378 54.266243 104.057739 ++ 2.705093 4.797981 6.727909 17.557537 +L 0.000000 179 83.761864 53.983177 45.701317 103.788528 ++ 2.204089 4.617751 8.689490 18.752487 +L 0.000000 51 64.928383 75.046921 18.459869 72.499321 ++ 4.044360 3.246527 2.230645 5.148387 +L 0.000000 25 127.122482 55.222935 83.618912 70.681557 ++ 3.240227 1.225302 3.088502 6.395640 +S 1.000000 200 60.866608 65.158737 82.533691 63.366776 65.221985 84.404449 ++ 7.452246 12.496560 14.630543 3.919774 3.815327 12.435593 + +18 1 +105 4f3 +13 473 +4 433 +2 573 +32 5f3 +6 4b3 +16 7f3 +8 6f3 +4 4d7 +1 6b3 +1 5b3 +1 4d3 +2 457 +1 4ab +1 4af +1 4eb +1 4e3 +1 4cf + +{1 { +B 1.000000 200 127.673088 76.948845 48.764980 111.008728 6.721030 32.083565 126.945259 ++ 1.849379 4.152786 6.940304 12.463645 1.126888 1.301260 4.594668 +B 0.000000 175 70.116867 52.105934 20.795830 108.948959 8.672771 28.453171 59.025860 ++ 2.565552 4.641238 4.973838 7.523918 1.998415 3.891269 9.999227 +B 0.000000 175 56.411503 54.732529 81.056732 110.514763 8.477478 35.699596 58.194031 ++ 2.117340 1.696769 8.152452 5.203948 2.329195 2.728536 14.810811 +L 0.000000 75 31.331718 70.045601 44.346737 71.782600 ++ 4.799385 4.211577 32.404488 14.556917 +L 0.000000 72 121.031616 70.040268 -31.301460 75.616737 ++ 1.774224 2.572830 5.096611 7.007307 +L 0.000000 27 81.480179 64.200630 102.040947 70.400780 ++ 14.421985 7.885526 33.996635 6.601910 +L 0.000000 13 35.396404 68.808647 11.168782 67.993614 ++ 3.644351 4.390502 4.557734 2.891825 +L 0.000000 2 97.722801 55.413952 48.685635 67.977898 ++ 6.159986 4.765109 19.013866 7.157951 +S 0.000000 121 64.581985 82.335014 65.055061 64.160019 68.984550 70.396408 ++ 5.925170 20.430475 5.943394 3.096610 4.028257 8.423204 +S 0.000000 66 66.162590 5.612901 63.175472 64.083733 49.601189 70.642113 ++ 10.202608 9.889603 6.455539 3.926339 8.612967 12.650011 +S 0.000000 5 50.690166 61.493851 84.739586 68.600845 69.623009 93.372719 ++ 7.430200 5.475628 3.753100 2.525562 3.060655 4.585694 +S 0.000000 5 63.044106 0.638300 109.364876 63.623062 64.273293 128.000000 ++ 6.911271 0.532708 2.436517 2.277982 2.305170 0.001000 +S 0.000000 2 85.852783 5.778881 39.525864 58.481728 47.764870 27.494415 ++ 7.566298 9.000285 4.647137 2.827598 4.462058 5.987449 +S 0.000000 1 56.482979 0.422444 85.010376 64.006477 47.990177 128.000000 ++ 7.566298 9.000285 4.647137 2.827598 4.462058 5.987449 + +48 1 +5 209 +5 201 +3 13f +36 107 +5 227 +2 203 +1 137 +5 205 +6 20f +1 1003 +1 125 +1 221 +6 127 +1 233 +1 10b +1 213 +1 20b +25 117 +4 147 +1 1a5 +16 10f +3 157 +16 11f +1 101 +3 12f +1 105 +1 103 +10 21f +4 217 +8 207 +5 807 +1 22f +1 223 +1 215 +1 225 +1 219 +3 24f +1 211 +1 123 +1 2a9 +1 201f +1 100f +2 25f +1 41f +2 40f +1 417 +1 14f +1 407 + +}1 } +B 1.000000 200 63.559364 53.794117 48.868015 110.751999 6.759519 32.347931 126.945396 ++ 1.778506 3.255461 5.819233 9.268789 1.054920 1.626490 7.631603 +B 0.000000 182 120.484009 76.007164 18.704113 110.881531 8.225213 35.461720 58.853573 ++ 2.000758 1.824448 7.670814 4.185468 2.208225 2.114261 12.859357 +B 0.000000 170 6.355283 79.011963 78.948509 108.784096 8.740952 28.433990 58.829994 ++ 2.749017 3.969042 6.378972 6.391382 2.220545 2.346315 9.246925 +L 0.000000 73 56.670677 60.680046 129.517578 75.430145 ++ 2.569440 3.111424 9.114188 10.622119 +L 0.000000 38 98.835907 61.091808 81.739799 70.350197 ++ 3.979411 4.677207 9.769058 20.880836 +L 0.000000 33 90.950996 59.803204 12.548512 67.688896 ++ 2.633395 3.153071 8.752556 3.056342 +L 0.000000 27 6.557302 58.520748 -32.041336 72.515427 ++ 2.804311 3.642895 5.448702 10.524003 +L 0.000000 17 20.212893 74.895477 23.009594 67.553833 ++ 5.610450 4.115465 18.050678 1.637987 +L 0.000000 4 51.824657 80.472092 60.628979 66.885178 ++ 0.701947 2.403031 4.720799 1.262015 +L 0.000000 1 91.844826 62.991520 35.073673 90.959145 ++ 3.049826 3.415991 8.886615 5.118499 +L 0.000000 1 39.092323 71.653404 91.352516 72.096718 ++ 3.049826 3.415991 8.886615 5.118499 +S 0.000000 187 68.131081 71.150078 67.804169 63.005291 65.406769 71.314919 ++ 9.923227 37.519688 9.653027 3.093933 10.646311 16.980450 +S 0.000000 10 68.461884 122.406807 112.460365 65.246590 65.458321 128.000000 ++ 7.882872 9.113213 6.095694 3.330409 4.132617 0.001000 +S 0.000000 3 66.341537 128.000000 72.708809 66.200493 90.772766 104.959435 ++ 2.937354 0.001000 3.219130 2.018851 0.679702 8.066925 + +43 1 +4 80b +48 807 +6 803 +12 847 +32 80f +2 891 +2 84b +2 813 +1 81b +16 81f +2 865 +4 84f +5 817 +1 857 +2 85f +12 827 +6 887 +2 8c7 +1 8af +4 82f +3 1007 +2 1087 +1 a0f +1 937 +4 1027 +1 10a7 +2 90f +1 907 +2 821 +1 8a1 +3 801 +2 885 +2 811 +1 c13 +1 831 +1 829 +1 809 +1 823 +1 83f +2 2017 +1 201f +1 845 +1 867 + +~1 ~ +B 1.000000 200 95.929482 55.263809 49.540985 109.272064 7.592074 32.088535 51.948685 ++ 0.777016 2.449516 6.594998 5.684134 1.923762 0.965635 6.309652 +B 1.000000 200 31.722961 76.623627 69.425728 108.742126 8.065698 32.432266 49.260254 ++ 0.926193 2.723093 6.129315 3.971954 2.165159 1.642222 6.103605 +L 0.000000 75 82.201660 36.365952 63.851883 90.249504 ++ 3.405952 2.280523 6.313567 16.463619 +L 0.000000 58 18.016506 93.627487 56.298435 93.142319 ++ 4.173990 2.409632 5.736339 18.645557 +L 0.000000 38 102.779007 35.000134 51.252567 75.748268 ++ 4.585302 1.898064 6.689207 8.332808 +L 0.000000 25 123.243507 74.015533 52.297825 80.174850 ++ 1.701501 4.237411 6.090243 10.615335 +L 0.000000 23 57.495289 58.143204 67.278862 75.020836 ++ 3.394267 6.097888 5.253304 10.667645 +L 0.000000 19 37.267071 96.620750 65.076370 71.888298 ++ 5.426067 2.709064 5.326566 5.759484 +L 0.000000 1 80.261490 84.394463 64.149063 97.303970 ++ 3.781180 3.272097 5.901537 11.747408 +S 0.000000 196 68.109436 61.493084 40.710907 65.672264 63.239918 20.555933 ++ 17.926043 14.032889 13.982686 6.020768 4.044930 10.738615 +S 0.000000 3 106.949654 64.673782 3.386474 73.726730 67.412086 2.344930 ++ 14.615839 1.022717 1.624485 2.716229 3.530555 2.349211 +S 0.000000 1 77.168457 113.624458 20.116713 64.406433 59.425316 10.741993 ++ 16.270941 5.383889 6.638361 4.368499 3.787742 6.543913 + +27 1 +57 203 +9 22f +27 213 +13 20b +31 207 +8 20f +7 24f +8 283 +3 293 +7 26f +5 21b +2 247 +1 2a3 +6 287 +2 223 +1 23b +1 24b +1 307 +1 44f +1 40f +2 243 +1 27b +2 22b +1 42f +1 84f +1 2c3 +1 233 + diff --git a/tessdata/tessconfigs/batch b/tessdata/tessconfigs/batch new file mode 100644 index 0000000000..176416473b --- /dev/null +++ b/tessdata/tessconfigs/batch @@ -0,0 +1,79 @@ +################################################# +# Adaptive Matcher Using PreAdapted Templates +################################################# + +acts_fx 0x800 +acts_ocr 0x20 + +RatingScale 30.0 +CertaintyScale 20.0 + +#EnableMatcher 0 +#CurrentFx 2 +MinSlope 0.414213562 +MaxSlope 2.414213562 +#ExtremityMode 1 +NormMethod 1 +EnableAdaptiveMatcher 1 + +NormAdjMidpoint 32.0 +NormAdjCurl 2.0 + +MinNormScaleX 0.0 +MaxNormScaleX 0.325 +MinNormScaleY 0.0 +MaxNormScaleY 0.325 + +BuiltInTemplatesFile tessdata/inttemp +BuiltInCutoffsFile tessdata/pffmtable + +EnableLearning 0 +SaveAdaptedTemplates 0 +UsePreAdaptedTemplates 0 +ReliableConfigThreshold 2 +MinNumPermClasses 3 + +#EnableStopper 1 +GoodAdaptiveMatch 0.125 +GreatAdaptiveMatch 0.0 + +EnableIntFX 1 +EnableNewAdaptRules 1 +################################################################################ +# +# File: marks/configs/knobs +# RCS: $Header: /cvsroot/tesseract-ocr/tesseract/tessdata/tessconfigs/batch,v 1.3 2007/02/02 23:45:33 theraysmith Exp $ +# Description: Control variables for 'marks' code +# Author: Mark Seaman, OCR Technology +# Created: Wed Feb 27 11:27:27 1991 +# Modified: Tue Jul 30 16:25:37 1991 (Mark Seaman) marks@hpgrlt +# Language: Text +# Package: N/A +# Status: Experimental (Do Not Distribute) +# +# (c) Copyright 1991, Hewlett-Packard Company, all rights reserved. +# +################################################################################ + +#hidden_edges 1 + +save_doc_words 1 +doc_dict_enable 1 +ClassPrunerThreshold 229 +ClassPrunerMultiplier 15 +IntThetaFudge 128 +CPCutoffStrength 0.15 +EvidenceTableBits 9 +IntEvidenceTruncBits 14 +SEExponentialMultiplier 0 +SimilarityCenter 0.0075 +################################################# +# Adaptive Matcher Using 2 Passes +################################################# + +EnableLearning 1 +SaveAdaptedTemplates 0 +UsePreAdaptedTemplates 0 + +#save_errors 0 + diff --git a/tessdata/tessconfigs/matdemo b/tessdata/tessconfigs/matdemo new file mode 100755 index 0000000000..24ce73da62 --- /dev/null +++ b/tessdata/tessconfigs/matdemo @@ -0,0 +1,82 @@ +################################################# +# Adaptive Matcher Using PreAdapted Templates +################################################# + +acts_fx 0x800 +acts_ocr 0x20 + +RatingScale 30.0 +CertaintyScale 20.0 + +#EnableMatcher 0 +#CurrentFx 2 +EnableAdaptiveMatcher 1 + +NormAdjMidpoint 32.0 +NormAdjCurl 2.0 + +MinNormScaleX 0.0 +MaxNormScaleX 0.325 +MinNormScaleY 0.0 +MaxNormScaleY 0.325 + +BuiltInTemplatesFile tessdata/inttemp +BuiltInCutoffsFile tessdata/pffmtable + +EnableLearning 0 +SaveAdaptedTemplates 0 +UsePreAdaptedTemplates 0 +ReliableConfigThreshold 2 +MinNumPermClasses 3 + +#EnableStopper 1 +GoodAdaptiveMatch 0.125 +GreatAdaptiveMatch 0.0 + +EnableIntFX 1 +EnableNewAdaptRules 1 +EnableAdaptiveDebugger 1 +MatchDebugFlags 6 +MatcherDebugLevel 1 +################################################################################ +# +# File: marks/configs/knobs +# RCS: $Header: /cvsroot/tesseract-ocr/tesseract/tessdata/tessconfigs/matdemo,v 1.2 2007/02/02 23:45:33 theraysmith Exp $ +# Description: Control variables for 'marks' code +# Author: Mark Seaman, OCR Technology +# Created: Wed Feb 27 11:27:27 1991 +# Modified: Tue Jul 30 16:25:37 1991 (Mark Seaman) marks@hpgrlt +# Language: Text +# Package: N/A +# Status: Experimental (Do Not Distribute) +# +# (c) Copyright 1991, Hewlett-Packard Company, all rights reserved. +# +################################################################################ + +#hidden_edges 1 + +save_doc_words 1 +doc_dict_enable 1 +ClassPrunerThreshold 229 +ClassPrunerMultiplier 15 +IntThetaFudge 128 +CPCutoffStrength 0.15 +EvidenceTableBits 9 +IntEvidenceTruncBits 14 +SEExponentialMultiplier 0 +SimilarityCenter 0.0075 +################################################# +# Adaptive Matcher Using 2 Passes +################################################# + +display_splits 0 +display_all_words 0 +display_all_blobs 0 +display_segmentations 0 +EnableLearning 1 +SaveAdaptedTemplates 0 +UsePreAdaptedTemplates 0 + +#save_errors 0 + diff --git a/tessdata/tessconfigs/old_batch b/tessdata/tessconfigs/old_batch new file mode 100755 index 0000000000..89de538488 --- /dev/null +++ b/tessdata/tessconfigs/old_batch @@ -0,0 +1,82 @@ +################################################# +# Adaptive Matcher Using PreAdapted Templates +################################################# + +acts_fx 0x800 +acts_ocr 0x20 + +RatingScale 30.0 +CertaintyScale 20.0 + +#EnableMatcher 0 +CurrentFx 2 +EnableMicroFeatures 1 +EnableCharNormFeatures 1 +MinSlope 0.414213562 +MaxSlope 2.414213562 +ExtremityMode 1 +NormMethod 1 +EnableAdaptiveMatcher 1 + +NormAdjMidpoint 32.0 +NormAdjCurl 2.0 + +MinNormScaleX 0.0 +MaxNormScaleX 0.325 +MinNormScaleY 0.0 +MaxNormScaleY 0.325 + +BuiltInTemplatesFile data/inttemp +BuiltInCutoffsFile data/pffmtable + +EnableLearning 0 +SaveAdaptedTemplates 0 +UsePreAdaptedTemplates 0 +ReliableConfigThreshold 2 +MinNumPermClasses 3 + +EnableStopper 1 +GoodAdaptiveMatch 0.125 +GreatAdaptiveMatch 0.0 + +EnableIntFX 1 +EnableNewAdaptRules 1 +################################################################################ +# +# File: marks/configs/knobs +# RCS: $Header: /cvsroot/tesseract-ocr/tesseract/tessdata/tessconfigs/old_batch,v 1.1 2006/06/16 22:17:08 lvincent Exp $ +# Description: Control variables for 'marks' code +# Author: Mark Seaman, OCR Technology +# Created: Wed Feb 27 11:27:27 1991 +# Modified: Tue Jul 30 16:25:37 1991 (Mark Seaman) marks@hpgrlt +# Language: Text +# Package: N/A +# Status: Experimental (Do Not Distribute) +# +# (c) Copyright 1991, Hewlett-Packard Company, all rights reserved. +# +################################################################################ + +#hidden_edges 1 + +save_doc_words 1 +doc_dict_enable 1 +ClassPrunerThreshold 229 +ClassPrunerMultiplier 15 +IntegerMatcherMultiplier 7 +IntThetaFudge 128 +CPCutoffStrength 0.15 +EvidenceTableBits 9 +IntEvidenceTruncBits 14 +SEExponentialMultiplier 0 +SimilarityCenter 0.0075 +################################################# +# Adaptive Matcher Using 2 Passes +################################################# + +EnableLearning 1 +SaveAdaptedTemplates 0 +UsePreAdaptedTemplates 0 + +save_errors 0 + diff --git a/tessdata/tessconfigs/segdemo b/tessdata/tessconfigs/segdemo new file mode 100755 index 0000000000..75ab1e66fc --- /dev/null +++ b/tessdata/tessconfigs/segdemo @@ -0,0 +1,74 @@ +################################################# +# Adaptive Matcher Using PreAdapted Templates +################################################# + +acts_fx 0x800 +acts_ocr 0x20 + +RatingScale 30.0 +CertaintyScale 20.0 + +#EnableMatcher 0 +#CurrentFx 2 +EnableAdaptiveMatcher 1 + +NormAdjMidpoint 32.0 +NormAdjCurl 2.0 + +MinNormScaleX 0.0 +MaxNormScaleX 0.325 +MinNormScaleY 0.0 +MaxNormScaleY 0.325 + +BuiltInTemplatesFile tessdata/inttemp +BuiltInCutoffsFile tessdata/pffmtable + +EnableLearning 0 +SaveAdaptedTemplates 0 +UsePreAdaptedTemplates 0 +ReliableConfigThreshold 2 +MinNumPermClasses 3 + +#EnableStopper 1 +GoodAdaptiveMatch 0.125 +GreatAdaptiveMatch 0.0 + +EnableIntFX 1 +EnableNewAdaptRules 1 +################################################################################ +# +# File: marks/configs/knobs +# RCS: $Header: /cvsroot/tesseract-ocr/tesseract/tessdata/tessconfigs/segdemo,v 1.2 2007/02/02 23:45:33 theraysmith Exp $ +# Description: Control variables for 'marks' code +# Author: Mark Seaman, OCR Technology +# Created: Wed Feb 27 11:27:27 1991 +# Modified: Tue Jul 30 16:25:37 1991 (Mark Seaman) marks@hpgrlt +# Language: Text +# Package: N/A +# Status: Experimental (Do Not Distribute) +# +# (c) Copyright 1991, Hewlett-Packard Company, all rights reserved. +# +################################################################################ + +#hidden_edges 1 + +save_doc_words 1 +doc_dict_enable 1 +ClassPrunerThreshold 229 +ClassPrunerMultiplier 15 +IntThetaFudge 128 +CPCutoffStrength 0.15 +EvidenceTableBits 9 +IntEvidenceTruncBits 14 +SEExponentialMultiplier 0 +SimilarityCenter 0.0075 +################################################# +# Adaptive Matcher Using 2 Passes +################################################# + +display_splits 0 +display_all_words 1 +display_all_blobs 1 +display_segmentations 2 +display_ratings 1 diff --git a/tessdata/tessconfigs/var_batch b/tessdata/tessconfigs/var_batch new file mode 100755 index 0000000000..1918b9ac90 --- /dev/null +++ b/tessdata/tessconfigs/var_batch @@ -0,0 +1,82 @@ +################################################# +# Adaptive Matcher Using PreAdapted Templates +################################################# + +#acts_fx 0x800 +#acts_ocr 0x20 + +#RatingScale 30.0 +#CertaintyScale 20.0 + +#EnableMatcher 0 +#CurrentFx 2 +#EnableMicroFeatures 1 +#EnableCharNormFeatures 1 +#MinSlope 0.414213562 +#MaxSlope 2.414213562 +#ExtremityMode 1 +#NormMethod 1 +#EnableAdaptiveMatcher 1 + +#NormAdjMidpoint 32.0 +#NormAdjCurl 2.0 + +#MinNormScaleX 0.0 +#MaxNormScaleX 0.325 +#MinNormScaleY 0.0 +#MaxNormScaleY 0.325 + +#BuiltInTemplatesFile data/inttemp +#BuiltInCutoffsFile data/pffmtable + +#EnableLearning 0 +#SaveAdaptedTemplates 0 +#UsePreAdaptedTemplates 0 +#ReliableConfigThreshold 2 +#MinNumPermClasses 3 + +#EnableStopper 1 +#GoodAdaptiveMatch 0.125 +#GreatAdaptiveMatch 0.0 + +#EnableIntFX 1 +#EnableNewAdaptRules 1 +################################################################################ +# +# File: marks/configs/knobs +# RCS: $Header: /cvsroot/tesseract-ocr/tesseract/tessdata/tessconfigs/var_batch,v 1.1 2006/06/16 22:17:08 lvincent Exp $ +# Description: Control variables for 'marks' code +# Author: Mark Seaman, OCR Technology +# Created: Wed Feb 27 11:27:27 1991 +# Modified: Tue Jul 30 16:25:37 1991 (Mark Seaman) marks@hpgrlt +# Language: Text +# Package: N/A +# Status: Experimental (Do Not Distribute) +# +# (c) Copyright 1991, Hewlett-Packard Company, all rights reserved. +# +################################################################################ + +#hidden_edges 1 + +#save_doc_words 1 +#doc_dict_enable 1 +#ClassPrunerThreshold 229 +#ClassPrunerMultiplier 15 +#IntegerMatcherMultiplier 7 +#IntThetaFudge 128 +#CPCutoffStrength 0.15 +#EvidenceTableBits 9 +#IntEvidenceTruncBits 14 +#SEExponentialMultiplier 0 +#SimilarityCenter 0.0075 +################################################# +# Adaptive Matcher Using 2 Passes +################################################# + +#EnableLearning 1 +#SaveAdaptedTemplates 0 +#UsePreAdaptedTemplates 0 + +#save_errors 0 + diff --git a/tessdata/test_matrix b/tessdata/test_matrix new file mode 100755 index 0000000000..2fdf2ec1d5 --- /dev/null +++ b/tessdata/test_matrix @@ -0,0 +1,96 @@ + FL RLHT VPOS X N DOT + 12 1234 1234 ENAB +! 11 0011 0110 1 0 1001 1 4 28 0 +" 11 1100 0011 0 1 0100 1 1 0 0 +# 11 0011 0110 0 0 0100 5 5 0 40 +$ 11 0011 0110 0 0 0100 4 5 29 44 +% 11 0111 1111 1 0 1011 1 5 0 0 / +& 11 0011 0110 0 0 0100 4 5 0 0 +' 11 1100 0011 0 0 0100 1 2 0 0 +( 11 0011 0110 0 0 0100 1 5 0 0 +) 11 0011 0110 0 0 0100 1 5 0 0 +* 11 0111 0110 0 0 0100 5 5 0 0 ++ 11 0111 0110 0 0 0100 4 5 0 35 +, 11 1100 1100 0 0 0100 1 2 0 0 +- 11 1000 0110 0 0 0100 1 8 0 0 =_ +. 11 1000 1100 0 0 0100 1 2 0 0 : +/ 11 0011 0110 0 0 0100 1 9 0 0 % +0 01 0011 0110 0 0 0100 1 3 0 0 o +1 01 0011 0110 0 0 0100 1 8 28 0 +2 01 0011 0110 0 0 0100 2 3 0 0 +3 11 0011 0110 0 0 0100 3 3 29 0 +4 11 0011 0110 0 0 0100 3 3 0 48 +5 01 0011 0110 0 0 0100 2 3 0 40 +6 11 0011 0110 0 0 0100 2 3 0 46 +7 01 0011 0110 0 0 0100 1 3 0 37 +8 01 0011 0110 0 0 0100 4 3 0 45 +9 01 0011 0110 0 0 0100 2 3 0 43 g +: 11 1000 1111 1 0 1011 1 0 0 0 +; 11 1100 1100 1 0 1010 1 0 0 0 +< 11 0011 0110 0 0 0100 1 5 0 36 += 11 1000 1111 1 0 1011 1 3 0 0 -_ +> 11 0011 0110 0 0 0100 1 5 0 36 +? 11 0011 0110 1 0 1001 1 5 0 43 +@ 11 0011 0110 0 0 0100 2 5 0 0 +A 01 0011 0110 0 0 0100 2 8 28 45 +B 01 0011 0110 0 0 0100 3 8 28 0 +C 01 0011 0110 0 0 0100 1 8 28 0 c +D 01 0011 0110 0 0 0100 2 8 29 39 +E 01 0011 0110 0 0 0100 2 8 28 38 +F 01 0011 0110 0 0 0100 2 8 28 40 +G 01 0011 0110 0 0 0100 1 8 0 0 +H 01 0011 0110 0 0 0100 2 8 28 36 +I 01 0011 0110 0 0 0100 1 8 28 0 +J 01 0011 0110 0 0 0100 1 8 28 37 j +K 01 0011 0110 0 0 0100 3 8 28 34 +L 01 0011 0110 0 0 0100 1 8 29 0 +M 01 0011 0110 0 0 0100 2 8 28 0 +N 01 0011 0110 0 0 0100 2 8 28 0 +O 01 0011 0110 0 0 0100 1 8 0 0 o +P 01 0011 0110 0 0 0100 2 8 28 0 p +Q 01 0011 0110 0 0 0100 2 8 0 38 +R 01 0011 0110 0 0 0100 3 8 28 40 +S 01 0011 0110 0 0 0100 2 8 29 0 s +T 01 0011 0110 0 0 0100 2 8 29 35 +U 01 0011 0110 0 0 0100 1 8 28 42 u +V 01 0011 0110 0 0 0100 1 8 29 0 v +W 01 0011 0110 0 0 0100 2 8 29 0 w +X 01 0011 0110 0 0 0100 4 8 29 0 x +Y 01 0011 0110 0 0 0100 2 8 0 0 y +Z 01 0011 0110 0 0 0100 2 8 0 0 z +[ 11 0011 0110 0 0 0100 1 5 29 0 +\ 11 0011 0110 0 0 0100 1 9 0 0 +] 11 0011 0110 0 0 0100 1 5 28 0 +^ 11 1110 0011 0 0 0100 1 5 0 0 +_ 11 1000 1000 0 0 0100 1 10 0 0 -= +` 11 1100 0011 0 0 0100 1 3 0 0 +a 10 0110 0110 0 0 0100 2 7 31 48 +b 01 0011 0110 0 0 0100 2 7 28 44 +c 10 0110 0110 0 0 0100 1 7 0 0 C +d 01 0011 0110 0 0 0100 2 7 27 44 +e 10 0110 0110 0 0 0100 2 7 0 0 +f 01 0011 0110 0 0 0100 3 7 0 0 +g 10 0011 1110 0 0 0100 2 7 29 42 +h 01 0011 0110 0 0 0100 2 7 29 42 +i 10 0111 0110 1 0 1010 1 7 28 0 +j 10 0111 0110 1 0 1010 1 7 28 0 J +k 11 0011 0110 0 0 0100 3 7 28 40 +l 01 0011 0110 0 0 0100 1 10 28 0 +m 10 0110 0110 0 0 0100 2 7 28 0 +n 10 0110 0110 0 0 0100 1 7 28 43 +o 10 0110 0110 0 0 0100 1 7 0 0 +p 10 0111 1110 0 0 0100 2 7 28 41 P +q 10 0111 1110 0 0 0100 2 7 28 42 +r 10 0110 0110 0 0 0100 1 7 27 0 +s 10 0110 0110 0 0 0100 2 7 29 0 S +t 11 0011 0110 0 0 0100 3 7 0 47 +u 10 0110 0110 0 0 0100 1 7 28 42 U +v 10 0110 0110 0 0 0100 1 7 29 35 V +w 10 0110 0110 0 0 0100 2 7 29 0 W +x 10 0110 0110 0 0 0100 4 7 29 0 X +y 10 0111 1110 0 0 0100 2 7 0 0 Y +z 10 0110 0110 0 0 0100 2 7 0 0 Z +{ 11 0011 0110 0 0 0100 3 5 0 0 +| 11 0011 0110 0 0 0100 1 5 0 0 +} 11 0011 0110 0 0 0100 3 5 0 0 +~ 11 1100 0011 0 0 0100 2 5 0 0 diff --git a/tessdata/user-words b/tessdata/user-words new file mode 100755 index 0000000000..f3e1f37f51 --- /dev/null +++ b/tessdata/user-words @@ -0,0 +1,920 @@ +a +absurdum +ac +acres +actions +adaption +adjustments +aerobes +affairs +agents +Alan +Albert +Alberta +Alfred +Alice +Alicia +alliances +americas +analysts +announcements +anouncements +apples +applications +apricots +architectures +areas +arguments +arrangements +Arthur +artists +arts +aspects +attitudes +attractions +auctions +aug +az +baccalaureat +backlit +bags +Barbara +Barnabas +Barry +beliefs +benchmarks +Betty +bi +bits +blades +bonaventure +brad +broadminded +broadway +broking +brows +Bruce +bs +buddha +buddhism +buddhist +buddhists +buffers +ca +caffein +calculational +calif +California +cam +cams +Canadian +cancelling +capitulated +caps +Carmel +Carolyn +Carroll +cars +cartridges +cassette +casuality +Catherine +centre +centres +chambermaid +chapters +characteristics +characters +Charles +cheesy +cherokee +Chicago +chloride +Christopher +Chrysler +Churchill +Cicero +cinema +cinemas +Claire +Clara +clark +cleaners +clients +cliffs +clubs +co +codirector +coinsurance +Columbus +combinations +combust +combustor +comparisons +components +computerised +computers +con +concepts +conclusions +connections +connectors +consequences +contemporizing +continued +contra +contractors +controls +coprocessor +corequisite +corp +corridors +corrosive +costmetology +counterparts +cpu +crops +cueing +culturess +curtis +customers +cuts +cutout +cyanide +Czechoslovakia +dan +databases +David +Davis +days +dealership +Deborah +debut +decibles +declarations +deductible +defrayed +degrees +deionized +demobilisation +densily +departments +descriptions +desensitization +desktop +developers +developments +devices +dharma +diameters +Dianne +dicators +differences +digitising +directions +directorate +disadvantages +disassembly +disclosures +discos +discs +discusing +disks +districts +doe +dogs +dominican +dominicans +Donald +dos +dots +Douglas +Douglass +downsize +downsized +drugs +dumplings +duns +eastside +ecconomic +ecconomics +ed +Edward +efforts +Egypt +eh +Einstein +Einsteins +Elaine +electrophotography +elements +Elizabeth +Elliot +emory +emulsion +energized +enquiry +ent +enthusiasts +entrylevel +environments +epilepsy +epistemic +er +eric +erosion +errors +estuary +et +events +everyone's +exactions +exegesis +exhilarating +expenditures +explanations +explicably +expo +ext +extensions +eyelevel +facts +fastidious +fathers +favourably +fax +feb +feint +ferneries +files +filters +Finland +fireplaces +flavours +flights +fluency +fluidized +fluorescent +fm +forceps +forces +forefront +foreknowledge +forman +formfeed +formletters +Francisco +Frankfurt +friends +frond +fronds +frontage +frontseat +ft +functions +funds +futures +futuristic +ga +Galileo +Garfield +Gary +gaskets +geiger +geist +gentiles +Georgia +georgian +giants +gigabytes +glitches +Gloria +gm +gods +gordon +governments +gravitated +gremlins +greyhound +Griffith +groups +guages +Gwen +Hague +halftone +halftones +handfull +hans +hardcopy +harkness +Harold +Harris +haven't +Hawaii +hazards +headlights +headquartered +Helen +helicopter +helicopters +Henderson +herbalists +hermeneutical +hills +hindu +historians +ho +hoc +homeowners +honduras +hong +hours +houses +Howard +hr +hrs +humours +hwy +hyper +hypercard +hypertalk +hz +i +I'd +i.e. +i/o +IBM +iceskating +id +Idaho +ideas +identification +identify +ie +ii +iii +imaged +implementations +inc +individuals +inferences +infrastructure +inoculation +inspectors +instructions +inter +interpretative +intro +intrusive +intrusives +irs +isaac +iso +Italy +its +iv +ix +Jackson +jaguar +Jan +Jane +Jeanne +Jed +Jennie +Jennings +jets +jew +jewish +Joe +John +Jonathan +Joseph +Joyce +jr +jurist +ka +Kansas +Karl +kbytes +Kenneth +Kepler +keyboards +kg +kids +Kirk +kits +knockout +Kong +la +labels +lakeshore +lama +lamps +lan +laptop +Larry +laserjet +laserwriter +latin +Lawrence +laws +layouts +lb +lbs +lcd +le +leaching +Lebanon +leo +leon +Lewis +licensee +licensees +limitations +limpid +Lincoln +Linda +lines +listings +literatures +lithographic +lithography +logo +London +Louis +Lynda +ma +mac +mach +machines +macintosh +macintoshes +maddened +magnetically +manmade +manufacturers +Margaret +Maria +marriages +Martha +Martin +Marvin +Mary +mbyte +mbytes +MD +meads +meals +measurements +mechanics +med +megabit +megabyte +megabytes +members +menus +mesa +methods +Mexico +Meyer +mg +MHz +Michael +micro +microbes +microbial +microbiological +microbiology +microorganism +microorganisms +microsoft +midrange +miles +mils +min +ming +mini +minors +mips +mirages +misnamed +missioned +Missouri +ml +mm +mobilisation +modules +monarchic +monastary +monochrome +month's +Moscow +motorways +msg +mt +multi +multimedia +multiuser +Nurray +museums +nasa +Nathan +nations +nd +ne +Nelson +neoprene +networks +newsletter +Newton +nicholas +nitrate +NJ +nonessential +nonimpact +noninfectious +normative +northside +nov +ns +nuns +nutrients +NY +o +odometer +oem +offcampus +offchip +offsets +offshore +ohm +Olsen +omni +onchip +ondemand +ones +opinions +optimised +options +orchards +oregon +organisations +organise +organises +organising +orginal +os +ot +Otto +outlets +outmoded +overdrive +overdrives +Owens +pages +paperless +papers +Paris +parkway +passages +passengers +passengerside +patio +Paul +payload +pb +pc +pcs +pedal +pedals +penicillin +peoples +perambulate +perils +periods +persons +pesticides +petri +pharmacological +phd +phenomenalists +pheobe +Phil +Philip +philosophers +phlegm +photolithography +photometer +photometrically +photosensitive +phototypsetter +Pierre +plainpaper +plans +platonic +platonism +plc +plots +pluralism +pm +pocketsized +Polly +poly +polygons +polypropylene +popup +potency +pre +precautions +precepts +premises +prep +prerecorded +presswork +pretested +primal +primo +problems +procedures +processors +prod +profits +programme +programs +promissory +propane +protestant +proto +protocols +prudential +ps +pubs +racism +racist +rad +radioactive +rads +Ralph +ramirez +rashers +raster +rd +reactions +realists +realtor +realty +reardeck +rearview +recalibration +recipients +recommendations +recut +redskins +reductions +ref +reflections +refund +reimburse +rel +requirements +resettable +residues +resources +restaurants +rev +revue +rh +rhizomes +rhodes +richard +rickey +risc +rn +Robert +Roberts +rom +roots +Rosemary +rotor +rotors +rovers +rpm +rt +rugby +rumania +rumohra +rumours +Ryan +ryhthm +s +sacrament +sales +samaritan +San +Sandra +santa +Sarah +savagery +schemes +scholasticism +schuler +sciencefiction +sciencehistory +scientists +scripts +scsi +sculptors +se +Seattle +sec +sel +selectable +selectivity +selfpaced +semesters +sensors +Sept +sets +shareholders +sheerest +Shelley +Sheridan +ships +Shirley +shortcomings +Sidney +sig +sinch +Sinclair +singlesheet +singleuser +sinkhole +sists +skiers +slots +socalled +solidstate +souls +sources +southeast +soy +spa +spans +spilt +splines +spots +sq +squelched +sr +st +Stacey +stacks +standalone +states +steps +sterility +Stevens +stoics +stoves +streakings +stucco +students +stylus +sulfuric +summa +summarise +summers +sums +surveyors +susceptibility +swab +swabs +Swiss +Switzerland +sys +systems +t +Taylor +teaparty +tech +techniques +tel +temperatures +Terry +tests +th +theatre +theatres +thinning +thirdparty +Thomas +tickets +timeout +Timothy +tm +Tokyo +tollfree +tom +topics +torah +touted +towels +toxin +toxins +trademarks +traditions +trans +transfers +treatments +trees +trenchant +tribes +trinity +turbidity +turnaround +tx +types +typeset +typesetter +typestyles +typology +U.S. +UK +ultrafine +unassisted +undercut +units +unix +upchucking +upriver +ups +urea +USA +userfriendly +users +utilise +utilised +v +va +vacillated +vehicles +vendors +veneer +vernal +vi +victorian +videotex +Vienna +vii +viii +visitors +vitro +viva +vol +vols +volumes +vt +Wallace +Walter +walz +Wang +warmup +wastebasket +Wayne +ways +weatherproof +well +wellknown +welt +Wesley +westminster +weston +Williams +winchester +windows +Winston +wireframe +Wisconsin +wittmann +words +workstation +workstations +worlds +wows +wraps +x +xerographic +xi +xii +xiii +xiv +xix +xv +xvi +xvii +xviii +xx +years +Yugoslavian +zealots +zion diff --git a/tessdata/word-dawg b/tessdata/word-dawg new file mode 100755 index 0000000000000000000000000000000000000000..e86fab694588fd411287f12c729266ed299d5055 GIT binary patch literal 191888 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

S z-h+J%g%><3ukS#%1GVb3jEQmTBE67P@Bx`;MuiNrEp!yRF}%=c~W|torBWx%g!c*X!r0L8C3+ z^Iv%;JYe(adw}b|$aja<57RW@-RU$B83x@rQO2ZX9ni#8Q+p$xe>Qs8p;Z@1&QA6q zbS&ZtOMDu?Fu@CT{>i|ys~hSLsK2ybEpuoegFDreIn3{%xf(Q4e-1Yz$2(w81&teJ zVa^!$cu>X(Paz(?(c;NH(eM5`;`2Mo!=44yr{U`!y2Idb@?|)abH5d?SeOil?~C!U zQxTkjn!UXEd1^JkuyTQqiJYSi)+v`J_5CmE(uOP9^+ZZOgyq? zt3{c5Qft<0@!;A%w@2Mw{E<%ZhxfEcKWY7FJ@b6zIx}`yAH%ann=LhN#rgZTc*w_D zTDm9$yqNuO)&7Pa!<7YJ?d{-&yBkI_EB?v3_Xp|zPy=6!TLu3@JHanxWe#+4pBT^S zmEbYkjlHg#qGb*GK*mT6Aah0XoaNen^ci!W6j{piKGcjCeH>GT-ZAc12H&$t{P}2} zJnCBTjdlwB;!C>ldt}cRZy&oumFjv_1iQ>FIR*Ky*Vnikfmo}Cvef41gYfiH^nyO( zQSXbg)Cskkj_}i#`GK0!GT<}+uxRS8xEeP|A+M#VTbx1v88jbTw`_MBf zF`V#(saeOlG3Gh)V3wz!@p%2?W{xJybblTrC(8>vYrdh%K4M#7AFG`3&~{6$zE9IZ z$AaiHrOtx^&-W25LUZ_kKbTr@|4Y(g(S_(GNFW!wGl~%{Uj2l@*WRg!eQ)&DjDA{% zXd8T0XAu18ZZ2=W)6j3;<$3CM)UB4CBKRl^7b4g3O>sE?SX%WPVdiQ)8Fn`?@0_)f zVd>9tCZjRp=aO@F<irXdfdsQs=a&0C$IO)tcG zSO~}bBV4&zv7cP6A4|@swiOoOz@1lRu=VulP)*!AFZ9DqxU3u>X|%OzEdH108B2W zP77S#EFzz zEjX&Czvw!gTe7&<7iDxr>r10=7q*CaR@RU3m{uLNT{ZL_w^4Y<<(Ufy7{C(`w6OC|0@ zpY8Q|q93w6_8REKUkHChGOXtec{%FLUJ#zV04qmAH9ki@s50Q*i(7X$AcI35ETDU)Y=2CH2=upJ*?GJd| ztvZ%7yiv6&qsC9qa|=Qb+)^(bd8h2(qfMVXRQqv@8n;C+a}W5T&%r-OoEiJ@g- z>8GW%ehc7P=aYLW_X zCwgI*s$95lEONkWidFM#adx%{dir6&+6OuNKg&9T1JLd%^o}jlbA=cD2L9hThx!J< zY4>Q1;4VXbkKpXos3hLOS%In`9CU@@oy~~Zx-C6_e~6=JI?BkX@t=)e*W=H}9dAKy zWGZ@(#10OtLQZnM;M$k|Lt65zrPg{GK8PA%9{t)Pr*d|6Z{AYt|7&yK`q1CnPY`@L zs=<4r6ZDx}c31rmJUq20?CpktE2i7D<5x!af$w;niAyw1cHadZ^+=xVodGTRIp}!S zhOe&!e4^J+pIz1YFZ6lXNp4x`@rZ_kL-dpF5E$Uk%+4|LpTg6jgZLi#dG3@FSR6X@ z4}-g_(AR%T^P0g&(_f$lzB}SioU_A7a}v-XyjE|c{G-rq-l(>UTHoOdjKL~sdR@(1K0pl2OjPc^tbhWy#Q zIXJg?Tb8H5CL8}GtyaSSJ5e0TOh6~^6FPZn*KxClk&XRu*QT^ey&tXRQTpvH^05xr zNBkzZS=I`+h;vbL4Xn4$eEg{+&QAn~r+LuRPU#jK{0lrWxH}$5BrnkG`-`MQPsj z`x3I-I-m#oY`^!_z*}=1Gx<`|xpMfRh0Z>8{>kM5YXdcEOiW#Xel6fob$MIXbZO=X z(fY2YYh}Usw!!}yyMN|mM{T{<=2JHEKzp^V#d`)$z%4OAIJkxzZt9y`mw965I~wsilv2;^laz&UYCq{@r%kUuDyxzG-qCiw>11!$ud!cxK$DU5zcZYH5yBn*yG)S!5%t z3|=D{@~P+D9<>PZeLT*(-VlV#c0>N*YZb}`uL8T_FX_XKVuQxWS=7H0@0=*&8MjtH zr=lxu#M?RU>DkolDodTbP1Y2DvwA(uCwMZveeg2$13e&PAb%dtMLn0HM!RfY*Vt9_ zaqhX$&=Yv!=g8kk#xmg;#PCwrFC1}I!N!@YJBoN2@*#U*gZ!g$4p38@k36lxZ;I#X zx>Pm)orS^T(RY>7zL(>?c1gg?2IiSBNvokJWzT?jvA}8gV%e)2e(m!mp3yjA#J?Da z{c7F_!se(^xBkHiJ4`}P+pHADXBSc90|{DpSBx{S$XM{W)-Mr0BRR!Bk$>26MG;jZ zFE+JNDW#KHPID=>L9BUdj*Ga};(9k)VW%>YL*$z03(mA1QuFG>xJ!TYeF$nj=Ww0k zeefT;qGnS%7&*_^Drhav+RRlRwM8zz;gk$@LT!Eq&NA+L+~Mt`Y;IMRrCp9ux?^){`vG-M@g*VZwiu%NvFVESE_CLe<74TH$^EhWe2sPEq zi&gSg?4$1a?>p4!Gvu${#5v(&M|E|O56DR@;c)-&4Bit@p-&=dWMj~^fjanfpf-M5 zq}mR2RqvaFa2jgMN30ZY&@~Ck@5Z>>aNOf?QgG@~lg2KMnyqt7v@4aO6<;SQ_j)|6 zk-)i##s>cz7aKpa<3mvnS6~#LmiJ`OfM>POjh1A1t%G{QQiqa1vw6WI*xxI0o;=&- zrjM0}L(l_XnC8$tKgC7=K`yIKBp2uY;4p8e*4s}0wZ|;xl%r|G|5%NJQafvwx$8jEuqfX{SKT* zK)p`v!U*^Q8>dD251ZG(S1&AwY|JU57Gs59P_H^|@V)I`o7?sfxi&u5T*TPZt_2+H zBsKxP7eROb%%NlGjjEcEqA??^u>C6u=({*o4Fzv@^VKQ%M`F{1#*pcRk=ZSKEgrTF ze&!eGBlKV!wh8=#Tf}oc&>wdj=5FW9cRH~$sixf=$?p05f@f!S`8kr`)gKO+~v23 z)8@jzsP0+uIQ6}r6XSfIa5g8{8uC}(2K85+OK@r8^)}W1n?=nL!#91(&;T_AYLE5c zbA+5mPd{1Qw%TVDm<^9l{|)kjZy0-dXgYdJ4xYcy>b1jjzCffK%{Yk><5Oa6znY~c zAK1L}r&1nU+v8z{JTKoB@gwp+pCdQf1MpZDx2f;Zlr330a(#^JRu+e~yQacP2Q93T zM1Kc*`!7BphLkErjz(-{|E4%Q{PQ66Zn0_1R>Yu)2Zv!_>t29fMyw~gv+!rBi6pr- z;$!Ju(mq!o;YIXQP4UAqsP#0jlY@;z9N#??I^Q72ySJFO??Io{mliFVo($K$in};k zmC^L6@a1b&!va~4bI0Gsxb>sOG{N*ysxcqsY5r9nl~!aa*VUmMYMIRz;>=kNJ_CH( z%3lRE#1TGAs@b0*HvMm)7S@aKQp`i%#2PyG^}^VK1lKv?sI^v{ce(+s{)4t%g#s^L}R0(wF|3;)9R=#Wt7*?8>7 z%v1al{7Tdh>LKqD>@HFZ$BM4~|L2fBppPu8-wA6cW88MK#=Fd^#?ABCZF>GYkv|mK zGDkJ}AD;m}_iv^DN@Oq6LD5+FY(IjhAG3L0U)*2usm+}pjeIFrHQkGIUSl(A>7$T) z^ni8j3EchG3maVLa{5mKL$y7p?QiDP{AZ!p9&pvLC-L_r`m52$HbeIwMfAlPK-7j$ zKb%q8M#82_J$6CuEH%2zre(+p9$FgXQx@nqN@(oSVd$;OSBts)i7Xe#Ew$*;jG8n; z))qW@JoGxjVS-!3Z{77LgJ<~h)i_6Pf{WZ}JkB#}+RD4=Bd-UYH%fAw!gEEu;+|qv zTo4>ZdM;3lb*~!k4=^=gYPL$=HMZWwFBRMQ zTa#-vcQWLt% zMlYd9jd#7M#{-Xs4#qD;95@&DVMl^TiVxgI>H*lx>(I5DUN)`#m#i7-v4}1eJZly5 zNe8qYre>GHj#OB5Oagf8FCgI)5%W zipHc>kJ3Pmo)Uze#`;u@{B7)EoPWRXe7FOOoBA~9fSOos`AzJuz?$Wy+P8>c4OHi= zuqV{>2A{E1oiEb3Zw2*Q)TlNj&Ziyj*Fi2RZR(Doah@v+24|TG0WWP}KF4u}qtOwM zI{ZUo@d_G)p1&PM(4U*5^&;M}XeRDs5+AwtsDSfL0-E@UUjnS0PUJzprYRA~#&<-7h3c=Hta8hRb~iT&CQ>wYG2Op01wm{e`ww0S<` zUYNk)3T<06eBg4A4lI{kIqu3CkNdVz1E}1IyUh-u2Kr!@HsfqV`&k+Gi_F^NJ~#Ho zz?P@AKMTSO#vrD|`n^}+97-s#`{D7=b5x05;qpt6Q+V5^+?8qCF}a8eT`XEYJVC=b z#+{Z#dIUAqS(=8ZBLv)Nu<>c-41WseRBO(MZJm(}7qm_g?*NYuaFng#5d6s94pQ5X zaE*BU8}yeI=V{YxILG;Z^t_asz=v8h6n7n=UvB;naux;ol6Ee)8sKotGa|<(KRXy_ z4fgd%P8vKbwxcO>D2uf}^r3ScY>< zTTHF4(~sb3(`^mrH(&yY{{lZPM)w>VxMo^%rUvJepTb%BHzT_g!SAV# z@22VG1WVoZvdivFS9Mw&{pN5Q=e!G@!CyDYo-%aO#$1K(RG;-(22T1(e=oR!J48z&oV(llXQ;+&wMjB4l+r=5* z{^)bZInyc6p_hIl?k4%b;`<>(xhFib|1I(taPX3)n*D_Q#=M9=@>kEnbJIDd|3uGY zy!j|{+V~B%Fx7pWO&yx!-0h#?7yb=-lTQ&766&4(V%!I3a!#Jo<3eu&&R-pLsS3JX z@?C=^2OxD_^idW@Z@sbarBK(?Ou0jy`7k8v8PI?bLJpTfjM&)Fb7*8v7k{x9B>=Z;MgW_zH8H+-F$h2k1XxgE!by ztes+iht`QwH&<;_0sGrCoS1LMY(o#=mH`nC((EiZ?;pKi=0YFSv$(G(=Bf6-h28!x zp(@J_JoUfDymxrVJ(n1Z`!l&+4(Gc+6MKm>=nXa8Ve5nVel{Mh+T<|l^Bv*?B5(8> z?WKt%J&-yApnqLe!!Pk7{>ESHSXdKvrn)Y>e3@FNi~K&G{B6F5Y7X&6)^+;dbs z)8Y*e>Gf5^`($4_KcfRW^A_%Ly2nuo)1yr@2cX~d!8{FK6)4nGWiH5<`1QzZ ztU-*wN#KfbA)mF+&xKh&FIb6O&r#glv{dLI>lpi9x!L1;2Y8%Aj`l!z^wZe#p0P2t z|88<73tpCbDsl$!lh%2-kLnW{BfJsgLE0ZOv=@I-{N2j-DL%X=&2!L;viOA@T~Kgo z+#3n5RfhAIi*RoIE!-_S#8b6$s8!q$?IBQ!tx=ApJnUQ-y?z&ZRBL>evQu$RB4Ja> zcUE`;HMhmf49!}?hyCB0F!U38ht7PAo`_G9s@qio)%deVYZifLCkt#ARibA?{J2iY zrHvgLgdIOaohxUlT`S5|JB^!M*smY%fy3F?-PtTxYX4l$j|=?*)#UXohKfIBbf1MxE!YJWu~S z^f_|JSWk6qZT_y*@*VkDE_Z%4&hgr5wZ5Cp7u=2Bm+t5@*{yX4><#Kdx68c1zlO=c z8RRBqy1*AgB%+2 z?(-gX+5Z%GFW?+RpQOmInFFx}*NjIr@_6S}W>4)q1hgByy_Gi#tiX-V69%fmEh$=g zEWzd1;4X|ev)t_+`8|S1KI0wajvBN|^8DX8T%vIZKIrWu2PeNrYrzH}ZzDZ|OJ4D* z#zvc)O$lhx-BQy+9MoLK3f_?q{T$&MqohOdgA)OsWEx${{(ykV&x(4j}C;eOI}G9G%gyYATxk>?Qo1YVE$ z!BHjeS$y!Rh~5#LsAZs+ta2UBP_D;(l;z#aqn=fR)BRnLr@YDH&L5?C$8neM`CIgE zj`pm?JuFyXdi>VEWOPtrz3`0qedqg&0R_JmxWQM@@hw`)93G7+HTn*GNutws^$5a> z`_p0T={UoDzZbf_T$f!G;~PXz&wBvM)@~)|tq49u@u*IB+lOa~9A46tZ}r z7~j)s-XZhcz!hiIJ5`s~J}+*FT*TK2-u{xo%`x;b_N+d)ZJwPxs~e`2l!b%4BF6$- z(xiKqx}SDwO?%|_|4~fMo`!GxO@bEBLmuWK+*^B_tf%Fbmu>TT^Bp*Y+)nVA;YBf= zcivVF5A+}8Mf>1cuD{^?=OQ~Y$GLrpeu-o>!f~aRd%YI<37p%jT|xC$h@X@TFF5Lk zrI%;I$u{mHE`=`qajt(0c(5`0k$!`?GGv74Y(z(NBdf1}^EJl{onD=K&x3KEcNk&*skSS2cKsPF^ba1qnaH-=BoD|GPCF zU{55MfP2r5fquphrSbO*Y=&=U&V$~43TwO&jhEl#v-UH&*Q4h7S^dJx7l5}0d%UD8 z@^v2~CTN}KUDu^mWtI4Rk(}e6oI54oQ$bzI#P=}uf-Rr+SKxVYnl2P@vXd;?uegZkS&QJauuxDd3C#*Lpm@MV?LuS(=Alhuk)c z7A(TqvEN0y68)ENl+vQ7GOGJi*t7Nl(HGhjee-~``D$Nzr;wM@d=9)FgMPR-&a1L{ z9*KUK>2+~OB>bkPu(!fzlhB9KueQUPMM4ksAuYz4*V{~(WBTWZxd{X9%{0_qdZEi>IrNrX( znx{pkaKCjz#+i3Sce%S^_e{T_YFU8a`ZatF)GWGg&GCSJdMt}P?l_cItG3{LGHSB5 zegq96&(qnw zwZ_0waTvM%ThJFbIL51Bo9UM}7ky*wj`%94MnrZe&!;{`58zEPl`b#iV^<>vdne2D zHWjNz51SaH-pmN*q;gLC0d?D+;P$@{c+!D@m-p3l2kin?ccH1v z)U1bdfCDW>Kl-Y1wXg5c_-AnUeEB*0_^@F%`u26cdvy$PH139dGfTxSunz7EIr$O( zZZ6@fAps3L?ovf2!A;6>FEaYEI!(yZ=wTLZZ35h1Dsu|2CDjS^+f98UppqrYaNl1N z+`mh}4eJIpdsH!3t_6;L)j0fL zfGU~ap{B^48r^O21vul{9Q{)M;T*e{WO?IXb?=NqeqP3ei$4ck6)vxD;)aLnXH@Qb zoLNA8wE8h$ZLHM(6yU9(#&3upA^T0MPUu}3+eX&X`WJoz;+`+i$BunxKhQZEz4QS(q+-SMvBO}U3<;-^8__(|wkeWnHb@9n9fbCYV|;Al?<9#7|SBHGDs+Wyj{ z`;8AjV>{~RI)~v>PuUM_TjX47<;6Hp zxeoF38QcMWz0bp&$Q-!m=QEsDXf1GP-h-cI@d*xiZvQOTK}}C=58$Su=+j!W+o5sK zCDG%SpjG4GH|7wVjrO?+~qM zhkj(^2Sjwu@V12kuYBIrN6z|bd}1Wdzbx{3%@cWchvB}{ks0oO4|)~;iT>_uZ0_(A z)~cDH%26T{5naJICT>vOj`?iwi)^F(9(_*p>VLUxS0Y#Sp)I&lUG6VeRqY%ay2Rq5 zW@fDP9#jo8{eW`*^W@2M@k+Fp+;oTPuMs-gJWluZM*QH@BHWA83^76BO~I}3yifb@ zL|zW{GZSwj=XHIS3O#L_JrO1%uw1~^z4d{&aK_TO}0Z#>&w{0@KakO=WXMsISTfr+G7g%cA1czHy+I$*$ zqO(4c#B2B*(GR{|61j!?4zK*c@PFYQ{6C3}F+M!aeBX(jUiN2F%ZT&hIYw8E>5RO{ z46_%UYlnP9d}#KzVW9a-i)4(zg#T-B>-p>Yym|q8l#U>G9Kx3$5WQbPb((=5?M-}B ze9Q{#|3P>%#mjM?Z^2J^{srWYobUwhZmB|!2)bN#9q6k@$e|oa8<;Emx#}SJqrTCd z(9<|S4Vya|XDrX<#iGyWa3OWDzL!~Q|Gz!ne=1O?`pX#cKTD~V)c#PnJ(u4T9hK#N z$ZIb~-%pPpz|*5~$J>v{73#Vu;zk|EXV|XksoD?6c;)Y-H4W~Jt;+J6UzO70?U-N3 zC-4=Ar^v0F z(BI&AulB3OuhsPBvD#Kt!vuej+z0rc~Ei%G6pzkYt|X}jKG31rY9h5iTr-0zHE3AE-$G%-|840 zV}vJ+TqF~)bzKZ>^J>EX+MHB72g>_N?llOp;GJbuw|*)0L|=zHBMA54JoGU9ADLnB zWsmqSZJOdx^QAhM3OJKtzGIv=AtyKZeVa4C5*p*24szk8d(jWG6+NiOJ+=LJmg<0d z;~C+dhLH~|criXy)^%;k!05sfWo?Ke80MVt?u$nv;dIJ0@q*M`3?dLmG@ zSKv%a41VlppNhWEQKK#n@gpg^tfA;={9mK-S=9tSetX=DQ^4I)%j;xmU=lg9A1zur z5NA%F#Q%NK=NV;fkrllMtp|?aUSsKxjT9jTH!vT&r6A4%F*PS8R|CF z2`jEla6`vaeQ@7a@*)SlXAyq1F4DN({J2l`UiZjOKP)JV(vj2RTEYzCP~8 z0B*3h)G|TG6BX2~zvxwYN1i(>b%F3V&LQdk&xnqwwOm{tE<>Jhn(m7+{IMH4c%aHu=P%cJ~*HZl->B72zF=>W@G#=1*~&j2^CHoHcB8J9@v-!yr0- zUL5)BQPCQ*SK;+6^}7W2LMi zst4$M$%=r>=AkbDHTFtfj}reNpq*`SA5St#JF`+xz z$JvV&h_Ct}PF~mr=jCB<>ipN?ZZ}(MztCL z+J<}aciy7Ehp7yH!yO~#3p_e8!{NQ3`)b8m`87H<;C<5^p5~hOCmUU^7MHm^cbCQ) z@P+>Dhvc)-fq8xBkDObG{9fSAoV`$9BB`3Zi&*XVI3oc1h<~uICiv%U9>v&L{4c71 zm#dP%%o*Lcoa(RF18!uGxI1ive20&Od#rcejT-l>u&s_fgG}9qUX*@WzCicbW1R(Q zZt_3NCR`b^})4JKWd;AU+CwOseYh@OI;uH^#`882gfwKOcH+HLZ@_U~qmS(@L@mz@aq zB2FHU^TKQOINBh7;&|Y4FaB2N0}$U#PpYvO3BCJ;8+Y!ku~hke_iX94>7V1-bMbW^sGL7$K&01h2I3uJ>Ft9??$gJ=2UsL2C!;S;u}*<~u+3x#U8_8(H{nqDzl6dU&?u|6II?|1a_fkGFn?Ikev#?MrI1;4%Ea z#JR@a==XH*c^Bcq>Tw*5LC##q_;PP5@=N<-oLynl?zkoTQWXyS>R^?@$DxDKd*`o* z`FhLqh_?dO?gvk`xXBB*Do35T-s96-QEzyvTpgKL!HpY5b~2K4vC}EokpwUOO`K~! z9?7}cYlGjR2gZ%o%~QkgvlL&8JQrl4_biK=Ah*`*o1~h6`{Y{v6941uz7#E4gFA|@ z@zgrPIl3X1YH|+eSWB-gJg%Bh%8P5}x%*Vq?jG0? z?ztkM7MEtYMR(D;0+UUH+FC@IqwMwwM(DFY{trHA;u-V(&|6vR{4sLN&kFs~e_Y>D z?XQdIFEqAr-z4tryV1;T`av}w)Bp zS!QND((Y<^r6t|HD>7RK8S_h=h8t>_Io2vkZ>UKdY|>x@69*cGHfcC{-!r3;)c4*W z{;a%v@4%UvGY7v1e)pzpElqQI&j_5|Q5Y+9(ILGZ-ka^?Q;)$`**K5e54ZaY@jfM* zzd&{=^RoW~j91`tju{W0_qCt;w1U3;aEe#ID{~TBMsdDNyoIw-qhg{;HN@!)*h?jGIp_l!6kR}Yx!`_rO^)n=K@~z*2-(2g0 z_Pp3Jo4qV+eF!r1ntj7ebg6#Bk~IBhmQ7f^ZVm`pw_qSD)+rOWnPNBo>g&b zk`I3qbfBtj;*TxFzC^D1Pq3FGN3v#(x%Ft8OIUVDYTf@8`agZ~Y$7lp>m9yv8 zA1`?4Yd&tEbKqTXJ#>KHxSvkizNVG+*Nb}-9Z zWKSTzZx&)DvWltTjXdskEzYU7u^3%v&PL(oRwl~jc27B~8F+@&ESbLrwYX*`sd9OQ zPwM;K&ILN_dk#W0q5uXyVYX$tTvZUJjqG`+6P33Qy1F(J$J8IMchtq!sR?!u} z(8@CW=Nz9`&>yQWgWc;!2Ow2ASQyDBykwiJDz zi0mkR+4syCj690^rtca1X)8TX&9AucVA!~k6W{v-m)n02Ik1V95%hhAw&XL!$OX~y zqh`-yElZI*b-T3zhWMk~p9Gu^+M`w(JrE`m?*F~n{BLv&bcr*Kr)#?V3LqsEBV z)1;4bnsE=XK$9+Y2G4JIYs8Cv<>OtZEpN_m$fqxkAy+iWy|92lQUy(XrXFGgLpn_w?a*8QHC1s-=?{%>eS!z6zM z@z1xT7MzxgAjkJa+|Tcb8GQr3THNnA)2dl#V8t6*0Q*MXb*T(7nxi{~xMvH*s-8s5 zK6KmkThtqjmkSSp<9J?@2m_803NQ@y#*u<=m|rdlPw?E#QwCy26ex&T}4y zuWxx$1s7YM4;>Xaq_G|eu6Uu0X5`1YQB&9JKLmA;zHn&suNceMmJhq$Okk)V7=f6v zJAu6gKf;9GQP3{p1!qAoYX(g4g=T)E-;8mWHjXNf!|wWPiq{S;QA@iys^MFPuT< zY9QkL4>&L8h1y2FZF$J8TY-G0`wOT#2lZ;740%-#1ZmmT`Ly@X0Xj9n?{zrpqcLCR z@d(&D=llR&k42Ec6P>ny4K^%Dk~}`MLC1F|B&j^^V+| z^m30nd5Bl(gt$GuKC-5k#z~$qC&;Ismv%+=!5#bs zuv;E>6m5v9l4?J<>8aP0bKZyivk`o#hr|6BJG}Tz2pEpA-(fB%fMb+1#I6O-#{lr> z1&F&z_`RXXLv434FjIcTK7I`K0UktN4sukgoY~=B zXYV@TRJ2P_uh~&JYIa94M%XD2 zuq0PfIV0J(i+z8zN z3uTX_D0`|y>pqN9pXX3BY;KU-&wx#2bAacMwBt2~L0OXygeX&aW%ZUE+_>iO-Ak z(ZPuSztiDn&$+Y@@n|!9YgwC{_l>FQde^hB+1P>*VwL4CY*&W*GkZ`+{tbCY_Obm8@!I>bmI1^o z+~jhjt3WUCIryt;_LQq;c1o&bGR%QBz~xCJhvRnpZW(Ran4&3XlQe%6;$Gq381Qk7 z)*x4QXsfU{^Rf_451_tR!(27%DLZEKnZDVyFR>2$?}?~*D`~T~PhN7X{67LY2ENWS zb-(p14A4@@&NDjrc+3?J4IP0q^Eu)QK159GOswfM(7n2W9?(Zoe~o+Uy=dnvHZ5sO zXcFehH`KOEMehiWu~x`A?DVmGZ_gxh18)W1?AR17y&iZzh=bXnb>}+X9r`#xQ~UZj z@EQEVli>GuEPt=y58mx(RnJc5sajrcD=k7?)d_9o{0k}bHmbPNOd&t$un zutjti`#=eef1sGQy_li|VytH(=dJ16z^`hQ@D6r@FK}{<`d)zAIu7jgTMDV{Ut!vG zC_ru3`EmYz)M+Pj&k_4vbp!BO-iY%A?AHyyVU4n6jNpfjgY4I~5j);u-UFZWgtL}E zIBGHCs#n~B*qk>Zug;Iqw(>%6#lv|1E7ZC7OFMc!A;+6G#_e@^LcmcaZ$`OlM3APv zfjjgSmycB$9c|a`xjg=`mPzrhtOeEv{+3}+Sz0D=wnf$nQB}KquWv^m&uR_X4SHPq zzw(T{BUqEJmlsHSt>!x3mdWyb)Y~^aLbrZe=*@gNPYu_)s@*5pv;F<5LLn zwoWS_yYMdwCT`~|fgdr@?n{?DgCBr@dE*fKHKshygAf-M+JrVBBj+J@Cky@{+IcZ=_P&TJ5BPZG!BVgM5yWm*#;Eso5h}XQPg~Gl+hgzzjSq3VC(T-S zDGAf&$>_hOp`Wk6rGwt5YWEDALnZvkQ3ppt-n&uGfqDOek6-UWu0j%Bwc0Q;R_9w*t>ezMY=nrBIK1H0^0sX$3 zTw(XRT?dy3K5l5`hVwY<0^1Jc^rl9)0Ub4;%!K{>>^JgWh|(PbxLYDrxEg%p{9K*{ zIePib2;zq1cXN&npA{P5+;7TN-JXdlo)YEV$R#Yt+Es0X4|qE;N7h2WD=_2R^+Jq` z_4zgP9o8Q_0Cvr7bA>-4=4%~r)pY)hydUIcFT@m;W{T%%;%tcvmdMyhN( zh!1#Dd`hS*a44XTePO>(arKgz8rB6h&Q|zUUMJ*)t;X6OvOEqt$6jEebd^434#4DD zYWarM4O3Lw8a5;3^;EZq&FU&(ot|*Wy*^HRpwp!hLySCEUYHkAOP>Tj))(b`bdH^W z-CUL#Sp+#HX1~cDlMbNP)U&{&-s;j$*xaf|`FIk1`7ZqKvxnBbn_eU|LSH!&%LD9~ zo;CYV`z;NP`s=#}`Tf{G1ws9tD)_Lr4Ndo=3xORQLF_E>eOj)>{@H-KVxtq(QN<~C zB1S9QM(8+r;;C2isoV>A8HIj2^@xwQ^~&SuXkZ3rqekZYI$qC9KY{TylJ;e7^XcsS zLbr&Tr1zTqT_ZPY{~&6=Ujv)f0(l;GpQGRl+vIP(?>Qf9(7aZz##*(6z3O^k#oUFO zHE-Y!Yo+}y-hwRH*r0=To+>mHdsT_5xKe17@cJRwWJVD6{tg>@9KHr?-w*g#eWf4p zvK-+>kiq&P2jL4xIiCc1@$MK`U0Tis3B#`j!hYQVu~wTA(~{KluyM2S3Ek(w<_`>0 z=#Ood0bj9wnU`^6iZ+H^?{H^emLe`_?;V9S4gRRJLZNuCeVf&2U%{-^w%pz*gwsjT+CGVP@?zVu+sCkQ`9rlnn+Mj4&h z>$xBG9sXnX`?5b9I_Ve@oiBpB5%k*rIPUsgu&0&k`xN`ur78bS(ELAzxC-(}>bV4U zI~S*kDeQ-Zx(!BNIMz`1Cv4--MJTHfGJq53#3>n<-s{}>LD99$ICp&;;GtX0-}CMP zpBv)$HsXG3@q@#Wov1(dh`F0rjEmqM?MI=06XjkHQ_~#*HSkN9O9vn??-M^qFF@V( zH!)Z3Kgr{w&ruU-zpL77-OP?l`odZdiKtTKQ=9ud#(nyh11H#_cGmoIZ$NhsrQS#ua5U~)zm4q@8fP1T}FPFcZKJhwlZ2HF5Hi}i^6W>*T`)f=! zDn~xy>>55j82JF#rzn>+?YJ;Mpek-pQQkwy8+y)O1^U)`Of_gnW{U|V|~<3he_fwuP;UfZm<*^isx7ah>v zd^gyGy<>5WjNp{0wgW89kz;q<^`>6p5Z|4kWglt32(^txD0O?78hzlSiC5+F&egF0 zEP(A1e#+(_p`M@SD>dWS_6i@bX=B^8pAJQ#m-u(YRD%@wC)$&9ATfli|Ag9fFUxaQ zUcfxI@6GwFXfE%`d3U^Kx5TI- zJK{~f4%iSg5NrQB<_*3lbRORxbEF~q_*L(+J+x*PaUw-9G7 zO`*nwrX!`B+O@*Etpz?Aau?@+S375~$$dCqh!vB#<8-5%@eFo(^DW4C`bgTq-K@`+ zAAg&%TAnYteG{I6{j@#sLubglB5xGaX2cqqn3Vu8I4x~xorUIqoJal#r47jKhY~ar zF#!W_!lYeTyF9m#tZxR`hKXF zI0$RAB*A0b!7n=jaoZok_I(**3O~Vn6#;MQ64b1J!KZraxkahyTEm-%es20P``F5{ zHMGL{Hay(g=MewC#;zUkcTSbllpy4tZK%O^5SVjOyH};{+H)ZHo*K4U+7kIsezR+6 z+n4=@e3r$a$uvKA`bg^l-0ziQYFL_}E+?YYZ5?!w=j-?o#_O$Vp=pSBWo@M0`g1Ad zs=2_%IbP1&5d#|GV&42Y;w0A~wgUWpALLl^sSOUpZ}3$>&EHVO?cRpYrN;@s;9WkR zahG08$O6DqyD&`qk=wQiw&bdTwjYsYWqkr17;L^1+frqj!!3Tbbiy9cGF%>S{V2$i z6EpQi)MLKL&+GTeJC;t^=WBuM(%rT#?b>%kJgEs}@MRGl*HSB&y#%bNU62Xic6dcE zhx7kgO1+dzBd(9~iI3~IgIJ3V@_(UGK+Q}>ROZX5wHSau;$rX3WtWTUO;G;bIzWgn6mpl);f%i?QusURz^S*F-K>>L3czF&u zUdQD*2V*Vgnfv5B)H%tM@9aL3Pt+uM*5%L+)AqXd_IKErU9YlC<@#_^0#u zbiU;=E;oD#_QE!~y!9*aBE%iddr984zt{gePQ&1P&VrqBZxe?Gyq)0YAG@mK(Yj}f z{ILDR;ls#J@JZOG3kz@uq+wIJ0`xc;P&?r>-nSsdC6gRZ0{5BjU8G z{@?uG&Pm#W{SkY1<@_)e{~RFy>xd2e2{wjH0t#3NH0Ku7JK3qv9p^k&Mk@!xCXRRy zBjW|A*EzI35j^`3b#w0)NUE7thW?QExcMsBKd+0Zfm%PT-?!LH

UBkiTQAgW_O0-gmbmz{PJvS?CMU> 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⁢3FX`(NHZ6B%%Uyq_*lWAYZ^gL#@&Ou#>H?h zQWkow`{3^ZC9-MEKX_;#%M2xWk*%g5@N|9%Dvqnb5Uk_J+y18SJyMuthb#2%@(rju z@&x!fh`^=n0WJO^IHt$VV*Z>Hf$W2WwCR!stqEYc#{2E`=(Z?&F|A>hI9AyL)PzRI8mg}9L;}=26olt&Z`($Pc9RkUKu*RF#waE_tQf{mo_L= z6>fG{vI-@+tWI7TYstt^BSkA@Y;5ss69^f*H}rF|h;|$FkV#)ZqNm+0Je+g@WNp)E z(&us}L}d@UPAmk~RSm>$aVXu}7D3NReL>Tib<|hw6Zbm)4o)*h8b3o|0oX`$DNG$b zc~6f~I2{a&XJw-7i|d$@#L_E`%EH<21TNVB8n3%Y@kV;;#OB{I$PoWcj89v#&N>I- zT*3y3`OpHIGaI4ub3c}yABP>}J}xMEfKC3+%sLC+ZPqPv8d@KlW>7}{_FFTA`?_qGaN z&vh$NF~S0ud8XjuW!quBL?~+13Uk_hp7<=w0%DdW!!)Hu&?30oZd{S#PIg{`6N~S` z&S6e;c6>K4^|yytn46E=nm(XJ*kh({`Z;1ay_DpCJqyc&HJO?pH$iuX4S8>E23}5! zNwuUT`6km$)}`NOK5Pl3@2>@;l}$J9e|nW#=>=j;-fBFVn}Zc{ftXXYic<4vI^+9$ zjKX-FuPTj(5p#L37IA*#?lG8g{Wi=~aRKuJC)ii11F;D)^u?|9P+czYsB)vp3+65U z5xBnZwab=&5pP?cK{{FZna9O3$#)U2cF;)pz320q zk4pG(IGfNOtbYDel;AK1AI|*4Z{h-Z zOH1H&!t?Q{VLr+SUPA8jGrZ~ljhGiHk>KV;3^voFBdsFI-t%pQ)9+zwWj=`NnMh7M zCm78o=3$g=EGlbf;kXD%JbKCyizJp~#j3AZt3&X`_3^w~n=^h-x94Yk(&M+jZ-y!F z;%J}e9H?=b4HY+Hfm?hKZuO{;5ZBE(Y}gY})wu*dp#leB$~Rv9{AP66JQ6b@l-J5S z%G+;}v4>Rn}yRy|7p;C&jNvMWkb8iWKa|FZ8%r? z2T}dH1?42muq0;=%t{>uiU`>h!#ml)F1O8U$T-Bt9( zVFNS`O~CyLg{W_$!%tQS=B;uQc;&zknE%6!zU!L{vC>|w$%jwu$Ot)5pLr6}GAOIu zKVOvOSBP{PkcP+41%{$J+6JB`S(7VZ{WCApaA+}3R*6P!>27|Eg+8y}=YeC;#Da8JHAy}z`Q-ktM>c{g$gJ-y)sJw08I4qIPGk4iSs;`&u(cYlT0 zUs9hz0uIc?YOg8S|4b1dUs&((QhyoLEmUL^BuBHe1Wu52=^fDWdxkdc4YbB=H|CAs zf#HjMxKNW-xKI>}ugGFz?=qED?6?IIC#7(^-XVzXu;It_?xKXVLXs_pGvhLNYj))jjq!x>wB2HOa3vH^1q1`*$loL-jJbVw;0ix%oGnj$gLozX zW%LRvvD24W!mCR&KzFhRDVR73^m^~Xy<{HhNCH?GHIXG-Khcc8`8axx6LI*niFRpk zqZLseq9@(PILtT~mF!$;#z;So<-#Fm{VVYF8-yEa(y$~iij_*$WPOcI*x5An3X~Hf^ZKWagYpUUg*NgK;mknq{{6BL0*jiS_ zMus&`^@ZjU^6=6~$iv&)!SS(|A#O;Lv}tyLQ%4m|vVVa(hopcb}q?+XWts zQXDE~2)*#(7swn1C6M?mP10Unh7%#C&^y}{xWa$z4&7!p!&ZgOxUz>m^CN@3pf1Tq zzPDqKrz~QNJuBIWn@#NGwYhX4-vA@uH)F=`vAkOSW&Hd04or#&U~Se`vg*(6*quw4 zvU^q)v2xjmgwJ{e`Q{RfUfer0TV5@^(FKnA?AfSW6OIb2e;`@wi_^R=QmGAF>6b+o z#6;jc6c)8}ezroNu?+gn|GM>e%igH3+*o3(S4hO68n z*0jQ!O;B}!-xn6*dD|k|_pyY#*|ZPegf-KN(_M+|%z4mYXv`YsUt?1h?z0zq@3NY` zVQ@0+7x!8}4pZ`M@nlmlj!@Mir+&Rcr^R|SAFQ~kR-Uvxe29M0Wod9o1y#>wxQpBv zn(S6f{`yETYVu`Zx%4IEeXsqLiy2g5F`&tju*3tg@&r-_@QKegFa ziapxqxH}Ua*4u+-ZQ7ebUoXV2@?pk=yfzUw`mr@4vl-ql46N4GM|N6lllKKx9E z-xj(BTaiZfmeIhEax{ba4!d@S!~C4d?BWDvHc~o-wLd)v4hv7q$ett>+PhR_vJ*|IW!G>gXvet4R(cIM;Uw_@sG%x;4W9~(Z zBJO{HwE@O3>SrI#d>l>2W;~!pEh|x4xsxcXM&R(BzNmOp7RS8wC#jKUG(Yqr&AV!d zVQN{}y+M^9ekPGj|8!M!(0D$aJMxRQFWtmyEx!X>`W4}!XN&zfoo#UTP>Oxb!%A%O zw1F`xSKxt~kO^wC$DqXd_`cx=miT?6xL%LMTa4vl^Du=k{zhv%B z9|x!EFFE{Oah~jKyGYHxC!uWcWx7Mh48^v}(A#6|(aGy7ZQGTIew*gu#hyf5y)BKO zuxtmf7^lt444Z-D9hP$s({DlgURNf|^)JRW9;1~F_fVoJSYRos^TQ7b@7lc&X#4j# z-sIz7E?oIKR=aBOWvDg=${1C^dC;$7s#Ws^=fi2y%B%T&f-Tb zdyoIc`C-I{)!-gJAHD=-;{K~{xY2YCxj+Z-&ZBk=({l$ifwipQWdb4E9|h9LBob$- z0;4kYF~jaFh9%q3)I&?q&iNESd<(@-`tdkXa}13>!h_P~IvPG?Lbio=(ALCN%t9qQ zvRq;s9g2TXXQyS+j@LS9E2~QRJB!ew&zPH5cF)1heK#GbK7>EHbY4DrJg*P4Fu&^x zGq|Bv*yGK?vu&LYTBc3(+t2~DShbcP9(RrC?6TveT673gdJtBhK0%$P`q4V&CM>(b z(g$vz1aH$KT(ETmW@Mbg&~4r{Z&)+V@Y@ewo0{p}jkCE<{XifDX?iKiD@TL%kr7v zL;K79DRaZg9=wCv%4nJvc@uX?pTl^qV%!(G1jl|pfO79$FmAOE)=a6wsEyySy268s z*2v?6(wkU0p^qd7ZXxR{&cQQ>86X~3$La;#X7vwgl3lOXg6cU1IF=h(c3Hv)JeDSc z+(>h@;qH+KmPf!(F8~UU-3RTV1R{UdnjGA3OqBZSsI10vIy$2qRgJ%){Z4hNSffs- z7`t$VaiekWd0mG;Rw+WqNQGELb-{!U>g3SXTX@4?nYUIF!+aT6d{&u^*)QtxVW0_p zHU1iGA9b8$<%iSee8E4p^bjl8at}Uzc@FXt4pg*n0z_;%Bk%wwL)MZTV4M4wdRB~P zwuQ}xm|cVL%A*QWN2|d^feqlCJd378oo4!lPQbD|b2*s$80Wv`=}WsBE-!q5c|IgX z-i1dK{eXIm5%Q2y(SMnN6PG6nbY#Wc@#VhluxK zXm0x%8+%63aru{_$o@M!S%+tJz9_)DdV~$L|B$Dd%Fwki5hULnfT-!G;g(Y{wEK>M z$Jg${4*#_vDavL3uDMOdFZxG6v6@6;_64HRwUayBdz1S#>Ib=4lMM4O_6R*6O^neP zjv)an==SHb^k#1r?XFsmZX-DiA2AQVj&?_H(@1>ty8H;r(BEa&S@)?y#IjN|aW? zfjwt2=*>e+H={UhRWRp&{RoC_KaQ3Q(=plZFK^l?c!J&^<`rX4V*JLlpyl-w;(hC} zy>%^?9L}asJ%_{iB01PpVgT2}o5{(CpTS461?o+BR{EmAcyw$9)y>kNr^EFJO;?F7k}xTOLwWZERxGo7TkvMLYL_5dsct#3{sHLis{4W2`-Ttcrwk4cawMG zeb##7&oftP_)INWay$SJ%vZqZ?`7DxXB1}bdxmMFqru&(16HMe0AjwFD}50Up}*I` z9EbgIV&!jCO)BS>u#@P0@g7J_IRklXMX>8f6ZuW*NXvdX^2BE%tn1~8+L#E3ZFAR= z!yzHY3=$*vk=VnV@Ln4)z zUFeS*mNA%hqnMvH{10z)DGUr}e}?>}0q~!{Bjk-3OQQb^po0Nc%=K%FaI1M4*Lr?G zYMu`w--{oB-;pM;c>Y=FO@xAxqBIye1i~;UCsJ%v%4C)7B+5q34pGIwL1C#W=vX!i zT#Gd@BBGf59lA$~hh~w&paF;fMpl#fP(QN6^9L&I)+AoNU3ler8-6%$irKpt;h|C| zOy8u29)7>k{$BvSeOi+qU#ANrB&rp zq}lx>F+GY`bACjwEEz-(ALnd^dVD5{L zrj2&7O#ZPNa{IKQsPED*9CvCEb!De9XCHi~=T<%?9bRKl@yt|AdNLmWj!MVZt^!kt z=c`-&a)!1cXg$+Kh@ERK9bFbmZJu<>Q zhi_x@DO((qJ`BtBU*d}g!t5a<8UmezVDQH__&N40YybB>JMHHuc>1Cjqi>GFmie8$ zbATVOIPoqo0l%>>Y7ZW|{}>%bRwR9x7h^DKER*fvP1=uzGIO?E;f_67LfYKg$!N6< zvNU-G={3zEL9e39*Nw0y5 z&U*9&J#qz4yXimr@j(tK|C0d&`(hIL?i|S(SOv0MT}j4_3>dEy4k{-)$difzI9hcS z#DyJuLgfe0`T0WRD_TeLrc4Fdyn1j9Tfj&h^+8pUEEDjyjhZ_R;*Xim{LI>UShwd1 zdVRIP>c|P8<*rC-TGVK)x0i$5jKid2k}W=aR)H}@o}V(WmiH60#Rjuy$=2;SNQ9GBw)lD?^nvj3e|4-nd znUW2mb^Pw^T3)`y2FD)G#a`18UiTF6^6Q5Q4xD=QuxKMEt+Pqbfk5bt>4cu+F;$T+nAlKhRUTS;Ord%6_HBpIQP{c^2&miD?-1c>Lfg| zT@1JEC}eSC$Izi0|S;&%b>?W50! zJZ|D$tzYwYi_YeoG$18wCc#ij0QoP=nhD`xrc7Cpo%p+(YnZt>7V2ztlEM&S>2SI?icJP#%xD`m*2 zU-RjiP;stUbt3a}bQSZnJAiI(TZ(z;j!|CnIN@I;4*eSB7w%ceYqY=M1IowoE1sU> z!!OpO|Mg&A=TI%LK2Z|?=4;}wp*#5M@E3$%&5(8RJt?sM$LS~+33-#9f=}TuvHd)U znGs+`ttDw75Q@WDxrn@X~v>HvU2TrWJ9;2qpJh` zwceF#URI&as;NxX=vaJNu@QZTYoS#8cr5eSgJI_mlbw=xF<&_mdtT}Ah60Z_swWml z#<<}e$ywy#u7i->XN=mddXQkhkJ-034OF(=g`nO2q*-`2%C`&5P!hz6+K$uEHA=Y2 zLmF>p9L1JWC7fRJk&FJp(+N{R zLuFGYlgz%!lxajdBYiUc6jgv5ihp37{9Tl``-7TKggyJ!dwBP37y89+!t_ZsaB=n& zqUv)8bTpzMRB(wckZKX;gW_Oco(y$9OIe)}TUo=*RMxgE9d^zlQ18w_=h18sbJz~` zCquzDaRWXWz8g8ACt*8A6Xjn{#Ro;>@M8Bnj9y)gx=+fW~Q1kc7XV(?9qP7r(@OTvqA@}`5d`q^44Wn+h1hkeF1THA2a{_S|M za26w5vj{`#40s9Ua%}kVS#Scc$E|C>5l63gP<_T8TqTuQvFS-Hx&I&Bz2;5cPOXOQ zGss$WPtJdcaStYOf9F|ow#&bv z;f5J#T%b&Ae%_}BFH%sqaTM)rQxp7IT{M)o(nYnx_oV^o(%N{1Q~#M?kcWJv++M8jer; zLOzCFqrYysfVkQQiuZ@}Qz{l1ieRP~B!31x0oMIG- zlL~5ag^*)b8F)u)U^l(7V-1e7^hLMnJ@ijkE^b>f3nxsJpy6GE!o7YSJ`F0OMM_%u zr8baPt=fnm4d!6}i5OZW5s%%yw_yCgW_XY|4UabmL;0;N$UE_b1__^Kv66^}cLtFY zN42nk#^Q*QzohE03luFbVLilqA#uDg-@M@nKHrMS`t`jyde&7u`&S(G=M3S@`OEQ? z#7CTX$q~g`9@3no^Rz{_1rIli1(NNETn>mECz@{F%&QKd#R+Hz6*m@OVwDMZWT8aQ!u zH@zL>OC7Dk=D8_xWcPT2=`RqC|-(; z=hcH&z+t%(cBJ@3R;;ItY*-_L>fcfLPPG^Nr-sn>RDZO7bpd0|+~}CV>vYbCX(U#D z7NqQofvs`@5WDy*Y`rs_D#(8#@m^Zg^Ts3EPj2C&MTaoF#SE2?xllIQhmH;|r3cz3 zF$$j_)3Q)o`rpLwRCIhdUaGl9L$qv(M58>7SFuH>Z~O4=$swHQ>`X=Rw%jhsqpa)f z9(XaV6!v#(sOd7*$$gh-!S8rl{r)^ydWhHhofrGG-7OUi;?JlhUqSwv7q0F9;;bI?^)l&lO%^} zDp^2YiTy#NPFpP0o{S#t3iOYL4b|?Nfr<|`1RlX=dS}`!QAM3O9X;2Ynw@DNm-g*O znM>b6tSb?ue4XdH-s{8}`&e=f+4PYl$WP;WvhefcgLUZ06&3yty2_+7M%c|}Sk3`xMRCDhhS2gd|_bVxF_!pR>U ziA4KVrs?N0t^+@j^w2cIzpaMQeXelg@@BZw(8J2Q*0Nevcj4)IGa&YQ#IraDPF*vE zyKc*&_Dl_&|8NX$1tfx+@llx2qE9dG8G+L5Z#--88YcNBursndV9vl(xRB)x=N_&W zcrPnxThASuyg3LnovzTghAPyr`3<=jqrp9^6da!(uSo3cWAw!3Pt1`(FGl(H3_3;T z9i5cnL++XAGPynxaOB}%$O>Hx-_B*hOWU2$WFT~$l+T0Q=~cjmFM!B7o7mxL&so_i zEIC=S9^90tg5tAh;3shG{XAGQMNb~MbIYNn)s3CK?F(ym@ieq}5=eXP1ix?3hV>zG z^xlb&ptwFkG$&&|=!~z01;!J}wL~@QA^MMNmpn{O{~X5=#uZfGV?P~U8clOn<}z1` zhRD1p1z30rMEC7&|VAci3m0uXJBe>s*v;PgTUXV?E1hWwqWmPc0)=joUkEq zIWZkt|C49OO`6SmdMsoo8oq?CTS;)UUjk0-Tp;)wr-StH2_kp&1gf9kK(}@sphu@l za~-+!=tH$VH1SC+xnmnjuB?a$%lt^NX`Kv5SDl5DwD;iCa2hn1L@+TG@l2jh54_i$ zNen_=;8CCvYkqzmPL#|;bC-ko{htJWn(`V(*A_rSWFDJ#b{ZQQIFgnXXTajFaCk3c z%4%6YVHNKEV<&AucKrC&@ZN0*d|d?h)RfP#?%{npr78?HHtfSqnbv5tXAVtbvvJIR zIe|HSnfl5f=jMx0YW-VODFVD^87xP*O%xzoB~{%K#-*bnibDd zha|H<(AcpX47=rF+v?fi+3*H*kGIp2UcvNh*m=sIt)@NSCZbNyJlec>3~e&A;k*}Q zqDfyOJt+5#8_bxDV$nxYX^9W_Z}dy@FM1WqZpea?hcn>U6Cr!QZydOF!I!qefn%2NbM0JGbHa<1`nkj097}dY@^W@mK{^b|oZ)_M z-AQ$8yJ%Q?A()eYB!}FjM?TJC{#HoQM`Om&oxji1RJCN>laYx_4jG`LXAwOpD~b9- zZ)Nd_pL96#gWzG=fK@Yie6#Qh2F-Hf8hTEWzD+lw{>yJDEy#iErM{3dZxJqx6ek7a zT^O@jl)I4i9cS;qhWd0JE>0}P&O=7@(YEnWup$~>2kJx0VJ*nss73BFWwlA4#|TSGst94lM||$Xsxbrdpll)cxFAdSTQQl-xU)=FHa=cAOmW%z{qL zjnTz3mYcAAr2;=vdmUb|{X=&|df_ICVThOh2Kt9Cz^fnCtlOe~IOLd33um4sPD?## z@J%tU{m^;TI$n-by|(fqK^m|wW+oJ8mO|^c2w0Z(AL*XDf?QLLAXT4=Y?QbRY7o{& zr=3V5x&CsTgN_B~cQugOO`!DChF<2wNF`#nbu=|>QJ@LqzvABHNNjw#1Fs1)lelf( z^v=k`_~623JP@yq6UN@AxmwR5!tx!He|rH`54EupFZME5Q#|PCyd^~W`3;hIBo}RS zcx<-Y&TG60#gLLh=1|f(@-?cLd>7Fm)>NEClqXZU_o_7U$_C;6vaqSRZBI0M57LEa z{F`$hdy_at`Ie?Dl=o_y!jJHs#LMSv!llxCc%#FN9=cr4stpvd;O!=)diqDSuo%ZJh*(J{Mf{-8UKq8Mr5<4pseI&)EOw>GN6CYDmN+?*=t%4$ z0UUh(5VPb3#F$8c_{_gxdHgha_WLNrMyW!<=499`X> z&|q^K%_umI-Yt=MP*DvHFU7U|xX{I&`>+f(w7u!@+1E6Bzalk?P9-;227~0~ucSg` zH$CD#ms#&T0jxLvfPk^?;FhsgI7b$NM?nvhxg2`#^msJXK8fba8&Qo3qkex~iBaD&Ci`L#eg2{t9nD6O!-6xz z-ee;b2qC@JlpC;jY#3xLeoq?bM1fVA1F-J{;BclYePl3!82fBt4i0CL_`TcdvQ$@-%9S4nKEwC=YA10{;(ZuF*oS>nOi5EKY)A66YuCpF5I^r|V zn-EA{T~Cl-de-FC=pkCHk&g}?KbdILN8q(42y|AiBOlrilluktK>>nkvIra~aGj75Lm)(0I@XDy2N(meWV*FW*i6 zydEaqs_!6pPa@$4g$&NqjR0BoP$^ps)r|s&iw3cpI?538E)0GpUVm& z#~Kcav+CI)>=09VL_~wQ|4Rdf*Z6-cie3qY+*DvgR+0tLfL_jWT2VANSAWm-_P%U`_jTj-$ z99y!E96ooA{tJsI9o$TiIXn$!=iY%<%!cSd zZh#TV+ad*rT|UBP*(c;jzgEkvz@%3;h)Hw>Kx1I_2z+9nzFd-_lWEdaqYvjeRO!BPj4VMoOX}(fE z7D%fIxt(`}PtOKp-B1|6yqf0d+$R2&Q^=~AdRkexgfz_DMkCkxF;7&+L#SF66ekLE z@b6k=Zci9!xd^ikzYpvLon$f`+{3I{e+83B9U7{l2g!Q?KFI%qLGgapYuR>)mb?L4 z9r0wDb}s~sXrzZn^uvV20X)Xnqel`nY4gjr+;=GWPa<))2H{FY@`yKfi+9RQE6J}WLHch&eoAat!CK9?FXyO zTTInPIox3Vj?=6Di))P{NlrpJo{JLhh?1ijm%bD_Ce#ipM(;s)zr_suqz(zmY@kwqwhf(x(*H z`O_B5jp$K#f*w*1MNQ2KkoL)n>kjJYRyv(UwW0OI8CvM5Cz@y%S5NP{CDXxA<6zZ; zlVozp2kN_dCM&U^gK9{i`hXbsF*uCe z6|7uCwvUtKOj+dUCkIk1t@b{P=`XXqVz=LU}e?Qja-b1O}h2=@$?0Jv; zeZQ1k+wzu7?oPwlgh(3W<;^`73kG$=!{qe%FYyIEFKd~59#E=yLDu4Y9^61|H%bZf2P4lvT#buD2A(DNS4jfN8MMxLa*`}Qu9|hRA#0hpgPK^U%NEm^EIg1fTs6z^66~NHAGJ_IS)EIJk(E z#XC{+J-4`9GaKo#t_Ctrw1Sfxg91nAC0(Lhg-#zo;o-rURw?pu-ErBunmh);g^Z8lYw`kt}O!SaHPwlq< zhBB~+tKYxFMW3r|7}~H7v!&T>fBad!wk~*o^$>7lWXPT6KIF=t-`u4|i&`>qGPn4{ zH1c(B4^G+q>Uj`Tmte{PZbWw9^X4 zeGbR#Cc?hOM_a5I*owi2-Y*wi*_$dk>duJ7|f~43tSz;=(tl)A9BznP>l&Gk;(H zB*{zwT^=}_(d_z5p@A@;i~6vx-UMe}G(pqq9pre!Q(B@j8;{7$Mwu&BNO$BT?&bM$ zIW@F=pBs8!egz^M39w@ZFy~Y_ZiotjIltyKt7f=C{OB0)bIygAK1T3aM_`PvaRcp> zi{V1wQdXCqh36}$5$R{cq<__SqFtjw)K}CoE#2Lm*4Llp%xps|9Sx~lN0W3<6Nj;v2~>HsJd>S6JzjD2MN=ax=O&_k zbs^?{4neiWgE(?|5?wx1@MP6SVz=%dyb%(M`A3dmuU0%h_p-&p^VN`4T2(5d<> zJ#%U$?Xb$g*dw2?(=!MU-~T~EXMCZt;_uMNcNt^l{**LX>u}1$a=5axn;ZO0LAs(1 zUMhHlT=E40gm4{7XZ?nw|L&2|7wf2PPXKxfUr6%-XMXzXTKw+*jGtQQ%xfptA*Rm| zdY+Fkc>XNfo$-xcK3>8#C-0_idjHUE9}Qhcf5u9%1&P4-EBV7(ug;i8Oo0n>*gI*4Df11r;w{3Lea}P zI7TCvTz4KMwGX1Ga;-75*;0avwTRL`4>hrRkrJ^ynm}@|tcSg_d$?GmN8H2SXHXTA z2*JB*$dFJQUR@bLq|9$&k<4x^GxMP7KkB(}J;6k&PJt}?st*VMt0t~lrPSq@;CIrg zBCBsY(5nLboNwMF%-wmc7oE#H))w#!G8~vk~OZf*nRHy6{ z$eAEH^fI5=CVEh1tL=3B>6MJ5L@n&C`vke$@?qNZnXtcI6vKD?Ar-IM$YarPRO^~b zYIF}_^_XfD&r!zZ!9(O~iICTN9YNbK7c<*ADJZm+q~rgKZg$9^6644Sc(+P&vQBVz7uz zFI=K0PI=)O)4Q~3cLYrMWQz5n(=Gc1^!)+1uRsr}L+jonWK=;aZol;c&BBlJBJYxM zvd3iFCG^pTRmT#eZC|-;&GS_2S`o>WE#meM%9CG4N6EvZ3sL8Qnsv0(POw#0%GY&k1tq=#>P8Yzsp0zg7N9OSI5%YPCi%+?>e;L@=G?ll}59d`% zD|wmCf-^^O!-pMI4ada4^>8%5|g4Ww4}2{eBAAV{o@ zBJ*9$VcMZ=FsT#rY3m$pj?{f6^H_%YP(IRn;>|y(D%p-Qn^MS&v=X}L=L$Mv%~w*a z$dXaR(P%LEnEWlxz#7k)bnQt=PPI;ySN##guQZ#4(j&JCj`&f$O~y!GD|RyaJvf7k zsaC9HnZRJY9?r_i>C;=KnN0G}({T69K~^vxzyrJAtgO&mX%!8DfLI3ud=xLk@qPaER`?D2OLsarx(f5{ zc1$XRG7ee8jO*}s?*eH0=)$VB1FKac34<@B1=mCZcm=+P&1UOB zDQp658edH|eLY2zN~~aFbU7@PX`@BO`CN(OY;^QxxvK`ZNn^z$nj=<#)=px?v>+Z_ z9y`(Ii0ybyHvro%Ey4RPmAsMZDPE#NxLe&+#>&49n7a2Z_id1JgMCsYRJn{iE751A zOisbj3rBX+pgC*3Rf*MZTmcE@58=Pu3^?hK48Nazh4=45p-D#--2MrfufXNFPSlP5 z9x<0T#NX!DTuvo?fHg|MV=_rQ4E^1wVwL(*=G}jQ*>Tel#$|)9^(%N)FAZIJ7UIkJt=nB+{VM>^v+tXd|t!CBR$Hhg6Em&>io0qnZj&3oc5M{t+)o(JM*N zENB7w!CZRT%a(dwUyS3wI#DgLBNzt+eT?5=)v_J*sY4r8`I(NpwFs{kj=Vap;$415 z@@~dAc`G9w!4Xo5tEcEP(W#*2uSEI|D#5YWeo$ms2rHL~1Dl)y^%`p- zJKYXE<2oV4FPF+54r(@@(#mC?TF2LI*o&d#RUp9r7H+K6!v_`%aOBSWkg719RoQhE zKHYx_FJ=_O?gJ@sXsiaEqx_giymTX8|9vJ`6#8k7nLRx;wugGu#4>rIZ^$%TVVjco zu^D2|Qr)xeG&=t^ecj8@@0xiy;l3HFO;*G}gBi4j^WzSmvbRe6k;sKE-$k4crGtNf zI41nNjmIL3(0o@rD-~MB&Kg()ROAO#L@XkwKS{ToG*^e?p6RsnmMT|j+D21`zM;a$ zdg^1(2wsqUn0W3Z>A1d^c}IR z$R`oEjA-j4YufV3hUEX)LhkyQGj*a$w0AwyvIG9yoV)vA z+X||p(_z-`cJS;u3yEj)$p>C@Zzk)8WvSxGD@^O)Ka`RipzTd7=yv5k+MSh17q7n0 zsKvP2q`%%s@6O9Zwdz+0y~(&XHJ3g+5(85cjtOqvQy5`28|u2|(we=0K+5FceEc!F8G%bV?AD7WL=cZGa<5f)j^IEcFrW83)q9W`#ej^=?EwEDY z9)0%TNObxcM(35r;PatN=<+og3bGdP<2$}_KZ^!oTu&=G-@6U69FDM>`?SFK;AU2& z@h40CLReFES+;_gWF`MCf$RIfK&06N*r>i$*x5;j>K!8?Gw2WLIeeVFI%;q8Eli3` zS@n~|t7>x@hBK&-gcK)#c{MFKZAwzTb!{fgUj(t{CMq|2GugS5r>!Tnn6DeY5>4Z; zIP>9oY|&`L{1AUWt6Y*_TC<(E@8|G}YZHG$aRi@!ek5<*rNaBnE+&#SSJ-kevcKkbq)BJF}4u9-zF%E0lV~S4+yG(U7d)%UuZP_Qr z9y^oH9(F&?uFsKY&C?qM?#mqber(9bS2~0=UfI?1>hM42&(nz{aB?T<`BEV`a1?3Y zmTh$70!yazt{WBkl1EDC?qd%9ts>piJR#+&Bl&P)0X;r$Hz>clhh6DPnCjw$>O)uX z>iM<&_!IB&TD2_il&y}x&5HQcsOvnbypNy79%9DkMi2`Pg02sDAbD#RyWZEF^@`MG z*N!^ElGZj>Z&Da5*u7IA6y}w=|_2(b%yyRJZbb zONDziv#{O1C2#W#V)-&0mfjr!Y2iZt`A!j47<++C6q!jE&FjME%1hX(_5eRkIKs=# zPvTtzx8Q=i+58wUWgMGx5tMAc;eiToW=QH3`gtn|++Agm6=u|}e>hgR>m(~1q`{j0 zsbOui<5~N^=U8XUSaw2GKm0d(35>nA4fYM0!0Z!~$%~#=kiQm7)*cz(^xwVD97+$7 z7rE=n`O%itFQ^0@i{3&)V-R7GF|T7437fnFM~{7p z#=5QSNaOQxUii;T-F}Q|?P|B_>|McIcjl5(tM{1LFG~VjG|9>OMXb>-8CIqFBg`!{ zhbrrEcskLI9sMPr)hd`sKFEd$PSYWa9V|fQ>S6QN)W5?a|`Tu-i!+kqcKY}iv)P= z6queLNa@C-B$GHnHhU2hek#&Gb#AzCrx{)oHQ{wiM?qyu5@pM+iR&p<;#4GY7cUgt%YEeDdKPS8DcW_JfR^nSXt}x(roM53<(-oRu-OHukNHm8Vs5j_o|oA* zbL!yxa18EkABmmXL40__On&yAe*R$fXdD?C2Gd6kz}!+FI&#)m(El_9$0gmMFSuLC zz+ED5jNC!<`X`dVsSC);Rm{J-+02iKU(AuIj^xI@O|*DsDvA5Ok2LGO2Bo(f$+oM*H~%58 z!xH|)5-C2h_9j2JcL3+E^u@_rA5hQZN5F45877Q6-Lg!W0fhyV+zivOZ)!aW~$x3ecak$%sPD^g}>nLh^gkN0>>?Irv!=NWvV z{2IP=)ptI^*9|X@ki&@NUT!2>K;F=QG%?kmcxi#m%vHXVqLnIvU5i>82wqCkKY^5=ae<@HTJ%|`{+}= zN%L8uuAhUdzoSuaQ5SQ1qBCagor3YVRjBrEU;1KFFPArF79|R+==|-qG&gVy?a{tT zLGmB*`+9~-RrnKw%g$t_A6;H*4z?pvUu|t|*DMy(h}XM9Hw>6(%hchyL-F7i;*VJ3jM1PkZ=igDL#{&pclJ z&jorInUe|7td6}jBRT$PR1TB)sK<2+KtWk1J_Z%;MC?d)rw6bVX-tJCX?`&waAiZgEkWF z*)|2?%ZNhlksn7gu1DdyJ($C&ppVXUEC7ufx=kS==Fh^ zFGpdy#A2|tHKaBMpOIJ(5`%kUB=&JF=J4{2p2&T~#N(_)bOua|{)1BIE1~j-H(c~~ zV9m$6!bi3NKIm4k_SzreTz54lN1f%z*>B+IoUg;<1&#cK^h$w+)sKZ^wDDiUUF@%S z#F-tdX#Y<`!Kv)Vt@C|DN|&3{47XPx)}D&`p*6Hvc;<1RI*9F{A-K5D0qdekpg&>- z9$$9}&$;cTW%<&ye1beyVxONx3(tHf7yjHpwZSg5?-s|zIxqV1bt~->yk$Byj;ORb1&6Jzc!O^v zFxH|1!`9BluY-5+sizP5&%c~%-drho_}cL$tB9LqzZT7cW zZL!b1g5+V|(`pm%nr+OBIU8fbMK3JXmBRAAcwTZz4W?<9U>yo)FdOK4r_ID# zRD?DUNs@{2GiXbIHrD+5jaNo#;B$iySaWh7wn&8HX2YrYZ_!uW@MhRes9$6=3%JD@3mN2d8g%!1!5DxvB-3^vjQ}kbP$&tPe=RQtMs3 zgVrkEfWLqj4MMRjBNh{K-U_|k`Pj8u78_jBknlTjb*L+{e;;$H29xPlQ$5<2%`(k0 zUuiSm5xfKp7S-OsK4OX|N7m!T`x3m`=u&+Cyq=d2J4?@2bb-aQ(`eu=2aDT+No(#_ zPUC(Grquf5#9V<-e5Q@q8FIvGgE<@$XkRgFUx3ceOXOttGTasAjl)0V@SsyJ9#JVq zb)Nx>O_o?o!tr7@;Qp;4cpNKn+oBSbn5RKZA9C~|RVH~+6Ps15(`cybc}yQzLi@!0 z@rbBD&ME)MOM8oB%l=5LidDkmC2x7L_#aevng@=c5luU7`=DsIE-E^I2DOt1k?A)i zdsjrlrVsKU(kx^GrPsqui4sU3bp-?7{>CZo4Ja~j8TC!B;o1Nb8rz+Uv$_Uw*~3Eo z)Fy|QC*8rG8G=6`Qjtm~3;gRmPa3!8AJA?JQoUITfaHR8j zz2IfM#KEK3a%>Ye&oxKAk1wd?NC#-}Q(=ScCE@7%N38LKXV4_%XNJ-Oc*(aF^h@j! zym|Kl4AoS^)~(}s8C(N(r<_RZ;g@)L$_{$Z;RVf0&nLS-_Hy-p8n|iIOFY-ni5DmT z!{GEXG_&8s{WgrCJ!}|CT51S8at-w0%}11t6TFB|I>}Sl5}T(VCsF3cC0>TPia(UT zVv*TH1mg~J>DmgS@Fo?uj!$JxO?t^a+0|ebbP8?e`SPaXyU-$jBa9MuYi6}uK-8hv zth9D7?f%fk`X=6EM?2^Vd+$0l>172jw7kQ$_}!;Ho%gV1{2Kgrw-0M>9K|`6YP9?M zUz?b|e0pbfC;c*V6McR-07Lqppx=)&s&P_YaJ0OonWOLF;xS72%Huf}eXc`YIZ-_L zGY0Sb9psD@(!kwSmc;C~!m`rM%(HRZ$*w{}>}{zd7kA~LuZksk)E7=`^?(gzYQcWh zW;WdPH+(mK3w_0_Fnae!JZCnFb5BV^wIjPRVTveTaqGodOZH)s=Y1-D`a^S>*hVsX zW-aBK-07?T(kY*$M%#SSi0zA=uw~z8@~UJQ9q4^tD&Rh?7BU0t{lsy#slcD}PsQ~3 zwY+xt1-yJxi&u*s;%AM?;eCW!s6=cvsC}OU75^rZeC1IzrY-}uG8eHE%%`wY#lA57 zv;^{EZ zF`8GnbpXzewPyZ%XGPB4*(UhXuaiN+ zpWvxf0Ao`1fGB5Tr10zx2>fP6jpwwsy`J9QTSDL6(&8-aHj)&~V5$OyJaC{AxBp>E z^A*KF=s(KC!z*#5sbT|(zGn!oYx_xMw->TP!^5QeIaXe8=NC-cf`bZAaEF8q4SDwu z|3RjJQMU`XG!*C?c8xXv2cH@x#h`}Pu{rGHt5X`l#WhLY} zfDpGvwQgD2+ zB-A<|1^dQ(xPNX0X>+@Yb39la_re701(&)=+Em=KgW`nwy39xEyX?dfmxz>8GYl55 zhUDqJxOmBQJiU7xROp*SLRJ=4X!%ATkCr9h9^1iU?{27RDj;;(F&uR*i@q9ZE`$v{ zxU-Xwb6oLb=G}<`lK0+|YyVwMpYFA%jYOB3I}pG)W*(!m3nPi%96bni4IsU1Z;_if z9#Op$PqD?!1`GDh!{lTeJYg{oEAL0+rn3dSy5n9<+15mUCpduEMHR@O6b|>?4nkbM z2q*?m2g|vJWQSe?`t2S-#Rv76GFQQ--qe=+_f(k27Uz)MczKY#vY+Jb?F0Gi2_!MO zg8Rl*Qu#+SXv4B}V&%95W7^W#nbz;AXX8^od8KcwK~rq2h^$kqg`WoB>|w|kzgFYT z*Y3ltgh?C`ICV!Rw==&h9LZ&&H*)e(EvP7tF-(vSHe`?NaC4G%ZVvGue~Ff)uOVx9o*}(r0pPp+1w2?$1FK2T+s_zC`cdT}=<}AEaO^OglC; z!}5L`VC>Xktgvq|YflP}@!3c2SbYHFkNqTd^FQ6EJ<_2YPt85%*c; zf$8)v2o!6@Ny+~(|KnZCCFs$#R%5Qh*NPl2TT7mo?4lY2!!-QIV{U!TG5Y!RpB6PA zcNBRtkNPQRv_wlfGS9llap$}C3U>?z%B=6jnW9&T?y@@KIA;SbZ21GO+sw(c`Yw`~ zwwcl(BYG=v8;Op;0JGPNfU3`IGHZSZRhab~XY3WjoM%1sNeaOcx9>ng{|>k>xg7s) z$>Ht%B5>h5P13gK5y@Au6>R)_c5XgX zzWzJapC!Q*CCjvY`S_C>x>}Ly#WR`%@2XPi7+aEW{Fvb<>XL7vJUMX77HsAZfk(Cn zqzsqA)_^T=LM9dtKHmls$3K(Ontp72>%zHxn+y^nRyZlr9D`O461BqVkY^vqi@jNh zzaoS##LP$3uC0Qm6^w%^s_INh0zZOKSy_LwaBNiGQ4IFv=SW+CPrbNhkO(+(nbMLY^=}ecfN2X5wul%s#T||Zc!y+7o-ek@zGj7w!F78L= zBVupl1@`Ms(|J2Y$^4;uaxElvYzl+TM}8;Yl-km-pqC>wklF@e_#Y zTVdmEk_!@HrYI+HMVC0U=#CpGSiC^}W(L=WrlQslBjJ?~B;uog2;8T4Aalh6T#uA7 zpF@1;eitKR;*mjiE&oEbLyI9VR~Fi|#bA@$1@L=UO1G3;q?tE1Fy%v6xmwc#`X&4c zPK>w%F3U8b&Ys7u85VfEHx0waT%jAvg8ArugST`wOrZWBT@jLE%t3(}lKB1fTNE5@Q>ddd!h2+xfbueqBJ1KKjCe;n1 zG-kL9LrS# z8zCESRtq^-p_L1GZU=qDyu^g4Sp~?z-3<++-;W zp5s$kxnD`FMqL%$oU@*l8~#i_c|0X|%1z1MxlK@hqMK~?wV)d#b`$HeVQTF#j#kX{ z#_%W4(ARtgy%$!2vR^0A(Jj8r*Z(xMdHOo%oe(AWr00rx(2I?}G!OGoVUc z7OsC?2;*fq*x=Ry*V2R>c-drj-kpu?%5{$js$7QySIt@ZJ{FqS%!UNt6c}tMV{J_z zusRi&*{XM+*o9R)SZATX+j0C2{EgcW!#WFrdGid`dk>J&T2ag@(=jwCI*RhjmvD4` zKkYK~pzrl;=*;`-+UdAhrqxs#PlYD3n-_-(Q<xoE@zBIEUz~BeP{yIo&Xi#U=8A82I%TojaJ& zlDG4)Ii;0HN`(%H7W+&KQV%Pr=Tt%UG;pW^UFp9ZBpI%_$ z(&J~ak8_70^GV#HFIiNj1nGqKTpE48k}AHQkBcmR;&#UwRBoLFy=s?Dd%m;G#Xd@J z9a_TKcE=Ny?V-f`M<>^R(gVTaHp<4dfpYaSh@N1N%357m{M{X`rl<03%|u=*;{#KY z7KSwivb^P9^-SDBG7ykN7VkbC!M-c9)yz=c@#8HwJ z@Gr_0E{J`F)lG7&`OAeclUo3v_P0R5p^ZRgg!^FGb1WCRM3XOj>34NA>a=+^YI2si zVZ$EGm{x@6G@R%)uSj0f(g&Y^&cio}@&eN>gjS9!#z!sk7}iw+4f|fQVh>blu1P*z zvh@O+FC$P(Y=G|XmL$KoTXX-t*TMNi$LM>Z-e0{)4Oc1O#_4{kH1Q@whp!_j(IN7A z_eQd$>V(bGL+12Y%sw>SJsU^4Q`$dcG7-@Yrh_6r^v`ou`YWssCpn1Woe%$baWhG* zsm;YV_e}AH#t>dT?TI})eL|ntik}sF7dxKy^Wzi*X8VH$`1aBibZ$5R9xFC*DXz-+ zU`ic7am*CFbyA;~6zT{g@+&0`$|RJqbK2H)!LaDA6$=H_fO*|9aw>X6sF;; z@osooxaT*kjlzHX{^P}+J?P22i+pjP9KZHbCw{x2!i&D&$ldB00pI37#S0;u@baT+ zNXOdoq6znTInz3R_t=xz>UtY<#N1Kcekq-G=_eKQX%O6xmmvIcIcRUSgqe%qfP8Hl zD!v#^H8gaH#?lkC+7n51zXx|*KH28h?v)&rPr=>tvFKkhfKPj)d8uVpnDd_t`cKor z%)sY(du#;1bxtQQ{rUi};F&?)4z)t4W;b!r?+3eMAK3}P?I`PdjF%cej$b)H37;?U zq@C_#cuVz0-uP1*)`k_^Or947j_+oG{_>;LB*KI=$4mmpRX0F%!z;oy%x6A$>D#!R z&Y;=(4KzdS7bzT5FPwSRROOQ?7g6+GaFj}6#btpFtSOF9RI=%n3QDej6dcCCzTm^v zmwEM^T&%BUQDt)kF~~`SI|I|e>&rA~T~x(R+-%8NL`w7HbKLQA_d+IlVUKWtl;W{B z$FSBRfLi*lgN4K5P(C&i;<8?Y`qS;?vAYs+t`lkbu`G+M-h^OLLQ^T&pFV#DV!dg9PBzECO$ky+C_qDo-u6SeVDjBic^{-&UJl!OxJnUa_ODno0RB^xiJfCfGPAaB=p$g`Nv{b}C~ zrb|j->9vv8-36lL*}t1a?MEdUP}M-$l~Ls6YG-2Ykpr?qPe?M~y=CRf*>tsI2??aIg34u?fB&`LX8fQYAWCzs8WB^v4r>Azjgz@X|lENvC)^P@An1;gLwCP~^w*Y1+_>kHtJ=~kRo&1`eK_jQSP?=wuIBK;RGp->U z)}LK3`e-~0TviM2Od}l#F~|qS@()yPUS}0wVb%r%_j-B1aAEi4JOwig7g<| z1ZLGT67gs$>|CeS*|Mh zG@M!@bR6ee!nD93wAkxS8VnSv+vrwati2rSPJMvv(D$@s-YkrXlfe3A(#RNZC7q`- z;g*jM$=-31^sQV1(yEk1S@|&&bM8{xAa9c6@SY3lx2n1$u#z%x8BAGP2|M-8$w1d{ zaJ(iBUrbI2U92cp&iE;GOWDAzbXS2--@-)9I}I8?9?=C~lt9PM33CNDwo4-$6fJ9L2Q0-womiyI#^Jh3)Ai@ zlBDY~OiN<(7K_9U2)pB0x!dZUu{9gX# zu|vGLuQ@;Z=uf&~b12AWZHLI^a6gd688{WHofV=}Gu#E?s?_bQh@3DO})VP{{ zoT*Hef4rvkHddl&{6_*DwWxk|4H4gvNo$U5rXI_LJc00R%u3{G_Cy6p?d2I%O{1lU zRk=97!fNZ*7lJpj76L=EL26ni4VC|bQmY~{BYPs&dT~e%=keNq&+v<;&&7tU&-{py z&eWl7J(K4D1(s<$0y+rnkMA|`&Qc#_D$Tf8sS9b`kvr66MP2Dc?-UPAs)o>#(#-U#aB{4wuj8VYDrVf zEXkZN=jo3mGttn8+f@6)W|4Q;8AjDonM%Ff!#(jdq1_9N$yW94IHTMIH-B>Gp1&6M zjmH-dzn&+U?5`?%Db_{4xD=4!wiWE?jc-AG_AlHrNg1ZBy^ibqAMoN@(l~1UVOo(B z1A)WN;`HVOl>E96jU>8JG1QzI|MVe!_wEY5vV$}@-xW2Zyy$f9JUM&)Aq`b-A{H$> zZT1TL;1J`Zgh)R@HtcC3C#3JImtfP9HhiFf*0wf1@sBM#kDiU;IhG zxi#SOzCd0~)(zuB-7vi50A;V1izHfa6OGyGaN)BPMvq#E=~vZBpwLqz!-6=JUrs-V zilgDu^TI6XE6txezsl*sVN|~|0_8p5+k98+L7A9fdM@u8t&Dg>?^r*is{{nlj_~C) zI!}^Lu9TtO2h(WO$muA$QbiVwyi@&Memb!)x-qUyNP{t>A%6?i zw)E1VE3eV`&?@Rak#KJ-U(yAvH5!e~qhV12T*87SROgEo_4qr062Zl=U}+gW5aNrD zeg{yrq8cM>Gg1FkFR-J=MFNe-#C+5s@!S)40^+S5XVX`qO*z` zsG9OK?(!jJ+PL>Jw?DIyjvHY}PcIPijvI6NZ+i*<_VOsccIOg2nsiMD*ETktT;NAcfrzxCK`t$>k3g#QEo4CSs+XNNJXz&}kW>S0`0+ zm4AGyN`(8i@UthCd$Up0F))^-eEvk$cc0;Yj#Qu*{f}2`ibirr*Xz*rWk}tJIw5#W z7xB|FW}ZKL#-)kbkeZGDEu1N%uv?@u<7P#RfUCAS#JTld?gKRqSi1;sQuCOoIu{MWpNP4f0cB4NP8i21KV_xQaRRXjIG!`gSa3obL^y zkwKMpfsjSM_p+S)_2sGmASEGV2x?98!btr_EN(Ex^b=FD_S`*8&`Ke_`GpYE{Q*KI z+S6XU5}I8!4lliVjw=Rk;9=8Gcz*Q}xUA9-H`eqrp+(uS@pCWCinvDhrC*_Gjc;lF z*+0ym4t;Vm;16lrEBuc&+DXLcU`ReTo^;n7gQy#4xP%Q0tJH1}(#oktT;Yk`OyG4n zI!d`i*xf853V}b-X59eh&))N5`V8v@T9uxrPz*q+-&j26zM>ot{$ z?%iK#-XqLOzBHn0-fLd**iS4<%fX1ri?AhU6sG9j5Z#E=!xqCg7_>ndE4{w(n~68S zD6o+?(I1O#+JyAY@Pod4C*j|8cUFFiqA(ltB*iUXN$a1fbX&F=UO4ZLBX!F`CAkL{ zz4-zrv1$NJ0>Jjsurk<#hW|0EzO*cgV}*QdKTDsU zkijs%2QwCYM4wMVxNPG{8>O35QGfPDu0+p?)C=xgjq+T6+-hassf6Y2FKy-zR@(6! z9e<$no&a#L5V|)tOIej`>hLmIm`k;1k?-eaKt0w9i-%RCPG>jFnw$wK+owU5|5`{K zodvrcRRku_LZ*SsqFt*_;_!kf6f=4X@@+}_WmML)+;E7GXG&pO(= z?;Y)~OC!fLlSp}mK2G^?5teV+4LScRNVCQ-bbV!q4ebHE9IoU|5`j06?!khZhq%^K z6Rt~nv({>5tkmlq2%TyOlSlglyI>=#`+Wy{`#lixXde{14#0lbUdXn+2w}yi$lqjd zCMQLimdWd)^6B-5o(reFAJ>U*m_rMSovTGr8uNlqU>Wn^8n!Nq-GQ2YK7VqegfSmp-fvLKlSgG`Z{YXW4 zXd8@(p+6sF0$p&&D{Q#UjJqAJ@7DM~E0yq?72|kHYAk)5>TfgT5J+_D7 zYJs;fN-Po9%CCeIe`kYVfjXDJa2JUin#I^P^@BmdL(cBN9diEPO*j|w6;>J*RDB(m z21k#zfrNJkE2%b&OIuXN%PPF#ce;M$XQ_?mho`jhF0d6#*M>7!HvA-U2F`G4$7$eK zorjM@-$B+@lU~~Jv3luqHK=-j7j}4$f&M}Yn!;-R^bvKCeYaiUPg>)`8;W_ zb|fi|+W}DyZD5faL$lsLBe`Q^N#e~_;4Z5M+sz8Opx1ujR{j)b$t)9c)-Tzy?U$i+ z$y{OfcMk9LOyrX?oA?#375q*$Vb0^U3ma{9Fnjn%yjS6ko1V6!!J=*CdGclWo${U> z|2q=bGMOIa4)?4ylx& zL7p+*Zhl6F^qN6N?l4t8afP<$M8jyYMiP`fi&Z-Iku^%KgMr#JaPinH8hbyDx>!}= z$-70OfiH9MVAo3wAEpdWqbp&zd@K~)E{B=-K9J%^zd_ks3akH{ga#k}(&6(|V6EL7 z9JMK)EIVS!9b9sk77JOJ92FI&cvVVushvBSwyTHJh>9Vdv+T&a&Kw%jdlAq4^~1tv zzVt@UQ(R(p1dl^3T{}60j!*Yx^}N5c>tE@!8(+7uaY^^#T&vLQ&@`Zt0d{zG$bq!$ zL}UK3_cYnSjn+3kWrxRngv&|0;YZ_U@YrF_Jd`cOtr5G?F-$LWf=5P?TuNY701uu#it%3SnBeHKOu#E`I=pczW3On3 z(y^=QH03f{ranMz!~2NC)y>t{ZyS*ddUi}oWgoLMC0^L$3(We*$6?{7t>o0h)0j6& z71j>#hSsiHI9uxvft(b5FXVVjt@Pm_oTFvC|KJKu!RfNO5CV0*p}tfSDi8Y-$JG5I z>m*BT7ksCwMnYa#-W>}r#A5uUTpIr57-~GQWh!$Ck#fl3GAae1c;P)-JYJkDNI%c4 zb~(s({ptm0{SjQ+>3q=55`o!2U9!8NsG7?=K|V|K-CK$=(@;I(AakiCP_(> z=x-Ns)wm8=*1C>3qam)E31s3MCFG2sp<0|X@w45->I@sl%BtyN{g?0DQ2l1C2sWo; zLWfdJOA`YJHPBglIf~`}X5!@+k~YuTbnI$tvfkCV>i(u!lI^*MOMCU2S;ZuQMtCCm zRN4a5H|3J9i%Vfj&eQ zYv1yiudj(WPCi2%xsuoQ9**m+LgAFT9;?=w3~&8pU}B{vT4Y`4rM`F22lujtv!n*m zjdK@v{PxVj)lbRST%nh@`WyK-n~=HJo)TP{MP4hokvEGxKwj{Zh%8e;UimH4^uY;E zy=Vq}t`8$inne-31su@WMOHrX2Dxe(xG3?JSba{z)Mc5trPv1pR(#?mZ%5JayNCE- z{xv@(B?G^9oFu>RJ%DMaZ<61cjU-iIH!lq|LbtSdJdv+TvRYn{Yp(Th`MWVBFR37R zH{O5@6Ax(6^o5Z_C8Te*H+fngO>SB|0lD{iqtUT~L#52N7Nm@oD-YG@l=W@j-~zX*&D_!Lt!1pUztcFXu;! z+mO`qk5FH<79xARDI6UI&kqMcYo`)_7)E8dk+m-dte@pgOwXHSXH@W z@NWBnu&;dur?O_Dnt=|UjGT$}PYz?#Mss8z-R2kVuHw!A9K*7?I<&9p9!&W>06}w> z!KcT1q`vY9easfp#UH*?^ILbh@O3?KeU$;LD!K(1Wu}w&z1rmLt!ctMZ4C*i-2@XA zO+eG&FF7r*Op>NACt_PhfcB>#NK@7YAHhRDrP&`GUk3r(S4*@u z(fi)w%{`)_6Bk<=r0LQuOoiru91IY zjiANEk{}ZoGS%$|sO<116a06=h*D!v4sE54PjBFtVRrcXXCtFA`($(8Z%e_8w>4_K9vSr7jTb+c{2)?X6+rKDi=>z~ZGuMxD} zW(1r%`T|~;dqSQ{D!jY!5u0z0qJJAQAgAUWmDB%%<|QihbGX38n)Q*kYRJ+TcPqGr zj)yScQeg9#$AE;VDN!`vN&@!Ua9W9*M7P?95l0om9P=-s``0-TLr*y@8cIja7rOlD z(200%ybtZP|BgY`@u+rYG7dhf#uBE2w^E40!(lqWn8x9T6iX_n+eYuMjAp*+6@lx| z&G_Iy1!@^IKr9YeQFiP^So`WPb2>W#*|-ks@M8uTf0<2&dCVflRM?5l?j-+xX(Y47 z{&5-lSk!b;u0-8 zHIJ8G^BgNYpWzxYGpvXW$1X`HGB2(adftrZx^L;@Hs5eAH#7+rKln#a2t9&-b&|Np zYP8Ug*a1mPoz(QFfTBGN{v-JDEP-xS$o`gPOm zBL)Q4WeRy5c7U!?F-CFJMX{In@xY>2cvNsuo!k2W%Xi3Q$J}-JyF3lQ3FpO(_r_dB zp&l>iF%9SboQ)qscVm&Y2ZV3Zg^&Gv++O+)SJ60}V25{xhPqc7idP#(sS*t}j;_!dRt^nJ-^b0pCzqqvjyp~PaT z6rD4AI~s2-#to=I?_>_*!Q>%)rW?;szx|LOFJwpmX6(jqr`Mun*b}^?*UoFddjxye znA2t#1x(iVfU~2ofiT901C0~l>#0H*8r;T8Dt?Bz)@NY-wiqs)|3=nqGop94?#0AA zeK^T#Ia%DQPBNc9q6NhXv@Ftx^RY-qqs^T}VX)1*X-YBuHNTsh>i(hc>^;#hBO7Cn zMdL{Ar|4JMjg18gysS|urp#&K#ovtNMG6Lj62g3l z;?llv#+fr2ob&c4)hM)w$`XC(nGnyiar>Z0m~%%YI>S!6YLc_^ypWarO7$O`(?RWM zE@griwJz(Yw~NwfQOPV~Kq+#6$n!u&#_;Z%$CgAXXD_T~#1NFxpLfyY6OlQyOXEHfX&8E>|rT^2Jjrd9Aj=*|-6B&#{;$2KHo z?0RnbFAthIdp0deab*o#n@9$y-dMz}Ag}3_``fraIT=!Ohas!^ zMkccSrKsHLK90%WL1S<0QEBymFwPWO%zlQ5hKOvL}LtLDvC-;%EC(OIWJz{~g~+O%nXM0L2}oOw)rb^*ckeT}#Nl?BgW!zlG%2eQh#PZv%IJXA!AzIRRGh z>cO&Y3#8V~fY$2+@bt<($W|7E(k*$A7Sjj|CMv<;w@Z+%a1Lr(^+4<0eiD2C3^IB> z*dVbE3(j<7yK5tQ6;44eP8A;uKC}<(g7ND2dpP{(Bh;L-2xsb~;=zjlaOJr``Z2PZ z>6Y9^vWnH{ZtGg>*0L*fy>dLw)O<)lst_8E^+u(iYGI*gIA#Ggm6@c|ES z;P29>us>4?_kHojKbvZ?^3f2Uohydnq6^5J`bzUMwo``y3z|07n)^@35zV7MaE5VT zX#9-=I(ca>^{9MKPR`X}uGAd_d5g26=Tn!G8K!k4Zig#b*Cz+dWh2SeXOkjcA z?Qrhl9*A}Fh3oc1@Do&6^@V3y)fpq<+RE9uF7E&a%8bN;?Y;a&>kfYLl=b*T+mi5& zKbcGeOHw)DM+4tKWptmZp{K)rklm9lDx185EZXQyn-?6eu3#FNJg*(}NRtJj+i5=tpYxe}mMs0up{Sj9979ppPFOAnKCdX9p-rwjePG(6%w6H7E7^AYfik5Kf)``(VY`122(JbM*w zw=05UUD*`-j3`?^0ykE0oZRN6bVQ{uCw|qKiXx{GkwF@XRh&cm99S582+6q#fiUKg zz+vm^CK6(AK;`;vWR_e&C3AUlvQJy+>oE|a(FiMq9+9}MJaJK2N-iIrP0#uJR!rfHq}vNX&))cAyj5IBH-%yQ=!> z*)M2VZi5dQ6O>HSK=k^9k$H^}v?mAp|H{ziPaAREia#9o=+hpnH(cgH9Xjp!IMRIL z3HNQzFD^OkE43*+hPori&>(riK^id#YBhGGvh!Z`qe&9fY4~1I*uV@hYw<@yyD)Br zDdt_egKm-(FEkFviyu|+&6l^lm3%Drj`)QZ9YUM$vkRGX!&~Ss2&-Ppy|njlAbp^- z9#8u#VQ{Yn2Dn~k#;07OSE|P`iG+1O|1S=N~1Lq&3N#uoI6uTC~87r$`QrG}q>4@je z7C+&&zDySw4$8c3mpXUwML!;y^oN()`It9-W6qnMGT>)dFt|75F``ldW|<%5#|3Ss za&a6n4)$up%^C4H^Td? zNAXsP(|BEI2PW|xFki0nqW9_iYR60b{({}SclsdS8Bj&LjkOT27!7mf6+qe`9A-on zL-e?RU_Ry($j+*UTV?0Kaa9wU$NNFGd>g5FR|?us9Ee%EEWP6^PTTbCh<3I=2@1Q( zXsvogo*BLc!)>P_C;r&v>P=2+kuEYNbmaySduaWv~J3P z)2Z7S^SM;_P>{vb5y6&$g1V3lP8Vch(O5HvUsk|jT|^6%8)eRL%Fu+FS{?H4#%6$=Od zIgtA@vP4^BAAJ?$N%@02xleJ)HQB*J>A$(M69U;8bLa;$;o)3MOg=?QVK zr{gY#Y6uc?btic>2;5)?F@ue8XG0EWn7fpG+iom;UrvTVF@c|B9t7Ha7Ua07a+elI z(%RZs(PQsCqSe{U{k&UBTrL=MN>1l!y61GND!!6@Uz7!k8!I55T?nVHlnB>kAxy1w z!7(8bD8DrY&!sITx|6<9S?gK6SmH2xUTOj+Cf35_5Mdwdk;_1Z1L;_jg+DVa=mMEA z*t^US8t=71zQO=FCjl`)VM!<4-3;Nzo!W<$Lj%3cwpvt z7m7hcIO{+FZ?#sQAC(%!D}Ss2v#;`C(_=uA`c{%(ahG75+TH5#_HW?gzZE700_gAl zkF31i2vN6|py=mWCj93)(l~)5^4#mHgJBKeuIvJGAI4Q{Mx`(-v$oMmC%!Qga)ljU z#xZm;E#!K_MYP=6fMg`uRy%g@fgORDp};k=$1q>nu=6lbS6T<&MfafR=Sr3d zV1xxHN810DfN@nV%#?lvNsHt`V|xhkuuJ8P^>|{wE{zWJFA`x+80}y84MnrUaBgxB zF5dW!Dk-)?O4L4Zx3PjIS`K5bzXJ>F1Q@@0Io&v#!))g&kb3M#Be!3{OT$OviyT?> ztF(iCu@X2r=Nm3H903OmoLHUg%TR+o=%{{=WHqOf*nCAO-g}5FxMYb})sw*_cP#f* zaQB$uOo*-B0!N%ik>{6&!Dj9iXl&Rb61zaTJmoA@Sa}F{`+uT0yOnAA^A#Y$tra>c z%gA&4Tg0&H12t_`SIcz8{C6MpJUB_BE%N zeu7?E+y|-K_hFvkS{(DxhQ8S2Byjcvcv%QU&s%HxDT)G*Ak&wXo-vwKUt=(?^A6to zo``RQ)NoXbFB9XWL8WimAykRm$6Y)Gp39j87*I}JgEx2 zTC&NNEup0N*BB}@^9d8^`j>t?uY$h9-*+F=i}Tkl6Xx6Jm?mj?l7Dj^SNUoZ9xBFH_!}8AJPHV^yD{p^1R)GByo-) zD{!h_TplFl`#MqX&>C#qwHi;I{DTX-K2kO*i`Y6WBNaQu$%EGyaEHn%n9?#FrA|zs z<@vW@=Gh0NE3=pUS33kZ94c6L+G2rM`+(gv@Cdt7teA24Yj(*HD8|U+t$TYdJGW7tGYCw zABlg|JOod45bm`YjtP63XlS|#oncYLxmCrH=yU&(%oFdxE#eoTyE^V_#A^R1ja#+IJJykfbMsyY0|JBF#8=3!MiO8DawJ`j^FH>_P1_uoLxCx z5z6>0aO&nds6KAaUEO33B~zzD+t)R8^4KwGF7cS*^OMosHv`9#Q>ZX30o^`pVZ<}Q zQ5pxUdq(Hex8EA*aq&#%SBVd2KW98Sqv<93@z;zN?8rw)Uv)g!RgG(G714LW9%?v2 zAA{<5VeHsee7zzYEeto)kWZ!b_aQG_WwQZF#y6t6*kSs2=UlLG z22)e(uxk7TGSO!xU9hK|X}W8H5o+6PHi-(j0ecHndD?-|#~bm;rQ!79^Ca$!&jz}4 z?=>3S)z7FqA0|nE{t?p@zbYT|?bK|#1mpPrINjx$iuSktP_({7=(!!n*bm11FwcwF zwb~4OhO6OzrAf5jD+=rC8}ai6X`H&Qi}#hkD7egh@pJOJ>Kw-~@_ETL{Pn2@v+MJC zr46UhEG-60T;|cdkO6v8Rh6zk^b8!`r_!Xeqv@<4qo`DtH3_scr@s0EujBU+S+h5p zk<=`p)-93XKJhM7WR^r$D;F}UpT?2ZZR%**?u|uPmSO0U6zo^A$IHq|n83cm51N6v zO>+r-JCulpX(5;^(=GUjukjN$p5xUrBlzK-I=F0l8VUmh%*;3AZ8HSFuaG&C{W?f> z2GcOcL59p)R!Kr-M#A_#xnzxODP6O$5T!$AlVx9ovz_HX8b7$3sRPzb9cHgshJ+p_Q8w$b{D{KiS#jkfz(J>?gZ+A$6{)i%PuCAT1QKx!8Rh)MnELLcV7EBn>Mbja&FLK9vq!?3kk+#kJ5Fx55?uD#M1d~qc(qiX?eL2o8|*el%B z(gT^I1c<&`$SyZk0bXxdB z_P-a`CWMDk_c5n&cX^*38N5e1ki*TwZ)dM3CSD53c73Npu7@YBv5O+%9uNqxQ@b2IV^$Y-yo zuks3Lz~0fs#IBu0_t?;DXRmT2#&;1r%e||A^ z8;bjhQb8zL*jEK>%vFd|)P36jRfowK_nl0AG>52*RiN}G9~{&>h8;K3dA2u>H`4Ut zXW#SWm53Z}=oG<-7hhmb!(ICBDliQbx04MLfutsc_*J#4=M(Yn^{liG!Udw@bT@}Wf-hG4Hhx0HatP))A29Uok z+aP{s6C@kCz^K;_q6+0h%+NG1QhmmZ#>i&Tl^=6xWxFhOH{VV)mh7lqQFj~;c)sh<}xiI&6vvBTNL8=a^LHhUKm-GE`jp17bF7nP(=~=~dY^B)Q@d#0ls0Q`ufH+k7Dx z>AaJ)R*9kGz5j4h);(^Uff;RVaG=_TlSEl#ifCq2rOo>&ePZ^}hg{w>n)KAHqY1Wx z2m913*xr*&rd~b>lQ(ZAJ*&fEkraW+nJUaB!)orO^;*2xmRdd7JA!u2EJlr>HeTss z7}ibY(c`ftj+wldTeG|eJt_}kP4qT$pB6H$k1xR0f*EkbCJM%l*hw1YoIoa49+bM2 zss^IM>BT=9%(9zzP+fPP;9U91J)Jnn_(a&!f>pWI?^k#;KQ%s)V&iTST=Py;_V0>t z=08L<B)Yhs z?97QEFAVN8{bOS2jSDFv$KAm;ogyWAx9br}_OT*|gEkO;^Lx6+=_n`_Nr3C5La=t1 zhS__1$+?$G#DB&nnE7}NsIL0T{akv3L>?XwF}lsr=~>8n-gtv^F3G_b>t7^fyf#!@ zoQJxTLRZVWl(o9dLXzW0C@-2x;)bh0=(s10_np_=h(m6~^wV4F_iTusixn86jwk8G zCxUaNtd112bE31i7b@Dc*OK6*8*_gYPQ+)jgbQ7-N}B83rypJBSc zIVyj;gbH;FsJp-dwU`}69j^;J`Zd;M%-4K!JaihlxJHc>E;jvt9^{^bJdIKeqLa)f z5w&BNIWNU+)kE)kxqu)3LXT)GJssE01aD3NsTWfq2&-XTaYA*v@Vl`}@}%Rf9Pa+w zgL8K-f$f>ua7$eb0?f)u^@hc0|67u~b!IV)pP0;S3HC!5{X#l$IfrDwWrZ&9d-7QK z9F1vNkKqgIFhg8N=yJK!>S6OZjPqok%#0^AW+m0!sKGt<&7(oQA~WE4n`yZGgplzq zv~d0&(lIukTwHXO+>M+7=Khn&+cy$$`rb!a#txDne^-!4ar!iN>K(L}-$_iod(p%A zJ+?kl1E##6?(nX`xidU*tK=ZmoTR9`;V^ieHUr1~r{RHJJxrZtK`IVzM%ZA9?g=L` zgOi|f3hJT)v0v2x(+%$Bq6c(qivy13WQ4tSGuJ=<1)~xu#r&6_!S%#668SC!O+S2v^ZL=%y2Zbst`+lJJkwumOq5SY)Z^XRMR^-RszNlbcZ3q7ahEV{B&fwPUX1@jakCnUEL zLPsHIkW)r1$TsG?jx9G3H-w7%jTD95e`roJetz|a*Y*kE*Zt>(nTID}Nah5AfA|8q z3!`Cm#80rWUbzxi${_%RKlI?y{hnC`qnI9wG1V%){k5uFM0{ z3(5b65y>^FkaDC1{z|KZ@8%v{l;BGrDUOC|(aWgatcPUnqa1Q(Q+Bn-N;~@5U52Ym zTt&Rra3n*lnOs+^C0kuakpmNsFh-8o$>im&G*53AJrVv5>$DzWUSk+u(Nf2a@k!|Z zb`|!Ii{e!(kK)JWBXC!UFK<1~96R@DVPVQLXn&i@Y8iyVgZE01Fl#Pm&JuiNtuYXD zS&V6!+fQZc~sUcz(6$8lwpG98;8MIQZ$CKKEYNd1=&G{5#c%0_IX4ZIw=_22?| zZ~U3`zVzjGW}7nNN=2fEhx@2mx)oZ9eCgVGp%`o+%%4m$@xdNxT4g#0cRc=s6*fP3 zDVr@c(|IP@_+Kf$wAf9%!>_}%A}QAV;8`FUgJjz6@l5ESI576Q!OaoQ5`~F@bbHTS z`k_xmUp|V*z?p4=J9{P3&~O8z)N?T9bUKMX+CYn!25~QDd()a%1LUG*De13kVLnFb z5PQ|nG|-Bn3d~=$88(qBZ5Ls9@L$~jRPRhW9;ZV1?WtOo%>$ zf9x|bg_ES=C$zZzgL>StAR}_gY6-pj!_{7X#dYXZJTPD!%8!?;`N)cS8E->#eE*Q-CrDargqWn|L)L>&mWQhR;@vUO)d~3RY)$KF9x-c zJZPNw0V3K8aQo?N{D}4OylTgGe8wKa>49(fF|vNx`c9s=AJLE5Wn)osOAn@M%3?7O~5VpQLk-AL;O!H@SI8m=Wb*#(9CKqCnAGa-?WM%`k3E z!(%SF^jNjn&(gEgU(gwjvuWn;&FJvR zkbZNzg<&&wg@kb#>Kc5srbiOMe#B`~)sYMnu0MfC4?ePbU9zlFg9hsft&0THqrK2I1l`qHC=Q=1pRf?F^uAq`PpEGA` zK8t2}S8@(I69{kOO!6*mM|m05+m z|0OZES$k5cQb9yXN64?cLooMz2ZTTIfQ}+P`1kHJm<9E*ZUaT&qxJ&>_D;lzYp%S? z%R$W7wcuwfJ>w_)%J3`JKjO9H>c)!!?2PTufm63tG|GNPYI3FrQu0$@pz%^v@?hT=?k+juxC0Tfc0jzwA_KhkGs^ z+|;Y|e+epC2{XuL+Z zB{$Kq9rncW;Rd<_Zg5vFenbO-sXo;#n5Nu4O>=WU&=1w~(LC4^70)bW=534yV~f)u zzVR{fN!Z5i{x%wt@85s`wO)d82JrZ|y70{MWGDRH2?<_X@tUm}Cf*I?#|s@FWm!$U z>TQTmC);A`@iv^kL5sFME~ZyU?_;(Ow$cjQal|Ihzq;XWxai{%YZy^7g*ccA%mb#L zbY&>nZ23?`#@!rEul{PF{<{9O^LjK*{~tr=9f;K%hG9Fq$gT(#LQxtx?{ktO4WT^| zNkdvl6Gdi4RFa+1kP*Un-se<8kxHZ#E$W9-DnTN)y@|jeA zk>DX8EkP=Nza_U7I$*nJJsZu+4ofM%yxQ31V+^Fnga+(hten)@>K z1%VKHdK3Atb1cQoD6~)*MSc8skfm2{;n6h*IZ@4BI;`M1Fe}dEmhQQ5WVE}e<3Kx8 z>c51h>C2J1ogQehUtm{izQ9DYK~$>wf|0WtX`;xQ7OhC)VoaCPdZk~?5XVs!Zzn1r zxQ7&asgl<-YlZF#(5=M{+}8R&=2oXIqc~zVS=X?Hc3BQ_ceY=J)$jL^J!ZmhSHTF% zw@8syCMNWC?o#6Cb_OgXV`<{|6w=Zy0XbqPsPv~jcr3@!HsqZKtMK$TNJV@nG5f|) zpK!tbE!T{B8-1~^XEAP_mxN(fQ&C4%!}jTf%k=1R6Om}`S#r(Wm>zWwWvZR>$o|9+ zT-5GKgjjv1dPVMJ_lLDy;dxgkkK4z6s;Ob#i8m6%jKAm*+J*A#^y%r-1sM5Q z3p*8jaK?Q@^owc4`3rV)KXZ?gQ(LuZR9X^U>RZS>mQbZWmt+}Nh5MxXO*~Ec_L*^3 zttFbxqiyr76=}kg8RYD*Y_emGB}sl2Poq8rGO<105ZqM)C4ZbD<5@D;nn%&Ew!P%e z>_o6U-USN23UFPRHOFUvVyKljJy(B>?3L{$hCzVGH(1e$g*mkJLIFzuOO$GCyGQgK3y0(~Z;5AHcQd@tjradDN&rK{DgpNGYR8uA3x_eugY0 zWtP3fOGcjgrLvl2+?zuqRSy}d~PONdA%=NeNq zE}5iX52CVn-`fs5{0SyxD2XB*?-Cr?22<85P??m!9HHvW&crbg5U)(~ugjtAF~OOA z)CTlzgzwmMJi07(rU3@})G7QQuy>{C$76%!m0k+jUoZl;M(iZFyeddso&@RF%_rrB z*9rZh4O3(rZCzRe$^QSGMZdC|K>B4SGcZ(3Fr)%Uj{eL2v^F6ZjtCuQ>q)dx;s%Xf zkW7c2wF0OTF>Y72h=s=-rsQZ9J?b5d>P{6xj%5>$>v)9Ba&LZCy#v|t~A2j(Y@exBLp@bT?j$pKVj#g zcc5-pMn)-{QVmxss*Kq~7M3jG=BZlS9(gXsJ=tha)=T~1CQMhR+zE54Cmlv2uGDhB zmphYQe_eV!^gNDTHi!}9-7$JqCLYNK$gV!z@&ZjP3%}1zs@gC2#(Q)o+unzM; zF~u?5{H<;D{<{a@xYmM|Xo-eO?-s~yIw^47vmm|qF%(umg9);(F#p|Ya`Dz1QoMN; z_#F&mqTf?;B{PeZ@Bc;;(tT*l8c&ip?FgaeTj?zCR$3L3K$KaQsT2D|+D8S52F3|4 zbb-evqq`TY8b0H7|8}eh^x~!dFm&O5V01lESyG3DL&L4D;qh^6nrKidRMd`~fQTn~xe zjU?)~h)kPVNA=*7t-_i=Tvcz1;EWSdruaY3R%1SD?MN2gSbmp2Fh@&BbN#vfcXMrSTF6uDX9mbTb;5a4!d*tvWn8wT0^`M& zak0{ExaWdDs#M=XmHU4Mzi%{rbo&SG8i&Cr@EIct`V6IqYgo-~#q5N)4Robm=1LTGl|GZ98YX+w3wI=O^qQBJYy| zfid1W@i&eA`;?1Z+|J~z_zo_e2k5}}hs6BXFkJt&3+3j%5bhH^@#?X9Oq3AV{T_?K zKWsVJ-BzaOjtg$wC(0zCM;z-sG~s9DMP~b@K)9gzg`M@qfSq^yDqW{hh8ALdbjz(L z^zzZE++}qiVSe(a7r(9p>lK1`)UO>T+53`WF-zi>dz*|XpKbfps+@TIdPGlaJ25^p zsyHmK;x0iE*ocRckI&1PHHVaNM1ur|D;~yC4iz+MV+*>tci?l&!+7fOd^qNQ3l7Q0 zlTxWZEb)-W{qM~%rEnv;^DPFnMw_u(C*xSh7Ad$oFq3?BTxMIj@d%0d+YS=PchVyt zC)hswGlm3wQ30t7OTl7OB{?FmNwk;>I?2Y2`4Q*9T-MvmeJE3A`$r@wEsqxfycX_WY*5ruCrtxb~Z=ay=N?FFRF*im`#vdu@!XVmB8Us7U|0OrYCnVCbxI|M>U@t6Z!1V z+^0P$WMbhNZs~+<>f!m5lq+1|wln{6Tg)cV1AC3=nb$qMmU;)Ta`Pv=@0`hAxiO!W zk-x{gj49)!l3of#zo>uAe zqH2}hw!V4Y)OwB@r{$tVW{&m&1+yUt6}pKAYsP`;m${&K+8U00D~H7SCqYU-i)2@A zC*5_P%$+W;4fNLsb>jq_2h$*=caTb~~B&Y!ejvZ-XCgU*N%rN|=q#pfh_8 zblgb+kC0^0Rk}kp?lp1Gj*bV_IdMULV*p6C!s4Aai{k_w9TCV zml!!G(Aj=dk%{5SDF1GzXy$Ayl^#3vxAlI(f9{ zI?;As!F*#T3M|TQkh$eRw)i+R$wy>J!@NbHV;o1;C>t=ZpDq`rlzycVKV`^r!4+e6 z?kOpaw1c_(Qov5gd>=9H1Yg&+0J5v0Xz2l1b>b2nzVAjKjVYm}y@Jcp&<_vE#PY*h z9WiOz3oP%vOA{V0As^3+fGP6l_8rKEn?D;M|7$Gzvr4@3rCgL(Sx;S6-qYToDs<0v zLmB0DC{fVKRUaB8+3(7z(LN&@JKDkKYtueL2PDWODSr~RB!vtAXF|QBP7s;hHemF$ z2$-J;af4@}<%B0x3f<7%B_p6L{v>!cucD@=-kjO>3KU$57!y&8EaUcOt6oxm;PO2{qK%Zo6ob6-hd<5Jm~PjKCjPU}-`QWY2pK zFK+yVe`>R!bzC9*RQCheWWn_xYG>%df0(#`G2YuE!8@Y^{jg>NmW>O?m|bP4cJ&3g zW`@Dw=%Xwc2SRfd1H(tlp`=tVo@?>Kc?MDFzrhPzmT$#b8S|Kz@_l5S?l1a=xlQ{f z*As)l*P@xW?p*fNZW^{CiUd4MB{D`4pGuipp#iEG$V zUF+Gok_TAr$(w{{TCL$ zhU;o1Trr;$pWDSXCjX_y4QfOqei@0nXbKAbRUmbHHXT+KM~2>NkXGgp$tib$14d&= zZr3)Vv||qSnKYj3vdbiANA=-ysm-=a*hI8p_3?BLkEsspA?2ebYb$k&CEX?PC1EY= zC`_&C{-dEdYA;B(eWqWs6mfjBI9lAiK`Y!2lNSatG)n3>NvJ3%DYXJWZNe!Mdb@~g z$X7rKDBsk3Hd1pL`&R`^31`_dV1;dpbXCY&lwfSpYjO7?Xm{!&r&pY-sYl4$Vz^VA-f5 z91#!D{3BB^s7n(op5>sCWfi^CyNEQsK8E_EWJq?a42WsoV+KOgNzUmbi``V` z%^L4v(^D%l&en~jwD+Tj;&G-y+0XVwrZMRq9SM3dZ^-E7o+R|mb+YoI5%aYtg52)+ zCCes9k=R!QD09*WrJO=hL;N^y)=T8R+NqFRa{cHO;YF~u0@R*Vg4i-g*!T4YUfAq| z0e&Jj%kbtU z)b{Y(OXu;o$wbG9TN$dCdk=1qpTAT&= zYnj8a_FW*GVMUd5nm|)2oIH2f#XO&IitZR#O~evRN!IKba=`Eai5mD!a}thIY*8aJ zi+0l6D!SjxsNoXny}U|z4L|BbJBy8%RhiUgQ0{UaK`_8wiX8%~dNznE*`LuBQ!L}sGknu~cLO-7|(r%{(S zlc{4*(3rvpWJgIQZOr*c`o9m*l-r-++2<+jhJ`w8P*?%nP@BM-B}u`%v~^g%_9XTV zH1p0cgspU#;Pu#eoHwp;1=l+huux$Rrj-99mt+FLU&#(!X2wBi)q2=@_X~udu@-tE z$3S9!KS`VSmAnaVqpN2N-Gf<)^dh*?<4wP5y}lK3z9HOUt*EE&F>W*{_bRE87|(Qc zS=;*8g@J^SD^j1hAJNSk+!v_Bxxuf%Ems77%^FtowkFLlUCOJD*2GS}ln>jc%a7dn z7#;4+N2R@!Smj4^QPtO+eqQHKzVGmYtib)?k$eUYC2fSLBO}S7*XNjDw{odvfbe_z zc!boO@YEr46lZzh0^Qy55~R|$leNE8X>jsORR497t5PJ)GWi&q`{^VxcpPC<{@wuE zU!)RJ6-AfHltHGb2+~4(Sgr2A?6`07kScebz9>;9=p{wdWo|;tnZIZd*94O@2f2#r3&Pb@T?hDQATa2c9O`gf4D=vfz0Kn zkC@keecYqtdgQutGClrffZo&}!nGPrHsfQ8$jsD_!o9~kS{vR)272tEd1yF{7&{df zw-1sXQBrt(%16++o(=!5vn1R5HW<%6i8@h%w$$;S`anZ1#`9zryIJjkdzHiIph8ATuk`@d8Xk9qaCtI9$j16;kcYE zw?0oVmq^m{#}jCN^9MAVRm&9?45DsUG#+soiF4G4sQIBCFu8d-s#_%BxVC8F+z>>m zrWSK^^#+W1wwU)0dB2SOR%B z4MoXaUdT9%BipivqqwFFl4tgqI87GUySkD{qki8SkNFGYfB9qX zlljXEN&Ng+Z(8fG3i_&oOLf_PIHP(SGO{P)L^Xk@=&g_E+&t-|TtAF@6C(J{+i2U7 za*};#IEX0-v+tM3obPLndo6XF=tLNS`07T&!b2`(>S-Zcl}-D0Z$Y(!S{$ozjy}XD zG+eSAv+M-d!y7g1|FI3*qc+mPU#|tn@m*fB-550*C9qS)gOBch!^u18sosnl|>3E|gX zAtzqhlcoGW5`ovqk6*`0*PTvKnISYmFVnn;BK|l82pnIolo_tL@2;oumQ##1S>T-m|iWUm+nU zowrDi=2ZpuTkRe-Fppo0?@yHRw$*QV=UOSOyDTu_DwFZp-dh~Iqls81#nV)MdBKOC zPkIVBk+n*-0M_P%6w=QP06)UEK-C7;c&8)PNpUb6CO@fcg+!;9As=T&a# z@rrNCc=f0W7_;6Rb8}X4vl2T%%Hu3iwth)|U0g(7UXTW@hBoH-4@K~O`@nX^3rli+ z@?_HDy@|7IWGVkZS?Fv3p`A;wqUFpd_`-TW?y#DSXKWR*r1%={elkc^;>z*qlohOo&L>* zo!&vaHwlc`P(r zj5p}VK4TiRde}KT2iW6byI6a#ZJ=#x!|Ms2oF6%>xqvol?)gtyFwpqPisvB&xCsoj zv(G^;P7N~iWF2v|DGirJTN&Ni&14wO&bf~0z5*pfH`X8Jv0Y8C`BzrOxQkDX{m zsr73(wMVDXMe!xp7!y2Dw;qq@PQeHNYLHG1qV;{Iuu|(Tk&8csE-Q|5DidD;d|$)L ztbPecHyXgF*@9bskI=oXGN7OM>8z&zSyqNoW_QeA!&(@|gXc~=kY1Dn=@Rc?-A!dy za-j4Wcz5uytt06YiA3QsSd1jI$*(1A_xDS=mmuK?m7j~=Yr}hAS zyS@~og-leN44}=UAGGMn6->Plf>+)NzVOt!^q-zF4R74VRT;@K$xn~7vpu%~-JJ~k z?oMZ|AEv|8M-r@Nq#^6PT@KD(y9JTwQrNi6LlCW-2kEo2Kvt@X)xPBk=UFqzn7sic z18ZpCf}Y5 zEs!GWho!hR$=ZVN`v)u|RMqeS z$sJZrp0w04LRA(<>2(mgZaTTQm2g>4Rx<}*{zp?B9q3L)M|$W_2O4zj7V=*!xQZLs zZI|yUfkjuth__M<(H6U)4~2X7S)BewYsTnc@mZ{g1Lr?{7e&p*>{36?pI-oCy04QMTFe?Z|gXZ@xNjc$eZLSugibc=xbu)7evJpp3W%Hin69x$C1Om1oYWmFeLlDYTKayOmou%hf3M%`bHZ6PLD zyW=WPJZv%a$4!3h<%0ry;|G~x*2%rva)*eloEX=o#k^X{X=cz!1lbAJq*maK?HsQM z`Z03gRNV@_>HopK54og^mm}QV6~t%%Cq^k!jcmWMmZ?c7A`Tzj>6G@dU~oCt#^vm9 zq2s3kCTb7BCp-}5PWFZsw>L4DeKP3<$tH3{oFcIVbIg94K?(Z(T2cmklAsI>u4Sa zi+Y#9@OlwwWP6Z=YD2K}UeDZrplvhk@f0Gt&5hU<9Hc>!mr3Frmbo4f%6SNxyuHVy zVM<*-1bmQ!`LAus1J6su=+zlqvqspFN7thCS}UA6Y5*fAl_4+W@OAF(=NDUb@+$_F z@rw3L;3dU~v6w#U2=AD|Jz|*j=`xp8%HhV220T3b8=a!0LJP7FG6O>TSodcTIn;HR zB&MzqJ+f^Bx5L9o%yca}S@srLTNgsI`?Hvjx26jB{GUkv9%1e+5twt)CXo9oT;Sl3 zg>_F{VT=7Ka?NE69q+1#u39VTo48e?&?m=n-TdL06m$v?_+R8rge(KE5Ft2*1jL{-y99JFY_bCcKd+I4U6K%g5w_U#oy)2H#AS2`mb zZ%HH`^pPhLi*4^2HPMcDVz^YRmySCIFiZU~Y_{D<>K#+aPC9VX`-H#M(p&ph2v2f7yN1> z4KviH)n|nMRfjQcUUCfsioMYPfFZ8DQH}gcBqW`W*p=YWB@*`}J{`kj> zH|;^6`5nBZRvqSrY$qFbwXiyhIjrQt3PS^|u6kLdN3cHYJSva1c+wC`aqpe*{iW60ZE(j3-uZ z1NFK)a3fj-w;%q2&`J^WZ$lDu>E17pyRZwBSJa69dhK9pg8zblbv8(CQ=?sr8HT^r zjrzw_(1g84X!d7eS6z67e6O8AOB9!&2c&SD&Hi%Aon_qFQ$aW{)B#IeqiN=BS@ipsBKUDt zV7!(OOl5sRe91FXe5HwmF1Up^vNgz=XIgkr!HiyP_=}-(9KHSdHI589f`-{=a5%={ z2|HuBX48Q&2KR0j)m}+FF!B za}1x7&kNS!1r-y#=w^d+6Jr^ds^hTSLWRCQG9GK*sNl=GeoSoez}&D}vQgn5_}DP4 z{=Ftv)@ULtf71}Ij#I+ASK25cvc(x1_PFbH3MPIK7*adFp?Stt)D|4#U4JK|xQ!Tk zJxStPd6~9c{Fv`SHy`~%iqmRg z+HzSqP$;l>Pramx-b&op;<+^c(Oa71FZ40Y7GdscJKQ2U7Wa;Y1OM@aXVy6lBOY9kr+wVswpH-)tkA740 zmu)Cky^`J-zny-wdn9;f8)?DhEA*R!DtBqoFI?!Y4i2|1L8iK$o^t<+>waE?*6|%s zc2EjupZNx&zB8 zcHKu%;>P}MmAr+Z4E>LD-s zx9ubM;7~X5W&()v_SK?B!C`hBF9n zs3=uIqrdnN51hcfUn!0g_ecwz?5~{Zo!MmCE<%jQJjSW^$~bPQnH;-zhJ&c*s=uzq6TrYYqK4YCAozGf3NVENOv(E7jV0jr@ak zbhW_`Txvg=;)GSG|KBSzp`{l!gpBB^!j#HHH(jRk+G|W40PyV&<-+nd!^+YTfDaZB z_s*yAF8u~6?%xi(>>Occ%5L7OLXlV8Y>IOuqwv#If4ZX7kW*M#gco$OL}g2!lLEmJ z;yTlYdPT0H8UkBbKe~h__Yc}$t38OCl?PDUvxu?$_MKT?c7qOOp9GVNKcsc6CHW!u z8;`n$qLD%p=kmCN@snhT!TLX_e4!paH^y`S?z+M3)O(4H-)K(8lCU3Dg*&>yP^q7>3D6~@9~Z+& zqbZ`&P79Kn{J^&M++w=ULR;`HZlJS`a=B)+O2I$*9b%p|l4h54G@-GY{HG&JFK5-@ zDd$w&WZ#GVHw*Z&P9ONmX-d3mnuF~`hf^5%HI+5=+rg%&y=RwN9Dyg_en3sA%m7l*-@LFIHFZN^scFd_m>$|hC>18cgAE`Rt;<`Rs}&ubbM%+YlWrefJ7mi(4^Fk}&?eWU14VMlNuF9{Fan0u&eg5y^j5Al;L! zn6K|LNojXA^V4lViqG*A9dnxmvF=;oVvHfFQrro7JsFU;i-F+Ey?Cm4G)h>_l7OyyC4gP0d z$(v0xxB!Iz<)g2wtElC|&0tXgWL&-)hFn14x4HXukbY?lR7`2z`8C;_ITXzuq(VIwfkI;p{=tBoJ=>sE}!v4GUpbU8JUnx+8=3ebqw=Bshf(oL=cm*h2W4q9eOv| zz;lI}(0TnSYqhC>o%LuToL;>T8U@Bsdw&vp?A9bUQzC~wnYe_#uWidNF6*L6P9?nV zMQPrx|31IVuZVX|`HAZkpVJdxC!zZB@fa@m6r}Tt>9*e*+y>>hsj2`G#&FCF_QmNGA?n9h(Jritd2t zGi_FOZ5BH|*9^X|o5so>y$21v7(3O$o883@uvdSZv7O?FA>+Xz(;6xAxiWp2vl_DjP(-{l@H#D|%G+BSW3J$gzk+;91VDfVfSVR@T z_>3kv%exVC!l}ND1eytPp0UKk>GTELj1$qS*0jvaE=a!m#O-!Ou`0M z+_soiO&esF^eMA8Win7bQiF9D_#$g-mcX}|R_@1EOZ+uzh<9$D!MmGQ^XHfB;Zq#t zFk;7c9A2JAQx7ZQnEnE8tos+Z`8?D1XV4;&DrXHjr74g$c#imnPKCXVyC6r{!*z@U;Pj~^zx9QX9XHI-Q-?^iRj*scrcAkfWz7QVRmFS`Mhu%NH<+3 z;^mFp`)gB3OnLw_V7!Ec7u@9DSeB8Zi^0sqQ{zZW?J6*OED6&R&e+~Ma*nu8swLmU zJ!tFxJYMx#Fivn9ft?wKyoydNrn$J%Csn6VoY{}*moLy;6Jp7K6MsSd410(h)&jTe zt?B&3FKO*lYn*y&7Axh{A@m-m&~n>5TrOh_i^UaS*gRKqkz6K!W`&ZTELU|psFU*b^O^l{51l<+2Xgt;ofB(CUY5Ruo_G2C1 zWK1RR+vbQxPT9DC83of0f9L#XlmiJqByhSHf_C{Sa;v)((r;wgF74O~?m;qSWPTWB zf7Q@M%oSF)&K(jSuEp^lWwcRc8f+f3o&1#8MVi)VfI}NcL@U2@9kax!QO8Ex3!_$W zug`Z8;&z(OY}+IHFUK3$Z^kg^dj%S-euLt6YjHr%8t?4*MB{vmd6^Bbc%_bZev!g# zUiZ)d9r%(+@`Y!E9@;=#)Jih2^s{JRYaZBJ?XrEF6ND#}ylr};Z0NFf!2{u-1H<$0 z!+Dce+?fP3lolz&sJAa5dfjf=Usz1|ZQIC)Z8NwowdRWJ>(9_akD;pg7t?G9FD>Mx zKhDDl&vdGL$AxSQcu9bxsavfxJ;+buAbC~bO9YpuNaFz_sLzUnN`fSrWwDx0Y z^YkH$b!S{EFnGOA?8fj#Qn)P3fEy{d1ts?u^Uiav@XsSDjOob6kT)Ok*M%j# z0N`g%hy6V3b=&vbu-6|5%P*eJOavf=lV}Gjpi4m?JKaeuqwbbvQ}a z4Rqa<={Rw22#M?FNsVJUXuh}%j!AcMW~DK94cw#8r(2@^&qQk9^A_z*Jn+Z3X7UIg zK-mo)uDMGeWK9l3>;nTx8MBL(nk&JI{pp7{?~-8IjCD9WK8rXzcEFz(LG0`kmC&;D z4J@!<19oA4AaTix#D8hxKBa7;ulK6cE}22@wq6arb95Gc)F)17w4I@%T~Xxd)$=f% zNYiHjIxu|G4Hvohq$}ec*>l#Ds0*CE)Bqjw^+z*g{~L)bmky&nra={#?QT%B>Tj@A zIh#z5-U$iCMPPcT3O?ISV?&(;etOwcxY56nJp1Dgl}6Ff{??f_4R;ihJ)2=cY?!cz ztB0u%|JeTUK1a*0ET^I8FQI0v6KXJNOqjSo>aAXgDn|FY*MFATntWVG{7gsVN$DQQ z$~1un3&}Od(!&Br%V_O$S6NAQSkI z8$22Ymx5WwRq+Ab9gKvhi(j%bbu7FpuZHtE!n@(rdGaVt1c&}!6?%{}INmEqbW-3% zM0?iQMvVv*oZ!YJ*Hnc*cx^`G)_8&Do+)5dtwOX*Yq8}0XxevkG%n~eA(>$gB+P3n zjjsI0=!EToS<9-ChL~c?eugzT4gvq8a4t}Wjw%|Kd~6Y9bvM)uA@ckCpedU2VH{3a@2VoC6U@eTzC6^;Rn8A!6B? z!RQ$Qlf{I}h?7`p~qhKGfZGh5CldQp0n4+?Br{Nb}lQoVhE4^!2YKU1N&L3x~Jl z`IJB~_rFWp#`VImyk)SY(}%Qm2%guLc5?RT1g7EI4CZ&`Kl1PMGID2+F{njOhD(3{ zkV}@`4Ci--m}lB^LbeUVHoV3yxifLm=Lr}Z#0$=$Oz!^tx#+2!h>vG9;jg+4yt#tF zl{;JxY6-hwF#a?wR#QVKF`?h8d6SMbH3s!ZJ}|NC9eA(FAb%EAKxWrdU~X-J@Lj9O ztqZ4_`)`Gv|L@5p=vSBR$G~LUii)Htb zNf#)0v+WvbXsNfoV0)IT@cAdMMDMD~6RFMd1-zn-v0GN4W~&xX)*H&n@w7)jXd7zA~hW~`NE zB`faO1Ks%};CZPtt2A>8eCR#_VPkHPkMlYq_j@GC2)+Zuq7HIu^4sVei_J7L^D(pH z$po_V+k7JSc{h7ngdq1JgWVES#_ARxhRG9mpdg^7gLgdyKB*M>H$sl|hQ{LvLC384 zLyNTOO<`shC6dC%UUH%&g1b4`OSV%Nlo^dUpK@mvAz*0T)qv$f{hFu zEt&|M{{AE}qXiFjW)Y(u-A#YjnlsglT}bSykIW*uy^wik0}0st8^V*az`R(OX*fAm zqLs@4X|Jmmh(# z0+%%A=|~93>0{C^o})&+V@Xl)4u}_6R+-*5Fykd5jay8?VO}$wnEC^~D+=-6&09ES zQYLopSi?_wThA-Uy~5{NlW0}1DOi{YjPWO0K=KWk9LsIAxmT6;J)Ofn9x5eIgun05 z7!OdK>rJ{fwMZ-<3u>tX7u{|#d2lX>+^J{;?UZL=-g2E_!vi=fbmLO`2Z22t4!Zso zw z_VF<8$k-q-mU=-NhmJvI!#VimnGR_!2cX0t!}gPq)pL29MfPqR2UmWsC9)cg;2?DF ze;BthG1Aju#hf76%f!M-lUYo*d=N1H1tj^iFa2*-A6dGi4-B<~;aJioFx?yhns={4 zqQzLaNc9E2|>LBl@}z@f#G`mf*ne{_TzcHrSBQqjaH0=QFu5tq)#r z>=i})l0wzq1vuBO6sy+lLYt7Oyo)G^k6Rtf>s)QZhGB>CnOOoCn;=KpDqV@>S7}&( zFa($S3^CzLLaDyRAlR(>kDM`klcIrWr z!f3Ea$%o-1ZxZ(A74FaP?c}PpDTIrzz<4Qj^2kZ>WH(zvm0zbypYpjWJ$4~ z5*EDIrLDkBFWc9UiYqW~UOM|4rIU=FLc_iLc%ZIf#+8 zL*VXqG?fU9n*z`}v>B{Ezk~~+-^tHQrowzVomKX|YjbIT4e1COiEI0w@CM_Ce^sftHrd=JSO3XO2X3H76TEB>~_iZC-@hYS>^%Cbct_&5vWuxh{yD0y#m<&8m zfhG389Wj^9VBlqjL2H2iX2Q$Ob zkWpB`PTcs49U~LOGBP!g`0q1DD!1U3tGPI0t`FKMb)oi7XR_b@0gb%A08b~CP=4WN z?tJ=xDouk0?w5F3q{vi9e2@H(S;juX; zFuOGxPPc96N~dfBW;v9FxAMn6VHRdtYww}$cKhG6ZY0)SF6((;c^ z`6wNlI$1b-3~*QHEI@f-M`^sq8v#e|st1Q2S&B&8wr|o;fDyh3dxpD>M4b5ZaN3Vrn zSxM};;1H>#to<_Ki6z(8J^nJPpj=Gz}-RE91>3gkE;$j3nX_AdQ z$T6Hhb`y^Od>79yY{ic_2HzbL$NP{0k-qYjD13vy$rH%pCPP^HVWjYxe!Qi<0AJ6| zWkTc@i!?*MnI_|%^zBJy5c}N5Qtxnh`{)>}Hrf#`Exp2e{J6-<>^6nFcMG6F&I{PE zWn|OdsdVq49L|sYhEwCtp!>can*L{#P1U=V*D{ORa1)zso zp)9n}oJS4n`WdD1dA7ep(&_U)1G+WvAXVE}%l++pKWxWF(`ELP!!473Y0UNhy`0B~mFF?X>tw{m$R+ zALpLyp8LMf`~7}Cdw;FQO{;g}j;oerkJ&n+QhS0)yz7Ye0VCabVn?X0p2)1030Q2YH(Yd!11{U67O^fT`ezK66R2T#CVGm$!uLaE6f1Or(UnJ-E zr_pOatZR}6%$SFt-D$?;8dTPpIHlq6VH&TqiRhf^1EVE1qDB=DZnWteQOvs_T2ixx z-m~H9?Cw&q^IFCjRRq&#pYM^fa9s>}aua3@-^*)T@53>#%ps$02|ndTcysG0#}^u5 zTyjq;EbR0kb4O}W*OLvxoO~u^HC~5FZxf)HOor;qw_sYOd$pL~4tnyIE=>8j4=n0) zgv?Sak#?KFc+TO_Oe&E%(yqr9uGv5$6;^@pDv+Hf^lgXFAQq15q+)CwP5yZrlvj)g zyQe0M!~RuxV&Y`-X~#5@wstR^^9?=#eH!)DX{O{Zt&kKOXKPV zPg)a$_oOmuO5FS!+YRTr?#>M;tM(uD)w~EBH@$_!HU2PQ{Tn9Nt%WZhm85prEfPEE zOgF8jL=m@g24jBE+j&96{MY@OHuu#v$>M*nTHKW)duFmQd_xr+t(7KER_&rs#*Lw_ z(f2?;Lyv@93xxCLJrKI0iB%h2z|0Wl;FoUO6TUGL^DcZQjeg0Hr+5fX%-9HdropJS z^CUO%&I}xDH=GWYO<__!{a{RIH~c&~8O}Ue1-;Bac>b*+guGe6JyDaI8q0r|HdLC#!m8T2@HeiC z%)aylF0GYhr?vF5h6n_J~TxW=4!^rS~DN%DFF zBNrvWsXSRs_xnc!-A2Q#O_O0QnFHT?w?Ndc%g{7miR3KKg4MQ?(0pVu>UEHoUv4ZGw|kxRj9V4Lp;wvmrPG;aL`wYkn6XxatWr%)% zmZW>lB5#V;3mK;)s2nHcXJ@Qq?l?PB4KGVLv!5ejMxVG_nvcNkM+C9@?q^Eg2xBcXE^7ij0y5vZwsLR2h&RV2L>lcQT{?zsKLA@biT8o4{1;P~R|XV0IZ#6&G}UFSqiMc5I3)YMz}F?k(7deVQm z_)06i_vr+d4z%*R`%BRDd<3twYzZcO>B1GmHMooa&QQ^oD3HSS# z8=FZJLoUT7%6PE_Y551ZlMToIDQ7(C3X(>r(PkWhDV=FaO;bYj?Y(tF&P8j$F>4CwS@luP<>r)=?yI?WN66->wUG;@4B0DEA!ins;jW$U@tlV%@o$ZQibg-O zr$BhF4pu__F-1Id{3PB`Yr%P|F5qmM$Z%%J5rJ#qKlG>m-s6wjy@ z;Nu=ET;HmN^P6r8**O*3xArR0VUwPd!c-eb0bv4+h}+t-oM<>L08>eh-El zjz*=84Osl8mY038fH%=F$FUxMyo5{zj@w{N!sn-P{)!4pjr;sX-ocRYG;~7^Yd=hJovYUyw2Rp#kR_T`L~0h=Q^im^gKVZ zOk0YM`T2sp9J873x%ikH^hu-@9ux4?=Q(8416k6k>jti_>6BV4kp%V+&1d3KQCtco z<;UQ_mUQIwtwC$kc6xT3kjE96(@(PF==y_AXml@!UND@CiXV<*afundJz^1-8tz4{ zbpsgE_l0(B-%BkQ=M&dW)fDnoncZ6iAC|;c&OWbQ$hSWre%|-!XX8LRxIPyZ;2)^$ zP7`{!UflP>8K`J&f=;#7&8(bD<1ZN!Zq!And@@AR8oFs;!Z!4{-^QF? zyPCd!u^W%vufpVUYv^FbT{Nx};o_6R7-r3L#G*mmSD{aD4c)|Xiv<_Eq9xs$=|-DJ zOeL!|mNV@w?X+p_Dl#kOG#O|0oQP}_M9EcZpt9GG1YH{?Dw%FV8{G_u)OJ~+S6f6a zO&)bj}iBeh!PIPu*-CAru7 zf=D%f2UFLBBz68+QSTLh&gj(*n&PWVYK9%+(rkN(($bxbb)P3CZ(|)JA8o~951rAh z{5oovW#X_{Z(^w0O$RMn(5y`cizOdojp9PQH2WaFd*^`(t8ZiIYDF;nnu{yD?MVOY zDxzUm1)C)_a173*KmW+$#Gn~){PtgRxpM*}ZEX|W;BEB#C3pJxXdfPZ8ivCiw$SGn z=F^9LGic=OZc_1Z1eMl1Pkz(CJ=C1DdXc;ZPyHOaklle@bq?6 zTlSuYh%S*Eg;I2e+%*gjyueGG$>gOrYVz`z6OrziOPr;AlUa*N&` zu9+_BL|ZQ2#EfQhOfoXU_@!?6&ew#>_8p`?Nt(>@E3uFH_a4^!>&rNwQGi@Jqh*P7tFr|$S@N{0A6B%ofrjhO#h_W~SSvEcO^pXI)F2uEBu~O$)9Qu!mJG_4rqRpE z?qt$6fom|)ix$6_OK;pBMa(BVGP%Jsz(z(FrvLsVaPU=0-rip@QsBb%2C3nQ|CZA! z(o1n!>?y3AI-MESxsmFNJ;yQIHpAhx7}S~MO{2XCncuJp){otfQx?7C9otkEWC2r!ae72DbFK@)9%VP+YMUCw&&xj16d~+un=S z%s=&vlb`*7Nc}V-K0mL3)V)54p1*~x(GDV4u4Yl^svO8{kcT4w&#)mcpL}UMg&wJ6 zu%&k;uAAqH!(UpW=k!hFoRT-jh`V!xQ}%$Z;t#mAN*gC1{K_vn7lfU6Ut)VlD^lY) zym7r7e}3YH^Fljb`)>lTZ?yv}#{9%`{fW3JtslkqD&RugLmXvWNOIaq=H4OCntd>X zvrEk;TSmMg)_v8_^51I8irT@sMK~W`*~HyfL0TcS(8UKAP_ZAI zFle3@Eju8?^vX=d+kI~6BOZ*EX;(z;o>O?Kw{LKq!w_zD?8b~EF8oM`R(xR|h65uD z`O!9Kc*S7ghg-Pg{VQ`Q-(N%3mu?~#+qa$(uUure!0&bu z=YFhc1 z-`yDSB$gi?wI5IL{wR~?Kr+6YpzYHUSie@0*FJU&le1V9h(1^qAuvs(*W;d|96Y5r zlJ@{nfR>vG3=%LHoQ*mLy7N&ps2vpLYK+JEJ3H;yZoWk@w%zo=_=)X~w-)1_3XEUlX zw9<(;b=KxbpYg{xKMQ!J>x=pQuLgOo1MjhCX*91C;)#(r7SdzccC@k1g7*A4L!-48 zfVh(d+4J=r@%^EOmR;Lu{H=qu=tCAt999;39`c;rkvF*E*?K0XD4E*+h^vVe`f`^a zTA*9dL3C+2Mc*5>fsv^a@U<_QUx}6Et#%Eo({qRR`bE$W|E2Nzzf&@luz2m{r?0kbJX#nm?#Q0&;W6Rh1Wz4)WkS z@e6mvd>V#cP(_!e^7PYkBd+zi1Gh2PhK@V<70u-yaBoYha9-IY^i7P#B&Xl#aQGD3 zJqzMKymW#qvg@J6NgA5I*9gvBE$EqD1A3TECI+2_tUCuFW5ZmW5PF6Wx|_-$F!$wM zn-uxsi)=70$Q|3~U7?Te&7fy_8R~LmD^n@q3p8jvq|BdAeBLhPW^X=MQ~F^Rbr~Ch z*1FXgP1ETn&2+RAb<$Txo46dG0+eZY zz{?kRlG+Y+T8CX^{9}%(n3N2&x<)|Zyf#>BWyXxX^aR~sf8<_%_M$cYTX4$C0vc*^ zpT0})5wfQnnLZ0+bQn;lOS<;sj58bQ$CxpAq%o4-FWUxM3Y*C1jZKi-SPcnr-C+8A z9X(pt1AVb+V51oW_uVsCtApL_n9nC**xqK+VR#0tkzcpmb>?n2i|^6_&~1 zy?qH8*!u@EUGyP(gE%DKhyl506X~-qSq$M!iEly?_iN!}-1d49WiC;iD>!^ZMgP&_ z#2%EtC9Lg>g>2LDnN-!zpC(7i6Zw+U%W% z^VAS3Y%JNiyPcr^>;f>&_l9#ZTOfLN7Mwo5o1Hmn27J%B2kD7z9#ZKMe8{ zpOQ#-Y2y3ag`C@~!MwEHK~ts7Y4)to%q=p4yOVMX4Wri5PmuvAR^&^+9N0)YWgaqZ zPUpCaj!YVJwUI89+J^ZP3A!B(aPB<@OZuGK*c96vwr!l))TU zW{n;^DA8ek1CFz$4|lWP%|&d$Xcso+s5+}8CeM!geuEYF;#oB_IaaICl$6y3fZpF^ z5|NotFKj8Lt2`}4Zf_&G;HEkn{^v7Mw`gVxAK6mHfHm~+^>B1MnL@7 z+QNE58AF}?dHFsQ439VFH^t@f=Fr2RRMx{6UvqhtvcGutpe8GxIgLG~zZLrAZQ1dh z2b-5XmpvhSi#2%b&qBm2Hu{+jYv&cij*ygOJ%){BB|4tL16wJg^ehJymgLjA7%kde z=0o?@sFKJM4`No~Nh33MGxz3O)1-3|ln=?QIiYa@m0$lRQTNNJT3jhQPJh5fIwznt z7l6&nwQ*>&3GdHV^LigQV~=A27Cy1AK6{{wS(jo4xdMOS<$nS6oR05{SH)jhdqifRl{L1?euo9EREl4&NS!V7xfB0w?bcCJnG3p>=`-C3i$(a8!us7 zW)G&$@WoYcU*f1Sm+9llwyZ>97ibUJ(mxwT*!X=d_7-Q+k6sd_e77X}-<89n?PKwf zuh3Fp5{cZjWe_!01)NVzsp(pxi#k`^aNoy|7+-Q0m)JZcA@ih(*o4Wu4eOO!)o=V{XYZyg9du4>3Q7MZ4bf!*PJOm&oR|pZ~!Qu}8d)j}&T%4`719 zS-h1BxNUDKMlHU9Yqm$>xh;|Sx@;+0EAC~sC)ShH_vgqTg-P5)SsS|9d=b<9eGUEe z=>@8MHl}XJR?+6mADIHh0P<>&1P#4D9yBJ)LTphzFm7R_>-i3l+5DW99J)YWpI%LC zZdv1yl@nH4cVS)cDPG;PkC(bJieEyX^K8f|ytT}cm#d$``)DikQ@99zWaGx!Z*!gKtT(@FTmu$yNnRz) z(j$9VkLn8UBMj}z{l9d5q96Q6NEc~{T9{Gn$~{G?m=`PCmicy{7$OcK_t zPM!gn((qV#e(qt;2y@2Q?`j3EF$?m&u}q}7H(hR3hmuE5i9TL9!?gt}qEY)xro3`A zSl>2>9mBn0w|gSQR32fqBy1tZOAl>!N-fdb znGwy$Bwyz+hpm!q#tmHzqA_M`dCp)=Za_T3}2AGKzIJYts?&P z85Q1t=_Nklog2Swauu)jqMWz(YUdTRi?D8aEuK3WgfoX)NQv48l$eQ>x`%MZCAOkl zrXL_8>MOaVAq@)0e8}b7gEjw|C3DJGt7Atb%f( z_xl4Vh?+pgeOn8Pb?qR3VLQ~uw!)I#F>tay6!KHGxNpzj(%5UJ*a74C3CjAsvx5{L z)Ho6c*KOe^G;ijmAK%0R-EWvX=?~o+nnOFZyJ51M>h?%5+m3x^i zbSg9@IE9*gGIRPjGVJU!)U_6)_A;sTcD zs2FtBqrv>lZ%U*r1fS*%Olq7;9@i8>-r>YI&$VwKr0ro{H2PQ{{X>7ER{H_Ex-x~XsOVv;v-~+<`Q4C`tOmu-wGb4J zU^`_Y$Yj1`I=4K4`Hq8dGie>GvpJ8oYUm($0Z8+xcYx;NP~Y@`43}BU&a*oU)f%_K zL%ka=zNsajPn0@_+}{9ZJw0HhXD|ABCX3piRYZG@@o2k878C1t(bM8PsY}6pZpv3f zdd@nSR>kX1$xd@3Itu^c*vFDI?1urplBj_y^;2lnK^yA!d^8-}PzfpRugS}l?@(p> z7tV$=LS9UdwVi&LU8Ay*U0~(RPIQ{dT8HjpRS%9~m9Kn(i-`;?x3ZkIOVWj*(lXdK z!W+tSTR^))0p`A32+Xz$X2f|@?vcGRjZX+dpKMLEEm%KyYc{?sn_a86hF!e!FuO;gfej6iWF6d|L*4hWtVZ#C$XhWArhYvR zs?}u>C_6}+zZT$FTn~?DR96UPF0(124k$>minW5k>pw|48Jagxng6&mM zpJ2wWFD+nqE_=adevxEV2Sn`IJDXXd$HGcmTx3UOde%&mcZ8gz`$Fz13A|ryfY`$t zoTugv=APkLGE)8yir;nNTwI4R;y+*7H@zDd?MkG51G)6-gmhH!+d>!oRwIR?uf%6^ zGs(YoUf{AtahGPUL21+FWLLKlxgI_hp5IW0uFWVtO z$)8n58+iP{jW7YqtaI`T*4lf3D=pkek9n1n&yxcn%|#QcZz#f8oC7n$zR|aSzbO~c zfrjUL(0TJ>TA&h$QZC-~j=COAkK0FWs}fMUM~$xjs?LoaY@%z^7mD7jnm{Z&-9Tg< zP2-Vmsw6`h-$6>Q37M5HIAYZVv=lfGlXEuF zLS`6!>Q;>8kuN>}>Y}K*9X^mZgKRR6%{Pb*=rSOfY@NWkIW{t@=vS-k)150sf$ z!z%QJryk4U{Jnor_I(>GzA+76E2Mx+)-#%A-GU{(Q?SN$As%SbLU!gl+P8NCnk1g1 zf_{T`PG3az{(B{|9x$W}_#ZTM>~vbzV?+Cj_tS#0iBu)!C3P)`MZG;?s65Vso^IMt zSBOc0f#-2h*FMi&iu#3LpE+QM<0PCr@RkN$S&C;Z1ok1|OIZJ5` z`YQXAkdd7VpZqScqb$AHc=d1CE?v)0@2cfb|H$Ee1*f+8`W(z@JA_+5IMN=KYg8Nx zXiJ2Rg$dCMmAhZ;U>jcV`NbNh_z54usM%vxn1g8#@|0_Y?i~ zc>ytdqmM8uj^=Ae(0ko;nfOP-epTZx={)d*c4P(PxZvG1Mj{@hM|;Ak@?_Af>7&<& z&4*bP(J<$=Hj(>~jnkLAF;nVg;a<=;h!)odgAjM{6c|lWi)ItWZ?jNWs-B4pUrf0d zHG&=3o+fmEqcOGuFKXUIrr@$74OlKm9(36=92jsRLe6OKs8uvs@V9-fJVh6L7S7*3 zWg%-h3031qQS*Hcs zPABghH`l23&m<88rDWXf^F*O&H*FmCnLbl_Pjj*h(Ba5M9QQ!*FZ(#)%smO{_{Qy_xrhxzZ+;(_J5dHF%nLt%*OMn?;#|&11-mlCsCdK5R!e0v>J}V{dVIq@_shM z?sJ4%#bw;<){9_qT2<((QR4l=m|VN|uDW?B8KqPga_Q>6^zrNOWPFLh*ZEUWb0U5n zxe)h~)Z05S-lfY$Lni$+-t;Pt+V&fRluR&owcse}ipFCDcd^>;IQ9}7?D<%a4|hjl z$l@o&ROqSh%9bG+FT-$6%XIqu*Ay=DzD`Y!=4^;%`mxi!yz{AA@N$3l(M2hf-x zOWss^g348WXlBZwVEr@L9aK%5a@BGC;3QI5BFhM(zpO$%-Vz>vQ4mQM-x_8pBFWXd_ZTb zJIzb#z{UF|@wm%J&=xI)4eNAB&Zj8Iaa4l+75b2WYy*9|QOH_{l?go@2bkRl@b=Cv zR+jj&a`Sv(vvC(mfdHD=xP!>up3KCbI81jHjR3<7MqoKc78Gq_=ynfbuAkp9F*oWZ z)zT2S!eaYSPv-1ribuZxeYSDH(>h_SG>=?p?2@}!2Q%Hc9P^}cy>by zrmMXGNz1on{|X6uvoi=54Bt#T+t-kWJ5NA7tdQK%T*9mv2v3KVQnze{^@mP?dC*K!9(fcB?DoUi8!sSBq$~9B2-FtMhaDZR zqVU8sB>Kc(I=SE_X|1V)1wp1DS!zJk&dYIKTs-|%l}jI2%Fz^07uvDvGEUzoj^)#} zc(r47`2Bnumil?}iq(_Q{;xGEni|kNi#XgS?Z|za7>;o#7GR)%9UW*{PQ|`i!hZ^V ztX)(Jt9!j0F8%rmt^PaVXoDZEeUb*VoPI-^o)oD2g>!ZPMSybo3((orL?6ecaUuSJ zbhfaDyt1BW;#=3#$={{9>jm@a1ThJkv(kp9mJXrXOJyvPuEX1HeY}|bQhu^vueF({ z#xJLt{KUCFc)aW`UfUat<^LT<`5gyI>%ZSLKxQrdd~`Lp3VX zK{(?m7|+dm`y1|g_OkA;*MqR6gl*#*(P;l()KvUH4o=vD7H|N~RY&7(3q#snY)U^I z(?R9yHuPBB8Wite$LNPG#F&rAu|_7GAF+5h9uuv{CvTJSqZ-3cUUC-8>@4UPD^HZr zDipE?ZM@d2ahPN^6)SIffTyKCj954b=NHXns()*s&X70}-`@m%1}bc@StslDcPl$N zd$8^~u6dhvKhCmQTE*;+t?$5F$WBGS zZiA#T<6yPvMqDJPg?=SRaWR*MCjxV5aa1y9shU#-Q6$;BBc1am=V*qRH@8goC_S20 zNxx*yMHzE{;rCzQ$sO(_0|7oFe`8M&4}U^x4=d43i$)}Q-88YblJs>7bF!dNlr?kb zWu-)+blN zq+_>FU|OC=)U??i6V37tU|OSsn2Y6C$lTfYnCFcfIg=L`KzYdtU}t7RrM4=h<`_Wz zQYWg!=HTKdR+K+G8KU^tpe(Grw?8k0Ws|Sbxl2aioU}Zg9UyXS&P{{NN#nq|Pz44j zdb4KX2CU|mZ|v~6b`l=ohvKpKxw1@S>hj_|&Mx-Hi(1o&cSjl>m#PW+G8f1Um(#@i z&{HPpeg_S=-Nn@DH_*9zJ~DfPrqHopR*;ye%b>bh7o2V!V-*%7JTI6AVUu>k2D2Z; z?41;(9I1whLG4VG{51G=_%DRN3dCliPb44LOdh%ggB3W_ACXJ2wyJ|@wLB&Lv&J&Z z(tOD6S<7&t;x2S}`4hM0l;giGYcOf+Hk^G;hjx1WA?iu)jJ1^*eR_N~^C*;`veVm{ zPS|;hE-k)7UFKwv+Y&cPx?v!-UOFATtTsW#%0c*be^A#!;G$Aau8?LIngq|e{ z&@yE$&=s!mhGke;k38bJbPYYDY>4LDg?{)C8*E)VoOhHs!H?5Ahq?PcV*6}cUT#G_ z@4a?NnBltMQ|ndu?RPDf-TjFz(zmfd`wA&rBF63fbOlwWXi)o}AyVGRa-YvT2>qj6 zn((BAdTxqmVuJRQ+^1^fq3kPoan%d{S;@1Cccww@Z(mm4MT{j^LfH}Lwz8_5>fyh$ ziSW!dixuD61gQ%vVcec)pmX#-GksDIa(;67a9190A8?Y_dv%aky6VCQUa{em8|Lxq z%O!Zxx+lEu%~D>w$(`3?v+=jk?{|rF#gIx{l)t&Z=Aoq+$Bt%5v{w)*f6z$fht-jN zl39%6$V@V1yc#B{?||i)G%>Bk1&5M<@ai62JX6%hZzr{UaIXRHpZo!xCq)aLEuowF z^8{M%n2J|Kb1^-~1Xu34fbxtnx_-|h>*NJbT8Q8Tcr8hv^sHqnk42LeJ2Z&4?P8|S z$r&b%s%6JWDX=DKA?!}u7}iSb9BX_(oE_dbhgBS|#Ex3B1=g=r0RG2S=)DpEWs}bk z&t8D!GF!0N|Bv4D+KAFx68y-U2E0Q2Pb>(T&KoXYg+(3x^zJ@Ot{jKa+f{y`v^O3@ z!sK{~?IK($xG>taQ_w=;rtp5xaG$ia$kn<$vP4f^(*u(vP<^<+D+Y}YHy zSTm1|sklLxUw)7EWdgrEvR9bn*)thuZ(?57UuKDK5DpuD7wN@%E=KVz7_2VEMmrBK zXS_xP`}zwxL8+59WMI7{s zbWnqrI=PIWd}kclQt60pAD5GvnN8$xjV!IPOT_cVGGJM7A54rEVgB`7Xi~Dd=GCYKU4xEB1^hU5m^`PL{RR54#=yzBwnrU zEAB1Sq+?8SG33}GY`C_Ed++@mPgr@Nz3eJ5nD!b@I!q&ZgW)JXw2Iz5dy&R0u&*h# z=^*ps!mFDXZ6hUq|CnLTkv0GBaiqz)()fXs}okUqK*8l5)7HRl2-39*4d?mlTN z;z4R<8`_1P=N0s2as0;NuyyPcYZDri7l90ABJncxS>PP3|>|LEdDq>mIlPKbpBgAvOoL)a=A9RqqhN6 zi_hXHHNdrQg6B}7A8^zgf+vQk-kL%B-s3dNJ`$noy$I2{^VihyO_|o z34Q>hRY4-78s_@Gpg$6`(Zk^vt=622qInmXH5q>7#F4|~lX{Yjoj(eHeEi|t@Yzs&)QfB1oB^vIUV%yC z(IB6FnTmNk5RHFJ$n_B|p#MNbb{j;JnKy+TOTs-ex9&et+O{5;u*+X$HuWhTq~UmM zmEa7h3r90npO<&d;0nsrWz^BS6ecO@TJ?@JC z4WEOG(;xB{$GmVvRUSFp{1#WcKLf>sh48XjOqkCav8q|wK%*)l~C0NHXcKL zTXE2JFE;;+!N~TN*xCD-A1m$#VQG~RR06F2l~Cdp<%)k>%4nh)2jypz$@h|KX5tzK zjvrb?F4n)KwT%(*B1jt^+O30EgID3o_%~2|NpNj`nn3K6d|-#-OvXH`8)iN`z>PFI zKw`(mke8p%5Pe5y8roe?n|F;txhh2(t)@fxacNwyLnxZK_3{Rne9`*wS}fkEh8Z_z zVa+}ZUjF-5-u!nGYTdkzQ%9_VrZ+h?+uJALHI#}BC6T-4; zh)%tZ=n!5c)81SmQX_|O%+44pU6{hW6Y}<^#tvvVavg>nRbp)5FwC^uidhS4@$l4Q zBJm**6z-kJm=FHs_Y!YtNEyWC?M>7nQiEq&mh;lVhXkkn1F)a2%s8&6P!VqghYuUG zs-a=9NGukdL*2-|&R$SW`38o2?TC)o2S`eu48vPCgUJ_t&@V^r&80T2-K%<@Vq~_OCqMqprvFf$3if*7Tt(&ma zbr?SUP=u>)9K+?mZtw<~xAFD+E7YL$7MkgTB=$Tls}6 zSzaz`S};gXoF79@W@wYoK@VZL;My%X^n;Oa7S?}9cGGG@U-IvQCE30CJNdNIgjz|D zC6TwZA>XS}aQjUrzg`IJdyz2&Zq$US^EZ=!W@qsE9XFgB`hw_htYx%a_cJZUDO}s@ zlQ?rhBMq{T#EpsC^sqxdE;@gNY<_%g`6@iM1nXc*}8nuvxG9rTCxe{|HB1Kev_ zF-!{x#wx3i^!2ITc*rCPLmlU0@d6v%)HIjA(JP1KHTDplH~_)DD`8qi6Ul`02k6Yqov4vh&b_@SOBIETRp6{= zWUQ#4+*l+*jtp0<>5)|y`p-*fo|6=@i#MklS?=g6C-mee=P9d;RJajmb)q969rT!IJMzOnQ-|@!v|5E_HeUWiYwtwThN6Wu*`*c5A8hAt57g0Am2jF~-a-~v ze&sHz>;+A=PU@zT$0abIf z#odcCY2Sfgcv#XHLtf@^e;z1sg=bmZY&8jG=(!9rm_*e`fzwQIKd=KLZplHxrV^;!XAP}NXGwWc9q}>gp}#*p z$FdqTI=9vw3NxEgBWZxdinp?cBQCR3Y&SvGn`Bz(@D<xue+YRwem#ELLPqcdB zOGuI$M@~QXpwm;7q00l&?9)3^T=5#Mw7z3H*+I-AwYlRnPSS?K1NbMc77y*#qlFQ9 zLU;c*dPz#7-MNeCouf@d|9eLNDimRg3PG_qtpcao9+#9;VL!ee#t3Y!;oGIjk6Erv zZ*V@`lTC#_cP-ZPgB+ZhT?iGI%b|g+hi?zRv2rgmp?r1%eESSb`E>rQ}Xj}{yZ)MXM-oZI0M4|m?!LD$WG zI3p|UbE3XO>R3mS&Qw30xl@4LeqxgDWcnNP(j8eScg^ zgI6hFV(Bq#TG+^s+**Zf$9W8EFvhQ2;?TfP6K(E)p|vta*n3imc1~K)+m9O2 z>dsW^9Fs;TeO-v5`(yFRUBSDhY6VI2PQcuzAza>ihrY1A%1zsp%Xv+Cg}YjAz>L@P zV6>|;T80Y#hK>=WVAu-$vU4^~A0_m44^0r%-_|fndWJk+Hadwbp0%OZvsMhDIT&HP9<$f7s1Z6Absroc=4~fv=fjO?ke$dx zhRD%^c*mNfU5{vkWHx60EfK}N%fKho-qL$J-;p`s*yEHdci@uhFceql zB}+Yc@J{OImKm4QxF-YXYU3`44&vjCXZ?r z9rcW-v*x~oqI1hwRv5}S#LBWhh8I|=D-+0_tyOel`#cn-o+c5u{&Lqd(xJ}tGE_!} zGq*CWxvh4SF?DPJH7n{NJ98X}=Y&c0m97fyQjDkN4%)cX$A~s6htSgjPNe^iB3P+A z(YMM2#Q)+h?weX6E%EDu=x8mpZvTQsslhaKXDrOJ+D6xzSz>+iJi%EhbR31A*Fd`? zH!Ex!>k(SVP7DoX&3m0;;K?v}b1)y$tDXXCEE9N!gq8Hzz$(EZIA5RwW~LKqlkXp# z?mkR3Uif3_^dBU2dL$~myhZN^O%WXnFQRqrMJV=8f~vdSqs}v`A!mj+hu`*cmxNQT zn=`yfN@_9rd(a3sWq-se8hbJERXcIWI)+M4>AdCUAH0@fJg*mYgdg)T8jo(NgR=k1 z!ED+D7_?Z;&c1n%wXm^-W6JI@kUSp(lZ%~(OyF`y%$53M>EyC}n zFid$8mfC;Dtn*qlf7EE^*@o*F+UL$s9G;B7L|^!MGp_Pm-}LjdZ{*R-7uM2QtB8>J)mBzX=;Y^DS2KmD(jn>RZgNv^IT7nBr}2+()8w$3#P`B^ux@bz zxtu6UA70=-{dV}Dq4V&|sSV?JDYT~~(J)H!ritp@*U_M1gpkNeW=8fX+R@&nC@o1* zAsWxQuM^5DqKv2z8Og|MiQe-UJfF|=ob#M>U)S&V{i31KXWTU6E$zNvglDg5W5W3; zl2qeL+1z>hZaBLO&%|mWQR-iig;}gYg}(!#?L#Div_lp{PH&j z_`#0F;8RQs<({9kDdcbQIag_`3kk2)$rKA96kp4!4kW62ok*_iEB2I^WOJq zsGKF;ePIn5xcCliM%*TIw+<17xNzFvDoa0(Y(S%tE*N)o1g=-?#I#z&s)98MXtnbN zZZWb!v28baN&QydvEV5$KD(8dTfJX!7wGcC4tU@`PfOl%mI^YpUT=9Ft2CUXCN1o4rD%G~7kV^mG*+$@T!*JzQLAAU%@lkOrzagH%WR&L+qMXq zap5p=`zR=VT?FMY2+s@0vEtnUuxR{EShiy^q{wW5XR1QJ{C+j;JUtxJR))a>m05xV zJC3_6yOt{oiljr9`Iu#U2Mf;jpi81Q7S-RtG_*jeoe#LBvrY+oOf$N)JRkSIm&eCL zA=r{{LGO$2C(pevf%5xadiw8s%=Pp{$^VS->4;Hihlb=t`DEta8Bems_qgCI(ua_t zXt-hW4@6!(JI1Vso&L*={qIO8yskVXIK8I=zgrygR8`3fH&4!)8O5aSH>BT(kE5@4 zzre+f7U(1wfE)U>u&5(bnA2w=_NZ%82+h+g|Hh+1&+$V zTj-q7eJu8ZkhWkA~U} z81SzVYODd4GUKhWHf48UU#!`n-ozzL#smZVE!V0HqwBRN1R&Wb(0jcQI0vB7m0u2~i?Dpd1=bNZiHLy1Y?X7_@IjX6t7 z@2sLDRgwuGWs4J&qVVyg&luVK8b!w$ye6ZFCKV}g==(xCzG|G+@8RlL_CZl_%@{C; z6f#JIkoB9maSV;BSwghFt+!rnIEh;mDtw1!i!duK1m!1HTkW25bG z>;w<&{;q@h$Lx7Ek9J%+Tp!KceVID}(E< zhMRLp;1G)uje6d!JTnuH@COEv9-p^0dr; z045}EfivopA;?${p6#B;ra5Xk+I-Q6al-R3I>>t2#xzja7s@>|-$-}= z*oF%8<3S})W9xebd_+P?kUKy z_y&n{+8{(Zn;(AT8UFcphMxNMiMMri5qy{TQT1sw)448}1l8%l){RM|tzrR*m^p>+ zm^TbV_qX9ar$;E7kx$#sy+EtL5H!_1Ky#~#iC1q3bzI?0{|*bG`IlGY?1L88e+?(X zf`>mLct8Q_Ry~4<^W$Js<8x9ZIHEQgvh=!WIjKDHhddQ#|6Z+9I^B?W)L~-4ur{lCo-MmnZbkppe?WqD%7@vnWtf0@ zRkJZJYctjB&p`dPZq(eviRL8(ng)$yYBn#%VXy1yt>LQZ{Z0g9N>_o{+xKuT`!~Fd zISePgmocH~JE*V8e!3zdh>R|27j<+@Bn7Sh7*?Lnl$Z;%fVKZ|6NNKh>uiGgrvAd& zp&yMGJK?2*Br;)Q1Jn&)MH1yr$f=pR5Ia7B9X9eKsT`WhWk*gIoJW_?;7=yZ+FD6Y z-}%NZeqYMGUoi(|zg5y_b3b!;-se+0(Q0ln`xgy*wP8}PkZ*hQ2$(aj;Gw^iDxHg= z1snE>jw?N;AHFuymAQeC)MZ0{>y=_*Y%=-wUk#BOWrF4DUU+Sb7GCAMcniD#c+=lg zady#n=I5#@n64%b0pjhrw!@eU2`eTYa$n)x<(VXSw+Re4F@W;{4p6r02?WGR!l*An zFrxf1txIp>uILYCxBShas{e3#wN? z!3DUQyw7fAC1uY-RH7<9D7a1DjNFClmrF2GbCJL}IV|LFCi8ng=<~z2aM+|;jIp1- zV(Y;>+*P3$VOm-UIv?HXv^{p<*^mRDbs31sUBXWHdjx$xhS1ct5sWW{aG@VR(xdh& zv}MjyCSCj;%0HX`=^)Ot#Ws zB{^W$by%XGJynNm1L$V{a`A8%aYU4>xr(;Z)^P1K%kuw z8Q&7ReN{u?_E-yYaz=8}re|r>gtOqW{xD=T$uQ2lyI|?cBh=)s(BaFq=NvD6CIM$m z1TVThfq+W#H}W?k`O2^Qu^kJH+EC_RD2iHkpiXlWTs|oVyO$OdF(qLK(SDDJ`?*mu z{o63rQiaM;QIcP+@ezx9WO>a@FMj-|K#Z9Flbg}1iuub6QSa+TGP^|*_9?}Z0?}~MTt`XK z^T*FcJ&&dkgZ|%4kXj0JRBVU!8*2w*nWs(zgZi1~*=8tRn~e@9)XCc93=r)rg+9M= z5PHUmT$0zTDv_3f0uyEE^=fAP!{n?WehoM9+DVe6hXxQJQ#i6}S*-Kew@`{mki^N&c z?TueZV%c~iH;?1ed!%W=t~y%0`7xELkmb5xSSXxdKRUEK?R?Uf*+HIS9L6v3v&OR*7E*V)RHdiL0t zJFMMvNBDSx1tUdiQqo&U^W&{atcfYPzCe>PIg(BdivH21HZImxR}4|%{!c0{cmcN$ z=eh9asnmD*eO%EQfD8OOQG2NpPUJ@*fAJTt%$31$@fH}E{e<4%eUxecTLDtz9J$F` z4PZJa3nB9t!rd)nKssk1E{^+$%KTnh?zNTHj{Am_re%=)btWiP`~|ai3XVkOPW-9+ zi`VW+!UroVG3C1o4%;lw=!@J@;d>sA?3AMgDC|>fzH*X}my4LDQ?%0U7O@N8gU+`r z(aFae=WacMOP92u$F*q~F(7mkeRa@v$2S^x@*w>*s|F_iDT1Jyr{EuS1tO;HhvAmV zP<>Y!W#zSb`DrHjewZ6CANv3=?#saw)>o^Tz78!-aQ9}*KtFJ;ChjuiwdW)mS-=+WDy@<-oVOH_dAL#g=Lsr{_4xdru9NeaH z32(0diwCE;p}g`U(i-vwW#!f|Pp2)1yAtOiL-q}%L|msQeujg)*L~6@tp*P=n&Iww zUt0R)BVKs<6?u*KOvKgsc*6V*-B4Rhb;gB*{K!gj@YFUCGrk8Zb5dxj;a@xus)g=m ziR9?S+syf78?r*;|c|^yBMfn(5?4c4~fQvZ`M*X9SMQEWciAe7zgZE!J}t zzQWl({4CAmipcW{D{}YAPq27VKzrXX*zKy*odib_e&-dygsS z)KQO$o&ij8R}+diT}SQw7^X^VrS*vb3GRWhKJ%}37|Ih#q7%@_h2JdYxHD;xpBqS` zV-3+@eG|5fc#WHl1m|abI&Ve$VWhx}nw2RDulDU^=Z@xxNlh!w+@p(esV?v!eJ0F% z?}-|}W};ZlZR{wOVAR)6CsF(V(c4P9MXc{>a`Nvxl5n>gm+sGG)L|46cekQ3`g%Ab z<2ilsl0&IemfV6TI!u^s0-g9rg-HmyM^{+C0I6~#7(U9Fn5H=r=&!(#dVB8ifY8}L zwT=2g-AuIV{BCt{Vh*ksS>G8BgrfjG!eFfmxGBKBqU(_<;&u#bYa$ zFZJTB-R{y#ok6Pn@*``eFo#u{iNrpBD_nTJ4)i`1alHCx7%rO!iIT^-(3}|bdgp}4 z($15yuDOIuxq(S;k5Sq*lBzymk5SPcsOk-L#i;@A%c`SH$A`F%L}{}I9bvJHetVtHi~rE(r7ia1 zmrrAOr3=Zhh8+}KzI)JU^M1IbvkNZGdLz1$t|a&qSCMJfhP1EJ8=tKMTzIUEc*aNb zBl;BZ>y({nQ!@i|4sXNYHXn?NF{0_e5~wW^aDUgQk{tu4RP1k$C|ym4Myc5o#jdTS zzcZcFP?rN?97FEs_rmkC7x2pc5uD#N0_q#ZKx=auC{k;*2@2&Uq6hHAFH5jly^z(* zI}5hPNM4jkv$DtTlRx9P!M4hC+&B4p=$7sfIG_^PpSDEs)2e2o($)!N?7Huw^zLL(Y&cEgCe0_k_uW7- z{1U1C@r9ivco@dKy8u(GFF=9xXUL35r@tl&XQ})>$o=Fv5@Jds>fKpMb`Kba>H@20 z197LW(eAEOVB}U$MP)*cX2(`;tj2b<***v7OfttC4WYd3?_}(lGoD_&u80fY#gXcR z(o9WeDxG`w1(_Q>lUXCoL90LB$5Ao1qGf{}WYUFs^k~c}?ucR(oqt+})M!i-oj#_> zO64DdJK~8n+U^qBJWxjxcj)1PTN0>~v<;iYXJNMXMmRmQn^jp50-lFL;g0H8QntN@ z-nqFzV46+m9jn$cJqixwb-o8?`bh~hfXCPxxEF6M%OFpN0axvAV_hyV)m@EW(n`I} z^jf08kJ4GrH6%IE)Q-E{*~h6g!N`P+oAQ|188wC0ywk_DkV`m6$&^f4_5l107K2Os zN*Y~%0oWhmFr;n;uFg+zy{01uztqKy%3H8orwVQpX$aZ<0Is*@v7>ad;KwNgNXvVL zLl-+>e#1Jbb_`^yqjX4a-~}|fVu#+LH{isDO=R%>FXrhObC^FOh9sG;Bioy2% zd>9!09-_;gsko;B83pUfkF_u0t@18bymOP#OWXy&;;*w}RbSb|`){)HPFm#hh!!Xf z2xi^B>avpJGl8T^Lx);Dmk{}u9Wzpg9c?lmPM%c+wst>gs`Zjbr9~u7>@5|UZzqSI z&!=NQYct0lJ>|XyRxz)mol&LU6PGrHEb zJ`uzkUuR9e6g4lw~tgK=({O(d=x2_yw zH#_REI+0SWURDVF=&)sVtY1M&ye2CpFt;6kXmeMcS8`dW=a7p}(7J8RI_g!$(z(`= zD4UUpPR2UaC{vd)+t7d$x@(zA6MOO{+=$ET-Uim6u0hw%awxr=4Ox6Y>onDn9cE?< zbyMdvdvDml!YdlYcGM5n=xYFLHzkgBdL+Yc>*$B9XE&gvUy|KTELf+#wydlBE_QRl zcQ%5&V;5>|W?f4+vXkCrv%|xzA+WWciJ(A<8V)jgvCMG+-X)Nq|0H*lEMGL~mYq|}fr z*S)cSPa8jJ`zFu|+W_$nS|D0>i`5g0WaWG~*1Ks8%a-b~YWk+^I?v^7NbFKJF3XGE z_N<=`h;@e{rBb+?(n~@=^>bI#JwX53AL3$lhKql@f(%>kWZf3Fk?DV{k19DSGa8xs zBY%KN#d20_nLMj7WCL}-J6PQW9|%7obZ!r~vpU0oJ$-y7yM0duFL^izhYNhahRCUS zQd}L6RgOS~5vi=Q{Cd`X)=Ri7Du=@6RV=Bx$Ex<0u##V2vMN#ujB@G?co`B4s~cHb zf4LLqluae-VM*}cG7Avzf5M&3zD?CE)^YW}^XabeWHjoQe{UOmBmnHlMmT1#bBtlfQ|nm&w8XB0!80KOxUTwPwF)1XS^@tLz8#& zv*s>?0 zTHII)5@Ys~w%FrL=EFfUrSd8ha4(98AGb%{m5a#15pRgSFsscvWC2W%KU5V4L3#UQ zP{`{8N_N4?%xFEQ|rx4a>+yn0K?Lqpw&2;xA?57O)$8EX_i9*VwGUUn&gU)7mGOD+UFM?juDmKAX`t8m8#OXj09M}OD3ar+}Q$lyvv+%m74K2HBj8=pKwjRkw4 zrOga>zI#W+7hhzpuMqg+vjyVK$8e@LO1wwy8Ejs76FpZ9hr`EYD&KmhqSgGb^z)fJ z_~FEUeE+?USK2n2CYA2RWYs}T{O}6nOwM85;Re)KIz|-SkUScmN8V{7uku67Tc*G$Cq^R{EI!$uS$qr8Ot0(^I^ zgqJjUNjm0c5)b=QOpg6cGgiuTEmyCh?AJ^xWwnSi-f)eqz8uY+Iac+U7awy7_{-(f`PkNcwXuL?BH{>94m z_|vnYoj7s6B=$td@ZL+FVQotbz8vz#B^rluAl8I-{F}gw*F5LNT%PfYhduE1ujOdc zZHu?B@4%uLqv@PaCrGK%CsJ1yMb4i{5^0v%S{E+cY-RlWDDyFJ2icsXN&Lg6a2w^O z(`~H{v`#MyE4m)yMWYt%60WJqTTk%HLuT~n)fnFK&JDca5y$I~u;qQr9r0VpfBd?c z(|D(xE&Qt2(|PNxt-R*aFl?|c=O+}O<+baVVDqBW_8WLS|0^Clk(8PWfuG#4#$;lRW#}^OLs;!>XN`C_v z@zD@3HrL>V*{d;0Y(8e+{eg`~HSm7VGalqF@k-`l{G#2y{I%C^e1gvd{?@b)e0q*7 zKdPvg-`f9}KbSv>kNum$SB%l;z58$SR)*gEa_=~P%djB4_ppxGK?0uj{)cIaD|xY} zpD;}M9Ca%YJ~y$7iFC3ifA|>E?*5sI1&-o=rSIYXY+Xvmy<0*h&DRk7*3FQtQ;5@( zdg)NoH+ueO2KCvYj_rxFF!l2%wCVrItJ(>i#c{2COy_0Z+j)pT8{p1|UkT%9zi#E1 z&9UU?jh+ln5n#Z>>&-P2A zTYx_pD~FRskG=4Q;dvtF7mHy(zw)!R7x42`m+=;##PELjcbvMwn}+z7F)9i>g^o}o zPpa191F45t`rQ@pdxg==PnPJE+|C%`k2!wI#b~e@xG|Y&G>r@Cp@X;(pL>fi935q&0fL7uUw4tdQ0pdX zHkxFMdLTLuYa3RV zKjDoH=ixrdGvL-EkNSR-@$P?#h)GsBO=l;ljqSjd9cyvu#%E!7?SV2Ajz(9}#OE&b!hS^(CpAbTf4(CbnXgD_bQ79o1#z?WkCIWx zN1}1tH7?IH2c*GK%9BcUa=w)=oz9 z-w}8oCkCaKp|mD@KY1}t7NiFHA*M$M&(8M5$eez(P-W3a=&Qq|6mrGKi-zCur6o@_ zsO{Rhw7yav?ezm`Vu~(zUHKci>zhqWU-**$oFR;wZ{_9E945O1O1d0X%)w2zBMNS(ThLaE+IQw5<=I_QC;1 z&9;fe{5py{W>bls+8>m)jz_7_7pzmYUXm+CR`jy4!%_L_O$~-d)4Eh6a;{AX+C$!;IB-0YUxKB=4znS z5mjEU`V=bN8i`|8MS@q#NZ5OE3lu&Iv~Jp7K=uSl!{mU8=xDf$OzBxgZ`n8qGcjwy zXZ1pK`FS(3KC*}Y+C7^tTgMaaKW^lJCQl;wZ)DCd{Z1tX=3-v~A(`)Pfq9b{Ouj4+ zql3FqB3v5ft{uXNXdC?e?JCCaF=md|Y@~s93Zy)F0QLUWf%GU20yRjy6KB zq4~siQY_5+E>6$jl8vjG_@OYSIdLVO*5PjLed#M$A6iRBXkR3?!-Gk@LNO`4ahhbE z(?IpY1dPa+!nJSakU39#=%tm9co|hOUh<+78LXMa66r{IR%Zy~bp8XW8B4gbhj-vX z{sQ*DeqI?DBhZ zZL~H?klsY>=f*HMU2ieF*RCaHaVNR2#hPT#sqrL5U5zNS;bcm-hHyRK=kl&xrWck8 z9++tY4{pvBW=6A)DBof_NFQECR9e0gy6zmv_rKtdlVeo%_G{AU)=0HNC&K(48bc$&5>Lz;F;TDrqrb?BJ%*m#>uOjdHR-*XF1EAl2hO=0-jx>q) zfSgwpxIGky`JW%bnW04xp3`iVX(vf8HeRNolVb6+;dOYUB1^`Gj$xIuE7<=^uCNyO zE7|Q%8{naD2dlGmG%Myc>3G}6^Ujlhk>U~V|om7`>&(@g66#FU;0Ga^S{dy%tSLI_mNC5eJ(d|Ji{ zcp&ZoSER&X=5Jf;2g9dB>f>GXo#}2YF81L1R)<0QL{&&C>1E~Rcd=O+=UA8GU{+6X z$%$3JV>ND+2strlDqYo!cSj@odu&35_^&O8_H)aWK0mX}GDZ{;J~<>QpWZ`m&at5v2rQ91S5i@+hm2YYNvl zMbbE#6z=7T5cH=9Q1Qkt+JELN>RNT-&gZgN@$DCuTU20RsuG4}7GeC8Y%K0?!7(p_ zXz^eY^;i~0zt}FJ2h&$jA7&FuT7Mm?P|u-)9N$*XkEAe`GFB^K-(VW__3yW`?7d`_j$@$|T!cnnu=~ z;|!gSqxr9TIwSX?sNO3X$xk0{^szuxnOQ}oJ9ltsATP=_%%+#Gc%uCJ2#hmbj3Kjk z@FSl_^2Rb8+P#+&@{uv@hz1o14LkyMHOjF4UmoNRz91TAg}A=V5t@?+K|(Vhr1u`D zSp5mFYfQjiyCHOq{bsF)gk`GjNL|DxH`H>kB=ix!SdCV`jqh+nKMefC3(raIoDKLXtahvImy z|K>*!w>m^@L{VhKtMfwE^A0F48iV$twPf{%8VtGi5!0F9RH5P-2F{i!UWiNcKE;sAh1YsK|a74Hno6tw~$RJ?BKQc%%bICZ>|9 z(MM5swK9GF8o{FKBgoter$eVVfcvXpc4D*!y|11Nx^oIEgM?l8rxVkw`r3sYYil!j zJe^GX-|r#1p<4uJZyR>y4MUkk7jl#3Y1}$RgmN41%$YxMd5;MwY5gLVzcXOsF$wxc zv7XDY@u4ku+-dJ}o+Jz*b7EE((OFuHlFKugs4rVcc>Z;=D}E0o)Xsnztq-ZLiw~#! z`2)_&nL$M3zmPli9?#Cr2PdF@t=KoxW1Uyx3%0CR6Yfw*N0rqxA57C2)LrBF}MWcLidw-t~+p|+&#Q-MT9cd7S=xx zJ_eNs)qtwM*-^@otc1mNn5}bh6)8q6Qcdtuk{EAn1-zc@S*-dw zn>#n+F}`dG#?U>T4(Nj zbj-L!N1sXu=Sd4#*{0=i<5@1rUhs{CN=TBMzjn~z(B-sU-I>19norXcw{kZguBT?Y zNz{6N1rfkVOn&24WPWr~Z!-(h)_sP`*h*AZJ$i>>|7izlJ^uG{?lfcN}N%{RPmoFCS&=0Fa1(k3xenyTkc zV&z_w_=O*c;fHfncfm^QpfP$VYiEhm-d*A{Z3jhN1#@YR$|jmP^$4?~+J-DsRi)d% zPU8-|62}SsziG977)_H>rX6=aV6Xl#yrANO5<%H%8RC#|P+NNH{ zRG#ATg9Vs*e;r1hHo#W{8CcfYgTwCsq2_aYAY#)X>5YsBzk$u9M)N7OkUMy3@KYI&N{<6QYe|!OL&!(dEHcei=Wj{YBb_WL3Y$h3QK<GSm0 znoqQ0SwuITs$~XJgtNtyk#teE9PMfg!2iCT!|Zk)OxWa(9gkelB(W9a^XK4- zTXp;dy9!?H{BWGplEBsVyI@36Cu&q5#ut5^c++D#EFw=h|M0soIU%FnENuo9~;yPuVdTMdscsxp@vBDw5_Q*?s#eVXOij>E>g;?_B{ z>8SRpTztwyvfr<;6~WknN#?BaLPWpuwct)r+ zJo9pggbGi%W0naKm-ms!mn^8cj3?^8$|Tz-TT^+bhxGZ(Bj~bu2~HCF?aI@E#8qU{ zQ#R_{yLH`kan(us2F%gp#VkDa`z`i-YQm(SIW(zb6s?e#M8DaGFk0yo%DNmNGhOP5 zQhlJPtK}W9INAm81-A+Rv0bc;Z!Ie(ImjCL3S1snhNdiNB!LGjaE(HM^@<=4Szn6L z>$_>N$q!t+p2Z}=xwqJPBiep)Mbq%(xY941?olbDDZ=|=Z@L{#J>*B`8wt*q3oo#3i_=MV4FQ$&StSU`u_?;=}Fy}6U~&*5D8O&I??0?D~x zG#b60xop-1W9O`-FXoGpTPZ%^_UX64^|c_URAgCs!L{1eX$yOOyI`=_npMw80JY0C zT<-D_m@csx`=vkQgDt0o9o0&d`Or-gBJF6*a2?DZ`<2=*UP9l2B06Lp$CxV)I4$El zarvoF%aZfR7MYvm>RF_hx;dhACy|~%>PfHbpP_e(gr0h{JjQzMMa5ok>Sy_Zab5A9 z`UhX9s+C6}ATt0a{_}-Kvt)>_e#MIa6^HJ=lca4}2Mm4}3A}Gr)>6BXop&;twlAEB zh3meco3r<6vsj2}Y(un8C4K^r8P3F14Tt6w{``id~z8 z{P_S#gy=BA_Qm96u_ZI;cY}_YyNE8VS0?BDZE&LGby2%dH0|_gfU%KwsC#7*m)%ss?a{_*_x#D7&^w^NsSHA;=YV3%V^VQdL@#s)5sQ{YFj}e!oVn_m`0PT{Fp**F&WDtuu_8I-Tr1agOZHn@Y0cgGl?i8dCP=8E8DY z#+0vkf@(dV(O1$B#Hu!+)S@iRnLi9Cm}S$LgT4?SbszSfJCCPCJ(wdl4!`|gi#g_V zXz`s@%+FJ6Vcw=6%xNKenSA9t%yLd9`ZZ2u*^D4?Qg4GPU-XH5(`RO?>nJWwMvj!* zPM{Hf-{=CF2F~e44pIM{4ac3I!dio|BvX1d?0Td@Zuq!E*0CY-M?;vicy!{De;TNH zA%DL4ue@0daS-#w)KPXYXQd#D3f}rXW9yhI;&^Q4vn)@@gr* z)m*}lcBMkTBaqrWsl(sR!VL82N#x6U?svEb@k(qW*Jw1z?MxxDW}$Fe+kiYPuY`Sj zSc&FyeAY<9*)Jh!MN*`sL7r@M$^=ur1ae=Y z4Gwjr!R)4pfjImq=8|q!H#$A7|%~|fARx}yFVRY%7@XRemP#@*GufaTa4-1A{tXXo}T-j zNFKR4;l-*u*x7TGJa83wm*20UXuSr?UV2AtYj1!{B_S`x8$r@Gm^&}z(axUUK>od_ z#C6Vg>*Z66n7zX*sG90quHlDpA9WUza?=6s_M;i(Pq+b5Pw4}R1L3e;y9au6-ox(j zAh-$h*-^gT@Y^7p9hRYQw@uTZNQQZN%&9jzs(=( zPv166;5k`KtXcX3*Rpc3B&-w7=1GxO3tv*#EhGGgN|H~y7r}088fg(H}+Y3Kl&XjLgXw+`)uj5HzF+ z=kMjgoARqPLd}F={8=;CmMrBS~A)bG%%*W57(r`&;z%|f<$vHCd@vG z&sAG_3qunoh4tXPMtH;6S!UdnL#st^WmkZ-<9hPQ`4>pXX$n1}U-VVz8NqMbL2{SB z=c=b}$Q2e++)P!n--&MuPfUQkI7tqP!lw?gQI`U`Yo-zvJmy_Ea6 zM33v6wws>Uen```7c$;q;V3&0NmfZ^lM%I%81;n1J>E8S>>o>#>T>|j59~v`&_FmR zFpq!V(IgtD+sKie`LN2-jk!DW1M2jDfjyGx5FzQ$N{{$ORlJkQzd?az9_0cHD$iA2 zs%|C!DRhyY^0zoSj^_$zS9AH(2g&M(15{Pri771FN~;})Q{ORnxWXhAu0i$-@e9gE zv3;Ig^@u^5am#=@q|c!CHtuNld=)=tW*IuG?nBiZ2kBzNRm`6xO&YaoFMYO1N8lKT zf~LnU=F#`}tjf(N>{v@rI97}76m(;q-JY^zV!Bx6m)68Lt-ROwS(KA6w0UdkKwl$$n(QG z?D-@sO>Er8VNm~eQQ_o7ni^t=qE-4_b~wSHdOcjQPKC}IJ%bBe_1UVRGKW+>xkE}D zu9LOA@E>*9Qs8n5&-3A~bce%st}$wm`I{F7P7#Du&d7q2X@M}KeHJ9lxD9JhjDvWg z@9pw?A1vN)Lc49JqH<0y&fKwzQJ6Urw8kkgMibu98~zJmf?_t!Z+uPn>%`-QUFrBU zxQnCDO`J# z%~9KI2^zY3lD51s(yHi-hXbl1CTBN2v@eJn@488wT36saB`scAq7tXx--OpDzvV}5 z3d8CR*8F(83wSU8J33l+Q7KDD?yTWDGD)kNTyytgl=rR@jaBSmN{%X!`noQfb#*KG zp`$^^{Sf-&rwYl5)fi-D?<&tr3yh0TCDpvs z8=l`&;(%{uuAqx#CU<7v5i)GB5zKPdap4O~M72Pr!R&e&?1l3nF}(@o_^Hesg#xPR_>H3*EJ5a#CHbkROXBL*!#^U|N5<9?AG zxiMZ5%IlIr^_wx|eDVPmnVAr+@SSMqJ>?!fc}N$Ts*xnEr9{iI8>Du4kR~DLr=0W~ zHkd7jO~s0kecd0vaPwJ>p|xyi^E>vQSr)iYz011)-NZf_s%D?6X|o5)Vp*4}Cvf&a znf0sS+w@D-1^#eK2R~IIkJk;><24HEcp2Fs-sSiJx=)#g8IEE&`FjsN5YY}>3|)bL z{tX`6b;0RoE7+mcC3GP+ky5{nOkx94<0p$?;_fA+(X)U&aTpI8|DA*b=IQV-=PNYk zrm;pFEm+y~rEIvtdbVo$6!u`~LUw8QYIf5|2ljoaD%;V%g*`2`nmrbj!A^;p$ph2ogsKHK0#A0M{-A{;JJ4>baV4k zJg$`tVKEjoO=dLuRURc#&`PY&7}2ii<8bzLlaNbX#w!0?$0|8%Le<~~_>q?m-)J%< zzlZ}@uk+-&uMHRdY(EK3eM-`$moT?hjbSE79RNmsB#b^14Y!)_fu-wxFfpoQ&Tfn3 zo!p-D3r?-ZXN!7pQTb&0F<2GVz8bM+*8$8nIpv=iAlW3u|gRhanF4Ib)U zCR!iutzBl#ru%j#Qpv?{n5Wj25d3;8JKp~qtNiGHhR(w;r#B4aQb`e^K?>2(Kv|{t zJoh0*M5Sf#y&_3QQA(2}qNPoRQqoZ8zE4S_kP1a)mM!~d#qazB@2Ah_?LF`NJlA!7 zzwYqKOWK@$ijg{STVchDC9uNU9Hdb)$oI_?^h#!O8$362W;P+hy$5fEUoVPnav2 zc|7&ZQ6mcrU2*WwexgBIPs(2JKu>&s(zwEA=b3)lhBOJ8xxa`{bI-F}hl;j0PbW@TCxtOQqRyT)g=YA0f z##@R#C;g(jMX#v9@)%CP`c%l-@QY>7Y8SQK?n3uIqeQp!k)rd>^`g>?>2%iQ5U@GZ zd2NYNX1RG3l^0DXOU<=({Iwr@RQVV0D&9fv`vp=`naA#FS3vl7tA>tErliy6_uv06^BlC zr-XGH)R<`q>h;oY)@cPy`E!TR!kc~;ZxefL?2vo|iPCe~O!oKKBnap=8(Jq{=bS`F zmQicVYgdbGobGlOF*JexK4#RCa+wA%PbAqOMKJcvEk6?T07mrkB6h}tJhT_#v57CZ zg?r}-re?N6a<&W~vG>p?Vtl#E-X(ZIcQDs>?I*sh*oU=)UeFG=-!vlHj;uUADDsOY z1Y|qFXJdD)2&uvGVRuApFE4SXe+ov7T1Y{2^4Z5iHK-V9#Se%LWP;;2VtJd`$#y%q z?EA)NfH{i91 znOLEwjx|LKI8Db)@bjyJ)l>UIozDT7elZxXsHec&))L9{xSwDCFC8XU|E1flDO5Np zfI1tzSh>WqKb=}t9yu?X=X9n(dxjU^FR+ET7Rq_I(hPq1oTZ>sRn4mXJ*cl+J)W*U zP*z*y!p;pw*0Gu~-w%ICuH&1m^XX$UH0XICl|9i%quHd~-q!{YZ# z{NKBmS&IB-yz;I=i1Moxip5C$>79U&w#*akE0no63pgQJUfRd+xhr&?vq0OnaF+M% zE*9yGh90LSX0}BpZz;RY52{$ehj?%0bxh2m=?JsFCx2Enc3MQo3%7z^tPe$$}9wiXUbZ0>V~?_uC>frW&OWzXg&z@7v%?$!2g zLT1A>9I!ToLs zL7zrIp8Yep^{aumHJHH<-Kz}$-9Jr>bhnD?)in~^-Hw$tIm36M8g81Zf?;7Ngp3P? z{eK>S$>Vz7BH}9?4oraYk0){mv@XgF{o=WRSI=>Imo7WF_p+?qCxpFu{uN!mN08Nt zTU<=wzw(JK?!upSH@K1KFJVQrFZH!KPV#pqQgmoLO~_e5Nz;QQt7s-oo0>}%7l%{u zlLj_~#kV7wTb5Z4UnX zo{pF0|Doz{d-UJ`00TG7rnrj=lr?1oHB0V9qj#f4+wChwqmrj0zr2??VZ;@xoA?Ei zjZeS{tpO6d-H#}4CmijS3)jc@r$DXa%<+B}-P*DX!sgC~bFVM+sxJ2h9F@Y#4qoB= ztaF8ip~eu@cn_wkuZ6ZP#&CFS6h!@&V|&`FSyAhL)Ze$5#`7k^r4QSx=HskBUpT+!sxkX^TD{)nd5H4RKbmmy}zq0j2pZ z)XVE78M@t}_Gjv_#{VfSanh66{3c?Lc!~3rHU}y#>iE$<^6Ywb9Xmai4D2aH87#(vbse}mU^R8S<|(|p8^p?ADKPVF1sbGr zkxZ*&X`!OCsHhbvj@Z9OH2E?^obvLoXz?gmT=TqH3|Z_V?*8OYO?%VWvW};sLEHt> zko>3WR{5~F-cRfku!D9qsL%^(%Q~xKwv_n>Na%1zX`s)m9#<2h`$bF z(q{9`F{& zfqlBD>#RwgvBh+G)@)JlFxXnuf2A&N80kUhgKaSQcN&I|2*7h0Us0pSXt-AO23o8eAlY_11Z4+9;aGR% z{)?yiGv+WGXMZegl(C_Xi-q%RHJE&UC)?B!!rYF3W_EgxWYE}$cC`r_FSWH zo%<{PmGH{>Rmn*x6Xbchq`qzZikeA3h|UN`VVN8_Jg@Q4l*bg>2!M!8dD;%>coLu&!%2n z|H8hYpu^Zf4^yt~KO63M`*h*QgImJQqs25_B@g3QE6~)<&O3#r72cRr!=XTPawM3=MLh)Ky7nv-1AZmQp z5*2%XhD6UY8kAQg_PcFF^JJT>Uv5s6_=1CIt49S@=sl++XC2vP2UQlBY5)TVu48{? zH$Z)<7MDS8Sa`CCsWlI0M;0TOYMCNN>;s)kaz zgJl0?s^m>bp?kkW>E_n=R98?-1Y`VA8%mS)3Q=Wk%QN)eBS`HBa#-ii)`Z{z#) zyOiz`E-F=zhmc;^%PQXWgb8tdF?^Q=e)&5OrvzEBvn>D@U@`51I^9n*W%#-;p9U^eGtAlZEed@ap|!~3eCKHrZY*4q-Ud|1g33y6je zr+;LjKU6eH%@Su{=O9fv0^fb}Kv~Bm+`ModC@;mbm%kLmki$F3$KOjFAhQrRC3nz6 z<^Ldf<_{?8>;!Dh<`E~mxb`u-VJmK%SU1vOn~J1p$k>ijNt zs(C0|KIjJ;uRkGi=*ilLB5aR3xBK1;6utser;VDlz-VP zD&N0J2O?|ee5$tC!&s3N&aI^4>RGTg?S$C#OR;!;@iZ|YFHoEv@j*-pTrZBW`ok$n z!>{8hS9pC#E$}&;2ZeVNVT-X6cP%#y6qp>_WunJ~H+x}D<43SlF=JxC|G+4Fju7&5 zCR=;@E%Z3>9fl1V0tW(q!mksR(%*N5H(K1lM{V!m4R5IOdmb+5Q#~XHn4yh0KDmap zzev}*Qs*@*+5yfdE~9N(6M2(q!(ia?XfZH4i#{%?6;EEdA^IK2qP7h$U}k3?l=^*w z%E&pe&?keDS374lI1tqIe}F>5KW?TV@o5l0iihNz>tI)KAU|%xDSm9BKCkPQ#9JP8fmf@alk(E%qMcC>(JrJJO8&Oe z$F`*`)GM1e`gnu#_AI89szfRpGFn_7^igzCc_G?a#glulOHgz60@RL6;C1|0O8u$S z@(;%Clz!`*>|>PVI%p{d756fB{%#bz{dWQkZ_i+xawdbEPaLR>*$j>|pRmr4(k>@5 z2U5I4!N%&l^n0%#gim_OyK3)*e=S~ceTOT=W#!Qn>=gS~wurWRuW;bZANa}d2VK~k zAdY+IEG{yAPCMrQ6P1_hLweIkaloCoV!Y-$amK6d@U`9+)|t(P*K*xq-q@j}6EmNJ zlzoKubN6A&FEeo4^^9wjI(iR(tAc8k537{e4>KLMGg#*jI&BldWvDY~(HHPldJT~^ z_E77x3M$w^ep}!?Uc+TP+-8qC6D#pTrZ`#AKP^{AIuN5 zfKus94E^(32Imu0>)^6(7KBGK=E$9Rpf74&{Yy39(&_W! zg`(!$U^wAkLNiqx*;V^TU^Uj5SDk)^H`D0FYk%5}f0i6%DIGrSrd1GZs!@gBhuWlV ze*+gj=_*Sv3Wq^`oml49`LaW2%-OSQcb4*{FL;ic3|B+VA*ad#baLHgmmCe*lfq}9 zIbkK(_x%T{qi4Xt{w)w{qYalHg>k#LX2Pi55v*~vB|TVZMUTRd(^v1sbW+Ow4xU#h z+0l38df172Ur=S;>-P%J)7Ha4k676~PZO4H)*u)*-vWcfq3nI9McKu=r>tsS2u|7- zgp(FNMEU6Vyo!@6KSp|5$Cd7=kOh1b>&!x_$EA3fbjBV^D{n%x>L;je42EL{t6_58 zdA48u7579vgjuBzWk*yVa?O@YFj95B@aEw*tY}&+9NG8)b6m$`b$u!37A(MC8t+-g zyA<4SF^VgVN?}9pq;VZP6}XLO+S!$=1k~8s0|(~W;jSm4-1?$?^8J2@j{E{#FCB$% z$@S+ghgI+fD&1J4TMA5!oevjnkQtn6mHb^6w6J*^4Rm@;W5&nPVkIjK8nOgSvSc)H zoi*28pTh@UBeCSq4;I$vB^N(o3NuqUgctYBW5z3toC(O4n_&sI#DVj@&zOs78eLuFHzbq`8C{F*^1p| zQ`n8_pDeSl#QQJ0i+*g-I!+Hx5B2M<0$q{ckEa<6&58NgSJ&O*}`vTpm<*m zCbe^*?Yn>s&!*r3ZaN0M(($P1R!F&yrAc(nV(8=bjK845+O%fF1j@j%D{T=XRXAj;pgY*ophySRu2O9Z!DD zZ5k`_&};vq1LsQWJHFA>(6U zuRHWvqeg?cT2>(!&C&yR3HObrGgX60D62TkIrl%nR5QD?SNG3gg6=_Pzxym>d;gRl zsIikBOJ9P!&Zr8zJ+oQVCR?^d>oM2nn=DMxd5;yB1BK?e)hsgMHy+!ZC0x4agWe8% z%f4CmMVdN~e9S)4@kQI{#DKAs9lMs@E3&1Om3q{9WC#6Q`hb>8oyp#NS}Esn01UhS zfMuqA#Ttj++;J5JP+hT>J@Jc`_UN0rhkLBpm4^n{!(%@?+omtve*RUMUSGkQo@wHc zEqerqdo$RHjKP>7r!MTdD8qWoznCR)!W53(gQ%*I8hk%>(ScV>#wqsR((X$Kq!15^`rB!P?vDIO%FR8XH_8er`H$``tx>NzdqH z*;r9w!E)xiHkM^COkzEcb+Kpu4{=Co zYWZ)wHtvGaA-4YUA;IWiH13p}hX)t!#|ugJc;#L&=R53?;Qg~xm_FnghI|@>AKLby z*@jWv*NRuHsH;neMKd;QMhX5H8;q|?evyguR+{%pcsFI?bMJ z`DG(Kl6{cnOTF;{sy?K0LIDkKW#PxGWwc8F0V*##fL#mYD0KS+A#QMmXoxy&V_Ffs zk}V;l)HQ6=X~|jE6-~3Y-W3kac*ix=-$k#m6<|En7y7So!-1cM2=8hiv;8ht1Ugj3 z=6?5;a+Ft0&)OBkHl3uX=77^TzZ#xB+eTUQUzZDgG+-7Ur`bchq5nQZ&M%f)f zw_LZlJ2QPfjx?qyal4g+*y<#aTlPg4AG+!ZnG#QUg{LS4pRN+U;%#BM?LUcwei7yw z9Dpg`Zb3qU#En0*hqALXF)-{DCzn#do^R*}vLh%2ViuR->4{6HH#%LIK{=> z9fQT&mkAkX-hj$)b1=OBR!};32v_Z?Ak~m1pkS;(dkg<#;rI5iThI= zyIaMzuIj>~wI)p0o##%PTx8>KBy&CzyWnB}k1Q>G9fp~Du`3@6gtc#1b79TnuuAG+ ze%8xi2i+PVYws6qtb9$iWFihQ_)h-YA5zw<#jtK&61X;?@S>jS8STY0MP+n{bx7ndKk7^hqhah3((z$0_N^COx))f&Efa zk=F0TmAPZe=WZJYWJcme)>MpF7x@vI&s2 zp&4wg%*s2C+!OrlwM4x|($?kND6!A)Bv`5cke3Veg!7J$qGhB7-W83>^HVpx7HlnT z*-}~k+YYcDF0rUg#$jPjJ8CX+6&gD}vt=>!@!^JHcu;>X7Jlo6DBp!;rPHjtB6kUN zF@~|*5{E~lCpY+e7}IcjjK_=>@cq?S!uR%*6y~mgRhQ$1q1wNh$=oI|)xX1QZ0-qP zO`WAY(hDcJNUt@cnhxi@2c!KDd7Iv&VZnVfvc8rh8eYB!y<>g33w3J1nW{s=s2MPH zjE9s7w6Y0%ZgBlx55ZGO8k}X`DCRxMA7`x&VsZMvSkEinxdfwLXzsqaeCvNt*w7Mp z7I6D8liga2a+7|O)-z|^{Iv&{cr*)Zt;*5pcO6z&@b*~T#(8v*d!SX2wJlr25cuf|eH{2_JHN*}SBep9w^qvZ(;{IwE)&wGsu zi2~j|v_g7sGcjLrDjrXqz?{y+u!H{Bg%g@R%E|7M5SLWPZYgzVaxGJYhu8LERFEF# z8rFgPS`*;Q)1dqI)l_0rNF@>rQ{Fs_PIV0sO`!mwq~9Hshpq!SMcnzau@aZG5?8G*!yOCE*^#B+ahd;F*_T^pIQfDp zCM1o($n~qxxK0oreILL*-!WWx@2W1m7q;uf-1ffFe5|ZIGVgA*nXkoSpFSy-fCpOGsk$aO^viL*z>GXw7 z>lQ4y^ccYvMsMMbmSuxaULpA|e@90Y^w8ks0B8-22HRPCnV#Bwvb;1Lo98ZqJVOtX z%}JmT7ah8^D3}J0+d$I|S8#QGuHw}Odu&%<$)f60*ofLcxVk7AUB~H{pP&AZrLat{ zDfT-?)(4@v+os!jGe9iVIHL~s7(irca$D)EQLCJWTEGV=m zPMuQ(3b6^)Dwj|BD|R!TfH{yqxs)INq?1w(-Di_`q|$xgxuWvgMPzooJ4HAxC6y;% z$tEIWp|G1-IvZdTs`+%Tru@sjr*Dk+1f4i_GaT51y#uU8W z$6S`t!;X7uT#WJ6x2!7_&f%8r6Y*(IG(LajN@FZ8;;6qj*oF_PtaATnnVZ~97WYi* zmiKh0ZMIHWHN#UJAM*;^`gxMPem}9>q-ttQ&=f~5kERu|{jertITcS|N8fs9lf}DF zWPM{NM!nlZYD)KoE8{PgW7`U}I(%M8*yF}kl=bFZ$JEMlwfnHRgqO_7tbv6(OlS7H zq`SnUqp;5F2lwXaaK`U4!P}i{@a@_v{CKUI_11b%-7GlrTDXR-T*H%IwHGS=SW7oc z9au_Op{OV9qPNpR=&MQw9oRgTsyvFsK69eU>n0SGADjQ zBb)Na)w=TRKMc9htL%v3UTl7Ki@C=p;eh0xGDE#1<;C6&c>awVj`x^V{_wTHrcG$V z(8vD?yrLcR`sB?tn>bc*XAr)QtQD?3*em?F9f}$MZh^etL3R~SqLWLKsJQB!?50^d zjeay->~XqLNZV`5-W&X&zPg_%TR9wC0?LK3CAq?IGk1zl^r0~-M=<>MG8z{hgD?3J z<;7}4QGBW`Oe%B}@+B_&wprV_%jK;?c-%JR)hbxu{+_Jzn)F<5^2eC!NkYw8CzdFm zEhN3DEc-d=q);~2fviU63hT`|ob1*ljH&w!0lSiz;S>{yys1UYhfd;>!zHG~3`J?r zlE5)*Z(c*;Icko5PSdZgK*iuNyqnv^d;I<$!fUZHp@%6qgRI~f%SM-ekUh3k`oafX0~S$|7>i%lE8FWkdapxaVeBI6f$r z3%$l;qrz229$i93>_T>aN&pMZABz)SUq|~Me)!*S7s*|wg=2mNpiX!xsXM=u^^a1) zF3VtyzA%GL&FExr^35UnQzl#unF!-*v{?PUQYkCqXhx4WqH51i?8mWBuw=q-uEgJ& zDk6QceeEhdBG@r`-2iT?S1A{@>?HRqNC7>5UdBnaZ&`?ctn4RB+3=pl7?V1S8|gR$ z^Oxmti!$|C%Jdq%RKwWu_gd0Dm?m02BFx!R!7{tI<&l5&>~ z=4OK2jd@&;x5puW=|`B6dLyaePBRxbfmxlzi^L4~c_oZ!l&}|@d|4WG7i`VX;E7dc*1JDvbAyHz3o$EISl*F8%>V9TR=(7Xd%XER%Kgp7 zg#YB(@!mNsfP2bK>imfDe%?Bp`ef3?0oZZv+kK2e^O9|(ru=CSzHL#)TQm(qSt z+J0|b&ur`u2sBoX6!#U7!Xr6KO4vpJt+1u{p#t3*vw{|Rc(WThOHoPc9;ut|WOF5k z_f!AB7`fO5hd!^t!wzq_W2XPHV>fLb)q=d0F{`?LTCf^>nXUZz8;@td!}{5qSk^Cf%x^!B1)GY6fl7(2^w@XSQhijY zl*`7*ACBCbcS@vWGMQBN29iqbL9QY1E7~<2q~jj0r2JV|@SbA{R%8Cbk{y0~WP}`szEctfmN+r4M<>x?0}lLNvc5!&T-sg zZNSCHzF@Wv@wlR=2Kk*XB9HMUWaCjuF_G?cGr=AU1G>>kH+|Ch@`_aJt0DOLGV0%$ zM{cP+oZOMXtB!Z$*JS1JhkDua<98q79hdas&37_>Y|L_gX8LPhw`D1$MvVZkUu`VR z=DFaa(g8YGR>6#E+t|FmG5F!nU#qTb?PzFiC4}EOFEs43FTZ6{!U6`R%UYc{DVHrl z@)4!Y>@4y<_?~*#Kcvj?Hrlnqm&~;~FtMizeaQjJN!SgW`^}|lvyl|Cb~$wGdkHFX ze8JM|6yGg5f_L7yk3CA;%1<&L$4{J`0q=DSdG*?7@USEet~~_sUonBT8gB!o)-?7o zV+Q-}{)A-@2u8E9#FpLPi=7i2NJsw)>j-@e-J6}cl6SgjttoZr3Ig!q?kwE9ZapoC z7HNOhFS00FMe#A-Wcp?%E-GuK{D=MN-a#{|<6Z?pJ+EQ#e0$DELE=lfMMAL3eE4+z z2Cq?g9Ln}jh9AoZz?$*M4Ddirr=x$GBX-*?)+W9kd;#Cid zigm==zY_cQ!4ujYb5U5O7C~1I<%t^It!PJ0Cs$`ahMloD#PX*v;b@Wrz-)ck`1}RD zQBQ?=y1j&+s}91D1sQlEy+63PZiM4;+U!5yb>Q$s8^#$MfOS|k(|YxR#w~q9z5KIS zc;Oj=tC99B<9(Q+c0H?WsA7>$e&v_)H{opO0i>OK4|7khCzZ-KG-k~-dO2n}B`yAp z!}|Qi#CyrYx7VXY?NME{WvAqVF+R$YCr*ZpGArQr5WJXT1UPsfY5e>Od&X{LryC@6 z=b~w_EX+grRQE#W(0mitsmfc&s~=`b=NNc5ZZ7MJy^X`O&B!vy3@bq<q9r3=_s8yPUPhU_*DBJZ#>4_*f9BICSsAT3 zp-OAYUz1X>F*Vg6pru>RQm(NV`EFTAj>q(Ap-Tnz`|+0)W43VKK5}?m@eIz$szROU zh4_7PJlS`Voq=OV^Gb)2#F#gecSY~t+ zByuF^8@*14#tDTM6*p2UKwq+9@81mWr&>(xt zUvo{EHOZdaladUzfx}>bS_ymGKA+-LAF*>=d$DDGRLbu9pM@Lai&#4rXmFnA< zQ^z7xdKUAJ8e@ZL^;&P->G4w##%N%h#%3H5UPc3qO>l$RAyEkoy!zYIe;Qhm4*i zPIN66r#kNuHB+O-zJ-#<`t2C8kHH{{{u~X_!<)r{QkU+ynJqoEa1xaqR*DL%mQlsi zFv63`_~Gn9;fux`?sqHCbmSs&@<{2dFV~C3U+_V=aaHQnIN*&=dA5Ia8LP{E2i?aV zgVU}LAlFxwHEY|mU8R#@WAEYcYK%2oY;THLy(Zwu1TWIcJ1q9TTPb?lDvNV&7mB@o zUWvBt;i9|qUwXUBT~t0TopHMc(>QUaXl}Sf>@)f&y%>2@9C|OA9) zC&tkHP+#G@#1zs9a}yFSY2cMP@7RUE-MNirzoF-zSyxp5daNggW!zB)&3D6ZJ?VLQ$xu?y5T!X?DoJ?v?;WsgrYg3 z@vqmS=HLRc|Ml=Y=zv)05ytzdD=ELn!nzjQvPQMbB|4N%e*Bo*3C^gZo|0xoRc8dLf52mhO zcA`OnC{7F6Ax;^vMVx){KdRl@F3Qb3PKg^uDt=o|rEh+bul^%i`(-vJKm03fvhX9L zBV8m{unsdvEkNZY>5gi+IeP#A%C3(fu>Ljt-Ip#ziSzj0$1Qn{OG%(x;|9&MZ2%X= z@Bym(c-Q9s{Qk)SyiRipgm#-On*UoQMs!$-v9nK0y?8llnPosv{RY$F?<(}KYMW@j z=7l&^T}529MoUz<9wh2)))4!6*i!H}6Z%l5PiFJVNnTSQKRWmcou}3^T@zk7b>mR^ zo_cGpp!x%I9KQlbX71!(bu0i!z8g#%YYA^B5^vm*2`^Lnlh^iDAh^qTv*Q!^3Aqm7 zab_NWx}$-2Jl6?LyG$u<&1Es-LbzyW_*qxf4!!l%nYbn?#L_VYeV$8&v3=;7eDv6 z0ncrbIy2FL5sFPfb!?&;|8pxDOo>9*gD_OC^Gw@osou82Zg4fr$4flWRKvRQk4+5_U|baK8c?SgJ=o1MlKd+Z^1w_%r*c?!>(JNxsNXPxjx;yZC*A5m?b* zR{Uxk?e5-+v6oY1Ew{DFEw2o=Iqyf6;j!3#HFa^ z$++0}+zLoCU%3F6(Y1~?C3R{3j3eAKt{de}-%YQIX41_}L&;sR z0xztKM>UHm+!gwo*{B5zS2n1yiX|^ubJbu-y>uKLOF|$t>EROdSM7bSL#M6d0r19#qxL`~fCEptER6cUcg;M6L~J!Lk96^6f-SV^wnF`B1YN90-g zl}!4rHmJxb+92M3o0y?VSbYySpC}t&7I0n8-EK%6h&ZXaWu1BWB~i5jYNe{ zFnwNjlpH>v;D%ofq-{s@N%?z}(Ek%-oAr$NUSV5#w{$gr7&9opY}5+&^%L20pE>B& zeullwzK-)pU1yhCZE61ETv~DbA&HGUC^mK=^&%%MC@g35`1j01`!KrEI1EvLShiwn z9>Ak-;Iz;mmM-*w(I@^w!pBxPbod@bv`hwf$48X6V>qclQ(_NXow34rYM-^8v?#U!h!FnprXm+JJp4W)`GMQbr^`V0!_pwnf2VtdmD;KT! z8uoqaPMJ!26q=AnX-V^GoAyQwO&E;Z*UZJ~&gmGK+9HH(Nt1CgHcWz+V!)UL=$APS zHi`u-b*jW?vAhp2#!O(k8D`&v{}?oZ7q~9+d)pnb13X+qo{Lmln7^<#G_4| z7&x(jp3MJ2-huTbf3At83%8-Vt`xF8oY@iWcVO~75zdA_g6=g16ua;=o)a@^y}liW zO#8rXo!!o6G%`%7Nnw_)v7F_wY^HSY9!C>@mdnhPDdvG0C8)n8tpyE?v(*LtHYXP2 za}Lf+X9%s^&Cu2T5Oj6B0i>(X59{AX!;b1v&~!gh7e9$ZqQ8oVm&u9FuS>D&^%WfO zTgJVY|HppKO9aa{6^L_s3pY}1;5m00Zhw8ktS7F+fK(9bw!gySm#Lr?$4C%=Ra1d2@)F6coVo$Eq;kZxkK! zm3||>*aIt#4NU^5KPkcarQpW&wEa;CAp81#zU!N1`zdosO^9dj5BrYqCfzJ_PaGUqBr zKWji^iKTT}^Ce@P#7BPght5|@I~wa@lyvb7K9tVrUJn1*g+~KOcK0%+e|<$S(3TE! zc@$I>N)LvILizDs5HoxMMs{)7)!T+*%Yvxm!X0XJz629r^n%a>J`n$9A4|UO2dhk9 z!L-m6ew^npIQ(-q3@Plxf*N8#uZ}=VHWGBW1Jb>WHe2}MH$dJGX20>PFlFL2R2#pO zMXbs#SCOwoTxd@ddW%#QI*QI!Eui_O_wek8E}GtVoVa!59dVd%16|(`K|_@6C@Dqa zwPQyV!l({;qjU# zSkU1Kds2?V!o^1HT;xsge;5t1Bi?`}n+TEf8o~2)4vtRLVI3b5QDtnUPke=<_|E%r5N5FM^B6OEQT)A&B?IYlWC(EVVJS@wfj z?4}|nj3_2yoj&z_F6E)KZP_?327<0SK)RU+i)kpQ!M~RasHL5p9r)_FBBNux!$c*lv(1?J=i#HB=zal#yKolL)&RS^^TOXO2L7am4#Ed3nwa%t(4B! zWvIC10#0o{3o^98G3TXTQIk5YNmF9koonf0_X)z&5Pe>G`zM|oIGeYyYl4c6*FgJ2 zW4X)SQ+RAtHCS%$!J7R3u|H;|ux`T-xSiuUvRWiHjVm_GAYAv!5X<_gyAjS~HWn zos5MrQ#)p}B8``q=gBLPDC=dh@N|3t9CrM~kJa12?+#*oj71*&r|JRy#B=QSu0WW5 zVJ)mb6Ub~wZ-E{+!&s(49(epKh7p&J!NSj_61OG|tlDH?SK}=0&pDPIZOKJ62jI1# zKS=vtCIwlKBfG(Bv|W83exXXv!tShabBhU$+T=~i4sR)_M;J{Jb7}CMg#?!tuuHcL z;4ow4uhB+^&R6h5hyquS=lOmIlGrWl2KMBZa3}$Qh1HziRV6}4w7Dxw&!);aQ-dhI}X4uowu0(RY?~8%LBUWU;m^q9+$cp7ovPnb#A*8Jl>Xd$R z35N#=4cBAJtA7Qv;Is&#rfxE;+0)LwZ72bYtud#Ywv(vEY696Q>9hT}X38wwx@1j{Y`EA(`PLKr z?_+s$&f$-_+gVX;fN*{OeO6am!9q`dV7a{yvr%?Wh0c2$*i?BYGie!tYr0P0nu-)$ z_TV1Mw{91{p4ceJOZPX8FDG!C*&o1LYc@M}Zvc$EUkJ8xN7<8ZRWP++A8db}$X;BX z4f5A6qw;%ATvp!8I&bB3tb6N37jkCN4m}aWo8~i*`$O4?lyYXhVk^72Po8DG{#;(I z*~+4Kd2-Ra>{v-gsZglt^-$NS=MPL zHuS>rKD8{_)tzLQiuCtCn-hm+E2i_W;cGSp%dv-gs-s(Y_KaHAF=B%=%{SQpHHxv}hEU+lHMo#|O@W(8g^!LsFt zaKQS6aB8x=us>SBK5Ph9SzKeWa}INNA75r!-%qeP!7q>*_T=o>O<-dkhhg4JKWhY~&M>x4Fv?`TEE_U+_c zd54{mpe;?QG-! zDLM~_p58BxM|&tON@S#rl*+8nea@u_C5g%^BQtx;N=r(jNl7Vbl9E~7bFQ5b((siL zBBUarMEvgWU#R=J_j%5Fzh5sDLUmD4Ji%-X1~D%uceYIJBzxGE%xrv9K>vaGLHY@s2w5l=zB{6Vn8hUVFKdTQ><|$6WDxnlo6z^7;uO;+d>V3$CK-m{z#9f=4VQ#<7dHq~XTN4I0*-RsG|#Ym3&*fOt2d+5EoIEn zuPQkz{AKa)fLSN>;<_E`&lSwJE;-<(&dnO%2lFMamSKoFTsFQ9M^5N5lmGN+>V|Lh zct|fgc`}Rqs#Y_l7Aq(cC5Ni*TTDGu&H76_wI-uYrheHl*!I85J z$l}~vJl3)fFU=XiY(|@6xXTch^Cpw~HfM#J98DJcU=R*?uZ|VY%2<)rn-(0?lRPaxq#9!i zlQd64<>GVTuwM~&OckM3qYdsqTnjljj`Ipl;~;iw4pf$(gy@vf(tPwO*sHu_m*tdM zy>x#5DJx}dqbCU6L$=_#a|LL>z@9E74#!Zhr(|+uHJ$GzIc2e%aK$o+Y04|$^Jg`} z+sDCNm{uAa`dB6u9xY`58D@iOfCGCv){GtJMsdOG_R!&)P73I;oKy>gp!?Tw_VGeA z^f<1}8qd~Ymd$V6>$8;xCY%G6g;6k9Q*sLgn8ChHu8?9m1N6raVD(wM@K(PI6gJ%r zhlzEdzyF-X*Bl0xQ#sk=YLP3pZ6*E83fj7PD%zOZa03n3<0m6ew))`#9Q5tGbgyx> zd)=79-PD|n{OMyQ`VA9=18WbW;q|T^p@Q@#RZ_C3jMG_sB0`hff7vkAgdtzqSy?I}K^<)YbIE^(4C|THx>lzNDgg1>_5&S;y`HkaD30w&hHN zxJnxc+bfUlBhoR?We&ElxQ(fK)A8|(bU|f67@8+%u>|hAux0o})bIX{-4*w-2mhq= zjZ=T_he9bC{OT>SDICac$4^l9UI(M09D19%5Zhcv8+RU{eRn3%3kQky^yDtAxYq;H zzg=f$iF%~_Qo8>PJVe=d;_=J;Na&}1zUZvG0i0NItfUKmf})3l?Zurxq5rrK5I4h> z+3K|m9jjYPCNDd|<(V(XJboB!u?@t+h2bnXFA4W}_2RD0`;Vy&Zp3|NhXl)aj5!bg zOZt1>;F7FVvfc2O8!&we^EukYR%NHrE;SWu+Ne(EvS-x7e+B(*qrfL;F|U@Vh<&Es_dEy1KO>@yUr3kBoOhtNo|sY`UyA2)?K*1M(Vy=p66c0ucz@PPifp% zCoH|c6)#8*(zG>S*fqO+JpIO-m2EEJ;*HO6U3tUMa^n}a@BUBB`q@$Z%kdq)6~|(o zj}rc8+>?#i?kD&~zJQ4V3rYUJWfZ5ngVWkQm1eG(jKe4TgYany`;+yMo_O7-pWHri z@SWSz4y72BBMPAH&}@E~Sj%gS_(E}>vxQ6RD_FF{Gsu*3B1(SBs8yj(tm86DfEke1m;uqP z{jp(!3GA$EqSeR0umc+>32ql8mYznH?6cbfSnQjKDZO`7qhW`r{zYB19`7gdBmLk* zvje2Kw!o#XXS`Y;Bl>sPw76}^Pp+!pVsNg|k!D&gxU_saM(!Pi!_ga;Y}RAzr)|OR zJ?^nW<&RiC`W_y*oP%-0Te$bTUX(1p>BQvLJ;kt?x{`DI&SBKbVv^g}n|s;Kg9}Jn z2?u9PU^7$_CCBRvbc?Bk3-_Lb+q_p0An}bOw8x2xckhY%*P2A@vSUKSJ1IjpD}sH0 z>J4+({(`G-)#=Rid2HJ*AK1CP1djg*6K?J4CCob`aW;pqC0X~e`2PKE;j#HBPIOqn zr7y`~aep<~(IySy^x`1d(vQ9I-^X$so^=4ftcoNT+o?Fa$OVrN7{;`B{t!$Re+Z9s zZs9j}S#ogLN(@XpcIo&_xY3t~^@?#6+AoA^gF{7|W{G2PS)J?${9`}-{h|2PD)?Gn z1!e#FllI>J*y;Hlw)6Yp(Cr`Wb5$1?HGjNtA+?cgS~W;#u`8+h>M`2|Wo+dqD>mV4 z22(e=#JuFkvaEqmse9lI9Hp}a?;m-JF8(I?_GTWQZT=z~`~D0YZY9TJZ)?)D6MM)^ z<34>Xu%MNn!@;&A8y4>!g28s`Ea74nl^WZMgZgjA^NTivnRK*FUT~g8bZ?S&^_y@{ zqXKj9w?tU`{SX{2SO;^|2T2(^Uz&0?yd>oLEqtIAj+>S)WCvrlSg1+?7Ui}GK?TmT z{Bhl|N*6J$_&!z|TVmhA^<=&lus*q!0%B7IOY=u1wSoVj-&cDK)apaK3K5aH&v-b_*zF!H$+ATKBtE31Q_E^Uy#^zz6 z)mXgtSXOdD;ss7A&%_HNkKey7z{V5zXk1iZ41gvoIp2+LD9Px4#(q)p_))SPpa;=8 z*5EKh53=GuKl;ujo_u7lXpX&G+b(1ddV{+s?#CC4mf+>OFuEyh61(5{kLr>qi~S^5O@NOa8+xP_ z+tnA4Pd6(Xr@EW^MR+nTw`nvjYYEC-Y=pioJ zmu$R*M&(Ms|I^Xr_9+nkd=|2@X@<1&K!>O?BTIz-heXS1A#|?Z7@td-bqD`evXXM^ z${zrRMA*X;e`^@{V5M}w_kdH1A*^3#KPU{-6?UadyR^rn$oZu$zDd=i9y``>{{l7? zm1vxol}Dv9UVbH`UkF{G%!fh&FI#Qpu? z%GB>SqGOR2z7438dW>>-H1{VO) zhqI&W<7m9nC(122C#vQ)ll$8lls7_=QafE}(r;s$>QGCHc{j-GhZz)pGJ&9<2^bX5 z%ier3pdsZB7}GhL#RnZ^GjEO|JNx+rK^w`c>jU;Xc@JMMJcrK}B|5cL!kD?Y*q;6-7=6o|6hMtjR#WHHpI4!m_(wb0Rvb3|s$#JK!y?z^hdbEq{QteB<3p(+)<`cYZ zZ_HMxzCjBg8J_vE3PXQ~)99cnLZjAkbW*=4v@|>+O@Arpxv-8B=g+2q3Ta=qO@^`M zy5!q192?i=a{n~0(#U(Ul+$MdML4}@C#3sN%(JiT%EV=iS|+kVTO~iUnG&RYIt(iX z7d*4>Bipt*jo303d24bx>tZSHx-Sh-G{b<|DYSTB z7v3(Mj-!qO`gP&bNci&)mV5qSZFfUC(d#4FTbDt1<9DoNN(8&m z+Jskn$#K@gXbdsV6I41rW9GW{l2L>2v&zpKnEY%7e6~ne>MwHK*R)=o)EkpMOOB%+ z9X&D4n8{++RG-_zBS zLwPz|uHl4fMvd5ek&I1j*~ea$r^`AFMqx&EA9Od_fZ|OvrrLidHYN=eeoo)dZudUH z@~$eAZn{f}>zOFFesLpfh$&{>rA+1Nk+zV~6%R`3!7x|D6Cdj&xbDuv=~1JE#@|0Ug>5dR zXpxEu-Qs9$@AsH!bP^Brd(2LZvcuq0fwX^ct*Bb=N=F_}qKvzb$^Fx0iYy&UgO(qs z!>cZHwO(6MVe>Ec_z(|r0lrdp)|fpxRn5M?DP-R_SIa!(by)vdF?Lk}{n`BA=Y^W^ z$!tM)f4pWkTky;GW!Z|Oao3?zjJ;dKrbfwgI@->pHF!1kd%qoDn(oE48yrR3?4d!9 zv+=;204o>%WvWzQHKYFu);|!sbK>Z3%0uJN!-I2cx*8h8ZZ0=>KX+pgB1khC=Is8Fda1qUxH~#w~%{RWd#lLRTgzA>TY^T*E zJo$ShK6jVL=Y?j%LB}>0Gq8;1&#*?DIX?uyl94QUYA?ZwwqjD!BnVc&%ifi|1DVx1 z(9_M9BsgYppqjBsM^0hMgkfxF=X6jDum|7HC47%R{o%rj>ripn8*(~SSlYQ}y1e(R zsPp!YXfbyN1e zah&A23FMT1`Iq!CKPUXNc!ptf%t>8tC(Z1mO3Gs{vDe;iuqkjg%-ypbQ@Y~mxVsmb zH?-1x3tL)oR+FAvZKkb@B<@yBEw~mQhRkVt@S^)E-mpoRSAE_CT<`3rxSH9b(Z!dd zxxJ&<*UO&P?o_4umwwZRvn~>wXom2l^eDNv_aMurCupgdPb#vF9N#YGAb#(rEtib2 ze6lXP#R9oo(J%1!XGQ~>=TOv@E!Ze=hu5x=!wXKgC_>YQV*JL^%?X#O*u9m8>`!Ao z-##MK73H9(JQzL{{p2+~h64S13qRUgdBvIf@L$6j*v-FWE|23W`@JIFIDMU7X4+8b zW_1=2FcTX}n~Q&)-oRemA3_UeER*Ie?j^@P?$Ge3FY&GZ6PjV!NZV(vqVCDYEc8l? zkoRONp7_t1bk6jm&;gsU@56bLTf~>8ck{*Nh{+QQ%0@63CVx7WI zFqd}l_Sf>rBIz_6wEQ>gulO53HdJG7`W$lUHiucf>09zxZzUd_wG=-#uAv=;6KU$9 zAxy=v0Rl88z@&-)U`kIDiZiaD^Yxo3DRTmh-XTp*Mo5X-zO-aq0T-Idn{{O z+d!#h`|y|XXx1%Q1#e9|ONzdgI4X6o@G-p`mc)DDi}Ai>cl#A}gxE{2WEoiZ@rH&? zJ9)J^CD0wm(7CCN7^OEIJEyn62s?=}eIp4b&UnZ+N&WR@d+%VNb`=)A1mW4YWwa$o ziEZy{r`;a%q!U@eLhOxbrK2as+ux<)6?WLwuO9@APGVJVzrcXvF;dl-ET>r%) zN9i>>{t3awO6M^+dWbKn@7 zIr=ZY8D=Rw&U%7*?p65I;|EPk{zWHdp2Yur<=AC?Be3UJYts z&{WL#iEn}o=WguS(qnk!&std5tW41f{xsC{GFv-7k$fM$!YAvc?pp9!$f?;tN4}V| zps%@P^tO z++%aF!#)r1{#}SM#v;bgj;GWItLVBJC%SCSrk&TU$o|G-a?weIkhb02o3&=J^v8E$ zhvo>1Ps)SHdOxPz7|*^L>|qZ&=JCq$8~DZFefaS66}+4LNPf(KO86?L35h<>A!17@ zYlyrhWj;p>o7Hsfj=3yl_U}vBui~>pko<3S`O%8KisglDA*$p*)k4ue9+Ab5z`F9GPvy6o#dQ!UjT)-y|l<-uaEiXGm3vSHCzx%@3xDI975*A2f z{5Mkfm~wW^zY8W$_`?UrZQ;Yrb$Dl;{=7r_KX_GMfil|$D0R+;4bmC=^3pZjs?$EC zcd2*Ll5_XS{lR9;tuw?5iFu-DJf7QVH=h>wI)p#s2h+@XhmbGIp!gk8bozKA?fW>K zGNlZ<_HIv6t$sHf?>(HlHT$w}Po?wux}DfDb|clNt*4xiC-HBO6$m5RDDksKNyYj~ zXxn`X!n^AE`5Q0r+Tm9G+M@-Kry2kr%T&qW&JJcVemjl)w_3>Z%BSKD`ZW7PDuvx@ zBUUAn!9y+l{&^n${L>2)uISMiZUFXIKZnt;-?5}c^Jw1FpQP{5lde2%5_@l)B=)Tw zB6j!cPA>6NC|%i$@`Rogyk;&wEUum*mmkY#8za%?qc@@J&HJZo?g6OMbD>5?#s1}7;gO^ z9!%(l6YKSG$-jf7@l@EihqQ7d~3<>!2){>S^!DRmRFmr0z3KlSu<+*N8(=4GP|uFAn;i zFHUm)Bc7d{D$b}8#luy*q{oN(L;OI38h`>e!T8EH%YukW#mi;JM_y)u{|cjfKuu7O=v zR`J`O8k84pL-FDw`Z(B#b{6fR7qC z=+C?@;!ddp_`q}}&ENiyuIx0R0aKr&-XorIv0*r+Gg7E8>O-1?(@C>`IIa9!D|zJN zc!dUI$a%OLDh$nGK+$)o+xP(r?b?OP^iEz^C6FI6K7wB!ewLqOa**#iH6LUVr>K|v z1+3ik5>xN^Q~$wl>CLVTy7KX{s1~YB=NHyXT)%$Ib*_L^5-AYw#i?dCU|pRj`MPzf zyy6EXZF8Xci=2y3ZTo;14^%L(qPzHR(^Ag!)(I~BXEhD{CUwgqpR+HGUh2PEoF z0k8R~sB-HxU{f=%FTKC|theOn&QIbU*3|R9PXr!5-sAOmm6rHC-bKFMD)7?hNi@~U zOwf}xiAodv=y7X@s5DNIj%c<}$S7M#uel2I9Oc=Qw>!XdZUay2dSnHsTU;TL9|>_C*38ENaGk6-_)5O+uOA=L?}wKKpPz5Bbka4+omnob z=v#8>51&Jhz8yp!-odNvbbtXthbdh0YwNt84>oN)u+M|Qy@wWzSsVqPMGHaOErQ(| z7|zB9=i`qX>eO?`3M?M4g8sh)OWw9tuskWBzjMtl+$fy^i({m7lgk|V88eEPtJno9 zj_#oEzM0aTKEWlv0k%ipp+8=GDEsX`Y+hL^jC(nYJmuxdY`H28%L))RgGS+9#|&6A z>MB${`3b*Wq`B4VcKnPB!D8WiVC%PlWA0EW{dpZGu3HJ4eHO7d$8y=CS*fg|-I@Cq zc!TslMAA{GGg20|3V#)Q;pnkH@UqfoRMIa2<5#EPb$lT98P(1fUD30BZ(_^pZp4Gx z^%2lzzMI=!>Ox<=J83>&D2{d-O)mMrB_B>W_8Rw==8bzq#V4g1c=I4GzcW#CP+oxL z3PpHp4#U#yb8xLa6hg|7v_f%IN}qqB=H@YVQkn=MRG8Vyz6AqQT`UBA(E}SfOV&M2TWGYGJhpRku(l|M8$Y=>X)pYRvp)~T zcjI4CpVu>3`UhiH;GqnP?y=fiN%q8O}Y>lNN7iqBOG;%;on}+En)fMmpwVhuixS@w4O-n*I%xBp1Pk zuG8SBQw|e{$3s7(T&7+9hvm-B5qt*)B4<2_9DZ)X%K8cH@-$cCpRS`x^XHS%?olvL zc8xvySirJoZzbpM#k}HWb*{7X3HeP*U`pQ>)6gMfu7Yj%U0^-S6cjZbK&@8|zNnZddEONvw;82ePAsfD6U0hyZ-#-V zd&1oH6Nq)DMRI9Sy53y3;KA z2D&-SpQb;kr;wH3+1A!`AU`S3R`zokJgI2|)7}yI^38G_r(%m04WHQChp*U+x2L$p zfA2Bx@=liO*(411Zs9tTjDmO!IS*M;pcc67b%d!;hC4nCT7OCs@pUA|zEDB=;-;o6qYg> z!X;mLej3bkBH<|m5RTaPN=BT0VLB{s?CI9Iy)Da+WA#_|uUG2{6O>{EjQ+nPEU zl;<1-qrxMQxikcdlcqzR@>+NrHV4kzJOz1074F4pTNbmh7ChGt!;uB6F**!j%Y|z~ z=~2WSKXs1gAAsz@+blC}87RfQB9&j7#O<#q&kH};(Irz!C0&h_EyBpUO`UYTTv^DW zZa6fjg^P|#!&gnNIJ}@xR;a5b?Ey1b^0ESUDqf4LxvR)_E!e{r?UzIC!4f0*;YP^4 zxfz}vYJ_y7AyBB8BKe6*VPf`X_U-ZkmMrl^!aWy)(oBIZx&2$}6JDVuL#NZ7vnT0< zlN{UV@tl$#44`Y&f?~%)rv2?D7N&m2jNKk&Z9?R}`9ewFFcTqtgAzNtWr$F=R*CiV zw&&7%{$`2sX)I-~E%R*JA)wfd)?Vk?hb_VE&W(}m**HBGaz2}h_zE0??}JtOH7LH7 z4k5=D!?dUou;al|PD#fA9QzG}$TOy}MdJXtYKOtB)`9Fs#2y;eriA5n>p1Vt+tBgw z9E`Dx#Rm1qq_e3NE83mV@5&;)TW8D$hHMv-`Ygk{`Jb6$b1vJ}l!H??uEA4L`s~BZ zYs}&BKs;n`$L{xN!n~iyDfWmCT^Th`l;9+U<392GS3C2erxx>`VhazuckxDMk9gIz z3|?XA3vju;| zP0dg}=o$n4P;RPqA96nPMo2nqj%bl9y!+UO)AzBL9nDKJ+SF>0HGtIV|9Z9(~2D6l;Tiw>)?g{u@r}&SJN&jK=pr zF0nbv)469FBL1iyizNrEFsjTI?=4>~tZ_6Vt)Ns)iDeiS*^SKJGt61>lv~msj}>lx zNI7qh@ZXr8QWmZaYme+9(^n73sYrtaZzZ;-$&Usa+KA&$KP34bX6&unCC|b3jSLS&!(F})82Q0M|-lLhsQI`>M=szfT642(VXWtG3`qHaF&{E$oQ)ifYzigI z+)mtd%!#J?O{3T5H$@fe+oIBSZF2jofF=>?wBX(%m?T!hMhiV0cDx8J?l0pfI=b?E zt7ZH&l@8wGM;pw|nFt^6&V}%~63h2P3#;r=iT>ZOa2Gn$Nk^!q0Z-aWNJ*Fb{o0)~ zPUX2<&0BEr#3l^7?uMZy=Wt>3a?%<0*fy&BPwtKLR$1w{_p zl&ia)t}GuQ>f8+x`)L_b01c+*>yOE5tVms41KYOB3FEaY$bF(AoNB4&t+(HY*}0~y z^y+Y$+UU-dKm1^!vwcV*+6ZqAvBhxv6(v@$W~0T;0knPkUACiHgBks*MfW!?c)c!z z6wT$h)WeTaO8f~M^!MRX6+wvVnLvG&pP}`Kc!sGM@>3>cslK!HJLlu6~=}*9mAOqPYCTF5bHj>U{h?1 z?XuXxc(q+(dsrus(pi73Xj_0?6%1j+js{!?#qOkSVGbYg26N9ddl$MmNo7(2q6J-eOxO zeTp7WB|GQRp$``+;e{XFSf)v_qqWKJ%_ID8&P0-5s*Z0z4kD9GcT9ZrkPY!MXV;BJ za67B}QpoDlq&`Z44nhElU)PE9Dn01b$Mw|aq)ySBR*8y#)XAsVh&I|+i#;xCQ`hz+ zQBms_)`UjTL3ukW;4A5q+;kyJx_=G0;)26>{18VaC5U<*a-!FWoL#d;ty$_b7I9G_IS`Qv|69bZQRj_DH~o;Pd_B%xNW5M z<^j5|pGAlF=TRJcNHuyHl(Y2~S$DQe?kW$=efbB=F5QBW$)+$YRFB#QMT;KOVnpx4 zLsTox+yi^7P{9Ccht=0yoRw=L4o^{~=j*>wy-OUWXZ9yO=c!b_%YyviKuTk0E(5goCn@+3BF3Zvak z<0;}TQRWRpTCm2LLd(n8&HVj%;gpOlP7a1fLmvY3x=#T>AGs z%s<+X9bOZ}=Jh|x+;zuez$k5GW3C82pWVdVCHtgj;GFECnFpnhSEH!vI4Yj`C0H5W%$*MLk{`hFa~ZF-D*~3RmxHob2T*a0yC|DnEGi%3 zz##A)l{d&!eDe={n|4eH=oDnFslV~i5RJ-Q+@+58vpv{Tt{AhA?IZSWk(5!)rSFSo(dkHQn$|U?IKtu^MipvWtXL7v*@VX>|n=d&Z%#uaAfTq;q+Za zTpc!&IVzvQRlB`N^O_s}{_JnpR(*hmJnzJ#%9?Z_t(e*$me9!RGFs7V0QD+ALEncN z)1UIYp7s%xo-_%6lBG`BhfVC^nIkN4@G4Yo z&?9+|D4}#|6x**F$1Z4vumgMUOVGy#68cMjE1j1Vdux|nSZe}#Z7!wDa2ORTJVg5x zRpHmi`4r>khH&Q+HA(MHvYO<)>@h?Wne zIfFGtZd*b2iJG8dU4$#u`Y^SrH(Bwgr})Ucfc4%vm+Nt99=goRX2QN<>{PdZa8jif z4s9oJuinVc^juGdn@`hO4X={wlS?43+j%+@y9(>=f6+tN&v>Zj1-EnLbBL{Xm8t&8 z#Ox^%LfNA#+MCcRu2EAYr7Abr(Q%F~UUrezo3Eua!Q*LY{VY0eo=0Kj)%5qo18f*} zolKAT;?XnbVPg3=p+%JL^zB-Bd51rq=`n(3w`CTG&wI;SP25@b)UUi^NfWQ-UI%x$BqsPThAVf@(6L!r z^kam#sNM7dt|jMVR`Gqfr&fYVomHTtcbMGg?%-nQ%2B@mR66rdhh;>)Vtum z$GoXY6Te_@18;5-3%Ut$u))iY@875mffYSb0GQo%%nS? zjKyR5xuUjPchFea3hN5SKyeREh}(Y;UL}M=ShzdHBsfEonhdJyqv3F~63h%qWr7k4 z{_6j+h{TadcX(O#L3#GMKA!tJ!yZF`ZI=M)4PZ!sai0yni^~=hHjB_cj9v+MGeF+&9qLiSS@!Fl$lc z$9hyRbb^oTLCQE?fxE3vaOd6&cm+ma{lg7HFV2F<#?|oA#uSDRoxu+3o3aGi7Iw+> z3I_BYjYIbS1a-M2481;(x$B&U==L|D(Q^PP8x+Ao;UIJ?xyA=PU&-$~yOQ61XeX~f zz7cYLwfGZh8hmKUOQ>`Q_O>((l^V%=e0S*w_L*7^wnXP}S^NH^$+muudKKN5-a}nAYrsWrC`G5X(ILX#>U zRJR|yQRpSH585FrIT(U}J!6`B<6%(eL6~@bIlB?pPweR#PiwXtLSWu%7JX*6*fXdP zl_jl!)IZB0Fz6{P4iAUKZC&{0Qx`c)Jcz7icPYkk109d{rsS@D5|ig2Wk#1$KOZ+5 zy`dGAR~ifNB~D}SWo{DN@HyA6n8EU^R59V&Q+&`@g}c`1N8z@f((X=0Vmfq|G}{Y8 zFgSu~!c5u6WAf1KUyZIm{(;=$aa=^jZ7yY=JoPm`A@))0ji>CZ$?CiGeJZ@g2JL+Y zTCVe9Pe%)cTu)>sYI%6!{2WTT`hvR22MCGTlVt0;7MdcYL2Zfs@0kxXU5b8_fR6M|=5A%%@85FLF9#A)5&u--c|p^7ez{y}m>l%=c)fnV81$XNM;H%X`! za*b_S!iocs*?JQ^yZ>eb_h&FMstiI+rohbU^{_zxn3R=yAq^)w;B5P9Uh7B#>`tx& zpAE6R{PInFuQmUmaL6)n%N@;bEFUjB^z9D~*T}|BKTC|4a+^8htVmAHh>a`w$$fSc z*&^XL%Z})Wr)+xC*uzUHy;OpE`jeruiZ2`ZdK8@VGUs(X`ofiiyx@rOFm7`)gg?0>`4R#l z=%xrF59G=7XhIe@EcNJ~-eNTfo-$zYlJFxvV1U5Pkg0&Be z;K=TkQ2EaURNiZ|_km?JcU2Mw&aC5NFTTVXeds6(EAzhizzrBL*uj52IG@t))YO<<{jOJ`4n32oEh*Up&$Pj9nFukUd?)O*3kVl8-=R1F0qw81-P z2TP88R#I6okDYEgB1F_};8Mosmz*dY!TL3IvN`!bnZvb8G|8|L?1vPy)y;2NWJWJg ziIQX8@{K_+>_AE56(4-!F-nv_(@Bvp4vSWWaiVg0xtRJvO|;32r)?YCA^%}B-`9CJ zzujUVpK(o&pPTfKpA%^a@9ciUy~ckKvSSP$nRJ8I`QHcOZ8xYbXo83g z9g&VMr;;OKc=>cZJ)HBGB^%74J(ImDXqf`Nai|fB7S5so$bg#7ReX$n5g(w|!WSjq z;eC~ocsJD=Xozp%6-_na#*ZG%^~YTJstd0vfpS6D>)=7yL!Miu?G_7sG_!20_2XMYEXpnEu;BKwV_^x4If zKkbgF_culAn%ttm4=ee;gZsmbUmBFtb&c-r*N5Q+OZX#`V|eYW`Fx1)Q(it`GVhw( z0K)n?(p=XJ4)1nGZH=?6{d5+)ALT^&%1azM->N79V!dRnk;naCS!bD1x?xeoTB8LD4~3| z)TNlr%1l&Yo%dY`%URAQkFjK?E6V5)Y{S+Yf7p%Vv%z_jC9i1vleb-+!m~vdd=Hms zezY>gzdeP1zEOa&|FywOq%X_3wZ_M&3e5m9&X&XZx5&OWS?G*2_bO#@!Ka_Vp^@ktXtp{(Bbs3&) zSPIgR9X`xfg4{)R(9Q7y^yuxxMQE>Ko_mX#kxMTOS~8@>GB=!^(D?&nAGGssdN=u7 zV;Az*B5e6*6=ra=V2^m={sOYY$rxO$FU;KT%+9Y)Vxe7|xTQHv2w11U?kynUvhPZq zInW37PDZdEk?+xUC5M$;o!PiNRkU?{VOLl+9=BOtz;`P%*rDB-KlY z#-B-Jr(IH^*Y$iDf5s4kTD0MGTQS?dUzuU(0x~J%@uBZ`G`Y@W@yI$HSp5YrZ-2~^ zA5^hvS}*@c(Rs(^^uBStJ(Py_B9bOWNqWwGova9vqOwzDkL>j&?Y&79MWsnm8qc|} z6CqhqRwy!}NT`g2-}(LBAJ41TGtPZq*XQ$oQ;Va&>6yNfqGLzT5C`)HGDpvdb6RXl znk!n!fYVlbwql%E+Buy5x!DTNe}!FuRu4#T()%a z+TC}Lch{80EW2RLwRnK{k~Ns#!Yoo3D7dt)gpimGRWSa|KGd`SCwh~e0=lcrXzd*x zx@1fU8u0dX+?bIhrTZ}TxNXP;Ur?h`BZDbPTR|Q)bW#o98Nyk~lDI$lOpVqTaEafK z5N+>OBDhmuA@;8=1NWsqkQbi>pX@JFM>>l7 z+*(I#ORXW;@&MFsa3w*1cEg@%JHkzk0EMwaFE;KSj2N+9=%#p*?28WE12-p9cr1iT zo_Cnse?GvSWrtDEA`_bIpv@$X-9S`kr;zs8P%>!WNm5Vtlc(<-NxJJe@?`m2DxY2t zM;{xLGL2u5I?NMZxPN1{KGZ?wauK`VQ<#Yv&Vku$eIagQ4C#FG5BFYOfHi_=tI~8k zI8Xcx%ROE}ef~%Y`SS%bjEhAN`xtOYcuVdzXpm=-&&c9i-OPX5N0{%k)>F$N_T)n3 zS?=E!j+_W^B%8YLi!EaBijw9|B#w!5$nN`QY!_W`Dm*+v) zUUwKdoWfCN2)OU>B0b8jtWD+`Fg@Z#UQg&kDgFgxyZ<0D`I-Rp-CRJ+^&7;ezl4V7 z7Z6%@9|HH-f|lUBihlbTb|;UgS5B$W8xpFdU|q2=Q%WLnh7vG- zIRPwF%ppW=KUJ$wM2qmpcztL%UX}bnCstcQ$@yAV!Dk(7_-`5O&=<~1rT|ojd5guu z{XX_+Dm^k`D)YVRBaxYZ0_PN4fI=DkN=OuIC0zE{QJ~o14F+p2> zA@e4RA@0+<=+Swl;$mAdXpneuRZKB=X}k`ZT^q=KPATA?_-7LNtnDQGqu}*8`-d4f zDG|-5XVGu=NXAX+7jhVr#R16*=)EOY@Mvl>a}Nd!%&;TEfc82qJ=lapk1rP2?fi|s zJ~z=nR*9E2tim%d_Ml|a3K-9uu_l@=@Y>V~dVJ*J%*i%#^GX}DBF2n{2V^>Z^|mF7 zTkevoE1D?vrTMWsvt?QC04pD*u6 zmCQW)ah{ml6F-^+N%`WMS_|4c^)3CofycXjKk#L%IR<++VQh#k9#=cfYqk~fV}&lj zMU|brq}KZ*4<95*E4S(U5lshaLxff0u)xj{iK-$*lNi((!XL;WfRI_g~ zQzVzb@WtPWpbqEa4s?MeZ^z77w2ixnf=@~BGiWuxAg8?^(eTJx8fa%hzb#*be*eXC z5z+T)L)SVyYo;vhBP?i{vK?N!F2)~{OL*rSD|mT>x4h`qJbw6EfgST*1v9Rm#p3(( z@yhmMj?Nam(-nW=_%s(#*V+UNb1p$%+J2^Ic8PF5=pdcF3>bJH6?|x`x$6U0#F6tl zm@!jskOgZLNT1qjdUoC}62EdZvAJGKnyf!M_4J@#;!e%cl^8PumQl20&cZ+w^w z2TJjd)oVZw|s5e780@gr}?VHw?_A(Rff=6bC(8D zUkZ%231H%Km*G)iH=!tVlWc7EBv(RLkiq8%VbS&*siFry7_-NLE>iAKp{_;77P1(my zo?nZj?E-P4&Nh5uw;l6UpQ3cqB>H9M8c5p0iw1RianY@PG}R}J6xDr$*dd+Zv`-g8 zJLh4m`8Jd}*~W>D7C}&M1eCWn!A0N6Fup5Fe8=b`8fWV>`mgP1eZ*uMJg0{!{OV8pGAo$-qh+b0@^3Ov#gjCYH$mvy17v9JDOg%m1Um}PfTmLca*yRW>AKnIRCr$G zvm*7P~De9mcP0}v`-C!&Jq>!JOYobQvKn)B;g5bGWJR1HyK}=qWvvhP6_k>9k+~%C_i{M$ z${Z#fbOzJRm82(gP&8I*2Ng>{6(<<{CQUP^GefT4CXWmr()u$~MbY0s(}lZ_leA_X zCND4rTv97wcyR+j!y`DMECosb`GB8dGK34yHS6QD;G8T8SNmqtuOq5qLGwP?KGy*{ z)J@rG!n{J!vIs6MYiIox4#Tgg4iGnthkVysIGfqTYq-qAv0i$#Jg&MXeW4BsY26Dx z7WE|Huo2nyX+1S|s3yTiM@Zs{N#w6p3a9p{l{$#-1jflkXCTr+#Ey zPcD%t6`l<@8e#UO6lSaC74G!4CVHz~0+JS3K$_eT_|M}VlpZbwSD)=r`rin4M&AV1 zPI3?Hrf1L2dZEaw8c9&M7i~1`Nhg`(yp@+MIfL`AW#gS!H}Led%k=Wk46xVJAm)O1 zrFLg9sa30G?0(-Ne?R(?Yr#@9b^dm7&)j$#5p|cIGoQ%4muRFxQyj?=1%@0Roq`te zcj00GcR0WPHwiJlO-j;R=+ekP5M7%J*V2Z-Bd6`GfhP;|V=l7Fstee~p=zxE$TT*@ z+<~1V@FiFOkr15-e}ER9&N$_bGDcYr!>rCMoF94-!z#X#Tf1*DM;wpAl<<#mxHVm1 z%B}=yN5Senjsay=Wyd-=fh+R=p5;+e9Z zGUBvHg$=T7%HC)_8A`H@LPbw!4$O;!OmsTzF>|{_9w?D zZ345hk)Une4I9!ufV2Jx$&q6ae0Hs*Pn&(>GhBvTTg2J9U!1V5el|w!10(+2w_ynQQfyN)awS-aUIUQ zQM?24*EEUvze62S)s71Nr&Q5#q;kMD>jXUiSTnmjVu9SudjZ zKpruD(gr0@-V5E$04S1+fW+oRSg~3X>^62YpA6!ef~X)O@Q&!Z8*()KmNZq67tx&0 zb*R`CkE-jg(ZFsoDVNs*sirZI?YN3LBe(|D6=#zc^}n#Nv5geHmq067J#ubn4vlgz z=ZEf#!*RYF$gRWv81t3GPyekx z-tbM0&nMwCm{kIX@4qv>k|9J#X9ehA{th8`51{Pp_cWom6U_sWO$8zU-4@#L(8*+VAwsu_1YZGYPm%~+iBr7S{6WN zz%|Bx?12SX z^>hUuq_1${tEY6slT_3`D2cfq?R5L8dE{j0A-I<%#i~gZz_s!t5Fm8{6dtVu4Pk%o zcg%)Un3T)Cl9#P9aC9f3g>mHR;S==uz!Z8RI*DP)APp2OW41PzX+-CF@J}^h3e*c| zzi=l`91H?h_Bk4ydrx=UTGFx4?ZCd?fHw|U#ZTV7pN~#XN8gD+l_tEwYi}t|PObs% znbu^xIhctIi`9}@h{$4IK`Dek{yU^=`!xU&`|pmHsdd``-M@n1K>UDYz`*64;&2NF^C zWEl5llsD}@J)4`pFOb~V3gk^KxAK!u&gPS+J>yj$Oc1((jl6Vl8z>gKlH)_l;rzK7 zpkro%Q{VgJ@LA)DbEgb+8Qg##w|bbKGoNwPYeA(&o5fA@YEe_=ELHSdLgi#+xTk4h zMB9?5YJS4bB6A!~YF$i?7ZuVE>$bq`(N!dJjs^s;%7@0I&%t888-iseJ-77=I<{4i zaJWN4n%C1CPb_epj0dk(6U>8Z8+I66!zRmPSl6o#9=}Y4IfpYV`)&zpigUs2yB`_` zl%mB033zm5vyi!OV%E>I6X$rXrWIWi(5>P>ocwz%#aiLJc&&lvo%_zTX%MpTwj`|+ zGK;(aDvI26-Kb3Xd!qNp@&BwUP`z#hekIvZ7pKTP+hYla1JXpV=OP+M9wnuY4&XSv z3e~Ho3)z_-0pAeCOJM;P%JibCg(j=udW@YqPl;8>L`=$f3Q2X+II;Ku4%*q#6Vbm= zd2b!g2)T{9*S=uvj`b+7aDu*nQ9$2MoJQ-TkUB*>F}J@slE?{_+|8WZ+?x@{Q7gV5 z<+hFhsUOOsyG7yjx8*Tzi+nR=nAQ;graO>qV+Zba#jwS22y1At5~yb#rbOi7U`y8Ma1pDqT$Y{SQ9`Fy7S`B%x8u#SF8n7m0dog^F>jSTR!-FB zCAYL<*qL=WFYq~PrevV(v}v?Bc__Uaew7-YJ;*(iJzN5g)6R&|2WCqs$52r)=3ion-9sz6@Aom)=Mn! zd5;5UC2*#)4BB5;K)X}5v}fCUvbN+NFYz=1d*_(btc2}o@2HQ=bYI+Lc8Hf#s>D}@ z+4#Ox7n>Xz9GES_KC2z55nhce4jIvd25waMfG5prYob+w0rXqIcPdbE=x~|))yGQC z;pkJ|wBKhHN?i_T-X;it>nW!{xP@fJ=2mjW#+ykI71Q4nR*U1OjiWdECu6p|KD|nw z;>OV?^v?)q#D~QgldQ%M(|e4cpS`E?Cc0QO{W5KmdrGg=deTCd2UxP`4(d2-;J?R@ zG1AKxb0_a2W$`09I_EZ1d10$K>(pL4crSuR-%$~`*zWZ7vvdxZhI3aEPjY94{))`4 zP9~kXBI;1@;<{%Ethd(#^x`%JnzMi-ZSA@wUyN94|bX0H=0Mg>*csi6+4{oHJ=&W`cRI!i-CTzp>t9xkgWmhg^Tq+vW?8Q+#H)49V z4i2mrvW%1FAoHvPHicBfUH8}UsdX%LCU0V;LY6}NlUZ=SwiLn*d_n)jSzKY5j=RoG z#4~%R;Gy)x^x|nJ`ty(>9kN@RPAy(fWQy~NkM>B^vfE3Jma8yEGj=k!PTLS8pE$a= zcrktH?v7)f?Wk-EiXWdb0PBREjQSJ?WgmQ_=cJV}=Ga5RCzavxx<^JfPE(IB3FR)lL3 z?5omV&Z3XPdMJ!M16t0>pfY4Mwbp&b9kF`LJ%}#hVieEv!;5>!XZ8XproLx09EIFM zzAu+4%w3fFN_na5$4vk5hu}U(ABL0xS%Nmy*mn~>Ggp<{9xrwZxwV8IH=aokzdcJ7 zR_r9`dx*B%-a-k(1@zOWe0u1#0vb2ZL7hVpbn$^QTzmO6dW+wo^i^XjPI(~?nyE_) zvkIa;Rg34b1i1JenjQoVZ=6d4EY+$!Hm@+=saKm=QcisDJE%TwD>IP zv78Hwe;0t~AO}Gj*M)w>}Xl_A2VLl?2qDa*)rJ?MAG3TgwpF8kY8;r9msrB1d z7^^8JiBT=+FJuVFys>Co>w%JLW>|5*5YKn5MXl>&;Pm{7(4TN#oH);uNVp2Sg%_@@ z!Q3(IIDy}_^2Qos+c%tkT@s9jmlPS5)TOlb&|X@jgfw)5BYivk0i6K0RWBf|U@WU9WLtuMULoSRT@c_X#mc@HvqN8& z2yAs}QnBVS{bp^z&4@TiwUac6=HE9oze1mJk$8n7?e%Ejlj?|^&U&1#m(!^g{uAyb?Pe_q8vfpE5&YODoT zLpIXU)lOWbRyl3YUrHCQY+%m8e(s@;Jg)g~2#%Ck%$={eLw`IN!)0%e!{PPjB)$16 z3BMVQ5&mn);PT^Opjt`2HMUZ}Bk!q0>T1~db3J*H-U@3yC&H9he@J;t7S^R+<;NBp z@e*MwG{U%nm6~$^u1(Bl^`>+J*K5z3F1*F+%v}m^HkU%?^P>=ZcO5vWUnbkn1<~2D z0_ena0u}F@L%lZ%E~aPR^ubUe03F*baQlksn;2<&ao1EDF!UqZ)h(wkY8_0s;F7)C zyo!u@`G`a=S_u0}>cC@#3K`Tm2uU&-G`+JLTvqLZUE7A^m8tW2Q)zcB-!l}o18$S1 z<m@s6Ogw9HWh1Np=_{)=sQ^9}SqMDWTkvRj00hOCGZUlTh@PDhY9`i- z4$BzR{EWkBb5=}S4te9)jgq3!pN+J3SS$VUF`Eu(PN5m+i@~RTG3aOhB$M(*r1RK9 z66~V~AA}iTVd6o^c~i^EKUxT-tQV{7mj+n_xE1iRa#OWxj@e@myoxuMdYl<(PQbn7nJ7^$MN6ok2YE*^0 zfOhOn8Z~wUtsYoG4S%&Wr|J#3A!dXT)zw!d$rAg&=28pJo)Nlzu9&ASzLC>aenPsRK2c80+W@{)GLurezXnsFT5pV+?J9V zZ-dD67eUliJ(Vf2SqMY+{2~9{98XIB$-~HB-&i@b)2!*usqj>7I&?+fhgV4F_xd+blz9bCa`=8c6Y%YWqE6e~LH@dqKyUz$C8{Zo{F}*60IUh5;D()Sf`sX zg>9cmk$oQ64SxqZH-!1hRt3^Vzp#^6^}{2n5a{}r26u&BYu%4uU{rP%vaeN>+bvdD z$XMfpLM6a8D6&LJqBzeXkB1NY*pPkSI5)L=zS-Nlg%^wAZ83n8?}IlCWTN51%DFlVndvC z|B$LJf1s_fQDE^Ku(DzA*-;@YA#TP;xZi#gPt8{ciJfa|KD9U99=x)-Jr> z*qL~JQZzWsru62sD|j#LKTNwkOw>1Y3ugY1;yn|;@Jgxo`Dy=dNL?pB|LbPJp2WC;1P>UW|N@Np_iPYx9 zQ0$okW9@q3c}gs6INlk@oF7JpkJrPqQFZ8Wvy+%BDpQf*w~==o#4j=4L~-3~44T8> zuoEVt%xzH^Hen(@F%%pCtS2w6n8jQ5i+J}MQ{GeWD=#PY98cfZz*#@P^RfkUsO=oh z-EsIxPCM0+{HLz;bYLJ^^17V9zwnwwCMGj?jhpG@v1# zayWCubmU&&rM{on@Y3UTd4(H7_v7v_fzKO8ZT4hyNsGcztx-5Ht1iN&<$viT{X{N3 z>o<2`aymI2a)x>fcZ$hXu5SAiYB>BT=WBe87Nw|R!hvL(@sXn!MI2aF%mU@YtuSL}GM!mlgwu;R zP`Ss)sm9^a=o#OPzQ0VF??!>N>{Ai#eSVhms~bV|u7%a>X2}eV9jG7C$lUwhj**25 zw51^qlGRMX(@sa&DH?OjcB|k64+&mgw*~DUoxwTM3beXRf#kIgrD-XnnPY>!wD3(E z_is@NIfDB|HU69Fvp1s!C#{>n;?1R_57dadblb>e804Oq?4Uo5eX+tVf`;~m(7S=d z=qtvRPCQx5oSJi=kz6P6a<+8RIn$gWd8-*KJ?k7-b|D{i9VcUkK_t@UrCeR#IT-oh zQ8@Ud5%ye7!=dts6(vHa#%?WMI6H-YjurBxz5BSDQ^n$s0()5Iz)|{f@pTb1(vfRi z#dE&f=HmEu+wkE`Ma*J$p>6J6TzRhrPl-08>n#;bY@f?Z2NdI^w$E7jXbVRE=S}%v z<4MI94RkRBe7N)`zBsvn93HuWY)v=`*193Iv?VVRn{q%fE!Oi<8}NU=YsebK)h99^=<# zj=b)-5Z-yeB0oO=2tU6qo0mzq<@K~~QrsI)K z(afGJS3&Nw1g-ho0Qwo5>AmJ{q+rfW?DGAIP4BJ?{_d;H?eIi2=`+Kors?#=;5o8O zW(>7%-w*O4MZvfGmn<<)rKiRi(faYBG-|u!}+1Gf}VfT2OnHl^} zg}uDbwW0VqlH(oi$&y`4?#I*gJIV0-@i+sX zNxT?O6dQLD+f9NYBx4ATS@ei;$XZKH+YbxgOcy-#q7cve48iV`pYUs92ESsuEw47+ z8?QVX#*3s1v1P3@Z#1ct*Bw>E+xOP-ZnT|WA7zg5CnfRo*o&CIup8HJpG%$}8!Gw* z<#hG#Cc%TS8|Pe)#MDPA__!&RHfps%X>l1^x-Z8)v#hB>XEf*ZXu$~M*F*9p(yB70R#!aE+&$tJULpYAYRtki}PcjWL^vNE2p@8m7DqIn&kCA`v%y}ak3 zB|cC7!jBgifiHp=^AkP^=TiT}yt(2Te5-Q@Lo27#uGi~P);Wt@DqKrtE}GIi8~S0y zxO-qxLHS`nPhv&BK52e11$NfY!1E24F;-#}oi?L@bgYV}N?LYIir!G>;@q9oYwCL< z>$r_;$T4A3^JLLIrv)>GnO)9yGgK+xk10G)2lD6hQX^b=

Jn30ukmiHj1{W=HIyHM*e;WE|mP$9)mlKG*9 zubv3B6%&sK+VWrIZOBh-GezFk@>-yMUjC$}lGjGtf6*AashRjGxN(<*?RV^koYsu^+z425}F*&b@pRlB{Z}<*KJz=WkN$+MewEO zx569J=S;ZdcqhJPS=r>Pj=wCh|GF`(&~a)3{K1?3GvTjQa*9m&?kd?)(wWfKy*IzBxhlME$>M@8=hk@V%caF# z&ZdQ%Ube+rTqhRVXWC*#*UI6CBC;RUR%i!hiJ1z$s8w-%u3RVi=b}LZe>M7Hc!;l7 zoaLk6Rv)z-@)I31gWMp&lb_^LeiCqKRs;M`1Rk38!M$1S_V}jq55mOmo`v>(4Ph2n z&%*Qj&a63ezB8e{`_lR@SLed=?yiz9*Xf;Qm0cxeHJ#T*be877 zlun@~gpGL8HL+O)5?!$d%Im18?Xv>PPYo z;^%3pjPv2i`j~ZuzcR>3iR6NMD}1B)uYGMS9X( z|DC=^2sU=)JtHpGO%QE)zM_e{TWibm)f&0Gw1Qrt7I6|N=-mF_^7gDpfX5${`~yAp z|CFexgTH2+O>vy^pWgb2)5w2b?X7RT;1b1xp4P^lt}3x$ac4t{t8-rY{?6SqdOPQ* z_k3ObWN#Iix-X@7Vc8?wQ;QS!FKisUKXp#z;soO235nq)qu)t%7!1n*tDQ!Wux_olozdM5k}xBPx| zDfir{taMD!CBzOh-npt9VPq z&q|XrD^`a`47Cj#Zy&*ZrmSqy3JmpSuTN#`u_X_o>j|$5)vkk@aR6u5Nm z@D+2mlSzrcXbw0al#n`N#&|XT^EnFg6hW9Ov)$NBv9wMDmyXzDb(= zEq|@YUrzp-0V+SWl2_M9>K7sZiKw5c|HG(3t7Gd+^DaLd(GpuAxJWA|J<@a zwm#g}3C4h}k6X4E)rS|JelR-&T=hBepb)a<-t5`G%@kIJ`|#>1W+nWYdWw}PkCA~1xfT;(9;R|+$;VkvSt z5%mR{IU;$uNuF}lB~co_m*43xv#J%yPwG{N5`Ecpz7FTlYE2Z#`poMlKjcjEG+=9yG{+}O=Th;eIJoQO3z=XRKz>~K0Qz0Cg35x5}vnGdg8|O zQjxr+*Zp4^_4#}s|F3xdI1kSA_8+4AWpdA}J>=Z|RPeosGyucxlF_H~9~=7KN2EVm z?(L9_Ii|+gO}YYwp4qad>v)RNmGk4$6)+fSUqWw`^WjSY!|UQ%lJz`y_jS#Nbr{e~Ktx zbtITqW{VRfe~pH}O@GgRD%CBA?T?=}G3I2r4o#L7kR?p=$`e;2Kbh=xBm((KTiC#( z`F|dLqJ5nY@g3)8?;{2F(SIo^x$kJfriC4j7tqae!FAv*PP@0h6wk-HevoXA3 zR#NbpmhJH^=Xb0=SJJV-;dFj%Z?=25YV_fT;&n%gLR8t&AMjdnqDm7|tJU&2gPwR0 zW^kOyxry&WPm9J)#B@3sGa{bjWZrU}){OHkZ!f(_`A>|Gt&sAQuzQab9DU+M(vM6=0OvO+Bu` zp-nPO+0kyMYe2!E8Gb`-g6Xkzi*wnT@?6q?H zLVNeeVVl~W@11wGApdb)bk;bIxg4MiW5>Cj=bSYqI~<*Fmb(r(x|%zGhy2IU<=pos z@}K4|XM4)#?pBAz)tu7OW^;(HRSzF-$lk22;0I-kwLIlN8s4Ur@*izIQ<6Wti84?J zf4VLQ?L_*aT}Jgzr2I!SI9h@ByE5~q2JhqcLxHu`EuqI-PCQZD^-n$SXon2cK%XZbg9`_7oF7w z`!cH9FS^PjxAwHYn7g+ivar=QRoI(7B-@r9!By~svn`ptj{GgTfWp55JethL=fqq)89SgETg z#j)?qF(dpdYdY4K!F#!@NAh3px`z6NzjIgdmh%_Sq}MEJPp`Pxn7(|GtGZ%KaeBrd zdaL&e(~LcDFSG2?O%q%5qLKd$;cZ!>)~FlI3wmBH>Qov*r~TXSnm-u)|jTbnOAx6Wz? zt6h829eX>Q(_MS_RUYW6oaIdGUf$c|97pU~+Me!QSp8T>YRQ(fDQ!=6ESg_1C#CVJ zj-|^A5)(?E*gkhz{#(d@>`@uwE@iCBrjHhPx$_@+oboQ2PA^yM(C>xvAME3@@AVIG z`!5*b_Fr(_zbNM@>^~it39>MX3LqDXtC;sGnF4L@iy{!#9U8b4)_I933aivvk zJ8)&&o9*O(xqW9B`7b)R^Wwtt$F`?#+VaMeZDZ|ECpEnBFmY)^?x4!ig`<{kKCPM` z*yO*|@HZBtvia)`e^cIt{70|;jQrJ{+dqJadJjT$NbAu@cvCsf!vz5vogVGq1wlNQ z2bx9~<$TBuMxQAM@~a`no~Vxa68(_$p1E5W?lVQE_gpJYM}C#Q`0~~+SK5x3HeKH8 zJe%fPZohoi)k$3RmFsWA%OBgC_*&T;3A4s_Jo=8UG=AKe4*$1pHr=nsZ1sQpwN~BZ zBfs*WJ-wBC(DzG!{q$CyLY6l|??*(pEbKq1e~>(Lf{@lY3_(UQL| zh}V+;TS0~T54pRAKa8*ocHINU=%~ekQ*{rd&zZY;=F1Br)91YJ$*(Ri?z+0La^Ci9 zdz@!eoEbgupLMkom!5Tcl9pEb18&(@kRj6;4>-`Zdvr|21&`%Z)T5#n^4 z*;nzUKN0?q<_xsq9$(xD+(c5xWM+`O&129r2(*qt&juX%ts~qtgY*rc8=*ma4f)p? z9>13(KM9IfhcM(nBcfH&s)ynY`cz+W&;#j)#MGIlCnM7fccsqnOab3db#}g2Ij`gW zcb%;}$^W#A*qZ^qS^l$%q};M1_+K4X-&rh-8`=vto5y+g_~gFru)^P`e)=&h{D;*~ zUys&no`253Y~)wy#y611@x%&$9M(a!r`*I*)a_4{{F#j9s5!|$Ppt*Laeb5?O~`vy zQEI(PME)~fKSmV8r#&%!_LzJlK8G9sU@ED)*!Wb_`zFWL3(m(o-g(c7BV$JI`=7WR zl0W!n`J9TRSN5(8pJnJB_D54#?!!}hz5my=-h6*ZkN0mLU2pBjZ}<75|9bS7C>?gt zf4!M;d85o;Asl&eUhqdfbLhr)!S67xUabCU9HmqarflBAaHbd{y7MJ5< zTWD{_Ntfe`ouOq?{*w}#A2WgcLr+eckQ!#aKcp%2)kp7xe_`k=lwWB@Lnh@{oPMs^ z>po8Mm*3@~;=1PzZ>)zjn5FYv8M9LTOa8zA?j7C@Gg-hEzGn!pHKL5hrI&U-bt1ntEEpO>@T=?SSOUwV%lxaCKzN1bwJ^_=@n!pmk7Yc-REwSN$@D(DBHxWM;vj83=DK2RC*lfAS%J)IarC z@znD-c*7sZd(Q2zMF<1`yQ9?JIxl(s5X0(m%o&T)4%bowMmdWq2BCm*M@_XQK)=QLfL#h5zzmmV_Y|VTt>Aaf!C2v`lww55j>PkAi9kg9a`qTE!^$GU+9jiB$ z3>CFMb8 z#4F#`KQ!X-E?VC0kj|$l|B-9m)N0X*n)hQ`o;dV6 zCwj^^sFj|di0D^GIT1cG^kL#?q5ptCy^a2mpD6MDAkXq}CCgHBW`+C+G3N$XvOE=+ z>!&!%^fFDZKX1o%bDC9mw1GrITX)+-HA;uv0*{BkmDF0ch5(}sC8+olt z6eH>QrqpN@IG#b`j{Wh|E2I2NhSO18=Ox@F&q6eA7K7zSG+6=G&Pnp48srjJh(`jm zdM3%&3Qd8BdSVqvh3EuZM=Wm_=b-<|{NPQZF+9I*UhtVBPkyp_d%`&>Kd~oVZK+({ zQ7my)$C_0M6-C9XzFIv+uoO1p_k`z_n%~xfIP+Y45a*xR18>StXsvDxcupGVa83V7 z`44_URv`b;BmY7FBOd*WA}4zv;a846-WBG3RQz!CiSAJ16YscUypKjUJ#oz$>vJ@6 z&cYt&@=)YIDd(KaCvDEJLVmJrwUnQvXRPUR>`yq~dtgnsqa%gd`#lbOip%K)S1mku zvUuHBMIQgi=T(yb1do4zI7Z>HD8o4gmK zdl&iVFs%ajaD~6fqdkNCM8M~aE>wW;b!rAhD{@!nC^OypPhjl}$gf(CJW(sV4`fxO89%INtmPfd@cN6FnG60`A>`||A`Hy{D=HEm3P3u zCCuJN{^zCq=YY$3tQ$GX0r+=I`AJPl#hI?QB{dn&>WVJpCyQM>&s7(s@85HE=hgDY z(*0*C|7o;TwNn05VCl3>)m4ZQmQEY4dVqgvJumQTS-;fNEFRwdQr~3@h>6LPe?R!g z!@u9?U3@+KiFHSGvhg#w2G^acQ@xUIp!{ce_P(i<|2$FK{nD|s?Z~g%yN_+{ru^#a zu{qr?_;;+YfcG-We;i$1%Utg_cR4Orzvz6cWJ|-vwwEgxRhC;WHm1#ATGLj#r8q6= z{R3yJ!lxNK-p;U9MM(LN#e~qgHBZzV#R$GMkN1`QaepDn|8?@0^&10k@*jcqK>ha% z!1T3B)}Q>>a+8-L>p$k7Jx8y=&R|rOoM%lb<&WE0T;N1ytZDp;o1Ysc!5|0L?z0X|qK{>ZN+BDX^Q zE8}0o{d?W?Y==_x61hxd+a4(ui1(&nzll=8=RByK9Rq?A9?@@SIHGNXL#`B51*+wjV< z6{9lj$bUwj_0O>XO&R0cIz4I=@}GWriTcD=rA@B-8Rd_Y|AvHFKWq2vXw8pLAJgvN(b}L}K4!1~ z_Kq{Uc_Xj*pS8Dgp3I_&pCvQgpXUaV|4><` zgFodzdQbk-I<`<>$PE#$jI;}z#0QK`Q5nXm5f7v{&D}He<+NE#imp{HxdcwVytVsM zding`YulZt)4MWy;a?A4&5-$TulMrr zonKUUFYCQr@A@>odzou-z3Y=1)$_I)^LOWmr^mR4HjsZz@36z2ubRh2_xP-M`k$EeRt3|aayH|BWjjN=DSW}07tSByAvbZAmNkuG^3vQ}f^lRbjbLhW6g`LDhQmlOa9# zta*FAbvWN~??=;1>QsE|FffN{@Cek^elr>Ri~GN5kM> z8oJ&;tWJge2h2f!HFvdFm=1nV^Xg!UoL=Gf1yRrNmpFpqu1J)-fjaWX>)>yb^WYk_ zMJ}opxy8PAwJ7?YZp&C^m}!W(usg$*RKB&PF>@dY^zr}LHkwW5N~TVIVbdPn&2^^Jx(!y*jg>9*~A4UUYG z3)S1ZlZy9NUg%2d#{S{F&}F!U{K`rG&docVpW>^_;n?r^6#kBnkpI9xbT#sy1s_HR zPgaMR18bjqb%Hv?sa}TZ`lv#z<_j=u9MSd0S<3;&Tj^3My`c$azl8E1naAKLG zbhGXz;&h1R;Lcof^5@aNgwte+Jo@RFf3vYHq-fOY`cF4W-lxB?cf~p~9H-wb>Y7)K z{Ax>aS4Jb{SIzK8ezm3LOxtGUS1FFhM#px?Cn?81X#6P2Q9peBgkbVleK;X_k{b76 zs_p*)aw{|P9}i`R6;w8BL=gUFJ#ryDl`|O5)F!{f7qHL1n zPu@!%^&39?qWI&*;Oh4(R&U*$D;&Nw=WwZ;f0EeHw$8FCS9iFeaf)aMCp3oeO*-X@ zyMyuXk#Nddkoo}~?Ee88k{tO6mLt@DGGvu;b&im|Ap+D1*)}m-47pBSt)-F5L=EoK z#aXqVYNTqEkC&EZm20F0T3Mz<^DVs5|Av#EzwYNLes9_B&vClDJpOl+zvj?}yH|~X zFL@`f<`|zlSjwyTTwf2x>!jRDliMJZD10iBW1mV5xI4{%-12uamg*ll53ct^g=vNU z@CS2gJ;?iR$y3UCw46KdxlU9q_CI%!@XclZP_=#E^WU7y?UX$J1n6I9&a{NzWKf=YMVPh;1sWV-rCHHCT+ zxzB9AP$VXSg{soCeT+fQQm8VXC9YB#w@$_LAu7=p!5gUm$w3iB``xN>=O>(J%YuJ@ zkPaW%?J57!D{)=2+=2?t!q?0ZdDh6RD)`F}J(6|B8@^!Az4En6+f!LR4=IiY7}G2r z5Ak+mX8`%vj-SuL^G{1Dh%TIk4K*-jQ&%w`R3-8}gstnpMti$bTxf ztvXjRCEQ{!;0=_Y{MiD+|0wP=MES`|tvf%VZ7QR0G&OFo>Y==k*#68{F3>$aWY~^! zy)s+)iI0WX24+P}@;Sn1J$2&JB%h;V?Ng_&f+U#hN#fAPuAOXd6aAwTK9RL}+gE?3Q)=t_@(az_-|z>^Lxy zZCMq^ke|r6Y$n&1Dsa7c5sALd1yaDt^0-7dQK9_DV?qa4@XqUl1 zVM+kyCbBPnn+&GPE`FHI#v}i^V1Hft$_ztDs~y+>RmX(d&YyU~Rvkw9PkK{ZEb<@t zx5rR^RlVIgZ_U{=*gIVF;V%*XJFDk8Uy}TnR4#UQ?VCGVR3$4VvtrOl;fJFAn<&n_vm>}-2^^PvXes~c%o9zN|6DoFn<)R;TE{Oa93dOO=rfkE zYK-i;MenlrR*hlzC;kcIj^1VIq4}kwQCA)vY*W&3@IvKI< z6eoKwt+S}E!>S5@+1PCBq5eMxN2|-7|8&B?+tGu(68RNUSMH2(c4<><1N@Pnlv)zX=WO%a-fBsx9=pwdd#i0w%~KV~Pfn}mJ#x-}E9E~!&P*?) z{HMPqF)y_>0NkZDbq2}qf9qXt?5-d-DD_?TNNNzfKQ);>g)-Jdnf3?x2gNC8!e6x@ z$S9t|{dhsje>A4C75cMM{^PXti0J=ga2PA2rTiyt+gb8&POH9>?kc&`yt}Kry0WBa zb6G3-U$eK`;6Dxi$7fV0ZA&aH%?nST)2(jmvu^#Ehx zakPjnL8h$ZoPxEE-J7>n%72`~c5#}~K0V1888NNeK6h(c`tDi#9O%E)wL870`SRB4 zF639PYn!&UrgtT|uG!DRe_nS|(^;E4KdHJoKXDCf=xodxH?`L%|DBULk3{zPG*JFC zq}4z@M&wt!D|^4T zw4X`un(z9$;R^Pj`Q1iv2J#bQKKaMi3~j8E@}Hp}R8juZ>s{cI`Z)@StwBRs87D+}W#vch<@C9O}hO+@pfM41x#kHHB4FV(oj8~$HX z{-b(O{EG4)5%~}0S7|RT>6o2XW=h|-e9q-GXJ>UKIMaExx^iCcwS1?ux_dtQ9rifL zKk1_@&2R3k+?KR@&*}{+B{hTBm?q>txvux_^``aKCs$n^P%<0&kGS>TW&`pcv2+-i z!-Tw1MLyHuodf?^unzvQ@c!&+<<{r`nd-A=St)W+25u+54u9UA{}}K~nfyyN&JU6Q z>{9!xHOPN@)V`wl# z{739T{)7Bv@c}9Ssa_`KKRdgZb?+}n{?m=Td>h!l`Kn9uFX?tl{_DHvyZvX)TTlIe zK8l<=IWfdM{7;duh9yq1sy@tLALe&oovPNhj`AN>mdy}4jPjqiW>4~_{OYqYyomft z_SqP%7>x6r&&U&b`e*((5%=eMeJIMDph3G9^)B+I5(}bAhekvN*EdFQMg9}Rn?5gbEkMpjJUCv854|82?Yq{!_@+-Ihr(Ns29mkOW>_q;vp7NiM z3ML^xF$W(0%?H7h|J45R*gam}b+X#}k9{VFBEPCPfH-f=N%STnpThkr;a@jGuBY=f zc9iruK22Z$kz;*^qyFJF6C{7+ zKa<`0&u>0Z&~;4KKYk$PKGyof3atXyUDe~h5V%_s@+&!!@=VGN;VZ{c5ybV?H^q$D$?}l>s!JXbjRsPT)NLSpxm;4gW3MR=>Ua zlbuapbRWCy$Q5e8Xj^40$`w|Wm9H)(o-wW<9zHXZs5)HOw!&h`Rn|r${}Bata47O0 zRAZ)MJZ8_aOkP9FLC%)>J#;_M;kEQS`d=D;h*Gjl-$sz(I#+=j@ZU>x^p%#?M+69-Wpf_>t3du(1I>78n@NOcA2l=`WlWG> zcY~a#?8g6lW4AwQ*5mU%#Iu}7^7iP?dnkOB)B8O650R?lwuyIh^!gt$aGv){%f;g# zCT=Fls6Q`=`>!Ftz`x1tC)dk}!{nleg%XF#Z=#IyA(TO1d9+N8&TPI6Jwvfn+=#_p z6BtwZhX>B{5)S`pFY+>^C-|J`kpmGEk)tTl=@mSx!>p80qs?y<(Jy456agD7+=<3eNbahO8I*`Y0)X zU~1$)TJroRrTho~7O|B7==C^0zUX6Fqz%j#ANM(un)OKGo(Fx7nzEiM>;;9aU%u_~ z@jkl1^2^z-2YuEqC|#1&`(Uiyw0mEY>v76|3id5Q?$doKpYjuzt1tiQ-RVL<50|Tt z6+7E2wjn<;q}V!&r;z`_jv}mwS0HCElutx{!WC}B?~9b5xcy~fA`9mO&~GM@CGc@> zhQ#6D5uYPZzz^P_!Wd2Y&oZB?Jz1k`BYy4kRaMqgO;;b9a>`Wul>O?1Q%;GszkJ6v zEM%>z;g@q=4^CNIRqpn$s@^xx_1LtNRb7q9f1EDLPr5*t>&=?p1MMzcZ-vLsZk#~j zG4dbypL6>+*&E6K$q()4;6HKrMEhBT4VWrBZ8Oo`pJbKgAYizO#bU5!iHk^AhWo)HhAXPnyUw{ z?(S~ya(4G3|7q`XboV0vsi;1AsbX_Qg<+PZrvdrZ^oO%MT0^2s9v!aT-ztCc*cg>| z&)4!xe;%Wh&H0*LLjFMhpCdo%Kl*FgMdUx|ha$W9XZSDND&;@FNy+f;>^L6SIAdOD zXYYw88fMK8{c4{*P0D{}QGP}FkK5l~1%GGEIy=?BBWClas-63iPQJLg$W-0-a?#=) zWxK1}(w1c$_`JHjAgyNqfzS69NcoQq`H#($|JW`SBL6W&O8HOZ^!tOixAJ1iqk|^E zeB$^GZeI-{xeobTi{=rv0Ajizhr5VXnAw*&x>Ynwum#@pP~FD zVn(}^pUg}sHpTAGu%+SpF9%UX{|7{tQaCe|3NySU2iu~{PCT*K*z}>cM7H?aM{xjh5?3c)|9v<_qatZv0kBL*hd|AncFL>R!^{h@de8Ia$ zOA9AUsH&>SJI@VD*lToJdfoX?X-%}$f2pd{lV7EGxbv&(^C`%$rk0*Plaf?|9Iz>W z;k;=X(Uy%HQs%uh$5J&B{ZDFqrTizxrD*!PNmw$i*Qf9=9m0!|y*^F$4&*;)46U2a z>J$$*%&ppo{O4iY`^Yy?pRMo5El1uneAg}|k4FBJG&OCxlwVCvtHA|pjw@5scDFCAboE4+ zoh?r>xUT7|nhH`3`xc8;@SpyYQSvv`_%>8ogmDI!qG4B|*`5EinBDo0Bgc1EtD%+h zpC@gztL7vBd9rqqX+HVaE=rvLd*p9fiK~t!UN)opCt(b_-_uCK-N zbJa^?U0+93oh(R??KVYL6(Kj-W_-13(uQ>8Cm$g{8MnFju8&gJn8&T|y{C9K`u{}q z+-|9J@ckCE~pBjrEQGpFtAm~F~moIby1+htSnmGotxvG{8BvdWrk z5!>3y|Ju|&%>}b!yBCM;DcX=;ye&GcYEpfgqcUQ$DY*XOkDbU*)=B+mkpI*Tw`>h{ z=RXz$_&O6(=N9?QPiCrjmx=~A^@jOD$bS|o_ihc6ss6f@T`dZdv3HlU-l72HKeO59 z)s+AE%ahQOl7Bf+ZWJ}he+K%F5QP}zC&+({?)*ng+qR@TS!HP`{<>1IsHf zzeq%G72}lr{}R*VXq*-1GKB5f{7L$nVnbNzy85(_rcIq}MXVpxe^ zSlu9tF|@>h{3p+70AFXiU2}^*cDVgph<{zGZ*fq51^+^ie{qmZ_U>HPTO80&_IecC zyhaG;bNqQjFdBoU{>WW4#rjTvQ4AJ5`OoKVi+31aM*pAcWe1X$BR}b0ws-$>`r2Q1 z;L1zH`Mtd{$gfcU=i&b;{A1ky`G&B!*5#T$DwsN1&u0c6E_gLekNl^$4EfK!$bUN5 zg&Jaz|8yEc$)9_7?xYV(a31s5r}?I0iQ1-OoZle-*;^POll^rRyHt!Ef0|{@@z&R}%ZnURN>w8sP;V z?Q#8t@W^aS1pHqqZz_ASXb18i`_*NR-V%5I<3j#ZDdj)NuPFanU)<= zel@G4#O=Q()&c+AaVy;ZV{03v{3o#X6XZXT*9}WB|_Zj~}Hm(~G`QL15LjD7PDgQw~ zwsdV~Ws%=@q`&bL;a;ElG2>|m}Z_o;XDV0|s}A05lgrTho}$ghZ$TOmy*BL5*$4g|s* zH#A3M7Jq2Yxbq*4;P$8diu_+mXe!&bx?=UGDfTw`H2p{gZQF7jf)91B60v)T<%Ro z)0@l1^AeG75OKXY5lucfl^#;til%>~jYVaesKNcoss1$rg2;=F=)0i8&AC5C-+Ux(Y7H$!!J&&F4eosE zhk5&rcFA8w{yJU*M&c+6BL5MxJ>J>au7ZGTYHlU=JWN-j{U*bGH!0b36Qa2CL>jKs zUHs>d$)7{46AnKP`ILtOk$j2RN}l}Z202Lu7k;|wsW5l137X`deuL7>{sklQr@_5_ zZ{$<%a-XmJHFq-ZOF1OU8FCbS+$3smpID$LFOnX)mroztJ09@y!E&XKPam^979OsLF#zv0KD8raYYvd*^{&ohAKJz-Y zoTq(R=jWln{6qS8LgM@>|532GfkbcO^Gcn!GTEO^1Ot*spr5sI=t$#S#cP7`Cq_#W z?yIx)C&sNei2P@;ajTU7j5O|f5&2N`NU;Iuy2_a0d+qnTr*?K`h&`PLUOLyitg!c7&r8UE z3SAXFGu_-ev*N0h|D;*2#D*9mrjO0+i2(q_nb)~Qov*lcn0Hem_msGOn73VT{-u69@}K{Q zxx0aD>c0ENf1-)iwzemlsHg~7v9)(Q@v&>WPEexN)|y~WLXZGj4j2?cOTd63_QATf z+b*yK1VvgaARy9pJV+4Gb?UwC+9oRz0Rz7Xlu+sRA0e}oo1YO1 zcT%6ehv#`De-VXAFwD;!jlv|DhF^3vN%;1T^y#*-Xum@-KcC)LEB7aUWzinu{K~={ zdiki+8-8_dP3R!{@9jyz`4SYl+TWGM*DVQyKeU*}^?h~dUv1O(jl0H+8=MV&cB{)q z{0A>cVlL?0{+)xIU#;jEv~O0oyuMJ?>6rTJ71Do#|A$L*TD5`!OFaFVTcOdiT%7;3_f-)8NogDCp(=j#mZ%IOV!vy>!i5AC=Qnt(J%}!AF zF2hguPVnykhWN=S@BUvCsjd4Qf_ojG(I3HoY=h{Zk{)4m-C1@yWlcn1Lu+ztzt0;` zpk&MVf;TLR*0g-P_oi?AUrSIAdaG30lV#7BE0D=uFL7^YINioO?ky;XMLWgjQEvfqXvGJJutRW)A`IorE`p*3ja|yj1vE$ zXW^jT?LR~Gy}yzEL(F{~FOc~e;wbPR#l6n2(Z4>-=JB6yEBvP>@Ue~ zO7tJsZ#vFgGlcc)C9gBr5+eE!s8mr$8h=u}TgB`RX?!X6fQCs9sn8}I&^N_|lxY*{ zk+NNEr^D?(IDR%(tCWnhoGC1hS2B(Pi8g2VOhtJw=RZpLRk8*B;cqs}N@|8~h_{hDGfiJvGu@hQS!h_;amIBAO5p^{}6rO?n^CC zx=r63hfnOi!~U*4k+&RqzcY&WR&sCYE%?u;$+=du>%E0WdqgMeyuPB#o=~8vu)Ni4 zHEfBGwY)XpO5d^@N%nszYkn)QOfQkk%rSY}^h~fcQ86s-*s;jCXF1vDr z4D+hXb4Lb(_RO|fj@q2^$ftWOr)+l7BTrpBeyXN>(UcXfH=@(vS1ShYMy;ub`y9AU z|6Gr4_|?nxq<tpK2n!vNp@OmBQ!OZeJA?sRjLy6zP$eWU?$%ja}50@$1_up z!G8{h$_$Z>I74`D(3%JrV=l}Oe0iHQ-&j2m_^i@tG1m;py<)p9T{V4j!HR2Vs%pBU z1uOn|SDQAH#9PsNH>&Y-^gnusZqTCt&U4gw73qJDc5(er(WU(x0teBndqZ*t-||M|_e zRL+0$yHYv-Ia8I^5C0*4HJ->@ap`CP%&-ZDA* zSK<0(Qqmv(BS(KGa2l`KsO#1Q&SRQeKb)j$O8#4g;=?57;?sY1Zul2%?!l)& z7<;Wi^S7&u6}@kK$A3QNFK^ym9SQ$g-h5sg*_cQnqYE10KUwH+;(Am4J>31Etv2+} zI!&Mb9r|Y+jUK@KkML9|+lBcb;m}dGI~V>l+RVOXA^vlERf+|EWjV7drrzyW%6b#_ z|7fhzWJ3SZZLy`@X$76O+3#cMm6iwpl$x?#&sHTf_T&>uTx@b&fRaxD9?~ z$NbVrLxR;AQPD&C4@Q=>ll~D~G^GDE_{qjh&QCc1X=!;)PW+_E`r4dLS9#`P=Nsyd zt32bd{*8F_4}t&K$t7_xI)c+erUYYO0#_H^P6o{@xPia7eML z%Uhw^#9);Va6>}@}?>qD-; z%+o(e3qOJXT!5dnY(UZ%Z}mSqM{*T@VxO)$aTR`|o34Rdt^I80v|8lkq5XoYWB7g0 z(0+a)yl~R({ZtkFYSIw0lk`_nzxDLbApT>FkjP&n{u2iOIa%cPAGix)(f7#RjX;1&BFO&T*3G%;WCjNu|O)c;r^uO67 z5ODoR1k5JlKUx9%hLl;TaI1~zO>#5Mn{-M@Et^hHp41Hg zc@+K4@Q3vwTMrrG59>cY*;Y#Y=l=Z;mw~KjOzFh)!|~30$ra-j1?Zm&zlyZE?x$Lv z_H8!Ty%*c}*cN=$mHAT3o_=rm$>PI%!lv^w0%u*$-Z+gML|rt$hRbomf0}Z*bzm2o ziupVoU%%Z{#g~!H)N&aljph>PfkVOrnit_blO%mf@_1RaJKw|Wh+qAI9RGUyOG369 z&BRYW-O*NBmRI@#dXwL4cEYKw#D7l49P5YwRP1Aq^;B)Dw(euw`$jfZ5AGAU#PJ~VPOnlh;2=O2BC21cHn5wFnB=y$Rv+AW6(61xeu-aH($-jzOGC4$UAb z@+1zuxNVZ;I4hVOTjBN|*I&l%e@TeU-T&l{ygcGR4|=0dUN`4H@8`MwXM>`U^Pde0 zyq@@v*zG?8G5iYtLy1DLCK~Po+Q#iW{sX7NEimw(rIdU7!}%)in9%*IAAia9$8i<$ zA9xS^4HzeUKew3pkN9vd=`H22Wn}2}-OR)`jn`-5)Dbj)k}v~@pEMsoNXX$Rj^6{_ zhoui4<;Oq!fB&_lKb%M6@hK#EJaT;>=Kufwhv)I12j26;B>O3lyudAxd-?AsK>^|| zNKQLAPvJH=S0P^?;w%4Q;-mcc=YQOFAAFn#p2X*_qeVXcFUUK90=V?$u8AvXtv zPfi~Cs1VHE1Ci$pXFrzsUIo8m;3<@R4)GKWMa_r*NM})M;y(xHQflHq7ksFA;y+h? zXuUW6YQV=nzPcrJ@Up6~RaLKuk8dB2(01w3w>P15P@g+k>PRpV2YQ$#F(&)2+`dvv zY(p*lgt_r_g9CoT{Oe-X#OA3}dBI}&`Pb1um`_KOIUsyNlbQ_`5BpT z3IFrlDEJS0=O)s!9qEtToKZqaOzPu(CiE|fc)V3BTP(?sm}+6({$IiQ@E_l%r5gu0 zzlu~j=8IK{Ckr&DXT*xVHElZkPyLHNx3z0WblRet{;~#VE&RlV(I3u#ND}|4ar;$% ziq(~X-p5}UbU8Pxul;JZ$_oFvEccgJIEep<#k)rN=J$OCLS};MUXSAg;RHqeg!3Qx zNyx%_{=JSwI%J`qZyT(nza;+SI^}mI*)YA&dCLEAY2uL(#e9j`kf4;EpAC2+YdkKY0-}9^v2kpVw1hT;k*9h!q0g! z|Im2l|Lg2-^v^DMsgL+cc0n@npOl;*@gFDWKdsJ&6U2X79nQ`1O7&B1 z@Spg0&BAT&{z9d@KRqk$oS694LhX6see_?Uo#6au#VXEEs5g@xod0~9;(-6$hF>`g zx;ekHp6D}OU;U@P$TuvdxvT%`jCjM6*ZT5roXGmIEBmAdEr8GLrRVBrkUvdnnbUEQO;a4hM z$Icl_$FMlnk|b2t4+(6RBzl&vQfRwI{HK!Zzfe0${DeY(kDrVc-NO9On<-;u#DA{g zJU^WO)LBpDmfcuQ{0i=~+U-BBFXy|Se>5EZ+gz{Ux^?D+7W5B}dOQ6X`Uh)+(s!jC z*vTN{3b#UOZ6)&Rp68XUrB$BX^W4ms z@?Lqe^O+gAK9?xju~L{)5C0ihNiWx&qHW#2I9+umZR^3;fj!55ek_W&Tsxt?2mjfZ z>M#-i*^xTt_Me6Z^#9+>&GriP|I2dp$K&%ar2pn{L1ktzDnOcj@^PjUl)PWe)VSk5dBg;{7Mi1DTkkEZqq51r2i0IgY`*%{^!`6 zMerZ(dF{;{_>ZpMbh97+lU{2zJKuYnaX34>8h`OZ*H}YdN!edk^;*+6$0z({)qrac zm-*wGsdF@!{7HY2`jS8T3xdPne(_kWMjRaa_Dhj9aSWq+`{l@caZF+U!I!t)JDbh< z&%KUoHG`c0bbI`#n)9DltZyRzW0}}qR}KHkAEoWYf8x(mPI z@VH7stfQF z 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 yzS+Q%=f8NM@( zs{|x!n*v%4rJ|^m=@ix>Mksq#mJv(Eh%)ML14}^(?9lN%P9YY-yHtnkD#0`rV} z5BA4&d_(?N65vd=0)L!{B3gt}(PCHq6YZ*h;xg4gahd9$xQzU5qD7-(r!x7f{zlj)uQMZ?Q*PktgUaGgDQ*d^zI2K4@2h>Cjjt zSc+`e$tgnBmttsO*qeO$#*W5<|6ki*kKqWSh@(>4s}gZBpH-vG{bMC+j%}Igu!3=x zmpMnCXDSW~4RMm{Z`Np%;4jfexCsBrmwRJOHd;2Rllbdb^;f@4uR8uG9Zlme18xHV zf5DIh!rz=^H>2GJ5e?nppJaCsN1kUY@1M9#(J3^Y%d%CLX7TwblxFea4|vEtpz$RGZgcQpFor%_TDrXQG(GFS-&ctO-y1s^4mZcH( zmeMH#mLNd@4Mcc@*j%D{C?92b3I5s>pbdc6Kb9gyV_1)gQMfCotcw#TBJGf^aTd^- zWW5PQ?g?5o%6cRE$5||TF*~l)(%YO4`v}HKNcH?k|%2-F7 zoRMNOMyG?a!5HHlDc_6gh>061VtZzYj*8>kg#=uiCP6QruLB_drY`KEmahFZ{JnVo z6||q>`3dr?RLlpd7}V+G)qv6ls2~L&;At8E%UItOt%j6Az zruow>h{u0o*aLrlr7ryaFvswL>I5P#%^})FJE+G)x|ivM_%O{TJA-$;8S@7m6Ai+j z6W+*A^bAB<^%iw}4HyHR z`BHPfzQgquCXMbJ@4^GWfc#IqaGxs?A_Klx@eU9A8+ZST0}T5N=2slt-@o?v8u)t+ zTtf{EUf-^@d<}#C7C$0s!n7HRJjXKV z9~;UVDNmsC040=5Q4Ukk${_h;VPM~I7p|Er(5CS5iA3@lAWa9AuRy8;dY{(? z@o&=U=(rmluFnE-kg54!jjA{NFIoO2>i5lels`~+AyH@WG*jJiTDU(GE+h_-dHF(0 z_#EXrAoL{~?D@%%cv@JfwBFYZ z_4^2HxW82xh0Q%JF{2QenXN<pY4{?q9P(10M2rfY!YZ4h+^BJz?;NdD;0jpM`qyk6Ad((vQy z1)W-d>fhhetARG0SiS~GepJ2+Re!!*p7#v<4(7Mye#u}5kN2A@UoxVM9M9S-S@dT^1(ZG#x@zlgkTIM62Ie;W-K zT^oPZhL67`tsJISyeh(h@@b`+eU#&LoFN$xGza1B$g1a~QQf;$Yj`$*{krEPc`Im2NqD4>l3_oqt6@Oc+zky`vF>K{vsI)Cxx zG;tCBR;eE#8h?lu+{5ltqG^jkhyGz2uzaD63kHbPN&NNNseSyapyAT|EaJ2X+*`1~ z2fL+%siXdAF$9Vikp@Bi2Z*8ZGQ9sf{*vZT-ZVXuN`A!=$|~U}!5e#fv_o|<;mP}} zgP0CHU9M=@ljbc&%ykk?jK&LWSd%J*G;XW!}H;N^pRpMa-d?1#w9`9=?e1D5)?Y~dIgF&GN$*@ zR(LSsFJW(;Hp!O~J+Gsaw9j6Ky!A8shlw`n5{(b7qZP_?cz#lZC+bm-Qe=y`B8O0Z zVT&*fQAD9giV%iioVYk=fEQ1PCUlK}AMY+84!SWOL5SBWk;DXRIZ>A|wR{TYKA@WW zILpd}-^c5X>6}Bra+Eb9KS5qWxr#VRiAGK|M7R(B+k_j0f4~@t-vNs-72kwEJn@6i zy$z8#Jbw%RHz-p;F(J8ZO2iUDpD?ZL#mE$!K6_eOS>zI%UP-SgPu!WMS28~+x9rRs zqGTQ;-k_w{y~ycsO|k5-4Us0HnR_vEl>6+W^p+6G{o6p*ANdfb@Y=HB<{d+lBT>U?i7X?^U7XKS}Y7oM6B^$VKSa}l2a#^Y)i4*^O^VeU0kw{{`q5csVV)#bAF%`=xc`G8Gu1qu{Z!@Pc#z4HPbbxw;UdG)AFCnaC z#g4~J2zsj-XLTh|kJHoe4wLT!UU@nobd84Sd3x#=&c31xx_s3d01h+?$ z;G2x+sFHUK%@T|>8XF?enyQm!jdVj1DqlkKOhkX8MR%b;JXHVlyt>*oeImRbNAOSU zutk74#*Z?+b)Gt%j1%S72<~;Bs9*Tv(OTze*UFr@*afwt9miai*Q9*Whc5OPutngV z71N;LFO3GxPow@)!jmDU#`2-F4^ZZ1gL>yE49%hr{HoMGb}b0|4V8x~|t>8yq~uER%T+cJTTGMjL!9)<CI?Qh8v2s_%9cm#hz3@2>z!`wrDBD&L?8GPbKqf}&#< zTrQl=SypusOgTF3262$8FS>+XwD?iB1Pwb z(xRf+97k%jWm{>i;z&(Zxu)Fm)Jw4j$6+w*1|jxiFdlsADR78j=Er;2?T@`G?uWJv zoMG?iNaPR22Jw~~rtAYF$0qOF7xA3whC5$4vNm$EeRz(qe}3fi_V49%H*HLOXTkS! zs(X%Db}dNHSxtN+EobGrwU(W4q-8tSAwMZ~DCxE3iHl!y7-pQ@m$++gvhah?_a$y0 zlPun(`iq)Bc5N+G|KS;MC5D$U)f+cZ7V*6zJm_6C62);-)@~PfykxvHZS6jB=c~iV z_4MzHc;St-aiRVrk@qc_mJ>SC5C0iCeRY8e?<|;+w?5FLz&~#T=d|30rgddI7ff?h zG^Um9w57|lUalx#96!nWD-^(FZZ7>TiS%-@hqr! z^3I00O}JHDlPGMN0{f=M1F)c52>PXr>@Y;OrljTci zX6823om&3Rg3LmHAmG`x;GRN%u!or2aAuun=Yo6K%1vpW9ro{9AN*a#vc;pm3;)IT zU1Poj|EJjYF+&9Si-VRQ@+U`4@e*Fve@Hgvm-_qfc@24AR^0tF{sjNI-_7y;so%J3 z?my)AHo_lX!P<3s_biyiHrLm!TRL+RYp8Fk+j-X{7CH_8yYBIXPWRM;B@Lfd)c*di zdy3Omu-a{=A;mwMD!;raUbOzyF54~OcpIlMn%3iZk(Pg+dtB@KJGlNe|0$~fQ02ij z)X#8Jp)tY*7x0wuGJbHMvze_q9Z@nw*|F&6cY{6v|yCRJDz zKh&6Jqx@>9k@6qVoQ9`l$fgv=X~z?nrm_mXJ*6`3B2MQ0b=JvC*a+l6)|6+BQ8KRY z&=;er5N(648nMXovigEI!W-znD1E`3m-wTd8vMx{^Kqzdip1Ml=~RDxis~<`w98Al zKa+QJe~pNUmN-VR3PeClWb&8Uvm*J+#$p_g;x~?tpGg(RbzC`Y9p(jsSNU^b<-m}r zJyotWIz-o=^^drGg=a#=UN;vJE;s zj#xP~??MWaS{5Xl#kjSbq+DnH(=<~To_`?b2kME|Gpiz$WEBfhXz*E*RbriLhxY;y z{!1H6tSjKzesYI(8R!E&;L1jiO3nX}hglWHp!BXWT2spmivP6>DM-EZ43bi$`r{T{ z=l#ReF_@~Z`YOR2|7kCc=LBF$7G5M9Nd8vCyRR-o6mB(Ez2vbv@T@Vmz%wzo#BTI9 zdM4KGuqXIWvh(!V6B-(s)uY&>eOP`RW%g7eyi@%+-k(xtC<|qQd&3lpdb0${QUrg@ zayl`~@~kxtYm9b9nLE#i`nFk!u&~6>{R~Sb_6k34B&i}p zhnrJqbE%XVk^G6*+FjOPC*bVenD$Q%f&~9brACti|MW8QGL$jJxJ)TGlsU^d_fEZ- zYfz@o&9ifV!#oq;s0efU{+J}Z#1{ieds3%_DIr&QU&OEBKdBU7hCfj%66VS_k^Hk{ z1y3_j&>oCfcAkP(zK}ToL|Xw^VM9P2{dGO$AHIf{%hW$#4~m2_+WKJANf-`1E+RHi zm`l`2{Pj|5y}xC^U-P;=1=OS3U5Vea;8h2IC9iP*s}A6Q?+5q~Atvv7iS6iLvr-U+ z)A(=|@*v`}4eW(T1|#Bk&^(BGwT$ag`1|KcCvDN`zwSivBWOty;>8H)4W`vFmipsp zWULe+;9Q3xL5e_|`Y?+P3D#7bMZe6PAgAIw8R8UTtV;V3B6h35R8G3?2W34D75H(; zb@@w}&otfe4AZDjMTXxdCg`USjbruuEW$)^*b&fboHwjr0TUjc+?*|0ixcK2QGX{~scr+yAq1&-Z_V z{O3pWyZe!!O#R8m?&d(^yKnxaxjPV0cD?yfvo8>^?Rqn>d1YXovh&LabE}&3l%4x$ z<$CMtQsGZIMQZ!p`wIR2{i*F^?z^So3jU~5>abVAyTRnU+w^`oKT+FH>2Dz}vYF@4 zID-7);~#EZ-QO?n{QO7#7+=H-U*`3P;C?tFKDb#8;V?!d2KejfQtU^)ErLSOW!4f2n`@f#X=5ClP1~ zCDJ^&kbHtH{1x~6K5d|`7a7;I>N^FFWtT<_8L1@~0?w;Vd!ZvRzfL%p}> z*Q0*r44tf7-ah76EcB;3`2Wi2T~m{&Ek76e%jAu%!bLdO1SnWvv)->qzzntpuos9~(dj(jD4DBEOXXh$^ESZ`AjiMY;9^*3N& z74;84?tc;K6g2f0=&TlX;rfq?R{e*D{j(oCTqBkszX~2+YkX$zcPY0*el;mKRNGT{ zk9$&8s6H5+l>b7v|3oNwkMo7@%_r9PCOe<6YO0*okd!jHt1u(uY!XY^lQ%u%tjvZ! zn>N+CMK%sadnRf>G}Ov)r&U^B;7!||0pe&4{sW$2-wR3JgZy=B{$m@e=091(lsvvY z7^bXKQjJOR!`M2HBQ0rEieq!_vAiVLq^dLZy}e1!=ev7n`+H?@WYk68+7n@ zWMn+9lbw5_@11xait1ck3N!{LjZ?B#`-`xOj zz2*n!h(50|0Je!E=Q`jEyUfGBTj=zN}!!@C!a9jrpqa;)!5l|PD0c!rD=6zLK zvE4UB39%BkpN3(g=5L*1)hT+zjZ@K1Y>nP9tSc=eBcL~BbS=-Q4(M5WS4BoO{0pdj zT%S(NJg%QqPzispZW8N+ze_)f?GWyj(JIc|2Jd*$a+jn|2T1;7Yo38qU?xe5F$9#QF7KokVWOqFadPx}OAjOHJI)#r^)AMR86qt%U-_R949AHsjC!{f^Jd0bO- z%Cd6(hZClLQf^cHf71Ax(f&y}&wuE6RASkhf4nMoMSt~ez6?>`MY#)du>pFrB$RFN zwh9hxqI+Mh)TNhHD&92*Cw|yjS(fQOes6DQ@SW#FrGtXa9@Y{`_Waat)aOZFPkJt`}zPNJ*xHt5=(SBr~uq`;xK>ik< zzacl)@*muxg^Lc*s@1<$^C!aJrulQG;$aA0NLpm;%T=wz*~&NH*lTVFfz zgHC%z#_77>^c=H)&=*|yUZ7{{sZfCU09g0zvAPG~zx>&@7$BEoy=TUmR0MmWlrQ*9^_Z6>dI}Mt~y6o(>~iN*D=Szf_+)1 z@;^we%q`Dqnoapn>fC}1jjC_hJ4_P2hHkKbc9G$wbnHxLFEYHEj*T(C`s$4&2ks~!2nFYI{VQ-jq#io;bQ8w@_8mu7Os#|RT7?0104k_68&qC=7Na@H7eI9M`s zEDj(n`c79iQSNl*a4ySzcz~1#mFA-T4e^Hx&wsE&5^?-^UH+2ht&;oFbzGucnsBF{ za;DO;cj{kM#1wGoT973?Jai-H>nY8$CFS7QfSkva9lPfRmi)+N+p*i-T$$%mb{=%+ zf-LEvtFCfg9b5XlSZg6t#4>xxA zZ;W`p{wK{{&CN-_KKri$B43oNZo+WGO%wEU-#7*i1>c+_|LjMv{9Ji5BG&)d+_(7W>GzFy=PD}Ap%)D!w5Fs=R7 zhsggPs(&ZE(UJ621nCdUaP>)T{n_y@m20n=Tejv&lb? z=RY57`OoM7N6mje{}(m?`SQU4^sQ>x1pnIZO5{JQeRDHC$bV+E&&@1D{&UaLtea^0;r`$d^2fDL z5kkCv2#94wpQKR^ll$ZPC)Iz1!@>RI@_7F9((U&@d5ZF@f8NORpI3j<3~%ID53UOZ z0)?yH(T@(mr>;;TA$re%X~%j)jXiN3SG+3$4u|Kr5j8O}Yc8m88t zNp|gVBLAsQiceuJHkJu0UYjxr`465brNB{dA!7MKrXnWdMvg=aXpv2z?mQFex(bvd zD1WezNR4$-{g1qVS|jqem+nCR^9kiwIiWutq5LX0bgGB)D`Mdc@<)D^N&fJju}bw% zTh(6O(&ycfx$60@f>jx3Gc%lfa(|m~Hp3bB8wce-EN&w5pGw&fNBNIa##5N!n*z$X zE(>+bI3<&e_8(Y81``cjcz<1rK9Mdb)1`>;m*7wNk0jYfQvM@t%Noh@vdb0O_FdMS zUA`uH?sqGDk5&ZkS@5gA(6M8I8SY7b)xX^z+8PRFtZMiBo(P2){Hx}8H<+G#o{p2x zno^v5jQ37HYm&`x8>6DO2o@9NKTME7G^W6HN@!d|k)TOdbb1hH2#GqYY(d=;6E7va zC*nFS^1s)D{0IIw8eMEKOm1k^49u3OkJasxr@$5tuY;a$jq0Um)Ez z$$zdT6ij!j{?2O6zgqKuzJmN`nfhW z{B4jStCD-^;7@)!_+OTqKfXiDk15K4{7ORpL+zI&i(we@AN^h}|2eETjO@x&^B?3@ zeStVO%is4zC>ZAq`2DE&xb6V2e{*+$>c7F%PX5Rr?je8KZB82f7V?6b(U!a5FW5o4 zkB~h=yvqU(c#Ammse0cvh$gK(K)OB-(?{2xVKb_W67T~d3BrxS6ttS<`Hxixf8;+- z=b!Y3IL+Tld0W8DW_2HbVneUl8T6fizqvc)>mvF>RV!wBTTH$xkKM`hlM=&3d9Jx4 zzX-=9SuqulV4Q+2hArTXlowDQCq>9vRtY|$WelgF8%%@N|2zl#M_@iuent6@Xo#{# zTXkhvE>(ZzKgcr!Lv*aW3jV|8-0lzOtZx{ea>my+#~&I_{$Q-;Z}wHuWs;5NLm6m) zz+N>UnvG|sDKEt=Uq~#Csc_>u8l^O{!iDxV@MrU^67Jb6Dq-(<%{R>R-ZY)aQ^NI6 z^ATm9`@ml(VLD1dbv=_!E6j1uP00T!zdAQ0$<4`XFgBAX{k%pxT!W3V%b6EC`WPWY>|s)V4B-iVpSv)HomwhO0t6@I2FFKMrL(j4eV{Kw^vnGrh8vGJGJ{@*UHSZ=YH!y-gU?y z+WgyKupT=Lp^rj=EzO}&-$#8tTbli?D=WP<3k&^Kg|8I8PWegUD;xWfKkOU#N`JrY zlTiP|_29nH>k31DvOPG@#*qIcvTTX+H;WR_^+foSCK^Mly!8G^@K;Ru(Wab0WvVh2 zIThL)DbwNYxHnMucwOj}cdBmS02e|*S zKOW=$%U9IYY=ZxkKhFQ+jl$4@`G4Nn{}TECnfzP!J^Wwfzpz}P{A&9`#YXuRcG^^b zrum}YFZ4$PR4QUC%OMzW9U1rbX#O50Vq#6rZ8hGSqFS<&nn!!@7DkB?>8KF@4Mx{KG|s4>nb-+n_KYc(=i?8N8$?>J{{dwe#EfG1(xL?e@Qe-Wpw`q zTu@2Bm&jW9@)HgQ&ijX_V=&JzH|PCFq5jj}`=Xz`QxDdb+nZ(~mjdf%RVd6;X7@0U zQtPR)ce3TFM?Gt$llo^;kFxuvMtyQ>Ez1$+=`GQv@R^63mD;d8&&6V(5*rhgDgPl- z{zIhvM?$-8qJU}OR86n;3V&Ru$d`Z4h-cS4WvZD~9`W{?r^p-auF!7KY#AWsS5&s! zh?3EUoC?3(oJNe@+zc<%PZ1idR!*lg3*M&Mhy3|!2 zUYKg`BN{HIUMuUdX42>NcAO`|UWm+)i(pSbg=Nc>|4d}_22t&AHLIVq8nyh#_*KQj zo<=NI*c*x;%mg73{Y%D-6((?w7m{_7ZqFFEn1*Kx@5x>u4#$uA9=BUgV~W1g?IOxf zS1#v@+ya#a1!(`H$SVre{AY!h|JZR=2G4)Y;TdoxUOau5=6pO)vX&;GzVI7YIyTIQ zAIn^wDQ=pJ{o06|TR&_Rc@9)$%*Jrg?q}@lY)UFP{tM5Gsp69Tn) z`A@*n{%0-!fj`er7R07zWFtShD>ml61nkO>iWvTO0!>Fm?2ZKPZ>9rI`0MFZG5ods z#9*e}r^se>srgUB{PZI=;*O4=zCP>7T5%`xEANrD5zl{G(BE)mZS?y`XCeQoiGKex zZ*yOBAg#JD7(fn`<~tWce$vw5JGT|N3i6Zn!9IU0@{?8m2Fia{*K7IDn_B+!rk4L) z;J@nf{tne2PuS%Ccv2|$2YLQua;W)_S+b}csM zwm0(pr{(?n-q5+$Kx;L$`a*$L-#LHZIlp&<@7(&nP;2O{Z%%JFafZ|H+q|hExZ2fy zD0H%EwQoV@^3b1}X0$KR@}HSEQT->&3(Ss6B{f;P%RGFyF?xb9N)M(cB0q8LFsE8T zR)VM(d62x=gnd4u-Gbxx5snI%A{E(?|4@GN@$K`T3Sl9P8B;1BLJkbg?jPLl)vDN7cc80Rh% zlTdzkyIrRIWW{n9ga1g^v_^+S`OlNbPASNLD8EvW|1`s!@}Gg6Ej;*VEVL>{0ta-INo#$_1|5wN#^I^8nSM6Qz-S9wr z73C*e%)fT-QSP0*1^LO_O4P(HW{YWJw9bmBLz}EIf))1ou69bjS9+13C zMzBS~2>5ftpBOb+hCk;7x-F7!vNcl8e@cf_{$n3jHmwZ#$;@G$d3K)vRMs(eIC;Uy z%5|L?f%_I@^tB%ALw=Qxcs3NAqt*ZHi~YAg!1I%g$IZWHW{m!NTwT0Vc6MT zi{szhd{vcrHZQe5S)xot{!Q4F~j`7_FYz^EcM{~6YX69%QCf&4Gbe+KfazScQB zzasD8?apB6ES8%)xoi+SV^&%w-=F`ekIV7f2%j}R~&LR1^_ANce9 z=cwK=g7Tl!T7DHAVsiFvn9~>h9-9^F+Zx*ZPw*%2pJ@L7toi@K4}Zo^j_+0TlhHk| z&~-5%$E@8#`ADR+UCg;)2l?j6RDe5B1It&f@a023lqs7 zL=zca|5l#+NP@)g8&!(OI9rWY-BBIoKjcsOPew+F@}K(IeIevOp%Ys}!GBsg*gwSU zAN~$!ZtY2TXjSgi+}h9Gp%b@EtyzQo1i%2w37KlmW|BSC+tJA`JGp|cP9LMyTSZ2A}ECzXXwUlxWwEL^p* z|Kq~Yp>eDFk95?8f)7{r*LA!YYGSOXqV4IRdl_RM@-LPc_b(=2_;ZRwHKq{BKijJM zD+tD(!ugla=mj|h`S+Xv z;lI4T2L7R{`6uT0e_0qjFh4~8oA-^w?|8>23->+T+uvV`{D=IDkpF1@idBF=>XGNU zqMlO_huUy105>zl4Ht<9Cw6vdx2;klZ?|1E#zyQta(qS z#as~Lm#F^PU8+C$L$8|uJcjd%l>ZcBf5D9$Wu3QfU2UD$+w46R@GQ^#uz4$ZV9ufD zbEg8G!6$YVo^1*oZEe|=ccQWR%@@XTBscmZ3j`;68#k=@cn$ljTbXMO5pXMouT<>C_g-2 zQ$zVrA?7pXKWV2M>nKObt*@*NCc;#B8HQ>`+n-*@nacmZGenY@-~;K=}~PHzV`0p`ihh@&hS+v_8jO;^@Yk7m9j-K86{lp!D+^Ypo+dZs_FuOn~dKir@5s8$BaQ>1)~@}En|^B?3wd{GSZ>n8@+=Z~C>{KU)n zac`cZ(I9QwaKofJ{h!cDnnc)ZFt5uI>l^RRQ=L7smcLv()%@o=R)i~fYSoPK^KKAD ztxTlFXb2I146zX<;X9Q7h!WbNW535JQT}611(&$y4Win=#HD@?`vbUzs)TU&3R+j~ z9rm?xFT}E!WyzSiLIj4jW6T2K2Jrnc$bGQa^!_}Jl>f+zzS^bcKN(v7vr5ZXwNhu+G^6D8cjJ@7am&cTxro?qvE72LBKUQgADAhC1liC_&57&@2!&Z}U5r9ysyAH7X2 ze?~<9=q)nWMVfKqUO;;qJtxjEQPW~cGMFjv0~vWsMTTr#cp)J7c@3oe=QW-O?H~Ud z_Af-a6FE=~a-f4hJ+-PQPtNH68Ii5>6&|lV$E{Nx6P zTVddE2RRC^LP7q(WIF$DGh=^_@*lGc+jpU;WAr@7@@(Wk?$;g5bHMRmI9|-L?B4JG zBK^f2Wp}6hi>kFXsk;w0ovQBX$*t}SoO*@m4|JdPdDmC_n|N-rdVR3l|0MF04F3k? zKS7@VAS3d<>Bn_fp|Q#JpX_a5f{!{2^bN)&I4OxxkY4Vp$JJ4$e) z7jcwKl*D+@j^A%w%jCg6$RZmfe`#BXYu=2WCbVCAbmK!k$bXPuH5}<@?|fPiXz1C< z-Z@%urlF^1)`y=pg|_ze%v#kKXx##4_yg6OR(sda_BVBHZbfdg{!rKEC&6?deM<4fz&SA0W0bs4nx?dmEO{tWt2qvs_-_a8zgKq)7|XzW4sT`;q@dPJ3tG{fXkJ zh-vS*a^am+wPZ8{Q&zq+8vFk7;7q$@$(mYK!I-ot%juftVbZpb^XEO;b4uK?e-6)o zc7Azb{?qL6oy^0o+wqpZ&+2gbTCinHH(kaSYqu4 z?SJyM{KrZ8kDcc~N*ZHT$baDPIe`49Vx_BMmuxCI^zQ8|pVp69>U-yoX;0~IURu5D zj`T&ko1d?GCeDdmW%8a~Bb`NpY4Us9?~N*w?C?)<7D?8yf5y@I#mKbn-#>b)EKf;Z z@PpbwXCYlbSQk3hROs7;{3j4Z{&tA!ee)J97R1MWhp7HrC_i!G`Y(q3B%AV+rR?qO zsEM=9CCp0skJ-koF$T(iQ0Z#^gS|h`PUJsT%STu26ip?*UAI>~CBlE_9qGRpZ(iCB z|82sJ&sQeLIkzE4*^@ld*#^EBd2dvkWSzbuW?~%qd!`g)1+i!BztuVf`N@s@f5;p> z|H})Qx=Z?Anq+ihx`Zm39bub zM%yo}(I(1&ej($2*T{X$B}q3sx1sU)JBx2l-zMs7-`#n0`XX^?OFOtt9MIY@ zeo68O_z%sHlTEA*v_@KWZPL&iJEA4ANYdTd5hIDne{SUY3H<*vR?DyQI-QhXt#3Vc zoboHAC&A5g@(+a0fsc1<^c~>-k03w6?NHqALm9GSmEEI17)f~n@}FPIdXvr`ZM$32 z!#@K4YHnp-BH!%9bj*9d1m1$){r(Q(&_~}Ve_`08n*SW~M}O`w2}7OMNXjAfQEk!K z{0riZ$bTq5fxlhXDO39&<~*8Y(()@4@~h*>uaKYMNNn>QC-P_bKhcfmdo&%$Pbj}y z*vtJ-x_goT$nuw?kBwxw@%hWQuK6X?nf3M<+b``pv&Z6!QZ(pVzL|lm_L(U@Q+(W`9qQkIZz4)Yl6S5D~h0#q843Sqyhdm zT`}7K9F^7l=cH_o>&jh){LSp_+qz!$M}BpQ{}TiLUgmMIlkVUv%Hj}N?Jv^tC* z-@0~-QRgrt|Jg#h5AvUd4y!Y+DAML$DrGp^kOM6(z8Qr7TO*uB@Za+m%JAnTe=GV6 zlllFfF7z+f{EM+$L;iN7tUIb_accg<;%I#gCOM|{Z9#rD!|`B<^0$Z5gH-RqCy-xp z`u!`_`uFBlqWz3JxIS-sUfsv;K;JFX*P{K*k#jjCYAQOW>}_GDI^;iP?n*oSC)nJ3 z$bW)u-X7K8y}KBm5kuX3im{xNKau;JSlHjn>z^LVETbJ6J*=5%5tiwh$!TURod2Ar z{0D83LYa<-`nLG{LNn4H;{Fdl7{vLOVDQO4I`0xZ{RI4vd%dS04N`v6^rbuaVIgvp zkKLOOq5aH}j z6wRqf#!TGb$%G3*s&VZtLw125J<5M*WuyFud0bAM|5^dg*7BeAD?fbjH03`laU!KU zeM4wNC=~J`zry*7-rffI2Li!X>yV#tf8-}k2MW)@9~=jE9BB+Ze7wK8 zV_zfXS7~kA8{HT)`MW(5em{x0ZFI`z?u6k%z)B zBE`kX6Aj%tjac5PY@C7n3gNgm-X|+`-0s44e&p}!W_6VRc)c|($bbHCL+Dfx`Oj%M zHZ%s^zxW|k}Q){{9Kvs|tTw+3=H z+Q3_ybJp4v*95w*+omK~sQxJTL4IWs*DN<*Z_Plgf%y zuF;k{VMp3YW>)G9JCaY@nUZbT7Ex=o>Ll|G~zrR?~iuRXt#tjE)t#M zr-M+DPWh`{_&6H*74@L}N+ULpf`I%=^>=FiPNm}bni}SvRlfYpYa7Yiagrv?%pAyn zI9(S}W?8Zk5w=Pb@*f+nT{B=bHau^Fm<5~55%2T+9>dUiQ0TpDXdQ zUmDz#FO}$dDQVHc^+{U3l5`b@KSzxM=zEyM-$)l8&R|-!;fI-|mUYQCK3d&9HpPl| z&AMOFb>v1H=)(SwP4{b;`ywuNsr^f^y{FI7{VMf%K)A$*BVG42ocE-Zg6cu7$H6Qa z6OUAY@}GFhX$hklKYk2U8J@$R&zKp-~|* zUa=C3KX+&|`L&r<#~GUi9c z#JJ&<{~$jxt2q#!Ys7tQrZBOy0nbvCLV1d{!=0^ie720dUfw@`4QJ=r!zpW0bq?;|zA<8g@b58&?>?NEC_WIA_fD?E0{^Nd+w@mhZ3X2?&R zRaLHB=_%wV>8?EK_wZkSs8HP1@#yR84&)ejcKq~_S+aezV6ZN-EcXneAHCnW^V6ROHuucu`PJs0 zLiXX&f>RAWb?o@jCiHK1ZTYaN8FX(sREPXw)|UE1bzO~Uf3k4p2VHJr)y}RKr*rEp zYX8HWb1uk#SUCTwa&+Rp*hzJ(bB`UkXZ$n0?FIeYzjOP${&x#D{$77ayZ>ER(>DFx z?cNuD@!7VBU%>nB&)Oo!@A0)i{#j9?yKANIac|L6GrJsz$bZ@F?Ec+9wCwauf3>;u zer0Fl$diQ!3R!z2J6d?4fcZ|lPi=sI--#xiDuaJ_Q!_~ZRq$U3|AV#2Puwd#yIs@4 zs*>HVsm{k;o{}YK=QE4hWHtXW$%uW~%A`@aBaFNAKoat`)$U`vl14uRuiouLZhzPJ z?&Gb$*N<%Xy*qpBHn8d$_($IHeD|)qyV@duv8Q|c<6UhN+#gl#yEnh+Dc47A-)~&f z@0U$~Yh$swtc2aXzp0FsJ>wqfDePnw@OM4-fy?1LTTlT1T=*BlJ9kTWGZFslusNCK zeiQy4BTP=n~N4rpZ->=)l??iZ`_}&urjp&@#Mk(g*TkJj~%Pa^=+C3 zU--XLr}|f~D-5arj^lN?@b`K;)%>KS%qov}eUMSIS7#k%EOW9C%vQ6}=CoIsi;-VB zJ?W#72by9%>33Id7s>zb_1i^r9eg)z7vqAy5AWFUdvRol`wPSTntyj!GWied`e?$v z)7zxU@4cNkaYT_c`CjrawM@U4@{_%?>BjxBYW_q1lwakx9;;iodLj2;x4vHUZ_)gN z>s0?9FXR3_88ZCcu45zRZ6@8!Ti4ty@4zw>V;gBH0V9RDBsR)H`PE9tkCC$p*}mm> zWd0ucmG1y~4-NW0AWjaUyndT7c|#TIe~~bG6-zcV9LGDCO<+1Osx87Ma7NmYKTPNO z5Bw?rLHnywBP%JtdSMjhZ{EP_{PfMOfiunP^Q*u>;g*)wsQ-X_OK>&$H*V_nBF{#C z5}2K2v&tX4Pu*dW?7~a8uDQdav&*IMm%%dfw@!5~iV(~`>-3Cm$bWn#BQv)}>R~^siTv-zL88!cYwa%; zAywZIGxg^<-j(-8j`;af$qE0c@jhz?{G;aiitota{%_rpzb!JQ+4t5Rv$sY3s@b;# z6o0j@D(QCkKM#N8=fYC9EP1q55=wN%iSelaMY`f43hEzszcnNOk(rtDpOc`PUonT4 zUpXj0XX?%Mb7n3vbxas}R~2&7MH6lu|Go_W2{+E`E*=U0VU68yjU@jT&gfsMG-i6M ztmgbhhU!Y3e_>Wbl@sk*tyY6)gp7Nh7aKe#rnl(!o00!uZ|gH^|Kq4~o}P^SC$~By znCY0&x3#YiM1F+>`Sr7U$v@EQ^^^Zekm~5AoBNz{bzCiqpkVyugN*uvJm;z{Qghf3)TF`y|8E8R?1Hn1|FUT|9uOa zm;?UX?{>l8)HY${me1s=$geWCe5U4CTk4BPxQZg4Yhllha7oeCRh-7vRcxXuTZ;ER z$MYYT4>vi(U#{*9`F07n?c4PmiF86ZEe@b1HpB(8gJwokg{?M%EKb|zo ze=3on6ismESL*D@uQKv0)%=R+lEmk{@O4SX>Z&C2hJVsT1N{92%P+<^S&IPw$9uY9Z1edrbPtNy{>Lk|YXzi0KVK*8ofZs&~rPmup)m!&UM z#+}}{v6Q)|ApZefQ|{b+WG!-^DayLNIUq*rLH@&FPq`I{=c7r9h^wv0P$E#R{~vkp zAJ)W~_HPFTZR@c$X+hU^-NjbB>-K9at=QUr)dUC>`&1L)o;c8eC=&{7RMY{HBA^1b zU6rVlG*Doz4VJh--5Oo6XaWLNix6;<4KZZfF0g=*buID;Lf-41gx2kD_s8)*&+-28 z&e8I5LP*XGLo)X{*L@96pE3r$11Tsk2!98>kf7qgtak!=z@Hb*Bm%#ikumUCkWk{o zE%^K<<{pFp?z%6zV3yo}OKjyJH0Xp^FwK2xybk-Hp40lO zQ>;GKe!7>;WVOhCdeoCuqmfHg@9>%t$aU#)NJ9V=*zipPoiq{VREY!zutDH~yy=6J zC*}KK-+>dn73Y7TY8Gq;jP!Ug1Qqc--U=puaQ=!1{wfb{2XE!$%^5fY*9oH;y#K`F z!zbAOv8ZyesuDa^$uzge{~)jbK@8Ht6T(Il8xXiNR1kIsOf?8=aiTXRz5)?A6(2ls z^Ck??xb=UOQ2m03e0Wg!PZ#dnfb2={WfovQA>RLSolg|(f53xa9vzp{++Ob^#wmh< z^#kbxqjuO*T)~F$+mv{pJFMM*X;WwK@X)k3<~H)d@+2@kuZE&IF2|1_t@g0Dy&oi+nNv!+ZgkzI<#^@Z!nuc)jcV z0N()c5P6ipCl@0gtcwmTW_SYd-35UvA-sL?o4MrkuP$eV0`OPYwv)ogA6mTl+9&cD z*=xI&I&aI7jHH#$QGLm2y{@KW9D5&KiIej`x_6zN|IvZ}NrU4(MqI5(#*I>i)ruG2 zc^cj%%AS7lwWonp{`AvfPx<)pBBP%I-viesje0B$rU!ukp2zVC(R6yb@%b?%6nD`0 zI7uiruHaI1U^dmx;ll2utL%!%#m8L!ee)F{CJ+`Ba8%mDv$59)uw|3F{pp?B$$IPh1R4dCB; zPE~3)ysde){nI=9*XJC|ZQprkpICV;w>;ynNt~srFJCoinjIYLa;_dS&JJpKJ9CEe zUQc&kaORxOJDlho!QAbtt!>Up;AeBRr+a~)eHq5TQdP5sW|GZF%60>PClU_g2a_O3HJxG>nbLsV=b9lPNi=x`W_V6nKN=b`I zBAIURKO1%vl|9LQ)(xvP7uqxLG_7A{ceiKXX%s)_bUXI|{~G7TidTUDm2#JJjoUao znCly3e4TcJ|Cz`;Ogpc0{KvOmvH!W!)WXQM7kWQ5x9EP1GFha$+KX^Xy9a0r_@#rx zGG}FhB^zW?-mPO!CtVAvuoWFwlvqrx)m~+YmS;O%^cXgy<&7^Vms_U-yy;H`}EN5wZ zjyrE(mh-B;`joNp{rG0h`^Z#)HZmGkYA!ScFf!o3Zh(57ZY1rQ1paPHI)eGTDcJrr zHP%hR{zIcdhQj=e4WZy&R-(WL{vpY-u7HImzJW|6s}2a)2_%s>G<2cb&p%F=kYR0| zwRnE-);)H}=)u}OL#?xxXrQmn1o$gE?r#J9PA@<9Kd}9uyV|2!=rGP+*w)i32Y)5M z`EKh*d7eoAK}*s52>j2+%S23!N{oDO5R6n6K?g7}UQ%Iyh54PppXTMC=%x}eKO{Xc zNTWW?-$x-9^Y@Wr7W4NlC}_s~Cl<0T0THY~vd~-@T*UhMKgq-gTTOoc^KDyK+nfCS zqkC#syPEt0fPb|c`zxG{e(}J+8n*v>XN_i=-sHQ!+o1vfj=0{GK^-Tre^3u2au3t|dYKE6xU=&h1`AKw5c zsVr-p;hX5xE~{yr>Gz=vd$*a9U%TwfavNu?@5gEK+0d^qU7DKDJ5qvV*qA_n+l9zOn4@K@o#2+(e0$va1WfvR%*sy?kqq3W~y z^(SkTiuw!vm79lifWKcc&3~rOkSb#kx=kChB5nvpf20koMVE!5j?FqqiMS(*TqQmb zBQ}RTvZ^Eo{7J~1CC(V|Cn4}gcpN;>%<$j2{;;|R{K*)k+w|CmewPhWkkGG>U8>LL z!#G|R%n12*QWX(=K*(nw$`21T=J6RwzOgtU=AoDcNR0i@#syG+wGIVn(_sE1oQ~7y z_SKBVlZyJ>{u=NciP{91hH&E=z zMO;5d#!g)v8X5EO_l=E^iVW}%%wURw7*r|D^|T^G7mYgTESgp{i6DcQZ*JmsoXlDw zeX236<5&uf9&3&}G!wB0?0gWRg zCwgOyP4T*_g;~IV*gd&02eQIFwQ(8v{R;QsM)*6O*)uWt!Rqt*k2#%soC2p)@xc<{ zcOAxl712Xwp6oAEkc8L&5FY_5jK6_+aU_E!_w zKj>4?&U;YjgHn=rPCzm_Tfm<{-A~RIX|g$^xGP10Vs$95iKISWn5E7O-CA>NIyWv= z=gr(&Q?+qvW!}t@A?*jj=ktJn-OUx}^MHT$&A<=ApInu(a70GCz5cVgJ)r5nlw?K~<BsZp5cg{Y{f z#vmEU^Iv@^>|7iL{78uX4-|%A|MPx85`uYm-jpM4%wh!ohtVE~^l>oggB5T})Y%+< zbA27dy_e?oJBI0il! zh53*HF;E8t=?67Hkl?LELLVs9J+K&Z0ptpiP#|v(H?DxuU&7^?#y}qSKQb0`Utp(G zKd6Tuoxtzq9vW}BV1e$PdN@C5(JP#`zB0n1Mb0aC%fhXY@BH;Ozhm$ChZlSm<`;bc zeAI_7XU2)+UnX~F?wc^A>Z5dkF#)-=YA2AB%UAW(+KcCc-ze8 zdD8FO^l*LWfxhF!_s5_QrcmGk#@zV!>74dt1%cSS@c1pu!yx(m*6m^7RrrBLn@H?` zmRQsA=edNk(<$Ou$O=Q2I1W4z_CKCf;M-9N)CFNrWT!H)2NF8z7lhdVID>*9dA#^V z_&Q@eUL^2fjxcZXe7wBg#Jn7*VA`WhXPB=ayx--)^-!<}5<@Qu;N~%b@a7EMmgQoc z{?_eWE-r;SAmCqR2mb?0j(xNSJ|8{)$EDZRJHZ2alJi3F;hC+q!dqMql?jQ2J@kw3 zbivzd&kHZU77ax4Prm?t0m1)lc_C~b^c@O|dIl2SX3;agkkGtfI+t+kdEkE*kl;~t zo;2!W;C!DDe3r)x72tV+JYEbFC_R99k{C-+!DY%fCdruumK zW0dk7;IDqiU&M7lK0@V| z7rp@hBP3-nys?t-@l7Ih)J1*xuyemm_!f_+#jki}> zZ#!7bZyj}c`8ogN_V}N1cbyCK59+~Rbz6|7r$Le2Gp0~qZ%E#G{mxS%*BhRG>H4Re zXNYsQymUQ7;UmtHy>e(zw-5NAZP(Wff&XdGy}ot`{ExH#aJF6u{-kEzwoAh6su73t zq8t2=TMG>Z!2e9ez<@;9{B;g^AS5a1Pf-(R-p)FubBv~dKdIDl{s&GF4r2e~qRVl*abo|Yr%O%)zq$haThCaf=19(| z?Dnxc`w!=w%mxp%FLLk6?4FDrI{Mm{UR}c0lFM$79>o#)!Cl~xlx^v2z z>jwYhayTb}zZdui^v0InDFg#TV{EQDrKC(jFm-8*b(4o5!DOh_ipef zXZscG0QM*SgeUt5b`YgxDRgA-I7+D@5=oc=kWx!fk)z;$PRn+YhVFBWCSf;muID`X zs~pWl`vvenAnw|8?=(i{+Alfxj+w;S_KOv3!9S#RPgMZ-?3KU|ujBO$@K<}?d51HA ze=qQ>T{inI?61nT?+sKMTUZ#cVSWpVvzT>eNg-9NlP0Vz(y=34Vt%5VLG;RCn?O99-%vT#t$hMS5B+VP$dfsB)uv<2M@jUn| z;D-W*ru9k?kKliRzZN`-Nt|oHc=~zZ-_|`_11BkZo;^!2T!G-un8>(SsK= zhgx4>tr?mEf3vkpj`DiQdjdKgvu z+1{T=IgmMhSMQTiju=zouHHvgSC%%O2t4tq`TFWm@F#QG_MFyEKmR#Q!X9hmti`ig z@K;UPUtQXR>#j5|x5r<(Ie(R4zl68H-v)clsbz!tz8kvBGz;W;ej99Dir%==TnOu; zHzJ!05CZ=bX(Xva@K>`svNpB?>qG>iF1pN(cQ|LAk7=B16Qz!T4@+E!uz^E2kqD(rurRLhszvA^n0S>?q3 zYOs10_^SmP*T@=ommdlH&l-1U0Oo(rZ4TIA*PnV$ukt@qLTZ*EiT@Giea$ms3I9;R z<-%tQA;T|=6@^0So8gzq%vQ6QoP9%{rZxjVv&{kiDhDA*@K-cSv!SRSB~A20+Hzv9LmH0re7Bz1QQ7zIvCv8p)R|(K_r~tN)ANnPMQcCju z(7aM|Z_oZ&D5_SwcBE-mz){!8^X{fuWS86Z+@;2O71m3xHJ3~Q6;^%iEBbu@(h{Uu z$q4=Juz#;C5b|vgn+l(4$>VEho5U}*7E3qIKEUzI=Nt$AFM%IzvoT*C$wAb%ZstSa z2Y<+H27bjU@NvMO+z(#S%P&2f#qmqQpM~UubFtK%NaaYIi&2LZWg$58ZW4vp zarHBz2%QtfmoQ=y>_Fgu>d+g1 zLZ*jeS%!Dl-k`qi&b4+0G!f^4Tmg9j{KNX8#XuT02|0%S8QV)o=DI|$v@2%5P*k7GL z40FP=E%MBAlB&zL%9G3Ar0XwOk;3sN$Iq~Dar`RX+rWQJ+CjZdg%C}G4(eBQet6eY z9bffz)LrV=K~mJ|K8j(NQUS32Rm?J!&U8uh(Ep** zWle%S=Ks)VBD$V3cD;GdJja}A>>|hh^k{D;@b_2!Xb;Cfxe(r8LNWhyZqrPTAN-q# z-~5=%Sr7awfy)tf$r-v8wDRU6An-P#;61OWn=$SokDW{r*5jkebHP0qlfyR zn&IT0_%R1c7|tE38S5bvCUafYpY|-2)#aYjK@wE5f>N;yhf{3*3Jp3r3)o5))5Kfqs8dw*fhP<|-PIp4T!D1YW9*YF3c zfPchUb~AVk_}gP4Rf}Bh^KUNt(8Eu|2jhgzGmam#J>Y*h?rD#L{SW4!Jk^sh)`P%b zjno1^WNppK>kA|5b5B(sJG?MlAyX(+;ssFwgep-aUMQ8)=0u_RCsOH4EL>Oj$4K=O zyII({G1mNZTXJ3_8GGZ2Do7Oj@q(1fywKE=TMJUaW2Kf1bN(u|r0V!8nD01fV{Qf` zqri2AfXoYYH3Pr&JO4Amdi==*3+jYhrnx;Hd4CS(-!p!l&!K&aq)H2xxKq8ZM+LT+ZI!UBIk)fNW&0zk^( z_|=5O$3Y+n^O5*K0MuQHaKb!6$O7!Id{9Bbaqw0?WV84t_E+G4u)hk7Z=QKG5UvtX z@AX&pUjB2ee*8iIbI~<4(Qw*YPxZj;wbN=n)$Oo4;;am^w~aYoheICw=bygz@UhFj z5n(&A|M7*k<1oQ53{H;2VX04;FLVHb0gVVoY*9j(p9Uk1=_Dku&KC=b7*7U3|3ADQ z09R$e8M7G1kI>HU_+l7H`8SJhK=K2cLpm12I1i?Fb_DWxft1HzQ4~vHj|HiR^P9j~ zGR=)W$}tpx)Rig3QAk7}A_@|Qcj3_piYo;|pJ-@S^+58VaS5Ez7s7Z0C;b2KBsbiH z`{Miu3wQIk{>NK20M!Es7VLkf6MHJmInB*^6u4mKgBjvk_X*)cBX!`zBhqivJ3_`c z{QwI7)_=TAKi2ot!{zl;-{Ta)I`%)@&wM}s0q6H0dwTsphW$@|0Jym2hw|TgtZCBe zwm0X|6?^! zoCi`vT@dt-MplYTp?z17Fch{;`*NJPzfh1c1oGemuVV(R3*mKe37mhM0lt2)>4a!H z!T%th2$~e~0wBTv_~PFTvk2xx^WgYzQ9)QBq;*lj0mwkjBJFW1OcHDjDr4Y06>X)l z|M^I-sR!><^HF^l_CJu;aeeG*xLvK^10vu!_E)SG9T(a&UcB7J1Aq0xiwBOu^Ypy% zLRd7U_Nkz-c|Oqg^RXpiv+%nz3ih4NaZcwF-&x!`633Fl$l@w4#d%fJ{zr^GkO(0W zR&Ww#Nyx!PJc*=tQ6cnc-W1rM*z$Q0Z~O97iQPBouGS%ZKB)hx)%WUk`aL!$VjV6j!!d!FHh1REFUEMHD{hbf5!bxRAyu{0i35YBGY0aPb(r*IqvG%ZMDL8Z%=7sQ^Eh-o{E7tT4BMN95K#( zniRn8#{sig7rt8FVDGVHpr8TkLJVbz7NfFI@`>H9E$gT8Xm z<9~j~)&GpU4PO6a&}ZAbk;O4ZWjZncROZh1F@f~T)Qc}(|5P9-xVUA9BN_RK>l8cW zdt!b3sFFGg)4 znb$wPBaOVcWu+q-{Lh8tHDubLXm)b<-g4lc^MW%Me2;L$j`j<#wct;7IHx%7qt0PB z=D!2{df>kc{8e&e^BC|~ksYcDTLGa(=8g-xLMOP1jzPM}sbhdYqqT=BpwCVvTO5$| zl#In5f*O>uH3aw{itMc?y#9y4{)ZgZ%fSB_NQ1r~{8fYE+{joZaik#``gwoVdAOl} zY~<3N&Np)V&pEe^nIg0M8{)Qse@F&@0(^5`a$>(?+>q(z-wEdzG50a&z{Iu*WBZoF zTA00WY^f}{pUCNy@&VIFf#0~X7ufNn@?|yYE@9>L9#yRfC53}cx z+W2l%By$S;9|GL~?~~W8L9Eq=I+#+50q z=lfg1UyWV7Fy6IZX~q1Vk-1jPZwCH);8)GsW~sXnH(U%JsH*;n;o^vGSK0anBgN~I z*J19wcV+8tD`HUx_@6HoHe_s}Q8DyEvJuJyYn~q zYF8fXi8XCLTlH*LnZmSrd)?2vZtgK9Za2&|f3XJq$(gygzgXL97Hpq8+LqheiTUN( z)+XTZRc2b7Uf&(v*O1xY^g8Cxp6FWV#UMzaBA!DfNk!6|-Q+p=59#L8^jOR{tE~v29--YeJ zz;0FaVgF-SGuCGqiN788ho2Wn{B7c1^|RoAY~iEm`9g`*7XBA97yOTQ&L8F3`^+5w zu*uwhQ6Q~wn552&LDEu39{8)}QPwu0)V6hbbk~)D?0GOfxSVy|(N&rf!R+iP4KpP%ZQ)m`KI?OJ!&ta_*W{@RJod2Wth72tLzd--Ly zm)MvUW8hC-YK~cPZy_Om2K-O;Pmy>f_@6THKNR?%x)dabTvtb-+FtFsBPj@7Vzrr0 zC50}q+ATfGU@w2LhhH8ngSv*J>Nl4Vh?O=*y}3joyMZ9-s)$>armkD{b*~TCc61Gl z|9*XSt+{DryiT6&GKaX_lk&Ch&hQD&|8<aAE$=`Sq@E`R}hC&n^*`0%e}ETNE3rqx^*@kgeYw7hOk>IOwu2O@40Dfcin@eJV-+1gz`eWJsN@Lgk`uk5#x;wh=x$i&M;5Ii+xqiPo z*JTEOb$>PRg1?%4>v?zQY-lI_Tky8?>z$MLU&Y)1TK)Z3$5cz)?a3bgY5xQMgaVIp z&+C6`y#B{k=LY|C2@Y29abtZ=O%2R{j0bNOU!C2JGqx(bTYj=k9$uBEMo|vTPaw<> z{zRJ306)PBjM6s)V&=nn5A&PogUHoeefYPl>rL~=+skhSPnhRTU5mRF%<<2^1^#CR_E)sW|KRaEeeYZU zGvVc)_9!0zQ&Zzeb|j2B$b{i+@IY;3!a#P9{AAn0h^lNgiaxYZOcB5@ULX}fT{q`{ zfFC?OLXQEzX(Q6kuc|bH|FO-lK5uL!6aMha>hq?3sN(%)S%ap~%8HxIav;yQJKkD0 zXqs`p-S+XSJtm*=>*`y<8D_!MRoSf-iW7nEmie~=rHRYk&6uB}J^qKH$2tE~554oI zxjp`;p7TF z^FLKkzeJVDlNnhb6!ZO8CKJ(rkR%_bK1 z6T}37|M5F8odI~A?+4!MZ!#>vAFqqUVgmhyJpKXEN6VqUg@0M}CrG~Fa>yTnJ5Z1} z6X$Y)&{IDKS_j4#2>#R8S3~_01)VWG+{`(W^FK@_>G3~sAMAg^oPA>a}4tN*)% z&4_QuEvN7z;=cYnUp)8!SNHV6yDluAwKu&!Tv|$AA#$C;A$`7xc&#y))n7=YJ&VZxXt-`SGv^Lf|q0l*a^wfd3Kl zwSxVe2PzRXL2lxg3Ob?wXH$SPh=KQqB;=&=`H_$G74;L~ef|XAXFByIwfaAS|H)!1 z(2X)H_@5zk{MUB+#moIX;XC%A7Z03-a;5Gd&i@FtixzqOk6^LyEVwj-kXqsKKi-s3 za6A0UB^2_D1+SvgLPEWaN*g0y0Qno}TL~#j0++EEbe1k4qwc$2|8tk~KcnD(vg{RT)Gtl2>vQe`&42Wf&G<00U}@caDH-zUmS3nGCgU|R&eX8 zDv$q3@6pEZtq~TccgMu9mJ7u@Y~t9Zq)?P$^I5;xS0p)C8Pc}I7x*_9wFLqH#m%i> z1o6%+zqrK?{I`SmMuR^=qg1MF0Q^rKa1KN5(zyGK>lT*=S2g&nagN`K?qYuh{>RYf z&H#TRmA1LDKamO!j_k~^MI)ccDn&XX2;RdK%8+MdgZ~-IlxJrP-?;4t|8qfj__q6R z{m%}k*0r}LuP~!$qcbNVuOQQLFisiQk)P?fvhM9!=Hl~}SKG7vRK+!lqV`o`ijNu; zt=EIY&Yf)l|3HV`rE|bPVEz=@_x;Z;_xL4T|5J_UujxJhr_VWs{SVT$PhtMe((6;0 zf3uY1mnlS%rxjT!E|s?~*|tY{3H;CRT-i7`{1tksUIPE~SGbnTCGaOl;c_V0qwH+g z4q<0ee;LJ$(peGU zfAm!fY908W!5U&yxWY&ydGo(XKJ*b2FI{sg=Va6jHYFtOEbJo5Bm?NAl) zE6V?1tx7dECzshEfxqlZYpTL*2L2)iqcYc|wzewRlV3KZ7F{PyJI^$vHI)&~JL!fv zt0R_G&~yoz;vx(IhQWR$J2FXSxUCFv1p4#1 zt(8X)SMSUiFYP<}M(vi2aSwl1CCAV1sZSf0%-PbNI-WWrdHv~b{rE3OiVr`1c<=(| z2LE#&sXN{y!2duUPzyn_(BInFEF)Q~5*Z8P$b#0b$i$W^T3WX<;2(fLl|9*u`DLap zpJ4tp)0XYOPr&{sdHdP9*#8iLpUlPnM=jX?sK@_MkBpXOtBWEG^9&Y^ico6k-4 zLpE&29_!fQ>MiFmzq}R__{q}>sxP)rIxm^gog-|L`7%Z~I?pDN$ryGtpu0FWI+8U6 zsEgxdk>7az&uDTiaO==0@aHx6gRhVS{}9Er#(VkWfj>UAwJ6?vtXG~|bU3at`V(bZ z)1kD+=|A4>3{>K3Rs%gXSE$14}pK98W zv*mol`9APhC(qxD1^)PxmG{WL6J%*6H4xjEI1g$q?-9%nN$?xiM&PHg zzuNFtv?2%m)drYv+K`F)k7eGA1OD=38KZHPCrJBIY9zKY(celiBQj=F0Pv5@FW3~o zqF(*8g_{Iy_$c*!VX>4A|4U?UQwR8;zpT%0?1<3L|KxD3$#|{jacO%6r1J&owQ{4W zyCNv6waf_oLD5BJ#-ptAndo;n8l!c}Uq&BHGDRn*JQ00`GDRg)3DPS|!2cxT1pgzA zMUhm?mwwVX@B{YSvyhBZD(rV2L9&dx_&4$)%+7ZBTeS^smGAOj%tr8=B2!emlVBw|j8Ven1HYR`xnl1hnldEfYy4V%esS}`s^2L5L|_!I1ZKH>b&+NM7WZq1dD zds!xPf7J%udzH)+p63)_zUp{<2!p9;e1;b&NtZEFw#e?muD`4R#q1npPLDIy%9 zrl@#I5zc}?DW@rd)taJu3{)(u?TG4zq_pPnZUarG4crbN)YheJ8@?kREUSBK8}RGP z>wcT<{(50U>7M(U6CW;|D$Ty1+^<^dE>FId4E*kR*&i~&|4fn5$36Zh>cB5K|04~1 z1^f?qAd(^m#L)4+417ui_#bC4t7~sS$jLVA+AqSox%tHPnqZ>TjQN9!IM%}-%b;VJ z|8*8MMaxsD2;e_fmQ@!wQe!$cSziaOYdVhI8?W11>oylnjn^r&N6g`_o=IiyR9E;! z&(vGnrn(-vTR!#MTzA)k`qFyfKe}M5^uo9N3h+M@;C~cs3_YfmF$xy^1Na|>T1)+{ z|7n0eIw@rYSy!Uhk+m#3S5b|K+FtRw_8JDV`NQiq1;pvz=A&02<9eG_EhNLlG9p!> z95HhSkTF!bOg9^H|F4vFPfpbso9@?4{><&}Xu9XRzvj||{s;U?F8H4b=kQzG++B~1 zwNGOHC+gcT0)OWdQ`aus&m23k)O|JiR>oI}E8JIc{SWvDtqdluQNWMqe^q(wf2zS- zT}<%$pIFZS=&`@b>`{`j*}dfX>q;%olh>66M4ApAJqQ^`AyomXQpJM*DU?f#9|3O? zstovOHe}OV==U7|?vne!Z*0n{@bK4Hc>L86@UL~7!T$_{zcSAn1OEf-0rl-Q_g7vp z2aI1!yC0l+VzK+`{9D1B(=oq;hVz2J4+;Ftbp21gm)pI~(?_Q!73PFtf0f2S{gP5% zmd(bGwJAw$_Kk%n4=Kec?RY@cK}Z?)C!|zB6!S%5QX-kfKpiF-5Hp+M{LlMOR_Tq6 zNwI(US@ndmkxKaUbJY{3eH8dBsJ{y7tGKlq>cm9A{nm36Cf-=P?YFD4P5k;+;(l