summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.clang-format79
-rw-r--r--.codespellrc2
-rw-r--r--.github/workflows/ci_formatting.yml21
-rw-r--r--.github/workflows/ci_ubuntu_latest.yml21
-rw-r--r--.gitignore22
-rw-r--r--CHANGELOG.md290
-rw-r--r--COPYING339
-rw-r--r--Makefile.am9
-rw-r--r--README.md225
-rwxr-xr-xautogen.sh8
-rw-r--r--configure.ac86
-rw-r--r--images/example_wipe.gifbin0 -> 6447728 bytes
-rw-r--r--man/Makefile.am1
-rw-r--r--man/nwipe.1144
-rw-r--r--src/Makefile.am10
-rw-r--r--src/PDFGen/pdfgen.c4057
-rw-r--r--src/PDFGen/pdfgen.h773
-rw-r--r--src/conf.c620
-rw-r--r--src/conf.h55
-rw-r--r--src/context.h228
-rw-r--r--src/create_pdf.c997
-rw-r--r--src/create_pdf.h52
-rw-r--r--src/customers.c713
-rw-r--r--src/customers.h42
-rw-r--r--src/device.c917
-rw-r--r--src/device.h49
-rw-r--r--src/embedded_images/nwipe_exclamation.jpgbin0 -> 63304 bytes
-rw-r--r--src/embedded_images/nwipe_exclamation.jpg.c4
-rw-r--r--src/embedded_images/nwipe_exclamation.jpg.h16
-rw-r--r--src/embedded_images/nwipe_exclamation.xcfbin0 -> 220190 bytes
-rw-r--r--src/embedded_images/redcross.c4
-rw-r--r--src/embedded_images/redcross.h16
-rw-r--r--src/embedded_images/redcross.jpgbin0 -> 60331 bytes
-rw-r--r--src/embedded_images/redcross.xcfbin0 -> 209152 bytes
-rw-r--r--src/embedded_images/shred_db.jpgbin0 -> 27063 bytes
-rw-r--r--src/embedded_images/shred_db.jpg.c4
-rw-r--r--src/embedded_images/shred_db.jpg.h16
-rw-r--r--src/embedded_images/tick_erased.jpgbin0 -> 54896 bytes
-rw-r--r--src/embedded_images/tick_erased.jpg.c4
-rw-r--r--src/embedded_images/tick_erased.jpg.h16
-rw-r--r--src/embedded_images/tick_erased.xcfbin0 -> 92141 bytes
-rw-r--r--src/gui.c7133
-rw-r--r--src/gui.h145
-rw-r--r--src/hddtemp_scsi/get_scsi_temp.c159
-rw-r--r--src/hddtemp_scsi/hddtemp.h91
-rw-r--r--src/hddtemp_scsi/scsi.c125
-rw-r--r--src/hddtemp_scsi/scsi.h24
-rw-r--r--src/hddtemp_scsi/scsicmds.c221
-rw-r--r--src/hddtemp_scsi/scsicmds.h36
-rw-r--r--src/hpa_dco.c858
-rw-r--r--src/hpa_dco.h43
-rw-r--r--src/isaac_rand/isaac64.c119
-rw-r--r--src/isaac_rand/isaac64.h41
-rw-r--r--src/isaac_rand/isaac_rand.c169
-rw-r--r--src/isaac_rand/isaac_rand.h52
-rw-r--r--src/isaac_rand/isaac_standard.h60
-rw-r--r--src/logging.c959
-rw-r--r--src/logging.h65
-rw-r--r--src/method.c1392
-rw-r--r--src/method.h60
-rw-r--r--src/miscellaneous.c666
-rw-r--r--src/miscellaneous.h132
-rw-r--r--src/mt19937ar-cok/mt19937ar-cok.c139
-rw-r--r--src/mt19937ar-cok/mt19937ar-cok.h32
-rw-r--r--src/nwipe.c1134
-rw-r--r--src/nwipe.h114
-rw-r--r--src/options.c707
-rw-r--r--src/options.h76
-rw-r--r--src/pass.c911
-rw-r--r--src/pass.h33
-rw-r--r--src/prng.c252
-rw-r--r--src/prng.h65
-rw-r--r--src/redcross.jpgbin0 -> 60331 bytes
-rw-r--r--src/te.jpgbin0 -> 54896 bytes
-rw-r--r--src/temperature.c495
-rw-r--r--src/temperature.h61
-rw-r--r--src/version.c17
-rw-r--r--src/version.h11
78 files changed, 26437 insertions, 0 deletions
diff --git a/.clang-format b/.clang-format
new file mode 100644
index 0000000..dee71c8
--- /dev/null
+++ b/.clang-format
@@ -0,0 +1,79 @@
+---
+Language: Cpp
+AlignAfterOpenBracket: Align
+AlignConsecutiveAssignments: false
+AlignConsecutiveDeclarations: false
+AlignEscapedNewlinesLeft: false
+AlignOperands: false
+AlignTrailingComments: false
+AllowAllParametersOfDeclarationOnNextLine: false
+AllowShortBlocksOnASingleLine: false
+AllowShortCaseLabelsOnASingleLine: false
+AllowShortFunctionsOnASingleLine: false
+AllowShortIfStatementsOnASingleLine: false
+AllowShortLoopsOnASingleLine: false
+AlwaysBreakAfterDefinitionReturnType: None
+AlwaysBreakAfterReturnType: None
+AlwaysBreakBeforeMultilineStrings: false
+AlwaysBreakTemplateDeclarations: false
+BinPackArguments: false
+BinPackParameters: false
+BraceWrapping:
+ AfterClass: true
+ AfterFunction: true
+ AfterControlStatement: true
+ AfterEnum: false
+ AfterNamespace: true
+ AfterStruct: true
+ AfterUnion: true
+ BeforeCatch: false
+ BeforeElse: true
+ IndentBraces: false
+BreakBeforeBinaryOperators: NonAssignment
+BreakBeforeBraces: Custom
+BreakBeforeTernaryOperators: true
+BreakConstructorInitializersBeforeComma: true
+ColumnLimit: 120
+CommentPragmas: '^ IWYU pragma:'
+ContinuationIndentWidth: 4
+DerivePointerAlignment: false
+DisableFormat: false
+ExperimentalAutoDetectBinPacking: false
+IncludeCategories:
+ - Regex: '^"(llvm|llvm-c|clang|clang-c)/'
+ Priority: 2
+ - Regex: '^(<|"(gtest|isl|json)/)'
+ Priority: 3
+ - Regex: '.*'
+ Priority: 1
+IndentCaseLabels: true
+IndentWidth: 4
+IndentWrappedFunctionNames: false
+KeepEmptyLinesAtTheStartOfBlocks: true
+MacroBlockBegin: ''
+MacroBlockEnd: ''
+MaxEmptyLinesToKeep: 1
+NamespaceIndentation: All
+PenaltyBreakBeforeFirstCallParameter: 19
+PenaltyBreakComment: 300
+PenaltyBreakFirstLessLess: 120
+PenaltyBreakString: 1000
+PenaltyExcessCharacter: 1000000
+PenaltyReturnTypeOnItsOwnLine: 60
+PointerAlignment: Left
+ReflowComments: true
+SortIncludes: false
+SpaceAfterCStyleCast: true
+SpaceBeforeAssignmentOperators: true
+SpaceBeforeParens: Never
+SpaceInEmptyParentheses: false
+SpacesBeforeTrailingComments: 2
+SpacesInAngles: false
+SpacesInContainerLiterals: true
+SpacesInCStyleCastParentheses: false
+SpacesInParentheses: true
+SpacesInSquareBrackets: false
+Standard: Cpp11
+TabWidth: 4
+UseTab: Never
+...
diff --git a/.codespellrc b/.codespellrc
new file mode 100644
index 0000000..8aae47b
--- /dev/null
+++ b/.codespellrc
@@ -0,0 +1,2 @@
+[codespell]
+skip = ./.git
diff --git a/.github/workflows/ci_formatting.yml b/.github/workflows/ci_formatting.yml
new file mode 100644
index 0000000..af5ba3a
--- /dev/null
+++ b/.github/workflows/ci_formatting.yml
@@ -0,0 +1,21 @@
+name: ci_formatting
+
+on: [push, pull_request]
+
+jobs:
+ build:
+
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v3
+ - name: updating available system dependencies
+ run: sudo apt-get update
+ - name: installing system dependencies
+ run: sudo apt-get install -y build-essential pkg-config automake libncurses5-dev autotools-dev libparted-dev libconfig-dev libconfig++-dev dmidecode clang-format
+ - name: creating autoconf files
+ run: ./autogen.sh
+ - name: configuring
+ run: ./configure CFLAGS='-O0 -g -Wall -Wextra'
+ - name: verifying code style
+ run: make check-format
diff --git a/.github/workflows/ci_ubuntu_latest.yml b/.github/workflows/ci_ubuntu_latest.yml
new file mode 100644
index 0000000..e397dce
--- /dev/null
+++ b/.github/workflows/ci_ubuntu_latest.yml
@@ -0,0 +1,21 @@
+name: ci_ubuntu_latest
+
+on: [push, pull_request]
+
+jobs:
+ build:
+
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v3
+ - name: updating available system dependencies
+ run: sudo apt-get update
+ - name: installing system dependencies
+ run: sudo apt-get install -y build-essential pkg-config automake libncurses5-dev autotools-dev libparted-dev libconfig-dev libconfig++-dev dmidecode
+ - name: creating autoconf files
+ run: ./autogen.sh
+ - name: configuring
+ run: ./configure CFLAGS='-O0 -g -Wall -Wextra'
+ - name: compiling
+ run: make
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..7c440c5
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,22 @@
+Makefile
+Makefile.in
+aclocal.m4
+autom4te.cache/
+compile
+config.h
+config.h.in
+config.log
+config.status
+configure
+depcomp
+install-sh
+man/Makefile
+man/Makefile.in
+missing
+src/**/.deps/
+src/**/.dirstamp
+*.o
+src/Makefile
+src/Makefile.in
+src/nwipe
+stamp-h1
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..93037f6
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,290 @@
+RELEASE NOTES
+=============
+
+v0.36
+-----------------------
+- Added the abbreviation MMC for mmcblk devices such as SD and microSD cards and some low budget laptops. #526
+- Fixed some serial numbers that were displaying garbage. #527
+- Fixed auto power off and nowait when the screen has been blanked by the user. #529
+- Fixed nwipe not auto exiting on completion when in non gui mode. #531
+- Fixed smart page titles so they have a consistent format with page 1 in the PDF report. #532
+- Fixed some of the config help messages that displayed incorrect information. #533
+- Inserted a space between temperature and model. #534
+- Fixed incorrect footer on return to organisation/customer preview screen. #535
+- Made footer completion message more informative. #538
+- Fixed hidden sector detection for devices with logical/physical size of 4096/4096. #543 #546
+- Fixed some strcpy compiler warnings. #548
+
+
+v0.35
+-----------------------
+- Nwipe will now optionally create a multi-page PDF certificate that shows details of a specific discs erasure. The first page forms the certificate of erasure and subsequent pages show the drives smart data. Two related options have been added to nwipe's command line options -P, --PDFreportpath=PATH Path to write PDF reports to. Default is "." If set to "noPDF" no PDF reports are written. From the drive selection screen you can now press 'c' for config. This takes you to the configuration screen where you can select various PDF certificate related options such as enabling PDF, entering customer or company data for entry onto the certificate and enabling a preview of customer/company info prior to the drive selection screen starting.
+- Nwipe now supports HPA/DCO detection, aka hidden sector detection. This is where the drive has been configured to report a smaller size to the operating system (O.S.) than it actually is. The HPA/DCO status is reported on the main drive selection screen as [HS? N/A] for drive that does not support HPA/DCO such as NvMe. [HS? YES] for a drive that is reporting a size smaller than it actually is, i.e has hidden sectors and [HS? NO] where the drive is reporting it's actual size correctly to the O.S. And finally [HS? ???] where nwipe cannot determine the HPA/DCO status as the drive is not responding to the ATA commands used to detect HPA/DCO. This might be because the drive does not support HPA/DCO or the interface adapter does not support ATA passthrough as is the case with a lot of the USB adapters on the market, but not all USB adapters. Nwipe does not currently allow removal of the HPA/DCO so you will still need to use hdparm to reset the drive so it reports its correct size before using nwipe to wipe the drive. HPA/DCO reset may be added in the next version. Thanks to @mdcato for the help testing the code and HPA/DCO results as displayed in the report.
+- This bug only applies to ones wipe and one or zero's verification. A very rare occurrence of a incorrect percentage on completion. The actual wipe was completed correctly it was just that the percentage calculation was wrong. #459
+- Nwipe now supports a configuration file /etc/nwipe/nwipe.conf. Currently it supports settings related to the PDF certificate but more options will be added in the future.
+- If you are running nwipe within the KDE konsole terminal and you resize the window by pulling on the corners, occasionally nwipe will exit with the error message: "GUI.c,nwipe_gui_select(), loop runaway, did you close the terminal without exiting nwipe? Initiating shutdown now" The loop runaway detection has been made less sensitive, i.e 32 iterations per second of the GUI update can now be completed before a loop runaway is detected. previously it was 8. In practise when sizing the konsole window, anywhere between 1 and 17 iterations will occur.#467
+- Nwipe now provides better temperature support for SAS drives. Thanks to @ggruber for all the code and testing he contributed.
+- Disc sizes are now shown differently to provide more information about their size. For instance a 1.2TB drive was shown as 1TB, now it is shown as 1200GB. Thanks to @ggruber for his code contribution.
+- Interface/bustype type was reported as UNK fo SAS drives, now reported correctly as SAS. Thanks to @ggruber for his code contribution.
+- Interface/bustype type has been enhanced to show SAS-SSD when a SSD drive is present. Thanks to @ggruber for his code contribution.
+- Nwipe's temperature retrieval code has been placed in it's own thread. This was done because it was found that any delays in obtaining the temperature resulted in a momentary freeze in the GUI wipe screen updating it's stats. This wasn't noticable if you were erasing a small number of drives but become apparent when wiping ten or twenty drives simultaneously.
+v0.34
+-----------------------
+- Fix a compiler warning -Wformat-zero-length string
+
+
+v0.33
+-----------------------
+- Fixes a slight screen corruption on 80 column display. When highlighting the verify ones option the first two digits of DoD 5220.20-M disappear. This patch fixes that issue.@PartialVolume #348
+- For some controllers/drivers the readlink method of obtaining the bus type for GUI display does not work. If we haven't already resolved the bus type, we then also check smartctl for the transport protocol for SAS. @PartialVolume #350
+- Check smartctl for unresolved bus types SATA @PartialVolume #358
+- Changed message from (No ATA pass-thru) to (S/N: unknown) as the reason the serial number is unknown is because there is no ATA pass through for the chipset being used by the USB to SATA adapter, basically we are making the message more meaningful for the end user rather than for the engineer/programmer that may understand the previous terminology used. @PartialVolume @Firminator #356
+- Add drive temperature monitoring and display temperature in degrees Celsius in the GUI. Requires the kernel drivetemp module and makes use of the hwmon sub system in the kernel to extract drive temperatures. Nwipe will automatically load the drivetemp module if it's available. @PartialVolume #360 #361 #364
+- Remove /dev/ from gui for long device names. This fixes column alignment issues in the gui with nvme drives i.e. nvme0n1 etc. If the drive name including path exceeds 8 characters the /dev/ is removed and prefixed with spaces to a total max length of 8 characters. @PartialVolume #365
+- Add -q --quiet option - anonymize serial numbers and SMBIOS-DMI data. This anonymizes serial numbers and related identifiable information for drives and hardware but does not remove model information in both the GUI and the log displayed by stdout at the end of a wipe and also in the log file if enabled in options. This feature is useful for uploading logs when submitting bug reports. @PartialVolume #366 #367 #371 #379 #383
+- Fixes a intermittent FAILED message that is displayed in the summary table when the message should have been UABORTED. The incorrect FAILED message only occurred when using control-C to abort a wipe. @PartialVolume #373
+- When many verification or pass errors are detected the status line can wrap on a 80 column display. This patch makes the error message more succinct which will free up about 10 characters & prevents the line wrapping. @PartialVolume #374
+- Fixes a problem that occurs with a unresponsive drive that causes the ETA to grow to an enormous value. We now do not calculate an individual drives ETA when the throughput of the drive is zero so avoiding the overall ETA being incorrect for drives that are working correctly when multiple drives are being simultaneously wiped. While a individual drives ETA is calculated it is not displayed but only used to determine the overall ETA when all drives have completed. @PartialVolume #375
+- Add temperature monitoring and display with NVMe drives. @PartialVolume #377 #380 #381
+- When one of the two verify only methods are selected change the drive selected text from WIPE to VRFY to indicate the drive is not being wiped, but is only being verified. @PartialVolume #378
+- Fixes a incorrect sector, block and device sizes in 32 bit builds only as displayed in the nwipe log. This problem had no affect on the wipe as the issue was caused by a incorrect format specifier that affected the log text only. @PartialVolume #387 #388
+- Fixes a issue where temperatures may not have been available on Debian systems due to the location of modprobe. Particularly relevant to Debian which when logged in as root doesn't put /sbin in the $PATH environment setting. This issue was not necessarily relevant for Linux distros based on Debian, for instance, Ubuntu where nwipe would have found the modprobe command. @PartialVolume #390 #391
+- Improve wipe thread cancellation error checking. @PartialVolume #392
+- Improve GUI thread messaging if a pthread_join fails. @PartialVolume #393
+- Fixed a missing serial number on SAS drive.@PartialVolume #394
+- Added ISAAC-64 for 64 bit systems. Thanks @chkboom #398 #401
+- Fixes a problem with the Gutmann wipe where the random passes at the beginning and end were being re-arranged when only the inner passes should be rearranged. Thanks @chkboom #399
+- Fixes a obscure incorrect summary table status, while the log text correctly reports the failure. If the drive becomes non responsive during the wipe, the MB/s throughput will slowly drop towards 0MB/s and will display a FAILURE -1 error. The logs will correctly display errors and nwipe's return status will be non zero, however the summary table may display erased rather than FAILURE, this is because
+the wipe thread exited prematurely without setting the pass error. This fixes the error by checking the context's result status, i.e non zero on failure and if pass equals zero it makes pass equal to one. This is then picked up by the summary table log code which then marks the status
+correctly as FAILURE in the summary table. @PartialVolume #400
+- Fixes a spurious message on abort before wipe.This patch fixes a minor display issue that occurs when a user aborts a wipe before a wipe has started. It only occurs if the user had selected one or more drives for wipe and then aborted before starting the wipe. The spurious message only occurs in a virtual terminal, i.e. /dev/tty1, /dev/tty2, /dev/console It does not occur in terminal applications such as konsole, xterm, terminator etc. The spurious message that appears in the main window, states that "/dev/sdxyz 100% complete" along with garbage values in the statistics window. The message appears for a fraction of a second before being replaced with the textual log information that correctly states that the user aborted and no wipe was started. Basically the gui status information update function tries to update the data when the wipe hasn't even started. The fix is to only update the statistics information only if a wipe has started by checking the 'global_wipe_status' value which indicates whether any wipe started. '1' indicates that a wipe has started, else '0' if no wipe has started. @PartialVolume #406
+- Fixes temperature update in drive selection window. This fixes a problem where the drive temperature is not updated
+automatically in only the drive selection window. The temperature is however updated correctly every 60 seconds during a wipe in the wipe status window. This bug would probably never be noticed by most people as usually the drive temperature changes slowly and only rises once a wipe has started. The only time I imagine it would have been noticed would have been if the drive temperature was already high and you were trying to reduce the temperature by cooling before starting a wipe. This has now been corrected so that the temperature in the drive
+selection window is updated every 60 seconds. @PartialVolume #407
+- Fixes a zombie nwipe process running at 100% CPU on one core but only on a Konsole based terminal. This only occurred when the Konsole terminal is exited while nwipe is sitting at the drive selection screen but nwipe did not exit when the konsole terminal was closed. If nwipe is exited normally on completion of a wipe or aborted by using control C then this problem would not be seen. Also occurs during a wipe if the konsole terminal is closed without exiting nwipe first, again only on Konsole based terminals. @PartialVolume #408 #409
+- Fixes a obscure segfault when --logfile option used with a non writable directory. @PartialVolume #410
+
+
+v0.32
+-----------------------
+- Add ones (0xFF) wipe to the methods. Renamed Zero Fill to Fill with Zeros and the new ones wipe, is called Fill with Ones.
+- Add ones verication to the methods. Renamed Verify Blank to Verify Zeros (0x00) and the new verification is called Verify Ones (0xFF).
+- Move method information from below the list of methods to the right of the method list. This allows better use of the screen space by allowing more methods to be added to the list, especially relevant to nwipe running as a standalone application on small distros such as shredos 2020 in frame buffer mode.
+- Removed the old DBAN syslinux.cfg configuration hints as not relevant to nwipe. See nwipe --help or man nwipe for command line options.
+- Add fdatasync errors to the error summary table.
+- During a wipe, you can now toggle between dark screen, blank screen and default blue screen by repeatedly pressing the b key. Dark screen, which is grey text on black background has been introduced to prevent TFT/LCD image persistence on monitors that are sensitive to that issue.
+
+v0.31
+-----------------------
+- Blanking disabled in GUI for OPS2 (mandatory requirement of standard). [#326](https://github.com/martijnvanbrummelen/nwipe/pull/326)
+- Total bytes written/read for ALL passes or verifications are now logged. [#326](https://github.com/martijnvanbrummelen/nwipe/pull/326)
+- Final blanking being enabled is no longer required for verification passes. GUI Fix. [#326](https://github.com/martijnvanbrummelen/nwipe/pull/326)
+- Add a summary table to the log that shows totals for pass & verification errors. [#325](https://github.com/martijnvanbrummelen/nwipe/pull/325)
+- Fix the missing 'Verifying' message on final blanking. [#324](https://github.com/martijnvanbrummelen/nwipe/pull/324)
+- Fix prng selection always using mersenne irrespective of whatever prng the user selected. [#323](https://github.com/martijnvanbrummelen/nwipe/pull/323)
+- Fix a non functional Isaac prng. (May have never worked even in DBAN/dwipe 2.3.0). [#322](https://github.com/martijnvanbrummelen/nwipe/pull/322)
+- Log whether the prng produces a stream, if not log failure message. [#321](https://github.com/martijnvanbrummelen/nwipe/pull/321)
+- Log the specific prng that is initialised. [#320](https://github.com/martijnvanbrummelen/nwipe/pull/320)
+- Log selection details to the log. [#319](https://github.com/martijnvanbrummelen/nwipe/pull/319)
+- Improve log messaging. [#317](https://github.com/martijnvanbrummelen/nwipe/pull/317)
+- Fix auto shutdown option for some distros. [#315](https://github.com/martijnvanbrummelen/nwipe/pull/315)
+- Fix build for musl. [#301](https://github.com/martijnvanbrummelen/nwipe/pull/301)
+- Fixes to summary table & fix final status message. [311](https://github.com/martijnvanbrummelen/nwipe/pull/311/commits/12063ad9549860cd625cb91d047bd304217a9ebf)
+- Updates to --help options [#309](https://github.com/martijnvanbrummelen/nwipe/pull/309/commits/69c9d7d6c5a091c58b3e747078d0022ccdd95a99)
+- Updates to manpage. [#300](https://github.com/martijnvanbrummelen/nwipe/commit/7cc1a68a89236c4b501dde9149be82c208defccd)
+
+v0.30
+-----------------------
+- Add auto power off option on completion of wipe ( --autopoweroff ) (Thanks PartialVolume)
+- Fixed --nowait option that wasn't working. (Thanks PartialVolume)
+- Add verbose option. -v, --verbose.
+- Add a spinner to the GUI for each drive being wiped. When nwipe is syncing the percentage completion pauses, having a spinner gives a clear indication that the wipe is still running. Each devices spinner disappears on completion of a given devices wipe. (Thanks PartialVolume)
+- Make log messages, especially the ones with the tag 'notice' succinct and less than 80 characters including the timestamp. This is of more importance when nwipe is used on a 80x30 terminal (ALT-F2, Shredos etc) but generally makes the logs more readable. While doing this all information was still retained. (Thanks PartialVolume)
+- Add a summary table to the log that shows each drives status, i.e. erased or failed, throughput, duration of wipe, model, serial no etc. In particular it benefits those that wipe many drives simultaneously in rack servers. At a glance any failed drives can be seen without having to browse back through the log. (Thanks PartialVolume)
+- Add ETA to --nogui wipes status when SIGUSR1 (kill -s USR1 (nwipes PID) is issued on the command line.
+- Fixed misleading throughput calculation. Throughput now shows average throughput calculated from start of wipe.
+- Fixed system info not being displayed in Debian Sid. [#229](https://github.com/martijnvanbrummelen/nwipe/issues/229) (Thanks PartialVolume)
+- Add serial number display for USB to IDE/SATA adapters. This only works if the USB to IDE/SATA adapter supports ATA pass through. See [#149](https://github.com/martijnvanbrummelen/nwipe/issues/149) for further details (Thanks PartialVolume)
+- Fixed disk capacity nomenclature, width and padding on drive selection screen. See [#237](https://github.com/martijnvanbrummelen/nwipe/issues/237) (Thanks PartialVolume)
+- Add bus type, ATA or USB, amongst others to drive selection and wipe windows. (Thanks PartialVolume)
+- Add --nousb option. If you use the option --nousb, all USB devices will be ignored. They won't show up in the GUI and they won't be wiped if you use the --nogui --autonuke command. They will even be ignored if you specifically name them on the command line.
+- Miscellaneous GUI fixes, throughput display format, percentage display format to improve column alignment when wiping multiple discs. (Thanks PartialVolume)
+- Improve visibility of failure messages with red text on white background. (Thanks PartialVolume)
+- Add NVME and VIRT (loop etc) devices to device type table for display in GUI and logs. NVME devices now show up as NVME devices rather than UNK (Thanks PartialVolume)
+- Fixed very obscure segmentation fault going back to at least 0.24 in drive selection window when resizing terminal vertical axis while drive focus symbol '>' is pointing to the last drive of a multi drive selection window. See [#248](https://github.com/martijnvanbrummelen/nwipe/pull/248) for further details (Thanks PartialVolume)
+- Warn the user if they are incorrectly typing a lower case s to start a wipe, when they should be typing a capital S [#262](https://github.com/martijnvanbrummelen/nwipe/issues/262) (Thanks PartialVolume)
+- Warn the user if they are typing capital S in order to start a wipe but haven't yet selected any drives for wiping [#261](https://github.com/martijnvanbrummelen/nwipe/issues/261) (Thanks PartialVolume)
+- Add ctrl A that toggles drive selection, all drives selected for wipe or all drives deselected. [#266](https://github.com/martijnvanbrummelen/nwipe/issues/266)
+- Fixed compilation issue with NixOS with broken musl libc error due to missing header [#275](https://github.com/martijnvanbrummelen/nwipe/issues/275)
+- Fixed status bar message showing incorrect information [#287](https://github.com/martijnvanbrummelen/nwipe/issues/287)
+- Right Justify log labels to maintain column alignment [#280](https://github.com/martijnvanbrummelen/nwipe/issues/280)
+- Added nwipe version & OS info to log [#297](https://github.com/martijnvanbrummelen/nwipe/pull/297)
+
+v0.28
+-----------------------
+- Fixed premature exit when terminal resized on completion of wipes (Thanks PartialVolume)
+- Fixed GUI when terminal is resized, currently not handled correctly causing missing or incorrectly sized ncurses windows/panels (Thanks PartialVolume)
+- Fixed GUI screen flicker under various situations. [#200](https://github.com/martijnvanbrummelen/nwipe/pull/200) Fixes [#115](https://github.com/martijnvanbrummelen/nwipe/issues/115) (Thanks PartialVolume)
+- Fixed responsivness of screen during wipe when resized. Info is updated every 10th/sec. Key presses are more responsive. (Thanks PartialVolume)
+- Fixed compiler warning regarding buffer overflow. Fixes [#202](https://github.com/martijnvanbrummelen/nwipe/issues/202) (Thanks PartialVolume)
+- Fixed Man page (Thanks martijnvanbrummelen)
+- Fixed individual device throughput. On completion of a wipe instead of the throughput calculation stopping for a completed wipe, it would continue to calculate resulting in a particular drives throughtput slowly dropping until eventually it reached zero. The overall throughput was not affected. (Thanks PartialVolume)
+
+v0.27
+-----------------------
+- Add `verify` method to verify a disk is zero filled [#128](https://github.com/martijnvanbrummelen/nwipe/pull/128) (Thanks Legogizmo)
+- Add new HMG IS5 enhanced wipe method [#168](https://github.com/martijnvanbrummelen/nwipe/pull/168) (Thanks infrastation)
+- Fixed percentage progress and show on completion of wipe (Thanks PartialVolume)
+- Implement clang-format support (Thanks louib)
+- Implement more frequent disk sync support (Thanks Legogizmo)
+- Format command line help to 80 character line length [#114](https://github.com/martijnvanbrummelen/nwipe/pull/114) (Thanks PartialVolume)
+- Fixed nwipe message log and missing messages that was causing segfaults under certain conditions (Thanks PartialVolume)
+- Add the Github build CI service and update Readme with build status labels (Thanks louib)
+- Miscellaneous smaller fixes
+
+
+v0.26
+-----
+- Add exclude drive option (Thanks PartialVolume)
+- Log hardware (Thanks PartialVolume)
+
+v0.25
+-----
+- Correct J=Up K=Down in footer (Thanks PartialVolume)
+- Fixed segfault initialize `nwipe_gui_thread` (Thanks PartialVolume)
+- Fixed memory leaks (Thanks PartialVolume)
+- Check right pointer (Thanks PartialVolume)
+- Fixed casting problem (Thanks PartialVolume)
+- Fixed serial number
+- Fixed uninitialized variable warning (Thanks PartialVolume)
+
+v0.24
+-----
+- use include values for version 0.17
+- display throughput value more friendly (Thanks Kelderek)
+
+v0.23
+-----
+- make serial visible again on 32Bit machines
+
+v0.22
+-----
+- Update manpage
+- use long long for device size
+- Use `ped_unit_format_byte` function to display(friendly) size of device
+
+v0.21
+-----
+- Fixed ETA not updating properly and bad total throughput display. (Thanks Niels Bassler).
+
+v0.20
+-----
+- Fixed build when panel header is not in `/usr/include` (Thanks Vincent Untz).
+
+v0.19
+-----
+- Fixed building on Fedora(Unknown `off64_t`) bug #19.
+- Use PRNG instead of zero's bug #7. (Thanks xambroz).
+
+v0.18
+-----
+- Fixed grammar.
+- Move from `loff_t` to `off64_t` for musl libc support.
+- Add `--nosignals` option.
+- Automake needs the `dist_` prefix to include man pages in `make dist`.
+- Remove more compiler warnings.
+- Add libintl, libuuid dependencies to allow parted static link
+
+v0.17
+-----
+- Remove control reaches end of non-void function" warnings (Thanks Vincent Untz).
+- Remove unused variables (Thanks Vincent Untz).
+- Change start key to 'S' instead of F10 (closes debian bug #755474).
+- Fixed problem with unusable device (Closes debian bug #755473).
+
+v0.16
+-----
+- Fixed problems building with clang compiler (Thanks Martijn van Brummelen)
+
+v0.15
+-----
+- Add more detailed information to status page when wiping
+- Add ability to send SIGUSR1 to print wiping current status to log
+- Fixed problem with status bar disappearing on narrow windows (Github issue #1)
+
+v0.14
+-----
+- Added explicit check for ncurses (required for Fedora). See bug 3604008.
+
+v0.13
+-----
+- Added nowait option (patch 3601259 - thanks David Shaw).
+- Added nogui option.
+- Updated man page and help command for above options and autonuke.
+- Added pkg-config check for ncurses (patch 3603140 - thanks Alon Bar-Lev).
+
+v0.12
+-----
+- Added ability to specify device on command line (patch 3587144).
+- Fixed segfault for -p option (patch 3587132).
+
+v0.11
+-----
+- Fixed bug 3568750. Not closing devices after initial scan.
+
+v0.10
+-----
+- Fixed bug 3553851. Not exiting on terminal kill. Fixed for all areas of
+ program including wiping.
+
+v0.09
+-----
+- Added feature #3545971. Display device name.
+- Added feature #3496858. Option to not perform a final blanking pass.
+
+v0.08
+-----
+- Fixed bug #3501746 whereby "wipe finished" was displayed too early
+
+v0.07
+-----
+- Added threading synchronisation for logging
+- Fixed bug #3486927 (incorrect Sourceforge URL)
+
+v0.06
+-----
+- Added man page (thanks Michal Ambroz <rebus@seznam.cz>)
+- Updated GPL licence and FSF address (thanks Michal Ambroz <rebus@seznam.cz>)
+
+v0.05
+-----
+- Added sequence number to disk selection
+- Added check for ncurses header files in subdir
+- Fixed screen corruption bug introduced in 0.04
+- Fixed occasional seg fault on start
+- Introduced dynamic array allocation for devices, with no hard limit
+- Minor updates to configure.ac
+
+v0.04
+-----
+- Removed references to DBAN in options.c
+- Added log file name option (-l|--logfile)
+- If no log file specified all messages go to STDOUT
+- Incorrect success message after an interruption fixed
+- Improved labelling of disks with no partition table
+- Added help command
+- Added version command
+- Added command 'b' to blank screen during wipe
+- Compilation needs to include panel library
+
+KNOWN BUG - display sometimes becomes corrupted after starting wipe
+
+v0.03
+-----
+- Added quit option label (ctrl-c)
+- Removed further references to DWIPE
+- Added GPL V2 licence file (COPYING)
+
+v0.02
+-----
+- Fixed segfault that happened during multiple disk wipes
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..d159169
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..0df77f5
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,9 @@
+SUBDIRS = src man
+
+# The set of files to be formatted.
+FORMATSOURCES = src/*.c src/*.h
+format:
+ clang-format -i -style=file $(FORMATSOURCES)
+
+check-format:
+ clang-format -i -style=file $(FORMATSOURCES) && git diff --exit-code
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..a864f7e
--- /dev/null
+++ b/README.md
@@ -0,0 +1,225 @@
+# nwipe
+![GitHub CI badge](https://github.com/martijnvanbrummelen/nwipe/workflows/ci_ubuntu_latest/badge.svg)
+[![GitHub release](https://img.shields.io/github/release/martijnvanbrummelen/nwipe)](https://github.com/martijnvanbrummelen/nwipe/releases/)
+
+nwipe is a fork of the dwipe command originally used by Darik's Boot and Nuke (DBAN). nwipe was created out of a need to run the DBAN dwipe command outside of DBAN, in order to allow its use with any host distribution, thus giving better hardware support.
+
+nwipe is a program that will securely erase the entire contents of disks. It can wipe a single drive or multiple disks simultaneously. It can operate as both a command line tool without a GUI or with a ncurses GUI as shown in the example below:
+
+> **Warning**
+> For some of nwipes features such as smart data in the PDF certificate, HPA/DCO detection and other uses, nwipe utilises smartmontools and hdparm. Therefore both hdparm & smartmontools are a mandatory requirement if you want all of nwipes features to be fully available. If you do not install smartmontools and hdparm, nwipe will provide a warning in the log that these programs cannot be found but will still run but many important features may not work as they should do.
+
+![Example wipe](/images/example_wipe.gif)
+
+<i>The video above shows six drives being simultaneously erased. It skips to the completion of all six wipes and shows five drives that were successfully erased and one drive that failed due to an I/O error. The drive that failed would then normally be physically destroyed. The five drives that were successfully wiped with zero errors or failures can then be redeployed.</i>
+
+![nwipe_certificate_0 35_5s](https://github.com/martijnvanbrummelen/nwipe/assets/22084881/cf181a9c-af2d-4bca-a6ed-15a4726cb12b)
+<i>The snapshot above shows nwipe's three page PDF certificate, drive identifiable information such as serial numbers have been anonymised using the nwipe command line option -q</i>
+
+## Erasure methods
+The user can select from a variety of recognised secure erase methods which include:
+
+* Fill With Zeros - Fills the device with zeros (0x00), one round only.
+* Fill With Ones - Fills the device with ones (0xFF), one round only.
+* RCMP TSSIT OPS-II - Royal Candian Mounted Police Technical Security Standard, OPS-II
+* DoD Short - The American Department of Defense 5220.22-M short 3 pass wipe (passes 1, 2 & 7).
+* DoD 5220.22M - The American Department of Defense 5220.22-M full 7 pass wipe.
+* Gutmann Wipe - Peter Gutmann's method (Secure Deletion of Data from Magnetic and Solid-State Memory).
+* PRNG Stream - Fills the device with a stream from the PRNG.
+* Verify Zeros - This method only reads the device and checks that it is filled with zeros (0x00).
+* Verify Ones - This method only reads the device and checks that it is filled with ones (0xFF).
+* HMG IS5 enhanced - Secure Sanitisation of Protectively Marked Information or Sensitive Information
+
+nwipe also includes the following pseudo random number generators:
+* Mersenne Twister
+* ISAAC
+
+These can be used to overwrite a drive with a stream of randomly generated characters.
+
+nwipe can be found in many [Linux distro repositories](#which-linux-distro-uses-the-latest-nwipe).
+
+nwipe is also included in [ShredOS](https://github.com/PartialVolume/shredos.x86_64) which was developed in particular to showcase nwipe as a fast-to-boot standalone method similar to DBAN. ShredOS always contains the latest nwipe version.
+
+## Compiling & Installing
+
+For a development setup, see the [Hacking section](#hacking) below. For a bootable version of the very latest nwipe master that you can write to an USB flash drive or CD/DVD, see the [Quick and easy bootable version of nwipe master section](#quick--easy-usb-bootable-version-of-nwipe-master-for-x86_64-systems) below.
+
+`nwipe` requires the following libraries to be installed:
+
+* ncurses
+* pthreads
+* parted
+* libconfig
+
+`nwipe` also requires the following program to be installed, it will abort with a warning if not found:
+
+* hdparm (as of current master and v0.35+)
+
+and optionally, but recommended, the following programs:
+
+* dmidecode
+* readlink
+* smartmontools
+
+### Debian & Ubuntu prerequisites
+
+If you are compiling `nwipe` from source, the following libraries will need to be installed first:
+
+```bash
+sudo apt install \
+ build-essential \
+ pkg-config \
+ automake \
+ libncurses5-dev \
+ autotools-dev \
+ libparted-dev \
+ libconfig-dev \
+ libconfig++-dev \
+ dmidecode \
+ coreutils \
+ smartmontools \
+ hdparm
+```
+
+### Fedora prerequisites
+
+```bash
+sudo bash
+dnf update
+dnf groupinstall "Development Tools"
+dnf groupinstall "C Development Tools and Libraries"
+yum install ncurses-devel
+yum install parted-devel
+yum install libconfig-devel
+yum install libconfig++-devel
+yum install dmidecode
+yum install coreutils
+yum install smartmontools
+yum install hdparm
+```
+Note. The following programs are optionally installed although recommended. 1. dmidecode 2. readlink 3. smartmontools.
+
+#### hdparm [REQUIRED]
+hdparm provides some of the information regarding disk size in sectors as related to the host protected area (HPA) and device configuration overlay (DCO). We do however have our own function that directly access the DCO to obtain the 'real max sectors' so reliance on hdparm may be removed at a future date.
+
+#### dmidecode [RECOMMENDED]
+dmidecode provides SMBIOS/DMI host data to stdout or the log file. If you don't install it you won't see the SMBIOS/DMI host data at the beginning of nwipes log.
+
+#### coreutils (provides readlink) [RECOMMENDED]
+readlink determines the bus type, i.e. ATA, USB etc. Without it the --nousb option won't work and bus type information will be missing from nwipes selection and wipe windows. The coreutils package is often automatically installed as default in most if not all distros.
+
+#### smartmontools [REQUIRED]
+smartmontools obtains serial number information for supported USB to IDE/SATA adapters. Without it, drives plugged into USB ports will not show serial number information.
+
+If you want a quick and easy way to keep your copy of nwipe running the latest master release of nwipe see the [automating the download and compilation](#automating-the-download-and-compilation-process-for-debian-based-distros) section.
+
+### Compilation
+
+First create all the autoconf files:
+```
+./autogen.sh
+```
+
+Then compile & install using the following standard commands:
+```
+./configure
+make format (only required if submitting pull requests)
+make
+make install
+```
+
+Then run nwipe !
+```
+cd src
+sudo ./nwipe
+```
+or
+```
+sudo nwipe
+```
+
+### Hacking
+
+If you wish to submit pull requests to this code we would prefer you enable all warnings when compiling.
+This can be done using the following compile commands:
+
+```
+./configure --prefix=/usr CFLAGS='-O0 -g -Wall -Wextra'
+make format (necessary if submitting pull requests)
+make
+make install
+```
+
+The `-O0 -g` flags disable optimisations. This is required if you're debugging with `gdb` in an IDE such as Kdevelop. With these optimisations enabled you won't be able to see the values of many variables in nwipe, not to mention the IDE won't step through the code properly.
+
+The `-Wall` and `-Wextra` flags enable all compiler warnings. Please submit code with zero warnings.
+
+Also make sure that your changes are consistent with the coding style defined in the `.clang-format` file, using:
+```
+make format
+```
+You will need `clang-format` installed to use the `format` command.
+
+Once done with your coding then the released/patch/fixed code can be compiled, with all the normal optimisations, using:
+```
+./configure --prefix=/usr && make && make install
+```
+
+## Automating the download and compilation process for Debian based distros.
+
+Here's a script that will do just that! It will create a directory in your home folder called 'nwipe_master'. It installs all the libraries required to compile the software (build-essential) and all the libraries that nwipe requires (libparted etc). It downloads the latest master copy of nwipe from github. It then compiles the software and then runs the latest nwipe. It doesn't write over the version of nwipe that's installed in the repository (If you had nwipe already installed). To run the latest master version of nwipe manually you would run it like this `sudo ~/nwipe_master/nwipe/src/nwipe`
+
+You can run the script multiple times; the first time it's run it will install all the libraries; subsequent times it will just say the libraries are up to date. As it always downloads a fresh copy of the nwipe master from Github, you can always stay up to date. Just run it to get the latest version of nwipe. It only takes 11 seconds to compile on my i7.
+
+If you already have nwipe installed from the repository, you need to take care which version you are running. If you typed `nwipe` from any directory it will always run the original repository copy of nwipe. To run the latest nwipe you have to explicitly tell it where the new copy is, e.g in the directory `~/nwipe_master/nwipe/src`. That's why you would run it by typing `sudo ~/nwipe_master/nwipe/src/nwipe` alternatively you could cd to the directory and run it like this:
+
+```
+cd ~/nwipe_master/nwipe/src
+./nwipe
+```
+
+Note the ./, that means only look in the current directory for nwipe. if you forgot to type ./ the computer would run the older repository version of nwipe.
+
+Once you have copied the script below into a file called buildnwipe, you need to give the file execute permissions `chmod +x buildnwipe` before you can run it.
+```
+#!/bin/bash
+cd "$HOME"
+nwipe_directory="nwipe_master"
+mkdir $nwipe_directory
+cd $nwipe_directory
+sudo apt install build-essential pkg-config automake libncurses5-dev autotools-dev libparted-dev libconfig-dev libconfig++-dev dmidecode readlink smartmontools git
+rm -rf nwipe
+git clone https://github.com/martijnvanbrummelen/nwipe.git
+cd "nwipe"
+./autogen.sh
+./configure
+make
+cd "src"
+sudo ./nwipe
+```
+
+## Quick & Easy, USB bootable version of nwipe master for x86_64 systems.
+If you want to just try out a bootable version of nwipe you can download [ShredOS](https://github.com/PartialVolume/shredos.x86_64). The ShredOS image is around 60MB and can be written to an USB flash drive in seconds, allowing you to boot straight into the latest version of nwipe. ShredOS is available for x86_64 (64-bit) and i686 (32-bit) CPU architectures and will boot both legacy BIOS and UEFI devices. It comes as .IMG (bootable USB flash drive image) or .ISO (for CD-R/DVD-R). Instructions and download can be found [here](https://github.com/PartialVolume/shredos.x86_64#obtaining-and-writing-shredos-to-a-usb-flash-drive-the-easy-way-).
+
+## Which Linux distro uses the latest nwipe?
+See [Repology](https://repology.org/project/nwipe/versions)
+
+And in addition checkout the following distros that all include nwipe:
+
+- [ShredOS](https://github.com/PartialVolume/shredos.x86_64) Always has the latest nwipe release.
+- [netboot.xyz](https://github.com/netbootxyz/netboot.xyz) Can network-boot ShredOS.
+- [partedmagic](https://partedmagic.com)
+- [SystemRescueCD](https://www.system-rescue.org)
+- [gparted live](https://sourceforge.net/projects/gparted/files/gparted-live-testing/1.2.0-2/)
+- [grml](https://grml.org/)
+
+Know of other distros that include nwipe? Then please let us know or issue a PR on this README.md. Thanks.
+
+## Bugs
+
+Bugs can be reported on GitHub:
+https://github.com/martijnvanbrummelen/nwipe
+
+## License
+
+GNU General Public License v2.0
diff --git a/autogen.sh b/autogen.sh
new file mode 100755
index 0000000..da9a805
--- /dev/null
+++ b/autogen.sh
@@ -0,0 +1,8 @@
+#!/bin/bash
+
+# Script to create all the required autoconf files
+
+aclocal
+autoheader
+automake --add-missing
+autoconf
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 0000000..a2b3450
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,86 @@
+# -*- Autoconf -*-
+# Process this file with autoconf to produce a configure script.
+
+AC_PREREQ([2.63])
+AC_INIT([nwipe],[0.36],[git@brumit.nl])
+AM_INIT_AUTOMAKE(foreign subdir-objects)
+AC_CONFIG_FILES([Makefile src/Makefile man/Makefile])
+AC_OUTPUT
+AC_CONFIG_SRCDIR([src/nwipe.c])
+AC_CONFIG_HEADERS([config.h])
+
+# Checks for programs.
+AC_PROG_CC
+PKG_PROG_PKG_CONFIG
+
+# Checks for libraries.
+
+PKG_CHECK_MODULES(
+ [PANEL],
+ [panel],
+ [
+ CFLAGS="${CFLAGS} ${PANEL_CFLAGS}"
+ LIBS="${LIBS} ${PANEL_LIBS}"
+ ],
+ [AC_CHECK_LIB([panel], [main], [
+ LIBS="-lpanel $LIBS"
+ AC_CHECK_HEADERS(panel.h,, [
+ AC_CHECK_HEADERS(ncurses/panel.h, [
+ AC_DEFINE([PANEL_IN_SUBDIR], [ncurses/], [Look for ncurses headers in subdir])
+ ], [AC_MSG_ERROR([ncurses panel headers not found])])
+ ])
+ ], [AC_MSG_ERROR([ncurses panel library not found])])]
+)
+
+PKG_CHECK_MODULES(
+ [NCURSES],
+ [ncurses],
+ [
+ CFLAGS="${CFLAGS} ${NCURSES_CFLAGS}"
+ LIBS="${LIBS} ${NCURSES_LIBS}"
+ ],
+ [AC_CHECK_LIB([ncurses], [delscreen], [
+ LIBS="-lncurses $LIBS"
+ AC_CHECK_HEADERS(ncurses.h,, [
+ AC_CHECK_HEADERS(ncurses/ncurses.h, [
+ AC_DEFINE([NCURSES_IN_SUBDIR], [ncurses/], [Look for ncurses headers in subdir])
+ ], [AC_MSG_ERROR([ncurses headers not found])])
+ ])
+ ], [AC_MSG_ERROR([ncurses development library not found])]
+ )]
+)
+
+PKG_CHECK_MODULES(
+ [LIBCONFIG],
+ [libconfig],
+ [
+ CFLAGS="${CFLAGS} ${LIBCONFIG_CFLAGS}"
+ LIBS="${LIBS} ${LIBCONFIG_LIBS}"
+ ],
+ [AC_CHECK_LIB([libconfig], [main], [
+ LIBS="-llibconfig $LIBS"
+ AC_CHECK_HEADERS(libconfig.h,, [
+ AC_CHECK_HEADERS(libconfig.h, [
+ AC_DEFINE([LIBCONFIG_IN_SUBDIR], [libconfig/], [Look for libconfig headers in subdir])
+ ], [AC_MSG_ERROR([libconfig headers not found])])
+ ])
+ ], [AC_MSG_ERROR([libconfig library not found])])]
+)
+
+AC_CHECK_LIB([intl], [libintl_dgettext]) # needed to statically link libparted, but not given in its pkgconfig file
+AC_CHECK_LIB([uuid], [uuid_generate]) # needed to statically link libparted, but not given in its pkgconfig file
+PKG_CHECK_MODULES([PARTED], [libparted], [libconfig])
+AC_CHECK_LIB([pthread], [main], ,[AC_MSG_ERROR([pthread development library not found])])
+
+# Checks for header files.
+AC_CHECK_HEADERS([libconfig.h fcntl.h inttypes.h netinet/in.h stddef.h stdint.h stdlib.h string.h sys/file.h sys/ioctl.h unistd.h])
+
+# Checks for typedefs, structures, and compiler characteristics.
+AC_TYPE_SIZE_T
+AC_CHECK_MEMBERS([struct stat.st_blksize])
+
+# Checks for library functions.
+AC_FUNC_MALLOC
+AC_CHECK_FUNCS([fdatasync memset regcomp strdup strerror])
+
+AC_OUTPUT
diff --git a/images/example_wipe.gif b/images/example_wipe.gif
new file mode 100644
index 0000000..68575a8
--- /dev/null
+++ b/images/example_wipe.gif
Binary files differ
diff --git a/man/Makefile.am b/man/Makefile.am
new file mode 100644
index 0000000..74200bc
--- /dev/null
+++ b/man/Makefile.am
@@ -0,0 +1 @@
+dist_man_MANS = nwipe.1
diff --git a/man/nwipe.1 b/man/nwipe.1
new file mode 100644
index 0000000..d7981ad
--- /dev/null
+++ b/man/nwipe.1
@@ -0,0 +1,144 @@
+.TH NWIPE "20" "February 2024" "nwipe version 0.36" "User Commands"
+.SH NAME
+nwipe \- securely erase disks
+.SH SYNOPSIS
+.B nwipe
+[\fIoptions\fR] [\fIdevice1\fR] [\fIdevice2\fR] ...
+.SH DESCRIPTION
+nwipe is a command that will securely erase disks using a variety of
+recognised methods. It is a fork of the dwipe command used by Darik's Boot
+and Nuke (dban). nwipe is included with partedmagic if you want a quick and
+easy bootable CD version. nwipe was created out of a need to run the DBAN
+dwipe command outside of DBAN, in order to allow its use with any host
+distribution, thus giving better hardware support. It is essentially the
+same as dwipe, with a few changes:
+.TP
+- pthreads is used instead of fork
+.TP
+- The parted library is used to detect drives
+.TP
+- The code is designed to be compiled with gcc
+.TP
+- SIGUSR1 can be used to log the stats of the current wipe
+
+.SH OPTIONS
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+Prints the version number
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+Prints a help summary
+.TP
+\fB\-\-autonuke\fR
+If no devices have been specified on the command line, starts wiping all
+devices immediately. If devices have been specified, starts wiping only
+those specified devices immediately.
+.TP
+\fB\-\-autopoweroff\fR
+Power off system on completion of wipe delayed for one minute. During
+this one minute delay you can abort the shutdown by typing sudo shutdown -c
+.TP
+\fB\-\-sync\fR=\fINUM\fR
+Will perform a syn after NUM writes (default: 100000)
+.IP
+0 \- fdatasync after the disk is completely written
+ fdatasync errors not detected until completion.
+ 0 is not recommended as disk errors may cause nwipe
+ to appear to hang
+.IP
+1 \- fdatasync after every write
+ Warning: Lower values will reduce wipe speeds.
+.IP
+1000 \- fdatasync after 1000 writes
+.TP
+\fB\-\-noblank\fR
+Do not perform the final blanking pass after the wipe (default is to blank,
+except when the method is RCMP TSSIT OPS\-II).
+.TP
+\fB\-\-nowait\fR
+Do not wait for a key before exiting (default is to wait).
+.TP
+\fB\-\-nosignals\fR
+Do not allow signals to interrupt a wipe (default is to allow).
+.TP
+\fB\-\-nousb\fR
+Do not show or wipe any USB devices, whether in GUI, --nogui or autonuke
+mode. (default is to allow USB devices to be shown and wiped).
+.TP
+\fB\-\-nogui\fR
+Do not show the GUI interface. Can only be used with the autonuke option.
+Nowait option is automatically invoked with the nogui option.
+SIGUSR1 can be used to retrieve the current wiping statistics.
+.TP
+\fB\-v\fR, \fB\-\-verbose\fR
+Log more messages, useful for debugging.
+.TP
+\fB\-\-verify\fR=\fITYPE\fR
+Whether to perform verification of erasure (default: last)
+.IP
+off \- Do not verify
+.IP
+last \- Verify after the last pass
+.IP
+all \- Verify every pass
+.IP
+Please mind that HMG IS5 enhanced always verifies the last (PRNG) pass
+regardless of this option.
+.TP
+\fB\-m\fR, \fB\-\-method\fR=\fIMETHOD\fR
+The wiping method (default: dodshort).
+.IP
+dod522022m / dod \- 7 pass DOD 5220.22\-M method
+.IP
+dodshort / dod3pass \- 3 pass DOD method
+.IP
+gutmann \- Peter Gutmann's Algorithm
+.IP
+ops2 \- RCMP TSSIT OPS\-II
+.IP
+random / prng / stream \- PRNG Stream
+.IP
+zero / quick \- Overwrite with zeros 0x00
+.IP
+one \- Overwrite with ones 0xFF
+.IP
+verify_zero \- Verifies disk is zero filled
+.IP
+verify_one \- Verifies disk is 0xFF filled
+.IP
+is5enh \- HMG IS5 enhanced
+.TP
+\fB\-l\fR, \fB\-\-logfile\fR=\fIFILE\fR
+Filename to log to. Default is STDOUT
+.TP
+\fB\-P\fR, \fB\-\-PDFreportpath\fR=\fIDIR\fR
+Directory to write the PDF nwipe reports/certificates to.
+Defaults to ".".
+If \fIDIR\fR is set to \fInoPDF\fR no report PDF files are written.
+.TP
+\fB\-p\fR, \fB\-\-prng\fR=\fIMETHOD\fR
+PRNG option (mersenne|twister|isaac|isaac64)
+.TP
+\fB\-q\fR, \fB\-\-quiet\fR
+Anonymize serial numbers, Gui & logs display:
+ XXXXXXXX = S/N obtained & anonymized.
+ ???????? = S/N not available.
+.TP
+\fB\-r\fR, \fB\-\-rounds\fR=\fINUM\fR
+Number of times to wipe the device using the selected method (default: 1)
+.TP
+\fB\-e\fR, \fB\-\-exclude\fR=\fIDEVICES\fR
+Up to ten comma separated devices to be excluded, examples:
+ --exclude=/dev/sdc
+ --exclude=/dev/sdc,/dev/sdd
+.SH BUGS
+Please see the GitHub site for the latest list
+(https://github.com/martijnvanbrummelen/nwipe/issues)
+.SH AUTHOR
+Nwipe is developed by Martijn van Brummelen <github@brumit.nl>
+.SH "SEE ALSO"
+.BR shred (1),
+.BR dwipe (1),
+.BR dd (1),
+.BR dcfldd (1),
+.BR dc3dd (1)
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..7d9bb44
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,10 @@
+# what flags you want to pass to the C compiler & linker
+#CFLAGS = -lncurses -lparted
+AM_CFLAGS =
+AM_LDFLAGS =
+
+# this lists the binaries to produce, the (non-PHONY, binary) targets in
+# the previous manual Makefile
+bin_PROGRAMS = nwipe
+nwipe_SOURCES = context.h logging.h options.h prng.h version.h temperature.h nwipe.c gui.c method.h pass.c device.c gui.h isaac_rand/isaac_standard.h isaac_rand/isaac_rand.h isaac_rand/isaac_rand.c isaac_rand/isaac64.h isaac_rand/isaac64.c mt19937ar-cok/mt19937ar-cok.c nwipe.h mt19937ar-cok/mt19937ar-cok.h pass.h device.h logging.c method.c options.c prng.c version.c temperature.c PDFGen/pdfgen.h PDFGen/pdfgen.c create_pdf.c create_pdf.h embedded_images/shred_db.jpg.c embedded_images/shred_db.jpg.h embedded_images/tick_erased.jpg.c embedded_images/tick_erased.jpg.h embedded_images/redcross.c embedded_images/redcross.h hpa_dco.h hpa_dco.c miscellaneous.h miscellaneous.c embedded_images/nwipe_exclamation.jpg.h embedded_images/nwipe_exclamation.jpg.c conf.h conf.c customers.h customers.c hddtemp_scsi/hddtemp.h hddtemp_scsi/scsi.h hddtemp_scsi/scsicmds.h hddtemp_scsi/get_scsi_temp.c hddtemp_scsi/scsi.c hddtemp_scsi/scsicmds.c
+nwipe_LDADD = $(PARTED_LIBS) $(LIBCONFIG)
diff --git a/src/PDFGen/pdfgen.c b/src/PDFGen/pdfgen.c
new file mode 100644
index 0000000..4d9cf45
--- /dev/null
+++ b/src/PDFGen/pdfgen.c
@@ -0,0 +1,4057 @@
+/* pdfgen.c provided courtesy of https://github.com/AndreRenaud/PDFGen
+ *
+ * Thank You !
+ */
+
+/**
+ * Simple engine for creating PDF files.
+ * It supports text, shapes, images etc...
+ * Capable of handling millions of objects without too much performance
+ * penalty.
+ * Public domain license - no warrenty implied; use at your own risk.
+ */
+
+/**
+ * PDF HINTS & TIPS
+ * The specification can be found at
+ * https://www.adobe.com/content/dam/acom/en/devnet/pdf/pdfs/pdf_reference_archives/PDFReference.pdf
+ * The following sites have various bits & pieces about PDF document
+ * generation
+ * http://www.mactech.com/articles/mactech/Vol.15/15.09/PDFIntro/index.html
+ * http://gnupdf.org/Introduction_to_PDF
+ * http://www.planetpdf.com/mainpage.asp?WebPageID=63
+ * http://archive.vector.org.uk/art10008970
+ * http://www.adobe.com/devnet/acrobat/pdfs/pdf_reference_1-7.pdf
+ * https://blog.idrsolutions.com/2013/01/understanding-the-pdf-file-format-overview/
+ *
+ * To validate the PDF output, there are several online validators:
+ * http://www.validatepdfa.com/online.htm
+ * http://www.datalogics.com/products/callas/callaspdfA-onlinedemo.asp
+ * http://www.pdf-tools.com/pdf/validate-pdfa-online.aspx
+ *
+ * In addition the 'pdftk' server can be used to analyse the output:
+ * https://www.pdflabs.com/docs/pdftk-cli-examples/
+ *
+ * PDF page markup operators:
+ * b closepath, fill,and stroke path.
+ * B fill and stroke path.
+ * b* closepath, eofill,and stroke path.
+ * B* eofill and stroke path.
+ * BI begin image.
+ * BMC begin marked content.
+ * BT begin text object.
+ * BX begin section allowing undefined operators.
+ * c curveto.
+ * cm concat. Concatenates the matrix to the current transform.
+ * cs setcolorspace for fill.
+ * CS setcolorspace for stroke.
+ * d setdash.
+ * Do execute the named XObject.
+ * DP mark a place in the content stream, with a dictionary.
+ * EI end image.
+ * EMC end marked content.
+ * ET end text object.
+ * EX end section that allows undefined operators.
+ * f fill path.
+ * f* eofill Even/odd fill path.
+ * g setgray (fill).
+ * G setgray (stroke).
+ * gs set parameters in the extended graphics state.
+ * h closepath.
+ * i setflat.
+ * ID begin image data.
+ * j setlinejoin.
+ * J setlinecap.
+ * k setcmykcolor (fill).
+ * K setcmykcolor (stroke).
+ * l lineto.
+ * m moveto.
+ * M setmiterlimit.
+ * n end path without fill or stroke.
+ * q save graphics state.
+ * Q restore graphics state.
+ * re rectangle.
+ * rg setrgbcolor (fill).
+ * RG setrgbcolor (stroke).
+ * s closepath and stroke path.
+ * S stroke path.
+ * sc setcolor (fill).
+ * SC setcolor (stroke).
+ * sh shfill (shaded fill).
+ * Tc set character spacing.
+ * Td move text current point.
+ * TD move text current point and set leading.
+ * Tf set font name and size.
+ * Tj show text.
+ * TJ show text, allowing individual character positioning.
+ * TL set leading.
+ * Tm set text matrix.
+ * Tr set text rendering mode.
+ * Ts set super/subscripting text rise.
+ * Tw set word spacing.
+ * Tz set horizontal scaling.
+ * T* move to start of next line.
+ * v curveto.
+ * w setlinewidth.
+ * W clip.
+ * y curveto.
+ */
+
+#if defined(_MSC_VER)
+#define _CRT_SECURE_NO_WARNINGS 1 // Drop the MSVC complaints about snprintf
+#define _USE_MATH_DEFINES
+#include <BaseTsd.h>
+typedef SSIZE_T ssize_t;
+#else
+
+#ifndef _POSIX_SOURCE
+#define _POSIX_SOURCE /* For localtime_r */
+#endif
+
+#ifndef _XOPEN_SOURCE
+#define _XOPEN_SOURCE 600 /* for M_SQRT2 */
+#endif
+
+#include <sys/types.h> /* for ssize_t */
+#endif
+
+#include <ctype.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <locale.h>
+#include <math.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <time.h>
+
+#include "pdfgen.h"
+
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
+
+#define PDF_RGB_R(c) (float)((((c) >> 16) & 0xff) / 255.0)
+#define PDF_RGB_G(c) (float)((((c) >> 8) & 0xff) / 255.0)
+#define PDF_RGB_B(c) (float)((((c) >> 0) & 0xff) / 255.0)
+#define PDF_IS_TRANSPARENT(c) (((c) >> 24) == 0xff)
+
+#if defined(_MSC_VER)
+#define inline __inline
+#define snprintf _snprintf
+#define strcasecmp _stricmp
+#define strncasecmp _strnicmp
+#ifdef stat
+#undef stat
+#endif
+#define stat _stat
+#define SKIP_ATTRIBUTE
+#else
+#include <strings.h> // strcasecmp
+#endif
+
+/**
+ * Try and support big & little endian machines
+ */
+static inline uint32_t bswap32(uint32_t x)
+{
+ return (((x & 0xff000000u) >> 24) | ((x & 0x00ff0000u) >> 8) |
+ ((x & 0x0000ff00u) << 8) | ((x & 0x000000ffu) << 24));
+}
+
+#ifdef __has_include // C++17, supported as extension to C++11 in clang, GCC
+ // 5+, vs2015
+#if __has_include(<endian.h>)
+#include <endian.h> // gnu libc normally provides, linux
+#elif __has_include(<machine/endian.h>)
+#include <machine/endian.h> //open bsd, macos
+#elif __has_include(<sys/param.h>)
+#include <sys/param.h> // mingw, some bsd (not open/macos)
+#elif __has_include(<sys/isadefs.h>)
+#include <sys/isadefs.h> // solaris
+#endif
+#endif
+
+#if !defined(__LITTLE_ENDIAN__) && !defined(__BIG_ENDIAN__)
+#ifndef __BYTE_ORDER__
+/* Fall back to little endian by default */
+#define __LITTLE_ENDIAN__
+#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ || __BYTE_ORDER == __BIG_ENDIAN
+#define __BIG_ENDIAN__
+#else
+#define __LITTLE_ENDIAN__
+#endif
+#endif
+
+#if defined(__LITTLE_ENDIAN__)
+#define ntoh32(x) bswap32((x))
+#elif defined(__BIG_ENDIAN__)
+#define ntoh32(x) (x)
+#endif
+
+// Limits on image sizes for sanity checking & to avoid plausible overflow
+// issues
+#define MAX_IMAGE_WIDTH (16 * 1024)
+#define MAX_IMAGE_HEIGHT (16 * 1024)
+
+// Signatures for various image formats
+static const uint8_t bmp_signature[] = {'B', 'M'};
+static const uint8_t png_signature[] = {0x89, 0x50, 0x4E, 0x47,
+ 0x0D, 0x0A, 0x1A, 0x0A};
+static const uint8_t jpeg_signature[] = {0xff, 0xd8};
+static const uint8_t ppm_signature[] = {'P', '6'};
+static const uint8_t pgm_signature[] = {'P', '5'};
+
+// Special signatures for PNG chunks
+static const char png_chunk_header[] = "IHDR";
+static const char png_chunk_palette[] = "PLTE";
+static const char png_chunk_data[] = "IDAT";
+static const char png_chunk_end[] = "IEND";
+
+typedef struct pdf_object pdf_object;
+
+enum {
+ OBJ_none, /* skipped */
+ OBJ_info,
+ OBJ_stream,
+ OBJ_font,
+ OBJ_page,
+ OBJ_bookmark,
+ OBJ_outline,
+ OBJ_catalog,
+ OBJ_pages,
+ OBJ_image,
+ OBJ_link,
+
+ OBJ_count,
+};
+
+struct flexarray {
+ void ***bins;
+ int item_count;
+ int bin_count;
+};
+
+/**
+ * Simple dynamic string object. Tries to store a reasonable amount on the
+ * stack before falling back to malloc once things get large
+ */
+struct dstr {
+ char static_data[128];
+ char *data;
+ size_t alloc_len;
+ size_t used_len;
+};
+
+struct pdf_object {
+ int type; /* See OBJ_xxxx */
+ int index; /* PDF output index */
+ int offset; /* Byte position within the output file */
+ struct pdf_object *prev; /* Previous of this type */
+ struct pdf_object *next; /* Next of this type */
+ union {
+ struct {
+ struct pdf_object *page;
+ char name[64];
+ struct pdf_object *parent;
+ struct flexarray children;
+ } bookmark;
+ struct dstr stream;
+ struct {
+ float width;
+ float height;
+ struct flexarray children;
+ struct flexarray annotations;
+ } page;
+ struct pdf_info *info;
+ struct {
+ char name[64];
+ int index;
+ } font;
+ struct {
+ struct pdf_object *page; /* Page containing link */
+ float llx; /* Clickable rectangle */
+ float lly;
+ float urx;
+ float ury;
+ struct pdf_object *target_page; /* Target page */
+ float target_x; /* Target location */
+ float target_y;
+ } link;
+ };
+};
+
+struct pdf_doc {
+ char errstr[128];
+ int errval;
+ struct flexarray objects;
+
+ float width;
+ float height;
+
+ struct pdf_object *current_font;
+
+ struct pdf_object *last_objects[OBJ_count];
+ struct pdf_object *first_objects[OBJ_count];
+};
+
+/**
+ * Since we're casting random areas of memory to these, make sure
+ * they're packed properly to match the image format requirements
+ */
+#pragma pack(push, 1)
+struct png_chunk {
+ uint32_t length;
+ // chunk type, see png_chunk_header, png_chunk_data, png_chunk_end
+ char type[4];
+};
+
+#pragma pack(pop)
+
+// Simple data container to store a single 24 Bit RGB value, used for
+// processing PNG images
+struct rgb_value {
+ uint8_t red;
+ uint8_t blue;
+ uint8_t green;
+};
+
+/**
+ * Simple flexible resizing array implementation
+ * The bins get larger in powers of two
+ * bin 0 = 1024 items
+ * 1 = 2048 items
+ * 2 = 4096 items
+ * etc...
+ */
+/* What is the first index that will be in the given bin? */
+#define MIN_SHIFT 10
+#define MIN_OFFSET ((1 << MIN_SHIFT) - 1)
+static int bin_offset[] = {
+ (1 << (MIN_SHIFT + 0)) - 1 - MIN_OFFSET,
+ (1 << (MIN_SHIFT + 1)) - 1 - MIN_OFFSET,
+ (1 << (MIN_SHIFT + 2)) - 1 - MIN_OFFSET,
+ (1 << (MIN_SHIFT + 3)) - 1 - MIN_OFFSET,
+ (1 << (MIN_SHIFT + 4)) - 1 - MIN_OFFSET,
+ (1 << (MIN_SHIFT + 5)) - 1 - MIN_OFFSET,
+ (1 << (MIN_SHIFT + 6)) - 1 - MIN_OFFSET,
+ (1 << (MIN_SHIFT + 7)) - 1 - MIN_OFFSET,
+ (1 << (MIN_SHIFT + 8)) - 1 - MIN_OFFSET,
+ (1 << (MIN_SHIFT + 9)) - 1 - MIN_OFFSET,
+ (1 << (MIN_SHIFT + 10)) - 1 - MIN_OFFSET,
+ (1 << (MIN_SHIFT + 11)) - 1 - MIN_OFFSET,
+ (1 << (MIN_SHIFT + 12)) - 1 - MIN_OFFSET,
+ (1 << (MIN_SHIFT + 13)) - 1 - MIN_OFFSET,
+ (1 << (MIN_SHIFT + 14)) - 1 - MIN_OFFSET,
+ (1 << (MIN_SHIFT + 15)) - 1 - MIN_OFFSET,
+};
+
+static inline int flexarray_get_bin(const struct flexarray *flex, int index)
+{
+ (void)flex;
+ for (size_t i = 0; i < ARRAY_SIZE(bin_offset); i++)
+ if (index < bin_offset[i])
+ return i - 1;
+ return -1;
+}
+
+static inline int flexarray_get_bin_size(const struct flexarray *flex,
+ int bin)
+{
+ (void)flex;
+ if (bin >= (int)ARRAY_SIZE(bin_offset) - 1)
+ return -1;
+ int next = bin_offset[bin + 1];
+ return next - bin_offset[bin];
+}
+
+static inline int flexarray_get_bin_offset(const struct flexarray *flex,
+ int bin, int index)
+{
+ (void)flex;
+ return index - bin_offset[bin];
+}
+
+static void flexarray_clear(struct flexarray *flex)
+{
+ for (int i = 0; i < flex->bin_count; i++)
+ free(flex->bins[i]);
+ free(flex->bins);
+ flex->bin_count = 0;
+ flex->item_count = 0;
+}
+
+static inline int flexarray_size(const struct flexarray *flex)
+{
+ return flex->item_count;
+}
+
+static int flexarray_set(struct flexarray *flex, int index, void *data)
+{
+ int bin = flexarray_get_bin(flex, index);
+ if (bin < 0)
+ return -EINVAL;
+ if (bin >= flex->bin_count) {
+ void ***bins = (void ***)realloc(flex->bins, (flex->bin_count + 1) *
+ sizeof(*flex->bins));
+ if (!bins)
+ return -ENOMEM;
+ flex->bin_count++;
+ flex->bins = bins;
+ flex->bins[flex->bin_count - 1] =
+ (void **)calloc(flexarray_get_bin_size(flex, flex->bin_count - 1),
+ sizeof(void *));
+ if (!flex->bins[flex->bin_count - 1]) {
+ flex->bin_count--;
+ return -ENOMEM;
+ }
+ }
+ flex->item_count++;
+ flex->bins[bin][flexarray_get_bin_offset(flex, bin, index)] = data;
+ return flex->item_count - 1;
+}
+
+static inline int flexarray_append(struct flexarray *flex, void *data)
+{
+ return flexarray_set(flex, flexarray_size(flex), data);
+}
+
+static inline void *flexarray_get(const struct flexarray *flex, int index)
+{
+ int bin;
+
+ if (index >= flex->item_count)
+ return NULL;
+ bin = flexarray_get_bin(flex, index);
+ if (bin < 0 || bin >= flex->bin_count)
+ return NULL;
+ return flex->bins[bin][flexarray_get_bin_offset(flex, bin, index)];
+}
+
+/**
+ * Simple dynamic string object. Tries to store a reasonable amount on the
+ * stack before falling back to malloc once things get large
+ */
+
+#define INIT_DSTR \
+ (struct dstr) \
+ { \
+ .static_data = {0}, .data = NULL, .alloc_len = 0, .used_len = 0 \
+ }
+
+static char *dstr_data(struct dstr *str)
+{
+ return str->data ? str->data : str->static_data;
+}
+
+static size_t dstr_len(const struct dstr *str)
+{
+ return str->used_len;
+}
+
+static ssize_t dstr_ensure(struct dstr *str, size_t len)
+{
+ if (len <= str->alloc_len)
+ return 0;
+ if (!str->data && len <= sizeof(str->static_data))
+ str->alloc_len = len;
+ else if (str->alloc_len < len) {
+ size_t new_len;
+
+ new_len = len + 4096;
+
+ if (str->data) {
+ char *new_data = (char *)realloc((void *)str->data, new_len);
+ if (!new_data)
+ return -ENOMEM;
+ str->data = new_data;
+ } else {
+ str->data = (char *)malloc(new_len);
+ if (!str->data)
+ return -ENOMEM;
+ if (str->used_len)
+ memcpy(str->data, str->static_data, str->used_len + 1);
+ }
+
+ str->alloc_len = new_len;
+ }
+ return 0;
+}
+
+// Locales can replace the decimal character with a ','.
+// This breaks the PDF output, so we force a 'safe' locale.
+static void force_locale(char *buf, int len)
+{
+ char *saved_locale = setlocale(LC_ALL, NULL);
+
+ if (!saved_locale) {
+ *buf = '\0';
+ } else {
+ strncpy(buf, saved_locale, len - 1);
+ buf[len - 1] = '\0';
+ }
+
+ setlocale(LC_NUMERIC, "POSIX");
+}
+
+static void restore_locale(char *buf)
+{
+ setlocale(LC_ALL, buf);
+}
+
+#ifndef SKIP_ATTRIBUTE
+static int dstr_printf(struct dstr *str, const char *fmt, ...)
+ __attribute__((format(printf, 2, 3)));
+#endif
+static int dstr_printf(struct dstr *str, const char *fmt, ...)
+{
+ va_list ap, aq;
+ int len;
+ char saved_locale[32];
+
+ force_locale(saved_locale, sizeof(saved_locale));
+
+ va_start(ap, fmt);
+ va_copy(aq, ap);
+ len = vsnprintf(NULL, 0, fmt, ap);
+ if (dstr_ensure(str, str->used_len + len + 1) < 0) {
+ va_end(ap);
+ va_end(aq);
+ restore_locale(saved_locale);
+ return -ENOMEM;
+ }
+ vsprintf(dstr_data(str) + str->used_len, fmt, aq);
+ str->used_len += len;
+ va_end(ap);
+ va_end(aq);
+ restore_locale(saved_locale);
+
+ return len;
+}
+
+static ssize_t dstr_append_data(struct dstr *str, const void *extend,
+ size_t len)
+{
+ if (dstr_ensure(str, str->used_len + len + 1) < 0)
+ return -ENOMEM;
+ memcpy(dstr_data(str) + str->used_len, extend, len);
+ str->used_len += len;
+ dstr_data(str)[str->used_len] = '\0';
+ return len;
+}
+
+static ssize_t dstr_append(struct dstr *str, const char *extend)
+{
+ return dstr_append_data(str, extend, strlen(extend));
+}
+
+static void dstr_free(struct dstr *str)
+{
+ if (str->data)
+ free(str->data);
+ *str = INIT_DSTR;
+}
+
+/**
+ * PDF Implementation
+ */
+
+#ifndef SKIP_ATTRIBUTE
+static int pdf_set_err(struct pdf_doc *doc, int errval, const char *buffer,
+ ...) __attribute__((format(printf, 3, 4)));
+#endif
+static int pdf_set_err(struct pdf_doc *doc, int errval, const char *buffer,
+ ...)
+{
+ va_list ap;
+ int len;
+
+ va_start(ap, buffer);
+ len = vsnprintf(doc->errstr, sizeof(doc->errstr) - 1, buffer, ap);
+ va_end(ap);
+
+ if (len < 0) {
+ doc->errstr[0] = '\0';
+ return errval;
+ }
+
+ if (len >= (int)(sizeof(doc->errstr) - 1))
+ len = (int)(sizeof(doc->errstr) - 1);
+
+ doc->errstr[len] = '\0';
+ doc->errval = errval;
+
+ return errval;
+}
+
+const char *pdf_get_err(const struct pdf_doc *pdf, int *errval)
+{
+ if (!pdf)
+ return NULL;
+ if (pdf->errstr[0] == '\0')
+ return NULL;
+ if (errval)
+ *errval = pdf->errval;
+ return pdf->errstr;
+}
+
+void pdf_clear_err(struct pdf_doc *pdf)
+{
+ if (!pdf)
+ return;
+ pdf->errstr[0] = '\0';
+ pdf->errval = 0;
+}
+
+static int pdf_get_errval(struct pdf_doc *pdf)
+{
+ if (!pdf)
+ return 0;
+ return pdf->errval;
+}
+
+static struct pdf_object *pdf_get_object(const struct pdf_doc *pdf, int index)
+{
+ return (struct pdf_object *)flexarray_get(&pdf->objects, index);
+}
+
+static int pdf_append_object(struct pdf_doc *pdf, struct pdf_object *obj)
+{
+ int index = flexarray_append(&pdf->objects, obj);
+
+ if (index < 0)
+ return index;
+ obj->index = index;
+
+ if (pdf->last_objects[obj->type]) {
+ obj->prev = pdf->last_objects[obj->type];
+ pdf->last_objects[obj->type]->next = obj;
+ }
+ pdf->last_objects[obj->type] = obj;
+
+ if (!pdf->first_objects[obj->type])
+ pdf->first_objects[obj->type] = obj;
+
+ return 0;
+}
+
+static void pdf_object_destroy(struct pdf_object *object)
+{
+ switch (object->type) {
+ case OBJ_stream:
+ case OBJ_image:
+ dstr_free(&object->stream);
+ break;
+ case OBJ_page:
+ flexarray_clear(&object->page.children);
+ flexarray_clear(&object->page.annotations);
+ break;
+ case OBJ_info:
+ free(object->info);
+ break;
+ case OBJ_bookmark:
+ flexarray_clear(&object->bookmark.children);
+ break;
+ }
+ free(object);
+}
+
+static struct pdf_object *pdf_add_object(struct pdf_doc *pdf, int type)
+{
+ struct pdf_object *obj;
+
+ if (!pdf)
+ return NULL;
+
+ obj = (struct pdf_object *)calloc(1, sizeof(*obj));
+ if (!obj) {
+ pdf_set_err(pdf, -errno,
+ "Unable to allocate object %d of type %d: %s",
+ flexarray_size(&pdf->objects) + 1, type, strerror(errno));
+ return NULL;
+ }
+
+ obj->type = type;
+
+ if (pdf_append_object(pdf, obj) < 0) {
+ free(obj);
+ return NULL;
+ }
+
+ return obj;
+}
+
+static void pdf_del_object(struct pdf_doc *pdf, struct pdf_object *obj)
+{
+ int type = obj->type;
+ flexarray_set(&pdf->objects, obj->index, NULL);
+ if (pdf->last_objects[type] == obj) {
+ pdf->last_objects[type] = NULL;
+ for (int i = 0; i < flexarray_size(&pdf->objects); i++) {
+ struct pdf_object *o = pdf_get_object(pdf, i);
+ if (o && o->type == type)
+ pdf->last_objects[type] = o;
+ }
+ }
+
+ if (pdf->first_objects[type] == obj) {
+ pdf->first_objects[type] = NULL;
+ for (int i = 0; i < flexarray_size(&pdf->objects); i++) {
+ struct pdf_object *o = pdf_get_object(pdf, i);
+ if (o && o->type == type) {
+ pdf->first_objects[type] = o;
+ break;
+ }
+ }
+ }
+
+ pdf_object_destroy(obj);
+}
+
+struct pdf_doc *pdf_create(float width, float height,
+ const struct pdf_info *info)
+{
+ struct pdf_doc *pdf;
+ struct pdf_object *obj;
+
+ pdf = (struct pdf_doc *)calloc(1, sizeof(*pdf));
+ if (!pdf)
+ return NULL;
+ pdf->width = width;
+ pdf->height = height;
+
+ /* We don't want to use ID 0 */
+ pdf_add_object(pdf, OBJ_none);
+
+ /* Create the 'info' object */
+ obj = pdf_add_object(pdf, OBJ_info);
+ if (!obj) {
+ pdf_destroy(pdf);
+ return NULL;
+ }
+ obj->info = (struct pdf_info *)calloc(sizeof(*obj->info), 1);
+ if (!obj->info) {
+ pdf_destroy(pdf);
+ return NULL;
+ }
+ if (info) {
+ *obj->info = *info;
+ obj->info->creator[sizeof(obj->info->creator) - 1] = '\0';
+ obj->info->producer[sizeof(obj->info->producer) - 1] = '\0';
+ obj->info->title[sizeof(obj->info->title) - 1] = '\0';
+ obj->info->author[sizeof(obj->info->author) - 1] = '\0';
+ obj->info->subject[sizeof(obj->info->subject) - 1] = '\0';
+ obj->info->date[sizeof(obj->info->date) - 1] = '\0';
+ }
+ /* FIXME: Should be quoting PDF strings? */
+ if (!obj->info->date[0]) {
+ time_t now = time(NULL);
+ struct tm tm;
+#ifdef _WIN32
+ struct tm *tmp;
+ tmp = localtime(&now);
+ tm = *tmp;
+#else
+ localtime_r(&now, &tm);
+#endif
+ strftime(obj->info->date, sizeof(obj->info->date), "%Y%m%d%H%M%SZ",
+ &tm);
+ }
+
+ if (!pdf_add_object(pdf, OBJ_pages)) {
+ pdf_destroy(pdf);
+ return NULL;
+ }
+ if (!pdf_add_object(pdf, OBJ_catalog)) {
+ pdf_destroy(pdf);
+ return NULL;
+ }
+
+ if (pdf_set_font(pdf, "Times-Roman") < 0) {
+ pdf_destroy(pdf);
+ return NULL;
+ }
+
+ return pdf;
+}
+
+float pdf_width(const struct pdf_doc *pdf)
+{
+ return pdf->width;
+}
+
+float pdf_height(const struct pdf_doc *pdf)
+{
+ return pdf->height;
+}
+
+float pdf_page_width(const struct pdf_object *page)
+{
+ return page->page.width;
+}
+
+float pdf_page_height(const struct pdf_object *page)
+{
+ return page->page.height;
+}
+
+void pdf_destroy(struct pdf_doc *pdf)
+{
+ if (pdf) {
+ for (int i = 0; i < flexarray_size(&pdf->objects); i++)
+ pdf_object_destroy(pdf_get_object(pdf, i));
+ flexarray_clear(&pdf->objects);
+ free(pdf);
+ }
+}
+
+static struct pdf_object *pdf_find_first_object(const struct pdf_doc *pdf,
+ int type)
+{
+ if (!pdf)
+ return NULL;
+ return pdf->first_objects[type];
+}
+
+static struct pdf_object *pdf_find_last_object(const struct pdf_doc *pdf,
+ int type)
+{
+ if (!pdf)
+ return NULL;
+ return pdf->last_objects[type];
+}
+
+int pdf_set_font(struct pdf_doc *pdf, const char *font)
+{
+ struct pdf_object *obj;
+ int last_index = 0;
+
+ /* See if we've used this font before */
+ for (obj = pdf_find_first_object(pdf, OBJ_font); obj; obj = obj->next) {
+ if (strcmp(obj->font.name, font) == 0)
+ break;
+ last_index = obj->font.index;
+ }
+
+ /* Create a new font object if we need it */
+ if (!obj) {
+ obj = pdf_add_object(pdf, OBJ_font);
+ if (!obj)
+ return pdf->errval;
+ strncpy(obj->font.name, font, sizeof(obj->font.name) - 1);
+ obj->font.name[sizeof(obj->font.name) - 1] = '\0';
+ obj->font.index = last_index + 1;
+ }
+
+ pdf->current_font = obj;
+
+ return 0;
+}
+
+struct pdf_object *pdf_append_page(struct pdf_doc *pdf)
+{
+ struct pdf_object *page;
+
+ page = pdf_add_object(pdf, OBJ_page);
+
+ if (!page)
+ return NULL;
+
+ page->page.width = pdf->width;
+ page->page.height = pdf->height;
+
+ return page;
+}
+
+int pdf_page_set_size(struct pdf_doc *pdf, struct pdf_object *page,
+ float width, float height)
+{
+ if (!page)
+ page = pdf_find_last_object(pdf, OBJ_page);
+
+ if (!page || page->type != OBJ_page)
+ return pdf_set_err(pdf, -EINVAL, "Invalid PDF page");
+ page->page.width = width;
+ page->page.height = height;
+ return 0;
+}
+
+// Recursively scan for the number of children
+static int pdf_get_bookmark_count(const struct pdf_object *obj)
+{
+ int count = 0;
+ if (obj->type == OBJ_bookmark) {
+ int nchildren = flexarray_size(&obj->bookmark.children);
+ count += nchildren;
+ for (int i = 0; i < nchildren; i++) {
+ count += pdf_get_bookmark_count(
+ (const struct pdf_object *)flexarray_get(
+ &obj->bookmark.children, i));
+ }
+ }
+ return count;
+}
+
+static int pdf_save_object(struct pdf_doc *pdf, FILE *fp, int index)
+{
+ struct pdf_object *object = pdf_get_object(pdf, index);
+ if (!object)
+ return -ENOENT;
+
+ if (object->type == OBJ_none)
+ return -ENOENT;
+
+ object->offset = ftell(fp);
+
+ fprintf(fp, "%d 0 obj\r\n", index);
+
+ switch (object->type) {
+ case OBJ_stream:
+ case OBJ_image: {
+ fwrite(dstr_data(&object->stream), dstr_len(&object->stream), 1, fp);
+ break;
+ }
+ case OBJ_info: {
+ struct pdf_info *info = object->info;
+
+ fprintf(fp, "<<\r\n");
+ if (info->creator[0])
+ fprintf(fp, " /Creator (%s)\r\n", info->creator);
+ if (info->producer[0])
+ fprintf(fp, " /Producer (%s)\r\n", info->producer);
+ if (info->title[0])
+ fprintf(fp, " /Title (%s)\r\n", info->title);
+ if (info->author[0])
+ fprintf(fp, " /Author (%s)\r\n", info->author);
+ if (info->subject[0])
+ fprintf(fp, " /Subject (%s)\r\n", info->subject);
+ if (info->date[0])
+ fprintf(fp, " /CreationDate (D:%s)\r\n", info->date);
+ fprintf(fp, ">>\r\n");
+ break;
+ }
+
+ case OBJ_page: {
+ struct pdf_object *pages = pdf_find_first_object(pdf, OBJ_pages);
+ struct pdf_object *image = pdf_find_first_object(pdf, OBJ_image);
+
+ fprintf(fp,
+ "<<\r\n"
+ " /Type /Page\r\n"
+ " /Parent %d 0 R\r\n",
+ pages->index);
+ fprintf(fp, " /MediaBox [0 0 %f %f]\r\n", object->page.width,
+ object->page.height);
+ fprintf(fp, " /Resources <<\r\n");
+ fprintf(fp, " /Font <<\r\n");
+ for (struct pdf_object *font = pdf_find_first_object(pdf, OBJ_font);
+ font; font = font->next)
+ fprintf(fp, " /F%d %d 0 R\r\n", font->font.index,
+ font->index);
+ fprintf(fp, " >>\r\n");
+ // We trim transparency to just 4-bits
+ fprintf(fp, " /ExtGState <<\r\n");
+ for (int i = 0; i < 16; i++) {
+ fprintf(fp, " /GS%d <</ca %f>>\r\n", i,
+ (float)(15 - i) / 15);
+ }
+ fprintf(fp, " >>\r\n");
+
+ if (image) {
+ fprintf(fp, " /XObject <<");
+ for (; image; image = image->next)
+ fprintf(fp, " /Image%d %d 0 R ", image->index,
+ image->index);
+ fprintf(fp, " >>\r\n");
+ }
+ fprintf(fp, " >>\r\n");
+
+ fprintf(fp, " /Contents [\r\n");
+ for (int i = 0; i < flexarray_size(&object->page.children); i++) {
+ struct pdf_object *child =
+ (struct pdf_object *)flexarray_get(&object->page.children, i);
+ fprintf(fp, "%d 0 R\r\n", child->index);
+ }
+ fprintf(fp, "]\r\n");
+
+ if (flexarray_size(&object->page.annotations)) {
+ fprintf(fp, " /Annots [\r\n");
+ for (int i = 0; i < flexarray_size(&object->page.annotations);
+ i++) {
+ struct pdf_object *child = (struct pdf_object *)flexarray_get(
+ &object->page.annotations, i);
+ fprintf(fp, "%d 0 R\r\n", child->index);
+ }
+ fprintf(fp, "]\r\n");
+ }
+
+ fprintf(fp, ">>\r\n");
+ break;
+ }
+
+ case OBJ_bookmark: {
+ struct pdf_object *parent, *other;
+
+ parent = object->bookmark.parent;
+ if (!parent)
+ parent = pdf_find_first_object(pdf, OBJ_outline);
+ if (!object->bookmark.page)
+ break;
+ fprintf(fp,
+ "<<\r\n"
+ " /Dest [%d 0 R /XYZ 0 %f null]\r\n"
+ " /Parent %d 0 R\r\n"
+ " /Title (%s)\r\n",
+ object->bookmark.page->index, pdf->height, parent->index,
+ object->bookmark.name);
+ int nchildren = flexarray_size(&object->bookmark.children);
+ if (nchildren > 0) {
+ struct pdf_object *f, *l;
+ f = (struct pdf_object *)flexarray_get(&object->bookmark.children,
+ 0);
+ l = (struct pdf_object *)flexarray_get(&object->bookmark.children,
+ nchildren - 1);
+ fprintf(fp, " /First %d 0 R\r\n", f->index);
+ fprintf(fp, " /Last %d 0 R\r\n", l->index);
+ fprintf(fp, " /Count %d\r\n", pdf_get_bookmark_count(object));
+ }
+ // Find the previous bookmark with the same parent
+ for (other = object->prev;
+ other && other->bookmark.parent != object->bookmark.parent;
+ other = other->prev)
+ ;
+ if (other)
+ fprintf(fp, " /Prev %d 0 R\r\n", other->index);
+ // Find the next bookmark with the same parent
+ for (other = object->next;
+ other && other->bookmark.parent != object->bookmark.parent;
+ other = other->next)
+ ;
+ if (other)
+ fprintf(fp, " /Next %d 0 R\r\n", other->index);
+ fprintf(fp, ">>\r\n");
+ break;
+ }
+
+ case OBJ_outline: {
+ struct pdf_object *first, *last, *cur;
+ first = pdf_find_first_object(pdf, OBJ_bookmark);
+ last = pdf_find_last_object(pdf, OBJ_bookmark);
+
+ if (first && last) {
+ int count = 0;
+ cur = first;
+ while (cur) {
+ if (!cur->bookmark.parent)
+ count += pdf_get_bookmark_count(cur) + 1;
+ cur = cur->next;
+ }
+
+ /* Bookmark outline */
+ fprintf(fp,
+ "<<\r\n"
+ " /Count %d\r\n"
+ " /Type /Outlines\r\n"
+ " /First %d 0 R\r\n"
+ " /Last %d 0 R\r\n"
+ ">>\r\n",
+ count, first->index, last->index);
+ }
+ break;
+ }
+
+ case OBJ_font:
+ fprintf(fp,
+ "<<\r\n"
+ " /Type /Font\r\n"
+ " /Subtype /Type1\r\n"
+ " /BaseFont /%s\r\n"
+ " /Encoding /WinAnsiEncoding\r\n"
+ ">>\r\n",
+ object->font.name);
+ break;
+
+ case OBJ_pages: {
+ int npages = 0;
+
+ fprintf(fp, "<<\r\n"
+ " /Type /Pages\r\n"
+ " /Kids [ ");
+ for (struct pdf_object *page = pdf_find_first_object(pdf, OBJ_page);
+ page; page = page->next) {
+ npages++;
+ fprintf(fp, "%d 0 R ", page->index);
+ }
+ fprintf(fp, "]\r\n");
+ fprintf(fp, " /Count %d\r\n", npages);
+ fprintf(fp, ">>\r\n");
+ break;
+ }
+
+ case OBJ_catalog: {
+ struct pdf_object *outline = pdf_find_first_object(pdf, OBJ_outline);
+ struct pdf_object *pages = pdf_find_first_object(pdf, OBJ_pages);
+
+ fprintf(fp, "<<\r\n"
+ " /Type /Catalog\r\n");
+ if (outline)
+ fprintf(fp,
+ " /Outlines %d 0 R\r\n"
+ " /PageMode /UseOutlines\r\n",
+ outline->index);
+ fprintf(fp,
+ " /Pages %d 0 R\r\n"
+ ">>\r\n",
+ pages->index);
+ break;
+ }
+
+ case OBJ_link: {
+ fprintf(fp,
+ "<<\r\n"
+ " /Type /Annot\r\n"
+ " /Subtype /Link\r\n"
+ " /Rect [%f %f %f %f]\r\n"
+ " /Dest [%u 0 R /XYZ %f %f null]\r\n"
+ " /Border [0 0 0]\r\n"
+ ">>\r\n",
+ object->link.llx, object->link.lly, object->link.urx,
+ object->link.ury, object->link.target_page->index,
+ object->link.target_x, object->link.target_y);
+ break;
+ }
+
+ default:
+ return pdf_set_err(pdf, -EINVAL, "Invalid PDF object type %d",
+ object->type);
+ }
+
+ fprintf(fp, "endobj\r\n");
+
+ return 0;
+}
+
+// Slightly modified djb2 hash algorithm to get pseudo-random ID
+static uint64_t hash(uint64_t hash, const void *data, size_t len)
+{
+ const uint8_t *d8 = (const uint8_t *)data;
+ for (; len; len--) {
+ hash = (((hash & 0x03ffffffffffffff) << 5) +
+ (hash & 0x7fffffffffffffff)) +
+ *d8++;
+ }
+ return hash;
+}
+
+int pdf_save_file(struct pdf_doc *pdf, FILE *fp)
+{
+ struct pdf_object *obj;
+ int xref_offset;
+ int xref_count = 0;
+ uint64_t id1, id2;
+ time_t now = time(NULL);
+ char saved_locale[32];
+
+ force_locale(saved_locale, sizeof(saved_locale));
+
+ fprintf(fp, "%%PDF-1.3\r\n");
+ /* Hibit bytes */
+ fprintf(fp, "%c%c%c%c%c\r\n", 0x25, 0xc7, 0xec, 0x8f, 0xa2);
+
+ /* Dump all the objects & get their file offsets */
+ for (int i = 0; i < flexarray_size(&pdf->objects); i++)
+ if (pdf_save_object(pdf, fp, i) >= 0)
+ xref_count++;
+
+ /* xref */
+ xref_offset = ftell(fp);
+ fprintf(fp, "xref\r\n");
+ fprintf(fp, "0 %d\r\n", xref_count + 1);
+ fprintf(fp, "0000000000 65535 f\r\n");
+ for (int i = 0; i < flexarray_size(&pdf->objects); i++) {
+ obj = pdf_get_object(pdf, i);
+ if (obj->type != OBJ_none)
+ fprintf(fp, "%10.10d 00000 n\r\n", obj->offset);
+ }
+
+ fprintf(fp,
+ "trailer\r\n"
+ "<<\r\n"
+ "/Size %d\r\n",
+ xref_count + 1);
+ obj = pdf_find_first_object(pdf, OBJ_catalog);
+ fprintf(fp, "/Root %d 0 R\r\n", obj->index);
+ obj = pdf_find_first_object(pdf, OBJ_info);
+ fprintf(fp, "/Info %d 0 R\r\n", obj->index);
+ /* Generate document unique IDs */
+ id1 = hash(5381, obj->info, sizeof(struct pdf_info));
+ id1 = hash(id1, &xref_count, sizeof(xref_count));
+ id2 = hash(5381, &now, sizeof(now));
+ fprintf(fp, "/ID [<%16.16" PRIx64 "> <%16.16" PRIx64 ">]\r\n", id1, id2);
+ fprintf(fp, ">>\r\n"
+ "startxref\r\n");
+ fprintf(fp, "%d\r\n", xref_offset);
+ fprintf(fp, "%%%%EOF\r\n");
+
+ restore_locale(saved_locale);
+
+ return 0;
+}
+
+int pdf_save(struct pdf_doc *pdf, const char *filename)
+{
+ FILE *fp;
+ int e;
+
+ if (filename == NULL)
+ fp = stdout;
+ else if ((fp = fopen(filename, "wb")) == NULL)
+ return pdf_set_err(pdf, -errno, "Unable to open '%s': %s", filename,
+ strerror(errno));
+
+ e = pdf_save_file(pdf, fp);
+
+ if (fp != stdout)
+ if (fclose(fp) != 0 && e >= 0)
+ return pdf_set_err(pdf, -errno, "Unable to close '%s': %s",
+ filename, strerror(errno));
+
+ return e;
+}
+
+static int pdf_add_stream(struct pdf_doc *pdf, struct pdf_object *page,
+ const char *buffer)
+{
+ struct pdf_object *obj;
+ size_t len;
+
+ if (!page)
+ page = pdf_find_last_object(pdf, OBJ_page);
+
+ if (!page)
+ return pdf_set_err(pdf, -EINVAL, "Invalid pdf page");
+
+ len = strlen(buffer);
+ /* We don't want any trailing whitespace in the stream */
+ while (len >= 1 && (buffer[len - 1] == '\r' || buffer[len - 1] == '\n'))
+ len--;
+
+ obj = pdf_add_object(pdf, OBJ_stream);
+ if (!obj)
+ return pdf->errval;
+
+ dstr_printf(&obj->stream, "<< /Length %zu >>stream\r\n", len);
+ dstr_append_data(&obj->stream, buffer, len);
+ dstr_append(&obj->stream, "\r\nendstream\r\n");
+
+ return flexarray_append(&page->page.children, obj);
+}
+
+int pdf_add_bookmark(struct pdf_doc *pdf, struct pdf_object *page, int parent,
+ const char *name)
+{
+ struct pdf_object *obj, *outline = NULL;
+
+ if (!page)
+ page = pdf_find_last_object(pdf, OBJ_page);
+
+ if (!page)
+ return pdf_set_err(pdf, -EINVAL,
+ "Unable to add bookmark, no pages available");
+
+ if (!pdf_find_first_object(pdf, OBJ_outline)) {
+ outline = pdf_add_object(pdf, OBJ_outline);
+ if (!outline)
+ return pdf->errval;
+ }
+
+ obj = pdf_add_object(pdf, OBJ_bookmark);
+ if (!obj) {
+ if (outline)
+ pdf_del_object(pdf, outline);
+ return pdf->errval;
+ }
+
+ strncpy(obj->bookmark.name, name, sizeof(obj->bookmark.name) - 1);
+ obj->bookmark.name[sizeof(obj->bookmark.name) - 1] = '\0';
+ obj->bookmark.page = page;
+ if (parent >= 0) {
+ struct pdf_object *parent_obj = pdf_get_object(pdf, parent);
+ if (!parent_obj)
+ return pdf_set_err(pdf, -EINVAL, "Invalid parent ID %d supplied",
+ parent);
+ obj->bookmark.parent = parent_obj;
+ flexarray_append(&parent_obj->bookmark.children, obj);
+ }
+
+ return obj->index;
+}
+
+int pdf_add_link(struct pdf_doc *pdf, struct pdf_object *page, float x,
+ float y, float width, float height,
+ struct pdf_object *target_page, float target_x,
+ float target_y)
+{
+ struct pdf_object *obj;
+
+ if (!page)
+ page = pdf_find_last_object(pdf, OBJ_page);
+
+ if (!page)
+ return pdf_set_err(pdf, -EINVAL,
+ "Unable to add link, no pages available");
+
+ if (!target_page)
+ return pdf_set_err(pdf, -EINVAL, "Unable to link, no target page");
+
+ obj = pdf_add_object(pdf, OBJ_link);
+ if (!obj) {
+ return pdf->errval;
+ }
+
+ obj->link.target_page = target_page;
+ obj->link.target_x = target_x;
+ obj->link.target_y = target_y;
+ obj->link.llx = x;
+ obj->link.lly = y;
+ obj->link.urx = x + width;
+ obj->link.ury = y + height;
+ flexarray_append(&page->page.annotations, obj);
+
+ return obj->index;
+}
+
+static int utf8_to_utf32(const char *utf8, int len, uint32_t *utf32)
+{
+ uint32_t ch;
+ uint8_t mask;
+
+ if (len <= 0 || !utf8 || !utf32)
+ return -EINVAL;
+
+ ch = *(uint8_t *)utf8;
+ if ((ch & 0x80) == 0) {
+ len = 1;
+ mask = 0x7f;
+ } else if ((ch & 0xe0) == 0xc0 && len >= 2) {
+ len = 2;
+ mask = 0x1f;
+ } else if ((ch & 0xf0) == 0xe0 && len >= 3) {
+ len = 3;
+ mask = 0xf;
+ } else if ((ch & 0xf8) == 0xf0 && len >= 4) {
+ len = 4;
+ mask = 0x7;
+ } else
+ return -EINVAL;
+
+ ch = 0;
+ for (int i = 0; i < len; i++) {
+ int shift = (len - i - 1) * 6;
+ if (!*utf8)
+ return -EINVAL;
+ if (i == 0)
+ ch |= ((uint32_t)(*utf8++) & mask) << shift;
+ else
+ ch |= ((uint32_t)(*utf8++) & 0x3f) << shift;
+ }
+
+ *utf32 = ch;
+
+ return len;
+}
+
+static int utf8_to_pdfencoding(struct pdf_doc *pdf, const char *utf8, int len,
+ uint8_t *res)
+{
+ uint32_t code;
+ int code_len;
+
+ *res = 0;
+
+ code_len = utf8_to_utf32(utf8, len, &code);
+ if (code_len < 0) {
+ return pdf_set_err(pdf, -EINVAL, "Invalid UTF-8 encoding");
+ }
+
+ if (code > 255) {
+ /* We support *some* minimal UTF-8 characters */
+ // See Appendix D of
+ // https://www.adobe.com/content/dam/acom/en/devnet/pdf/pdfs/pdf_reference_archives/PDFReference.pdf
+ // These are all in WinAnsiEncoding
+ switch (code) {
+ case 0x160: // Latin Capital Letter S with Caron
+ *res = 0212;
+ break;
+ case 0x161: // Latin Small Letter S with Caron
+ *res = 0232;
+ break;
+ case 0x17d: // Latin Capital Letter Z with Caron
+ *res = 0216;
+ break;
+ case 0x17e: // Latin Small Letter Z with Caron
+ *res = 0236;
+ break;
+ case 0x2014: // emdash
+ *res = 0227;
+ break;
+ case 0x2018: // left single quote
+ *res = 0221;
+ break;
+ case 0x2019: // right single quote
+ *res = 0222;
+ break;
+ case 0x201c: // left double quote
+ *res = 0223;
+ break;
+ case 0x201d: // right double quote
+ *res = 0224;
+ break;
+ case 0x20ac: // Euro
+ *res = 0200;
+ break;
+ default:
+ return pdf_set_err(pdf, -EINVAL,
+ "Unsupported UTF-8 character: 0x%x 0o%o", code,
+ code);
+ }
+ } else {
+ *res = code;
+ }
+ return code_len;
+}
+
+static int pdf_add_text_spacing(struct pdf_doc *pdf, struct pdf_object *page,
+ const char *text, float size, float xoff,
+ float yoff, uint32_t colour, float spacing)
+{
+ int ret;
+ size_t len = text ? strlen(text) : 0;
+ struct dstr str = INIT_DSTR;
+ int alpha = (colour >> 24) >> 4;
+
+ /* Don't bother adding empty/null strings */
+ if (!len)
+ return 0;
+
+ dstr_append(&str, "BT ");
+ dstr_printf(&str, "/GS%d gs ", alpha);
+ dstr_printf(&str, "%f %f TD ", xoff, yoff);
+ dstr_printf(&str, "/F%d %f Tf ", pdf->current_font->font.index, size);
+ dstr_printf(&str, "%f %f %f rg ", PDF_RGB_R(colour), PDF_RGB_G(colour),
+ PDF_RGB_B(colour));
+ dstr_printf(&str, "%f Tc ", spacing);
+ dstr_append(&str, "(");
+
+ /* Escape magic characters properly */
+ for (size_t i = 0; i < len;) {
+ int code_len;
+ uint8_t pdf_char;
+ code_len = utf8_to_pdfencoding(pdf, &text[i], len - i, &pdf_char);
+ if (code_len < 0) {
+ dstr_free(&str);
+ return code_len;
+ }
+
+ if (strchr("()\\", pdf_char)) {
+ char buf[3];
+ /* Escape some characters */
+ buf[0] = '\\';
+ buf[1] = pdf_char;
+ buf[2] = '\0';
+ dstr_append(&str, buf);
+ } else if (strrchr("\n\r\t\b\f", pdf_char)) {
+ /* Skip over these characters */
+ ;
+ } else {
+ dstr_append_data(&str, &pdf_char, 1);
+ }
+
+ i += code_len;
+ }
+ dstr_append(&str, ") Tj ");
+ dstr_append(&str, "ET");
+
+ ret = pdf_add_stream(pdf, page, dstr_data(&str));
+ dstr_free(&str);
+ return ret;
+}
+
+int pdf_add_text(struct pdf_doc *pdf, struct pdf_object *page,
+ const char *text, float size, float xoff, float yoff,
+ uint32_t colour)
+{
+ return pdf_add_text_spacing(pdf, page, text, size, xoff, yoff, colour, 0);
+}
+
+/* How wide is each character, in points, at size 14 */
+static const uint16_t helvetica_widths[256] = {
+ 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280,
+ 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280,
+ 280, 280, 280, 280, 280, 280, 280, 280, 357, 560, 560, 896, 672,
+ 192, 335, 335, 392, 588, 280, 335, 280, 280, 560, 560, 560, 560,
+ 560, 560, 560, 560, 560, 560, 280, 280, 588, 588, 588, 560, 1023,
+ 672, 672, 727, 727, 672, 615, 784, 727, 280, 504, 672, 560, 839,
+ 727, 784, 672, 784, 727, 672, 615, 727, 672, 951, 672, 672, 615,
+ 280, 280, 280, 472, 560, 335, 560, 560, 504, 560, 560, 280, 560,
+ 560, 223, 223, 504, 223, 839, 560, 560, 560, 560, 335, 504, 280,
+ 560, 504, 727, 504, 504, 504, 336, 262, 336, 588, 352, 560, 352,
+ 223, 560, 335, 1008, 560, 560, 335, 1008, 672, 335, 1008, 352, 615,
+ 352, 352, 223, 223, 335, 335, 352, 560, 1008, 335, 1008, 504, 335,
+ 951, 352, 504, 672, 280, 335, 560, 560, 560, 560, 262, 560, 335,
+ 742, 372, 560, 588, 335, 742, 335, 403, 588, 335, 335, 335, 560,
+ 541, 280, 335, 335, 367, 560, 840, 840, 840, 615, 672, 672, 672,
+ 672, 672, 672, 1008, 727, 672, 672, 672, 672, 280, 280, 280, 280,
+ 727, 727, 784, 784, 784, 784, 784, 588, 784, 727, 727, 727, 727,
+ 672, 672, 615, 560, 560, 560, 560, 560, 560, 896, 504, 560, 560,
+ 560, 560, 280, 280, 280, 280, 560, 560, 560, 560, 560, 560, 560,
+ 588, 615, 560, 560, 560, 560, 504, 560, 504,
+};
+
+static const uint16_t helvetica_bold_widths[256] = {
+ 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280,
+ 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280,
+ 280, 280, 280, 280, 280, 335, 477, 560, 560, 896, 727, 239, 335, 335,
+ 392, 588, 280, 335, 280, 280, 560, 560, 560, 560, 560, 560, 560, 560,
+ 560, 560, 335, 335, 588, 588, 588, 615, 982, 727, 727, 727, 727, 672,
+ 615, 784, 727, 280, 560, 727, 615, 839, 727, 784, 672, 784, 727, 672,
+ 615, 727, 672, 951, 672, 672, 615, 335, 280, 335, 588, 560, 335, 560,
+ 615, 560, 615, 560, 335, 615, 615, 280, 280, 560, 280, 896, 615, 615,
+ 615, 615, 392, 560, 335, 615, 560, 784, 560, 560, 504, 392, 282, 392,
+ 588, 352, 560, 352, 280, 560, 504, 1008, 560, 560, 335, 1008, 672, 335,
+ 1008, 352, 615, 352, 352, 280, 280, 504, 504, 352, 560, 1008, 335, 1008,
+ 560, 335, 951, 352, 504, 672, 280, 335, 560, 560, 560, 560, 282, 560,
+ 335, 742, 372, 560, 588, 335, 742, 335, 403, 588, 335, 335, 335, 615,
+ 560, 280, 335, 335, 367, 560, 840, 840, 840, 615, 727, 727, 727, 727,
+ 727, 727, 1008, 727, 672, 672, 672, 672, 280, 280, 280, 280, 727, 727,
+ 784, 784, 784, 784, 784, 588, 784, 727, 727, 727, 727, 672, 672, 615,
+ 560, 560, 560, 560, 560, 560, 896, 560, 560, 560, 560, 560, 280, 280,
+ 280, 280, 615, 615, 615, 615, 615, 615, 615, 588, 615, 615, 615, 615,
+ 615, 560, 615, 560,
+};
+
+static const uint16_t helvetica_bold_oblique_widths[256] = {
+ 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280,
+ 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280,
+ 280, 280, 280, 280, 280, 335, 477, 560, 560, 896, 727, 239, 335, 335,
+ 392, 588, 280, 335, 280, 280, 560, 560, 560, 560, 560, 560, 560, 560,
+ 560, 560, 335, 335, 588, 588, 588, 615, 982, 727, 727, 727, 727, 672,
+ 615, 784, 727, 280, 560, 727, 615, 839, 727, 784, 672, 784, 727, 672,
+ 615, 727, 672, 951, 672, 672, 615, 335, 280, 335, 588, 560, 335, 560,
+ 615, 560, 615, 560, 335, 615, 615, 280, 280, 560, 280, 896, 615, 615,
+ 615, 615, 392, 560, 335, 615, 560, 784, 560, 560, 504, 392, 282, 392,
+ 588, 352, 560, 352, 280, 560, 504, 1008, 560, 560, 335, 1008, 672, 335,
+ 1008, 352, 615, 352, 352, 280, 280, 504, 504, 352, 560, 1008, 335, 1008,
+ 560, 335, 951, 352, 504, 672, 280, 335, 560, 560, 560, 560, 282, 560,
+ 335, 742, 372, 560, 588, 335, 742, 335, 403, 588, 335, 335, 335, 615,
+ 560, 280, 335, 335, 367, 560, 840, 840, 840, 615, 727, 727, 727, 727,
+ 727, 727, 1008, 727, 672, 672, 672, 672, 280, 280, 280, 280, 727, 727,
+ 784, 784, 784, 784, 784, 588, 784, 727, 727, 727, 727, 672, 672, 615,
+ 560, 560, 560, 560, 560, 560, 896, 560, 560, 560, 560, 560, 280, 280,
+ 280, 280, 615, 615, 615, 615, 615, 615, 615, 588, 615, 615, 615, 615,
+ 615, 560, 615, 560,
+};
+
+static const uint16_t helvetica_oblique_widths[256] = {
+ 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280,
+ 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280,
+ 280, 280, 280, 280, 280, 280, 280, 280, 357, 560, 560, 896, 672,
+ 192, 335, 335, 392, 588, 280, 335, 280, 280, 560, 560, 560, 560,
+ 560, 560, 560, 560, 560, 560, 280, 280, 588, 588, 588, 560, 1023,
+ 672, 672, 727, 727, 672, 615, 784, 727, 280, 504, 672, 560, 839,
+ 727, 784, 672, 784, 727, 672, 615, 727, 672, 951, 672, 672, 615,
+ 280, 280, 280, 472, 560, 335, 560, 560, 504, 560, 560, 280, 560,
+ 560, 223, 223, 504, 223, 839, 560, 560, 560, 560, 335, 504, 280,
+ 560, 504, 727, 504, 504, 504, 336, 262, 336, 588, 352, 560, 352,
+ 223, 560, 335, 1008, 560, 560, 335, 1008, 672, 335, 1008, 352, 615,
+ 352, 352, 223, 223, 335, 335, 352, 560, 1008, 335, 1008, 504, 335,
+ 951, 352, 504, 672, 280, 335, 560, 560, 560, 560, 262, 560, 335,
+ 742, 372, 560, 588, 335, 742, 335, 403, 588, 335, 335, 335, 560,
+ 541, 280, 335, 335, 367, 560, 840, 840, 840, 615, 672, 672, 672,
+ 672, 672, 672, 1008, 727, 672, 672, 672, 672, 280, 280, 280, 280,
+ 727, 727, 784, 784, 784, 784, 784, 588, 784, 727, 727, 727, 727,
+ 672, 672, 615, 560, 560, 560, 560, 560, 560, 896, 504, 560, 560,
+ 560, 560, 280, 280, 280, 280, 560, 560, 560, 560, 560, 560, 560,
+ 588, 615, 560, 560, 560, 560, 504, 560, 504,
+};
+
+static const uint16_t symbol_widths[256] = {
+ 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252,
+ 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252,
+ 252, 252, 252, 252, 252, 335, 718, 504, 553, 839, 784, 442, 335, 335,
+ 504, 553, 252, 553, 252, 280, 504, 504, 504, 504, 504, 504, 504, 504,
+ 504, 504, 280, 280, 553, 553, 553, 447, 553, 727, 672, 727, 616, 615,
+ 769, 607, 727, 335, 636, 727, 691, 896, 727, 727, 774, 746, 560, 596,
+ 615, 695, 442, 774, 650, 801, 615, 335, 869, 335, 663, 504, 504, 636,
+ 553, 553, 497, 442, 525, 414, 607, 331, 607, 553, 553, 580, 525, 553,
+ 553, 525, 553, 607, 442, 580, 718, 691, 496, 691, 497, 483, 201, 483,
+ 553, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 756, 624, 248, 553, 168, 718, 504, 759,
+ 759, 759, 759, 1050, 994, 607, 994, 607, 403, 553, 414, 553, 553, 718,
+ 497, 463, 553, 553, 553, 553, 1008, 607, 1008, 663, 829, 691, 801, 994,
+ 774, 774, 829, 774, 774, 718, 718, 718, 718, 718, 718, 718, 774, 718,
+ 796, 796, 897, 829, 553, 252, 718, 607, 607, 1050, 994, 607, 994, 607,
+ 497, 331, 796, 796, 792, 718, 387, 387, 387, 387, 387, 387, 497, 497,
+ 497, 497, 0, 331, 276, 691, 691, 691, 387, 387, 387, 387, 387, 387,
+ 497, 497, 497, 0,
+};
+
+static const uint16_t times_widths[256] = {
+ 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252,
+ 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252,
+ 252, 252, 252, 252, 252, 335, 411, 504, 504, 839, 784, 181, 335, 335,
+ 504, 568, 252, 335, 252, 280, 504, 504, 504, 504, 504, 504, 504, 504,
+ 504, 504, 280, 280, 568, 568, 568, 447, 928, 727, 672, 672, 727, 615,
+ 560, 727, 727, 335, 392, 727, 615, 896, 727, 727, 560, 727, 672, 560,
+ 615, 727, 727, 951, 727, 727, 615, 335, 280, 335, 472, 504, 335, 447,
+ 504, 447, 504, 447, 335, 504, 504, 280, 280, 504, 280, 784, 504, 504,
+ 504, 504, 335, 392, 280, 504, 504, 727, 504, 504, 447, 483, 201, 483,
+ 545, 352, 504, 352, 335, 504, 447, 1008, 504, 504, 335, 1008, 560, 335,
+ 896, 352, 615, 352, 352, 335, 335, 447, 447, 352, 504, 1008, 335, 987,
+ 392, 335, 727, 352, 447, 727, 252, 335, 504, 504, 504, 504, 201, 504,
+ 335, 766, 278, 504, 568, 335, 766, 335, 403, 568, 302, 302, 335, 504,
+ 456, 252, 335, 302, 312, 504, 756, 756, 756, 447, 727, 727, 727, 727,
+ 727, 727, 896, 672, 615, 615, 615, 615, 335, 335, 335, 335, 727, 727,
+ 727, 727, 727, 727, 727, 568, 727, 727, 727, 727, 727, 727, 560, 504,
+ 447, 447, 447, 447, 447, 447, 672, 447, 447, 447, 447, 447, 280, 280,
+ 280, 280, 504, 504, 504, 504, 504, 504, 504, 568, 504, 504, 504, 504,
+ 504, 504, 504, 504,
+};
+
+static const uint16_t times_bold_widths[256] = {
+ 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252,
+ 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252,
+ 252, 252, 252, 252, 252, 252, 252, 335, 559, 504, 504, 1008, 839,
+ 280, 335, 335, 504, 574, 252, 335, 252, 280, 504, 504, 504, 504,
+ 504, 504, 504, 504, 504, 504, 335, 335, 574, 574, 574, 504, 937,
+ 727, 672, 727, 727, 672, 615, 784, 784, 392, 504, 784, 672, 951,
+ 727, 784, 615, 784, 727, 560, 672, 727, 727, 1008, 727, 727, 672,
+ 335, 280, 335, 585, 504, 335, 504, 560, 447, 560, 447, 335, 504,
+ 560, 280, 335, 560, 280, 839, 560, 504, 560, 560, 447, 392, 335,
+ 560, 504, 727, 504, 504, 447, 397, 221, 397, 524, 352, 504, 352,
+ 335, 504, 504, 1008, 504, 504, 335, 1008, 560, 335, 1008, 352, 672,
+ 352, 352, 335, 335, 504, 504, 352, 504, 1008, 335, 1008, 392, 335,
+ 727, 352, 447, 727, 252, 335, 504, 504, 504, 504, 221, 504, 335,
+ 752, 302, 504, 574, 335, 752, 335, 403, 574, 302, 302, 335, 560,
+ 544, 252, 335, 302, 332, 504, 756, 756, 756, 504, 727, 727, 727,
+ 727, 727, 727, 1008, 727, 672, 672, 672, 672, 392, 392, 392, 392,
+ 727, 727, 784, 784, 784, 784, 784, 574, 784, 727, 727, 727, 727,
+ 727, 615, 560, 504, 504, 504, 504, 504, 504, 727, 447, 447, 447,
+ 447, 447, 280, 280, 280, 280, 504, 560, 504, 504, 504, 504, 504,
+ 574, 504, 560, 560, 560, 560, 504, 560, 504,
+};
+
+static const uint16_t times_bold_italic_widths[256] = {
+ 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252,
+ 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252,
+ 252, 252, 252, 252, 252, 392, 559, 504, 504, 839, 784, 280, 335, 335,
+ 504, 574, 252, 335, 252, 280, 504, 504, 504, 504, 504, 504, 504, 504,
+ 504, 504, 335, 335, 574, 574, 574, 504, 838, 672, 672, 672, 727, 672,
+ 672, 727, 784, 392, 504, 672, 615, 896, 727, 727, 615, 727, 672, 560,
+ 615, 727, 672, 896, 672, 615, 615, 335, 280, 335, 574, 504, 335, 504,
+ 504, 447, 504, 447, 335, 504, 560, 280, 280, 504, 280, 784, 560, 504,
+ 504, 504, 392, 392, 280, 560, 447, 672, 504, 447, 392, 350, 221, 350,
+ 574, 352, 504, 352, 335, 504, 504, 1008, 504, 504, 335, 1008, 560, 335,
+ 951, 352, 615, 352, 352, 335, 335, 504, 504, 352, 504, 1008, 335, 1008,
+ 392, 335, 727, 352, 392, 615, 252, 392, 504, 504, 504, 504, 221, 504,
+ 335, 752, 268, 504, 610, 335, 752, 335, 403, 574, 302, 302, 335, 580,
+ 504, 252, 335, 302, 302, 504, 756, 756, 756, 504, 672, 672, 672, 672,
+ 672, 672, 951, 672, 672, 672, 672, 672, 392, 392, 392, 392, 727, 727,
+ 727, 727, 727, 727, 727, 574, 727, 727, 727, 727, 727, 615, 615, 504,
+ 504, 504, 504, 504, 504, 504, 727, 447, 447, 447, 447, 447, 280, 280,
+ 280, 280, 504, 560, 504, 504, 504, 504, 504, 574, 504, 560, 560, 560,
+ 560, 447, 504, 447,
+};
+
+static const uint16_t times_italic_widths[256] = {
+ 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252,
+ 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252,
+ 252, 252, 252, 252, 252, 335, 423, 504, 504, 839, 784, 215, 335, 335,
+ 504, 680, 252, 335, 252, 280, 504, 504, 504, 504, 504, 504, 504, 504,
+ 504, 504, 335, 335, 680, 680, 680, 504, 927, 615, 615, 672, 727, 615,
+ 615, 727, 727, 335, 447, 672, 560, 839, 672, 727, 615, 727, 615, 504,
+ 560, 727, 615, 839, 615, 560, 560, 392, 280, 392, 425, 504, 335, 504,
+ 504, 447, 504, 447, 280, 504, 504, 280, 280, 447, 280, 727, 504, 504,
+ 504, 504, 392, 392, 280, 504, 447, 672, 447, 447, 392, 403, 277, 403,
+ 545, 352, 504, 352, 335, 504, 560, 896, 504, 504, 335, 1008, 504, 335,
+ 951, 352, 560, 352, 352, 335, 335, 560, 560, 352, 504, 896, 335, 987,
+ 392, 335, 672, 352, 392, 560, 252, 392, 504, 504, 504, 504, 277, 504,
+ 335, 766, 278, 504, 680, 335, 766, 335, 403, 680, 302, 302, 335, 504,
+ 527, 252, 335, 302, 312, 504, 756, 756, 756, 504, 615, 615, 615, 615,
+ 615, 615, 896, 672, 615, 615, 615, 615, 335, 335, 335, 335, 727, 672,
+ 727, 727, 727, 727, 727, 680, 727, 727, 727, 727, 727, 560, 615, 504,
+ 504, 504, 504, 504, 504, 504, 672, 447, 447, 447, 447, 447, 280, 280,
+ 280, 280, 504, 504, 504, 504, 504, 504, 504, 680, 504, 504, 504, 504,
+ 504, 447, 504, 447,
+};
+
+static const uint16_t zapfdingbats_widths[256] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 280, 981, 968, 981, 987, 724, 795, 796, 797, 695,
+ 967, 946, 553, 861, 918, 940, 918, 952, 981, 761, 852, 768, 767, 575,
+ 682, 769, 766, 765, 760, 497, 556, 541, 581, 697, 792, 794, 794, 796,
+ 799, 800, 822, 829, 795, 847, 829, 839, 822, 837, 930, 749, 728, 754,
+ 796, 798, 700, 782, 774, 798, 765, 712, 713, 687, 706, 832, 821, 795,
+ 795, 712, 692, 701, 694, 792, 793, 718, 797, 791, 797, 879, 767, 768,
+ 768, 765, 765, 899, 899, 794, 790, 441, 139, 279, 418, 395, 395, 673,
+ 673, 0, 393, 393, 319, 319, 278, 278, 513, 513, 413, 413, 235, 235,
+ 336, 336, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 737, 548, 548, 917, 672, 766, 766,
+ 782, 599, 699, 631, 794, 794, 794, 794, 794, 794, 794, 794, 794, 794,
+ 794, 794, 794, 794, 794, 794, 794, 794, 794, 794, 794, 794, 794, 794,
+ 794, 794, 794, 794, 794, 794, 794, 794, 794, 794, 794, 794, 794, 794,
+ 794, 794, 901, 844, 1024, 461, 753, 931, 753, 925, 934, 935, 935, 840,
+ 879, 834, 931, 931, 924, 937, 938, 466, 890, 842, 842, 873, 873, 701,
+ 701, 880, 0, 880, 766, 953, 777, 871, 777, 895, 974, 895, 837, 879,
+ 934, 977, 925, 0,
+};
+
+static const uint16_t courier_widths[256] = {
+ 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604,
+ 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604,
+ 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604,
+ 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604,
+ 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604,
+ 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604,
+ 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604,
+ 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604,
+ 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604,
+ 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604,
+ 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604,
+ 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604,
+ 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604,
+ 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604,
+ 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604,
+ 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604,
+ 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604,
+ 604,
+};
+
+static int pdf_text_point_width(struct pdf_doc *pdf, const char *text,
+ ptrdiff_t text_len, float size,
+ const uint16_t *widths, float *point_width)
+{
+ uint32_t len = 0;
+ if (text_len < 0)
+ text_len = strlen(text);
+ *point_width = 0.0f;
+
+ for (int i = 0; i < (int)text_len;) {
+ uint8_t pdf_char = 0;
+ int code_len;
+ code_len =
+ utf8_to_pdfencoding(pdf, &text[i], text_len - i, &pdf_char);
+ if (code_len < 0)
+ return pdf_set_err(pdf, code_len,
+ "Invalid unicode string at position %d in %s",
+ i, text);
+ i += code_len;
+
+ if (pdf_char != '\n' && pdf_char != '\r')
+ len += widths[pdf_char];
+ }
+
+ /* Our widths arrays are for 14pt fonts */
+ *point_width = len * size / (14.0f * 72.0f);
+
+ return 0;
+}
+
+static const uint16_t *find_font_widths(const char *font_name)
+{
+ if (strcasecmp(font_name, "Helvetica") == 0)
+ return helvetica_widths;
+ if (strcasecmp(font_name, "Helvetica-Bold") == 0)
+ return helvetica_bold_widths;
+ if (strcasecmp(font_name, "Helvetica-BoldOblique") == 0)
+ return helvetica_bold_oblique_widths;
+ if (strcasecmp(font_name, "Helvetica-Oblique") == 0)
+ return helvetica_oblique_widths;
+ if (strcasecmp(font_name, "Courier") == 0 ||
+ strcasecmp(font_name, "Courier-Bold") == 0 ||
+ strcasecmp(font_name, "Courier-BoldOblique") == 0 ||
+ strcasecmp(font_name, "Courier-Oblique") == 0)
+ return courier_widths;
+ if (strcasecmp(font_name, "Times-Roman") == 0)
+ return times_widths;
+ if (strcasecmp(font_name, "Times-Bold") == 0)
+ return times_bold_widths;
+ if (strcasecmp(font_name, "Times-Italic") == 0)
+ return times_italic_widths;
+ if (strcasecmp(font_name, "Times-BoldItalic") == 0)
+ return times_bold_italic_widths;
+ if (strcasecmp(font_name, "Symbol") == 0)
+ return symbol_widths;
+ if (strcasecmp(font_name, "ZapfDingbats") == 0)
+ return zapfdingbats_widths;
+
+ return NULL;
+}
+
+int pdf_get_font_text_width(struct pdf_doc *pdf, const char *font_name,
+ const char *text, float size, float *text_width)
+{
+ if (!font_name)
+ font_name = pdf->current_font->font.name;
+ const uint16_t *widths = find_font_widths(font_name);
+
+ if (!widths)
+ return pdf_set_err(pdf, -EINVAL,
+ "Unable to determine width for font '%s'",
+ pdf->current_font->font.name);
+ return pdf_text_point_width(pdf, text, -1, size, widths, text_width);
+}
+
+static const char *find_word_break(const char *string)
+{
+ /* Skip over the actual word */
+ while (string && *string && (*string < 0 || !isspace(*string)))
+ string++;
+
+ return string;
+}
+
+int pdf_add_text_wrap(struct pdf_doc *pdf, struct pdf_object *page,
+ const char *text, float size, float xoff, float yoff,
+ uint32_t colour, float wrap_width, int align,
+ float *height)
+{
+ /* Move through the text string, stopping at word boundaries,
+ * trying to find the longest text string we can fit in the given width
+ */
+ const char *start = text;
+ const char *last_best = text;
+ const char *end = text;
+ char line[512];
+ const uint16_t *widths;
+ float orig_yoff = yoff;
+
+ widths = find_font_widths(pdf->current_font->font.name);
+ if (!widths)
+ return pdf_set_err(pdf, -EINVAL,
+ "Unable to determine width for font '%s'",
+ pdf->current_font->font.name);
+
+ while (start && *start) {
+ const char *new_end = find_word_break(end + 1);
+ float line_width;
+ int output = 0;
+ float xoff_align = xoff;
+ int e;
+
+ end = new_end;
+
+ e = pdf_text_point_width(pdf, start, end - start, size, widths,
+ &line_width);
+ if (e < 0)
+ return e;
+
+ if (line_width >= wrap_width) {
+ if (last_best == start) {
+ /* There is a single word that is too long for the line */
+ ptrdiff_t i;
+ /* Find the best character to chop it at */
+ for (i = end - start - 1; i > 0; i--) {
+ float this_width;
+ // Don't look at places that are in the middle of a utf-8
+ // sequence
+ if ((start[i - 1] & 0xc0) == 0xc0 ||
+ ((start[i - 1] & 0xc0) == 0x80 &&
+ (start[i] & 0xc0) == 0x80))
+ continue;
+ e = pdf_text_point_width(pdf, start, i, size, widths,
+ &this_width);
+ if (e < 0)
+ return e;
+ if (this_width < wrap_width)
+ break;
+ }
+ if (i == 0)
+ return pdf_set_err(pdf, -EINVAL,
+ "Unable to find suitable line break");
+
+ end = start + i;
+ } else
+ end = last_best;
+ output = 1;
+ }
+ if (*end == '\0')
+ output = 1;
+
+ if (*end == '\n' || *end == '\r')
+ output = 1;
+
+ if (output) {
+ int len = end - start;
+ float char_spacing = 0;
+ if (len >= (int)sizeof(line))
+ len = (int)sizeof(line) - 1;
+ strncpy(line, start, len);
+ line[len] = '\0';
+
+ e = pdf_text_point_width(pdf, start, len, size, widths,
+ &line_width);
+ if (e < 0)
+ return e;
+
+ switch (align) {
+ case PDF_ALIGN_RIGHT:
+ xoff_align += wrap_width - line_width;
+ break;
+ case PDF_ALIGN_CENTER:
+ xoff_align += (wrap_width - line_width) / 2;
+ break;
+ case PDF_ALIGN_JUSTIFY:
+ if ((len - 1) > 0 && *end != '\r' && *end != '\n' &&
+ *end != '\0')
+ char_spacing = (wrap_width - line_width) / (len - 2);
+ break;
+ case PDF_ALIGN_JUSTIFY_ALL:
+ if ((len - 1) > 0)
+ char_spacing = (wrap_width - line_width) / (len - 2);
+ break;
+ }
+
+ if (align != PDF_ALIGN_NO_WRITE) {
+ pdf_add_text_spacing(pdf, page, line, size, xoff_align, yoff,
+ colour, char_spacing);
+ }
+
+ if (*end == ' ')
+ end++;
+
+ start = last_best = end;
+ yoff -= size;
+ } else
+ last_best = end;
+ }
+
+ if (height)
+ *height = orig_yoff - yoff;
+ return 0;
+}
+
+int pdf_add_line(struct pdf_doc *pdf, struct pdf_object *page, float x1,
+ float y1, float x2, float y2, float width, uint32_t colour)
+{
+ int ret;
+ struct dstr str = INIT_DSTR;
+
+ dstr_printf(&str, "%f w\r\n", width);
+ dstr_printf(&str, "%f %f m\r\n", x1, y1);
+ dstr_printf(&str, "/DeviceRGB CS\r\n");
+ dstr_printf(&str, "%f %f %f RG\r\n", PDF_RGB_R(colour), PDF_RGB_G(colour),
+ PDF_RGB_B(colour));
+ dstr_printf(&str, "%f %f l S\r\n", x2, y2);
+
+ ret = pdf_add_stream(pdf, page, dstr_data(&str));
+ dstr_free(&str);
+
+ return ret;
+}
+
+int pdf_add_cubic_bezier(struct pdf_doc *pdf, struct pdf_object *page,
+ float x1, float y1, float x2, float y2, float xq1,
+ float yq1, float xq2, float yq2, float width,
+ uint32_t colour)
+{
+ int ret;
+ struct dstr str = INIT_DSTR;
+
+ dstr_printf(&str, "%f w\r\n", width);
+ dstr_printf(&str, "%f %f m\r\n", x1, y1);
+ dstr_printf(&str, "/DeviceRGB CS\r\n");
+ dstr_printf(&str, "%f %f %f RG\r\n", PDF_RGB_R(colour), PDF_RGB_G(colour),
+ PDF_RGB_B(colour));
+ dstr_printf(&str, "%f %f %f %f %f %f c S\r\n", xq1, yq1, xq2, yq2, x2,
+ y2);
+
+ ret = pdf_add_stream(pdf, page, dstr_data(&str));
+ dstr_free(&str);
+
+ return ret;
+}
+
+int pdf_add_quadratic_bezier(struct pdf_doc *pdf, struct pdf_object *page,
+ float x1, float y1, float x2, float y2,
+ float xq1, float yq1, float width,
+ uint32_t colour)
+{
+ float xc1 = x1 + (xq1 - x1) * (2.0f / 3.0f);
+ float yc1 = y1 + (yq1 - y1) * (2.0f / 3.0f);
+ float xc2 = x2 + (xq1 - x2) * (2.0f / 3.0f);
+ float yc2 = y2 + (yq1 - y2) * (2.0f / 3.0f);
+ return pdf_add_cubic_bezier(pdf, page, x1, y1, x2, y2, xc1, yc1, xc2, yc2,
+ width, colour);
+}
+
+int pdf_add_custom_path(struct pdf_doc *pdf, struct pdf_object *page,
+ struct pdf_path_operation *operations,
+ int operation_count, float stroke_width,
+ uint32_t stroke_colour, uint32_t fill_colour)
+{
+ int ret;
+ struct dstr str = INIT_DSTR;
+
+ if (!PDF_IS_TRANSPARENT(fill_colour)) {
+ dstr_printf(&str, "/DeviceRGB CS\r\n");
+ dstr_printf(&str, "%f %f %f rg\r\n", PDF_RGB_R(fill_colour),
+ PDF_RGB_G(fill_colour), PDF_RGB_B(fill_colour));
+ }
+ dstr_printf(&str, "%f w\r\n", stroke_width);
+ dstr_printf(&str, "/DeviceRGB CS\r\n");
+ dstr_printf(&str, "%f %f %f RG\r\n", PDF_RGB_R(stroke_colour),
+ PDF_RGB_G(stroke_colour), PDF_RGB_B(stroke_colour));
+
+ for (int i = 0; i < operation_count; i++) {
+ struct pdf_path_operation operation = operations[i];
+ switch (operation.op) {
+ case 'm':
+ dstr_printf(&str, "%f %f m\r\n", operation.x1, operation.y1);
+ break;
+ case 'l':
+ dstr_printf(&str, "%f %f l\r\n", operation.x1, operation.y1);
+ break;
+ case 'c':
+ dstr_printf(&str, "%f %f %f %f %f %f c\r\n", operation.x1,
+ operation.y1, operation.x2, operation.y2,
+ operation.x3, operation.y3);
+ break;
+ case 'v':
+ dstr_printf(&str, "%f %f %f %f v\r\n", operation.x1, operation.y1,
+ operation.x2, operation.y2);
+ break;
+ case 'y':
+ dstr_printf(&str, "%f %f %f %f y\r\n", operation.x1, operation.y1,
+ operation.x2, operation.y2);
+ break;
+ case 'h':
+ dstr_printf(&str, "h\r\n");
+ break;
+ default:
+ return pdf_set_err(pdf, -errno, "Invalid operation");
+ break;
+ }
+ }
+
+ if (PDF_IS_TRANSPARENT(fill_colour))
+ dstr_printf(&str, "%s", "S ");
+ else
+ dstr_printf(&str, "%s", "B ");
+ ret = pdf_add_stream(pdf, page, dstr_data(&str));
+ dstr_free(&str);
+
+ return ret;
+}
+
+int pdf_add_ellipse(struct pdf_doc *pdf, struct pdf_object *page, float x,
+ float y, float xradius, float yradius, float width,
+ uint32_t colour, uint32_t fill_colour)
+{
+ int ret;
+ struct dstr str = INIT_DSTR;
+ float lx, ly;
+
+ lx = (4.0f / 3.0f) * (M_SQRT2 - 1) * xradius;
+ ly = (4.0f / 3.0f) * (M_SQRT2 - 1) * yradius;
+
+ if (!PDF_IS_TRANSPARENT(fill_colour)) {
+ dstr_printf(&str, "/DeviceRGB CS\r\n");
+ dstr_printf(&str, "%f %f %f rg\r\n", PDF_RGB_R(fill_colour),
+ PDF_RGB_G(fill_colour), PDF_RGB_B(fill_colour));
+ }
+
+ /* stroke color */
+ dstr_printf(&str, "/DeviceRGB CS\r\n");
+ dstr_printf(&str, "%f %f %f RG\r\n", PDF_RGB_R(colour), PDF_RGB_G(colour),
+ PDF_RGB_B(colour));
+
+ dstr_printf(&str, "%f w ", width);
+
+ dstr_printf(&str, "%.2f %.2f m ", (x + xradius), (y));
+
+ dstr_printf(&str, "%.2f %.2f %.2f %.2f %.2f %.2f c ", (x + xradius),
+ (y - ly), (x + lx), (y - yradius), x, (y - yradius));
+
+ dstr_printf(&str, "%.2f %.2f %.2f %.2f %.2f %.2f c ", (x - lx),
+ (y - yradius), (x - xradius), (y - ly), (x - xradius), y);
+
+ dstr_printf(&str, "%.2f %.2f %.2f %.2f %.2f %.2f c ", (x - xradius),
+ (y + ly), (x - lx), (y + yradius), x, (y + yradius));
+
+ dstr_printf(&str, "%.2f %.2f %.2f %.2f %.2f %.2f c ", (x + lx),
+ (y + yradius), (x + xradius), (y + ly), (x + xradius), y);
+
+ if (PDF_IS_TRANSPARENT(fill_colour))
+ dstr_printf(&str, "%s", "S ");
+ else
+ dstr_printf(&str, "%s", "B ");
+
+ ret = pdf_add_stream(pdf, page, dstr_data(&str));
+ dstr_free(&str);
+
+ return ret;
+}
+
+int pdf_add_circle(struct pdf_doc *pdf, struct pdf_object *page, float xr,
+ float yr, float radius, float width, uint32_t colour,
+ uint32_t fill_colour)
+{
+ return pdf_add_ellipse(pdf, page, xr, yr, radius, radius, width, colour,
+ fill_colour);
+}
+
+int pdf_add_rectangle(struct pdf_doc *pdf, struct pdf_object *page, float x,
+ float y, float width, float height, float border_width,
+ uint32_t colour)
+{
+ int ret;
+ struct dstr str = INIT_DSTR;
+
+ dstr_printf(&str, "%f %f %f RG ", PDF_RGB_R(colour), PDF_RGB_G(colour),
+ PDF_RGB_B(colour));
+ dstr_printf(&str, "%f w ", border_width);
+ dstr_printf(&str, "%f %f %f %f re S ", x, y, width, height);
+
+ ret = pdf_add_stream(pdf, page, dstr_data(&str));
+ dstr_free(&str);
+
+ return ret;
+}
+
+int pdf_add_filled_rectangle(struct pdf_doc *pdf, struct pdf_object *page,
+ float x, float y, float width, float height,
+ float border_width, uint32_t colour_fill,
+ uint32_t colour_border)
+{
+ int ret;
+ struct dstr str = INIT_DSTR;
+
+ dstr_printf(&str, "%f %f %f rg ", PDF_RGB_R(colour_fill),
+ PDF_RGB_G(colour_fill), PDF_RGB_B(colour_fill));
+ if (border_width > 0) {
+ dstr_printf(&str, "%f %f %f RG ", PDF_RGB_R(colour_border),
+ PDF_RGB_G(colour_border), PDF_RGB_B(colour_border));
+ dstr_printf(&str, "%f w ", border_width);
+ dstr_printf(&str, "%f %f %f %f re B ", x, y, width, height);
+ } else {
+ dstr_printf(&str, "%f %f %f %f re f ", x, y, width, height);
+ }
+
+ ret = pdf_add_stream(pdf, page, dstr_data(&str));
+ dstr_free(&str);
+
+ return ret;
+}
+
+int pdf_add_polygon(struct pdf_doc *pdf, struct pdf_object *page, float x[],
+ float y[], int count, float border_width, uint32_t colour)
+{
+ int ret;
+ struct dstr str = INIT_DSTR;
+
+ dstr_printf(&str, "%f %f %f RG ", PDF_RGB_R(colour), PDF_RGB_G(colour),
+ PDF_RGB_B(colour));
+ dstr_printf(&str, "%f w ", border_width);
+ dstr_printf(&str, "%f %f m ", x[0], y[0]);
+ for (int i = 1; i < count; i++) {
+ dstr_printf(&str, "%f %f l ", x[i], y[i]);
+ }
+ dstr_printf(&str, "h S ");
+
+ ret = pdf_add_stream(pdf, page, dstr_data(&str));
+ dstr_free(&str);
+
+ return ret;
+}
+
+int pdf_add_filled_polygon(struct pdf_doc *pdf, struct pdf_object *page,
+ float x[], float y[], int count,
+ float border_width, uint32_t colour)
+{
+ int ret;
+ struct dstr str = INIT_DSTR;
+
+ dstr_printf(&str, "%f %f %f RG ", PDF_RGB_R(colour), PDF_RGB_G(colour),
+ PDF_RGB_B(colour));
+ dstr_printf(&str, "%f %f %f rg ", PDF_RGB_R(colour), PDF_RGB_G(colour),
+ PDF_RGB_B(colour));
+ dstr_printf(&str, "%f w ", border_width);
+ dstr_printf(&str, "%f %f m ", x[0], y[0]);
+ for (int i = 1; i < count; i++) {
+ dstr_printf(&str, "%f %f l ", x[i], y[i]);
+ }
+ dstr_printf(&str, "h f ");
+
+ ret = pdf_add_stream(pdf, page, dstr_data(&str));
+ dstr_free(&str);
+
+ return ret;
+}
+
+static const struct {
+ uint32_t code;
+ char ch;
+} code_128a_encoding[] = {
+ {0x212222, ' '}, {0x222122, '!'}, {0x222221, '"'}, {0x121223, '#'},
+ {0x121322, '$'}, {0x131222, '%'}, {0x122213, '&'}, {0x122312, '\''},
+ {0x132212, '('}, {0x221213, ')'}, {0x221312, '*'}, {0x231212, '+'},
+ {0x112232, ','}, {0x122132, '-'}, {0x122231, '.'}, {0x113222, '/'},
+ {0x123122, '0'}, {0x123221, '1'}, {0x223211, '2'}, {0x221132, '3'},
+ {0x221231, '4'}, {0x213212, '5'}, {0x223112, '6'}, {0x312131, '7'},
+ {0x311222, '8'}, {0x321122, '9'}, {0x321221, ':'}, {0x312212, ';'},
+ {0x322112, '<'}, {0x322211, '='}, {0x212123, '>'}, {0x212321, '?'},
+ {0x232121, '@'}, {0x111323, 'A'}, {0x131123, 'B'}, {0x131321, 'C'},
+ {0x112313, 'D'}, {0x132113, 'E'}, {0x132311, 'F'}, {0x211313, 'G'},
+ {0x231113, 'H'}, {0x231311, 'I'}, {0x112133, 'J'}, {0x112331, 'K'},
+ {0x132131, 'L'}, {0x113123, 'M'}, {0x113321, 'N'}, {0x133121, 'O'},
+ {0x313121, 'P'}, {0x211331, 'Q'}, {0x231131, 'R'}, {0x213113, 'S'},
+ {0x213311, 'T'}, {0x213131, 'U'}, {0x311123, 'V'}, {0x311321, 'W'},
+ {0x331121, 'X'}, {0x312113, 'Y'}, {0x312311, 'Z'}, {0x332111, '['},
+ {0x314111, '\\'}, {0x221411, ']'}, {0x431111, '^'}, {0x111224, '_'},
+ {0x111422, '`'}, {0x121124, 'a'}, {0x121421, 'b'}, {0x141122, 'c'},
+ {0x141221, 'd'}, {0x112214, 'e'}, {0x112412, 'f'}, {0x122114, 'g'},
+ {0x122411, 'h'}, {0x142112, 'i'}, {0x142211, 'j'}, {0x241211, 'k'},
+ {0x221114, 'l'}, {0x413111, 'm'}, {0x241112, 'n'}, {0x134111, 'o'},
+ {0x111242, 'p'}, {0x121142, 'q'}, {0x121241, 'r'}, {0x114212, 's'},
+ {0x124112, 't'}, {0x124211, 'u'}, {0x411212, 'v'}, {0x421112, 'w'},
+ {0x421211, 'x'}, {0x212141, 'y'}, {0x214121, 'z'}, {0x412121, '{'},
+ {0x111143, '|'}, {0x111341, '}'}, {0x131141, '~'}, {0x114113, '\0'},
+ {0x114311, '\0'}, {0x411113, '\0'}, {0x411311, '\0'}, {0x113141, '\0'},
+ {0x114131, '\0'}, {0x311141, '\0'}, {0x411131, '\0'}, {0x211412, '\0'},
+ {0x211214, '\0'}, {0x211232, '\0'}, {0x2331112, '\0'},
+};
+
+static int find_128_encoding(char ch)
+{
+ for (int i = 0; i < (int)ARRAY_SIZE(code_128a_encoding); i++) {
+ if (code_128a_encoding[i].ch == ch)
+ return i;
+ }
+ return -1;
+}
+
+static float pdf_barcode_128a_ch(struct pdf_doc *pdf, struct pdf_object *page,
+ float x, float y, float width, float height,
+ uint32_t colour, int index, int code_len)
+{
+ uint32_t code = code_128a_encoding[index].code;
+ float line_width = width / 11.0f;
+
+ for (int i = 0; i < code_len; i++) {
+ uint8_t shift = (code_len - 1 - i) * 4;
+ uint8_t mask = (code >> shift) & 0xf;
+
+ if (!(i % 2))
+ pdf_add_filled_rectangle(pdf, page, x, y, line_width * mask,
+ height, 0, colour, PDF_TRANSPARENT);
+ x += line_width * mask;
+ }
+ return x;
+}
+
+static int pdf_add_barcode_128a(struct pdf_doc *pdf, struct pdf_object *page,
+ float x, float y, float width, float height,
+ const char *string, uint32_t colour)
+{
+ const char *s;
+ size_t len = strlen(string) + 3;
+ float char_width = width / len;
+ int checksum, i;
+
+ if (char_width / 11.0f <= 0)
+ return pdf_set_err(pdf, -EINVAL,
+ "Insufficient width to draw barcode");
+
+ for (s = string; *s; s++)
+ if (find_128_encoding(*s) < 0)
+ return pdf_set_err(pdf, -EINVAL, "Invalid barcode character 0x%x",
+ *s);
+
+ x = pdf_barcode_128a_ch(pdf, page, x, y, char_width, height, colour, 104,
+ 6);
+ checksum = 104;
+
+ for (i = 1, s = string; *s; s++, i++) {
+ int index = find_128_encoding(*s);
+ // This should be impossible, due to the checks above, but confirm
+ // here anyway to stop coverity complaining
+ if (index < 0)
+ return pdf_set_err(pdf, -EINVAL,
+ "Invalid 128a barcode character 0x%x", *s);
+ x = pdf_barcode_128a_ch(pdf, page, x, y, char_width, height, colour,
+ index, 6);
+ checksum += index * i;
+ }
+ x = pdf_barcode_128a_ch(pdf, page, x, y, char_width, height, colour,
+ checksum % 103, 6);
+ pdf_barcode_128a_ch(pdf, page, x, y, char_width, height, colour, 106, 7);
+ return 0;
+}
+
+/* Code 39 character encoding. Each 4-bit value indicates:
+ * 0 => wide bar
+ * 1 => narrow bar
+ * 2 => wide space
+ */
+static const struct {
+ int code;
+ char ch;
+} code_39_encoding[] = {
+ {0x012110, '1'}, {0x102110, '2'}, {0x002111, '3'},
+ {0x112010, '4'}, {0x012011, '5'}, {0x102011, '6'},
+ {0x112100, '7'}, {0x012101, '8'}, {0x102101, '9'},
+ {0x112001, '0'}, {0x011210, 'A'}, {0x101210, 'B'},
+ {0x001211, 'C'}, {0x110210, 'D'}, {0x010211, 'E'},
+ {0x100211, 'F'}, {0x111200, 'G'}, {0x011201, 'H'},
+ {0x101201, 'I'}, {0x110201, 'J'}, {0x011120, 'K'},
+ {0x101120, 'L'}, {0x001121, 'M'}, {0x110120, 'N'},
+ {0x010121, 'O'}, {0x100121, 'P'}, {0x111020, 'Q'},
+ {0x011021, 'R'}, {0x101021, 'S'}, {0x110021, 'T'},
+ {0x021110, 'U'}, {0x120110, 'V'}, {0x020111, 'W'},
+ {0x121010, 'X'}, {0x021011, 'Y'}, {0x120011, 'Z'},
+ {0x121100, '-'}, {0x021101, '.'}, {0x120101, ' '},
+ {0x121001, '*'}, // 'stop' character
+};
+
+static int find_39_encoding(char ch)
+{
+ for (int i = 0; i < (int)ARRAY_SIZE(code_39_encoding); i++) {
+ if (code_39_encoding[i].ch == ch) {
+ return code_39_encoding[i].code;
+ }
+ }
+ return -1;
+}
+
+static int pdf_barcode_39_ch(struct pdf_doc *pdf, struct pdf_object *page,
+ float x, float y, float char_width, float height,
+ uint32_t colour, char ch, float *new_x)
+{
+ float nw = char_width / 12.0f;
+ float ww = char_width / 4.0f;
+ int code = 0;
+
+ code = find_39_encoding(ch);
+ if (code < 0)
+ return pdf_set_err(pdf, -EINVAL, "Invalid Code 39 character %c 0x%x",
+ ch, ch);
+
+ for (int i = 5; i >= 0; i--) {
+ int pattern = (code >> i * 4) & 0xf;
+ if (pattern == 0) { // wide
+ if (pdf_add_filled_rectangle(pdf, page, x, y, ww - 1, height, 0,
+ colour, PDF_TRANSPARENT) < 0)
+ return pdf->errval;
+ x += ww;
+ }
+ if (pattern == 1) { // narrow
+ if (pdf_add_filled_rectangle(pdf, page, x, y, nw - 1, height, 0,
+ colour, PDF_TRANSPARENT) < 0)
+ return pdf->errval;
+ x += nw;
+ }
+ if (pattern == 2) { // space
+ x += nw;
+ }
+ }
+ if (new_x)
+ *new_x = x;
+ return 0;
+}
+
+static int pdf_add_barcode_39(struct pdf_doc *pdf, struct pdf_object *page,
+ float x, float y, float width, float height,
+ const char *string, uint32_t colour)
+{
+ size_t len = strlen(string);
+ float char_width = width / (len + 2);
+ int e;
+
+ e = pdf_barcode_39_ch(pdf, page, x, y, char_width, height, colour, '*',
+ &x);
+ if (e < 0)
+ return e;
+
+ while (string && *string) {
+ e = pdf_barcode_39_ch(pdf, page, x, y, char_width, height, colour,
+ *string, &x);
+ if (e < 0)
+ return e;
+ string++;
+ }
+
+ e = pdf_barcode_39_ch(pdf, page, x, y, char_width, height, colour, '*',
+ NULL);
+ if (e < 0)
+ return e;
+
+ return 0;
+}
+
+/* EAN/UPC character encoding. Each 4-bit value indicates width in x units.
+ * Elements are SBSB (Space, Bar, Space, Bar) for LHS digits
+ * Elements are inverted for RHS digits
+ */
+static const int code_eanupc_encoding[] = {
+ 0x3211, // 0
+ 0x2221, // 1
+ 0x2122, // 2
+ 0x1411, // 3
+ 0x1132, // 4
+ 0x1231, // 5
+ 0x1114, // 6
+ 0x1312, // 7
+ 0x1213, // 8
+ 0x3112, // 9
+};
+
+/**
+ * List of different barcode guard patterns that are supported
+ */
+enum {
+ GUARD_NORMAL, //!< Produce normal guard pattern
+ GUARD_CENTRE, //!< Produce centre guard pattern
+ GUARD_SPECIAL, //!< Produce special guard pattern
+ GUARD_ADDON,
+ GUARD_ADDON_DELIN,
+};
+
+static const int code_eanupc_aux_encoding[] = {
+ 0x150, // Normal guard
+ 0x554, // Centre guard
+ 0x555, // Special guard
+ 0x160, // Add-on guard
+ 0x500, // Add-on delineator
+};
+
+static const int set_ean13_encoding[] = {
+ 0x00, // 0
+ 0x34, // 1
+ 0x2c, // 2
+ 0x1c, // 3
+ 0x32, // 4
+ 0x26, // 5
+ 0x0e, // 6
+ 0x2a, // 7
+ 0x1a, // 8
+ 0x16, // 9
+};
+
+static const int set_upce_encoding[] = {
+ 0x07, // 0
+ 0x0b, // 1
+ 0x13, // 2
+ 0x23, // 3
+ 0x0d, // 4
+ 0x19, // 5
+ 0x31, // 6
+ 0x15, // 7
+ 0x25, // 8
+ 0x29, // 9
+};
+
+#define EANUPC_X PDF_MM_TO_POINT(0.330f)
+
+static const struct {
+ unsigned modules;
+ float height_bar;
+ float height_outer;
+ unsigned quiet_left;
+ unsigned quiet_right;
+} eanupc_dimensions[] = {
+ {113, PDF_MM_TO_POINT(22.85), PDF_MM_TO_POINT(25.93), 11, 7}, // EAN-13
+ {113, PDF_MM_TO_POINT(22.85), PDF_MM_TO_POINT(25.93), 9, 9}, // UPC-A
+ {81, PDF_MM_TO_POINT(18.23), PDF_MM_TO_POINT(21.31), 7, 7}, // EAN-8
+ {67, PDF_MM_TO_POINT(22.85), PDF_MM_TO_POINT(25.93), 9, 7}, // UPC-E
+};
+
+static void pdf_barcode_eanupc_calc_dims(int type, float width, float height,
+ float *x_off, float *y_off,
+ float *new_width, float *new_height,
+ float *x, float *bar_height,
+ float *bar_ext, float *font_size)
+{
+ float aspectBarcode, aspectRect, scale;
+
+ aspectRect = width / height;
+ aspectBarcode = eanupc_dimensions[type - PDF_BARCODE_EAN13].modules *
+ EANUPC_X /
+ eanupc_dimensions[type - PDF_BARCODE_EAN13].height_outer;
+ if (aspectRect > aspectBarcode) {
+ *new_height = height;
+ *new_width = height * aspectBarcode;
+ } else if (aspectRect < aspectBarcode) {
+ *new_width = width;
+ *new_height = width / aspectBarcode;
+ } else {
+ *new_width = width;
+ *new_height = height;
+ }
+ scale = *new_height /
+ eanupc_dimensions[type - PDF_BARCODE_EAN13].height_outer;
+
+ *x = *new_width / eanupc_dimensions[type - PDF_BARCODE_EAN13].modules;
+ *bar_ext = *x * 5;
+ *bar_height =
+ eanupc_dimensions[type - PDF_BARCODE_EAN13].height_bar * scale;
+ *font_size = 8.0 * scale;
+ *x_off = (width - *new_width) / 2.0;
+ *y_off = (height - *new_height) / 2.0;
+}
+
+static int pdf_barcode_eanupc_ch(struct pdf_doc *pdf, struct pdf_object *page,
+ float x, float y, float x_width,
+ float height, uint32_t colour, char ch,
+ int set, float *new_x)
+{
+ if ('0' > ch || ch > '9')
+ return pdf_set_err(pdf, -EINVAL, "Invalid EAN/UPC character %c 0x%x",
+ ch, ch);
+
+ int code;
+ code = code_eanupc_encoding[ch - '0'];
+
+ for (int i = 3; i >= 0; i--) {
+ int shift = (set == 1 ? 3 - i : i) * 4;
+ int bar = (set == 2 && i & 0x1) || (set != 2 && (i & 0x1) == 0);
+ float width = (code >> shift) & 0xf;
+
+ switch (ch) {
+ case '1':
+ case '2':
+ if ((set == 0 && bar) || (set != 0 && !bar)) {
+ width -= 1.0 / 13.0;
+ } else {
+ width += 1.0 / 13.0;
+ }
+ break;
+
+ case '7':
+ case '8':
+ if ((set == 0 && bar) || (set != 0 && !bar)) {
+ width += 1.0 / 13.0;
+ } else {
+ width -= 1.0 / 13.0;
+ }
+ break;
+ }
+
+ width *= x_width;
+ if (bar) {
+ if (pdf_add_filled_rectangle(pdf, page, x, y, width, height, 0,
+ colour, PDF_TRANSPARENT) < 0)
+ return pdf->errval;
+ }
+ x += width;
+ }
+ if (new_x)
+ *new_x = x;
+ return 0;
+}
+
+static int pdf_barcode_eanupc_aux(struct pdf_doc *pdf,
+ struct pdf_object *page, float x, float y,
+ float x_width, float height,
+ uint32_t colour, int guard_type,
+ float *new_x)
+{
+ int code = code_eanupc_aux_encoding[guard_type];
+
+ for (int i = 5; i >= 0; i--) {
+ int value = code >> i * 2 & 0x3;
+ if (value) {
+ if ((i & 0x1) == 0) {
+ if (pdf_add_filled_rectangle(pdf, page, x, y, x_width * value,
+ height, 0, colour,
+ PDF_TRANSPARENT) < 0)
+ return pdf->errval;
+ }
+ x += x_width * value;
+ }
+ }
+ if (new_x)
+ *new_x = x;
+ return 0;
+}
+
+static int pdf_add_barcode_ean13(struct pdf_doc *pdf, struct pdf_object *page,
+ float x, float y, float width, float height,
+ const char *string, uint32_t colour)
+{
+ if (!string)
+ return 0;
+
+ size_t len = strlen(string);
+ int lead = 0;
+ if (len == 13) {
+ char ch = string[0];
+ if (!isdigit(*string))
+ return pdf_set_err(pdf, -EINVAL,
+ "Invalid EAN13 character %c 0x%x", ch, ch);
+ lead = ch - '0';
+ ++string;
+ } else if (len != 12)
+ return pdf_set_err(pdf, -EINVAL, "Invalid EAN13 string length %lu",
+ len);
+
+ /* Scale and calculate dimensions */
+ float x_off, y_off, new_width, new_height, x_width, bar_height, bar_ext,
+ font;
+
+ pdf_barcode_eanupc_calc_dims(PDF_BARCODE_EAN13, width, height, &x_off,
+ &y_off, &new_width, &new_height, &x_width,
+ &bar_height, &bar_ext, &font);
+
+ x += x_off;
+ y += y_off;
+ float bar_y = y + new_height - bar_height;
+
+ int e;
+ const char *save_font = pdf->current_font->font.name;
+ e = pdf_set_font(pdf, "Courier"); /* Built-in monospace font */
+ if (e < 0)
+ return e;
+
+ char text[2];
+ text[1] = 0;
+ text[0] = lead + '0';
+ e = pdf_add_text(pdf, page, text, font, x, y, colour);
+ if (e < 0) {
+ pdf_set_font(pdf, save_font);
+ return e;
+ }
+
+ x += eanupc_dimensions[0].quiet_left * x_width;
+ e = pdf_barcode_eanupc_aux(pdf, page, x, bar_y - bar_ext, x_width,
+ bar_height + bar_ext, colour, GUARD_NORMAL,
+ &x);
+ if (e < 0) {
+ pdf_set_font(pdf, save_font);
+ return e;
+ }
+
+ for (int i = 0; i != 6; i++) {
+ text[0] = *string;
+ e = pdf_add_text_wrap(pdf, page, text, font, x, y, colour,
+ 7 * x_width, PDF_ALIGN_CENTER, NULL);
+ if (e < 0) {
+ pdf_set_font(pdf, save_font);
+ return e;
+ }
+
+ int set = (set_ean13_encoding[lead] & 1 << i) ? 1 : 0;
+ e = pdf_barcode_eanupc_ch(pdf, page, x, bar_y, x_width, bar_height,
+ colour, *string, set, &x);
+ if (e < 0) {
+ pdf_set_font(pdf, save_font);
+ return e;
+ }
+ string++;
+ }
+
+ e = pdf_barcode_eanupc_aux(pdf, page, x, bar_y - bar_ext, x_width,
+ bar_height + bar_ext, colour, GUARD_CENTRE,
+ &x);
+ if (e < 0) {
+ pdf_set_font(pdf, save_font);
+ return e;
+ }
+
+ for (int i = 0; i != 6; i++) {
+ text[0] = *string;
+ e = pdf_add_text_wrap(pdf, page, text, font, x, y, colour,
+ 7 * x_width, PDF_ALIGN_CENTER, NULL);
+ if (e < 0) {
+ pdf_set_font(pdf, save_font);
+ return e;
+ }
+
+ e = pdf_barcode_eanupc_ch(pdf, page, x, bar_y, x_width, bar_height,
+ colour, *string, 2, &x);
+ if (e < 0) {
+ pdf_set_font(pdf, save_font);
+ return e;
+ }
+ string++;
+ }
+
+ e = pdf_barcode_eanupc_aux(pdf, page, x, bar_y - bar_ext, x_width,
+ bar_height + bar_ext, colour, GUARD_NORMAL,
+ &x);
+ if (e < 0) {
+ pdf_set_font(pdf, save_font);
+ return e;
+ }
+
+ text[0] = '>';
+ x += eanupc_dimensions[0].quiet_right * x_width -
+ 604.0f * font / (14.0f * 72.0f);
+ e = pdf_add_text(pdf, page, text, font, x, y, colour);
+ if (e < 0) {
+ pdf_set_font(pdf, save_font);
+ return e;
+ }
+ pdf_set_font(pdf, save_font);
+ return 0;
+}
+
+static int pdf_add_barcode_upca(struct pdf_doc *pdf, struct pdf_object *page,
+ float x, float y, float width, float height,
+ const char *string, uint32_t colour)
+{
+ if (!string)
+ return 0;
+
+ size_t len = strlen(string);
+ if (len != 12)
+ return pdf_set_err(pdf, -EINVAL, "Invalid UPCA string length %lu",
+ len);
+
+ /* Scale and calculate dimensions */
+ float x_off, y_off, new_width, new_height;
+ float x_width, bar_height, bar_ext, font;
+
+ pdf_barcode_eanupc_calc_dims(PDF_BARCODE_UPCA, width, height, &x_off,
+ &y_off, &new_width, &new_height, &x_width,
+ &bar_height, &bar_ext, &font);
+
+ x += x_off;
+ y += y_off;
+ float bar_y = y + new_height - bar_height;
+
+ int e;
+ const char *save_font = pdf->current_font->font.name;
+ e = pdf_set_font(pdf, "Courier");
+ if (e < 0)
+ return e;
+
+ char text[2];
+ text[1] = 0;
+ text[0] = *string;
+ e = pdf_add_text(pdf, page, text, font * 4.0 / 7.0, x, y, colour);
+ if (e < 0) {
+ pdf_set_font(pdf, save_font);
+ return e;
+ }
+
+ x += eanupc_dimensions[1].quiet_left * x_width;
+ e = pdf_barcode_eanupc_aux(pdf, page, x, bar_y - bar_ext, x_width,
+ bar_height + bar_ext, colour, GUARD_NORMAL,
+ &x);
+ if (e < 0) {
+ pdf_set_font(pdf, save_font);
+ return e;
+ }
+
+ for (int i = 0; i != 6; i++) {
+ text[0] = *string;
+ if (i) {
+ e = pdf_add_text_wrap(pdf, page, text, font, x, y, colour,
+ 7 * x_width, PDF_ALIGN_CENTER, NULL);
+ if (e < 0) {
+ pdf_set_font(pdf, save_font);
+ return e;
+ }
+ }
+
+ e = pdf_barcode_eanupc_ch(pdf, page, x, bar_y - (i ? 0 : bar_ext),
+ x_width, bar_height + (i ? 0 : bar_ext),
+ colour, *string, 0, &x);
+ if (e < 0) {
+ pdf_set_font(pdf, save_font);
+ return e;
+ }
+ string++;
+ }
+
+ e = pdf_barcode_eanupc_aux(pdf, page, x, bar_y - bar_ext, x_width,
+ bar_height + bar_ext, colour, GUARD_CENTRE,
+ &x);
+ if (e < 0) {
+ pdf_set_font(pdf, save_font);
+ return e;
+ }
+
+ for (int i = 0; i != 6; i++) {
+ text[0] = *string;
+ if (i != 5) {
+ e = pdf_add_text_wrap(pdf, page, text, font, x, y, colour,
+ 7 * x_width, PDF_ALIGN_CENTER, NULL);
+ if (e < 0) {
+ pdf_set_font(pdf, save_font);
+ return e;
+ }
+ }
+
+ e = pdf_barcode_eanupc_ch(
+ pdf, page, x, bar_y - (i != 5 ? 0 : bar_ext), x_width,
+ bar_height + (i != 5 ? 0 : bar_ext), colour, *string, 2, &x);
+ if (e < 0) {
+ pdf_set_font(pdf, save_font);
+ return e;
+ }
+ string++;
+ }
+
+ e = pdf_barcode_eanupc_aux(pdf, page, x, bar_y - bar_ext, x_width,
+ bar_height + bar_ext, colour, GUARD_NORMAL,
+ &x);
+ if (e < 0) {
+ pdf_set_font(pdf, save_font);
+ return e;
+ }
+
+ text[0] = *--string;
+
+ x += eanupc_dimensions[1].quiet_right * x_width -
+ 604.0f * font * 4.0f / 7.0f / (14.0f * 72.0f);
+ e = pdf_add_text(pdf, page, text, font * 4.0 / 7.0, x, y, colour);
+ if (e < 0) {
+ pdf_set_font(pdf, save_font);
+ return e;
+ }
+ pdf_set_font(pdf, save_font);
+ return 0;
+}
+
+static int pdf_add_barcode_ean8(struct pdf_doc *pdf, struct pdf_object *page,
+ float x, float y, float width, float height,
+ const char *string, uint32_t colour)
+{
+ if (!string)
+ return 0;
+
+ size_t len = strlen(string);
+ if (len != 8)
+ return pdf_set_err(pdf, -EINVAL, "Invalid EAN8 string length %lu",
+ len);
+
+ /* Scale and calculate dimensions */
+ float x_off, y_off, new_width, new_height, x_width, bar_height, bar_ext,
+ font;
+
+ pdf_barcode_eanupc_calc_dims(PDF_BARCODE_EAN8, width, height, &x_off,
+ &y_off, &new_width, &new_height, &x_width,
+ &bar_height, &bar_ext, &font);
+
+ x += x_off;
+ y += y_off;
+ float bar_y = y + new_height - bar_height;
+
+ int e;
+ const char *save_font = pdf->current_font->font.name;
+ e = pdf_set_font(pdf, "Courier"); /* Built-in monospace font */
+ if (e < 0)
+ return e;
+
+ char text[2];
+ text[1] = 0;
+ text[0] = '<';
+ e = pdf_add_text(pdf, page, text, font, x, y, colour);
+ if (e < 0) {
+ pdf_set_font(pdf, save_font);
+ return e;
+ }
+
+ x += eanupc_dimensions[2].quiet_left * x_width;
+ e = pdf_barcode_eanupc_aux(pdf, page, x, bar_y - bar_ext, x_width,
+ bar_height + bar_ext, colour, GUARD_NORMAL,
+ &x);
+ if (e < 0) {
+ pdf_set_font(pdf, save_font);
+ return e;
+ }
+
+ for (int i = 0; i != 4; i++) {
+ text[0] = *string;
+ e = pdf_add_text_wrap(pdf, page, text, font, x, y, colour,
+ 7 * x_width, PDF_ALIGN_CENTER, NULL);
+ if (e < 0) {
+ pdf_set_font(pdf, save_font);
+ return e;
+ }
+
+ e = pdf_barcode_eanupc_ch(pdf, page, x, bar_y, x_width, bar_height,
+ colour, *string, 0, &x);
+ if (e < 0) {
+ pdf_set_font(pdf, save_font);
+ return e;
+ }
+ string++;
+ }
+
+ e = pdf_barcode_eanupc_aux(pdf, page, x, bar_y - bar_ext, x_width,
+ bar_height + bar_ext, colour, GUARD_CENTRE,
+ &x);
+ if (e < 0) {
+ pdf_set_font(pdf, save_font);
+ return e;
+ }
+
+ for (int i = 0; i != 4; i++) {
+ text[0] = *string;
+ e = pdf_add_text_wrap(pdf, page, text, font, x, y, colour,
+ 7 * x_width, PDF_ALIGN_CENTER, NULL);
+ if (e < 0) {
+ pdf_set_font(pdf, save_font);
+ return e;
+ }
+
+ e = pdf_barcode_eanupc_ch(pdf, page, x, bar_y, x_width, bar_height,
+ colour, *string, 2, &x);
+ if (e < 0) {
+ pdf_set_font(pdf, save_font);
+ return e;
+ }
+ string++;
+ }
+
+ e = pdf_barcode_eanupc_aux(pdf, page, x, bar_y - bar_ext, x_width,
+ bar_height + bar_ext, colour, GUARD_NORMAL,
+ &x);
+ if (e < 0) {
+ pdf_set_font(pdf, save_font);
+ return e;
+ }
+
+ text[0] = '>';
+ x += eanupc_dimensions[0].quiet_right * x_width -
+ 604.0f * font / (14.0f * 72.0f);
+ e = pdf_add_text(pdf, page, text, font, x, y, colour);
+ if (e < 0) {
+ pdf_set_font(pdf, save_font);
+ return e;
+ }
+ pdf_set_font(pdf, save_font);
+ return 0;
+}
+
+static int pdf_add_barcode_upce(struct pdf_doc *pdf, struct pdf_object *page,
+ float x, float y, float width, float height,
+ const char *string, uint32_t colour)
+{
+ if (!string)
+ return 0;
+
+ size_t len = strlen(string);
+ if (len != 12)
+ return pdf_set_err(pdf, -EINVAL, "Invalid UPCE string length %lu",
+ len);
+
+ if (*string != '0')
+ return pdf_set_err(pdf, -EINVAL, "Invalid UPCE char %c at start",
+ *string);
+
+ for (size_t i = 0; i < len; i++) {
+ if (!isdigit(string[i]))
+ return pdf_set_err(pdf, -EINVAL, "Invalid UPCE char 0x%x at %zd",
+ string[i], i);
+ }
+
+ /* Scale and calculate dimensions */
+ float x_off, y_off, new_width, new_height;
+ float x_width, bar_height, bar_ext, font;
+
+ pdf_barcode_eanupc_calc_dims(PDF_BARCODE_UPCE, width, height, &x_off,
+ &y_off, &new_width, &new_height, &x_width,
+ &bar_height, &bar_ext, &font);
+
+ x += x_off;
+ y += y_off;
+ float bar_y = y + new_height - bar_height;
+
+ int e;
+ const char *save_font = pdf->current_font->font.name;
+ e = pdf_set_font(pdf, "Courier");
+ if (e < 0)
+ return e;
+
+ char text[2];
+ text[1] = 0;
+ text[0] = string[0];
+ e = pdf_add_text(pdf, page, text, font * 4.0 / 7.0, x, y, colour);
+ if (e < 0) {
+ pdf_set_font(pdf, save_font);
+ return e;
+ }
+
+ x += eanupc_dimensions[2].quiet_left * x_width;
+ e = pdf_barcode_eanupc_aux(pdf, page, x, bar_y, x_width, bar_height,
+ colour, GUARD_NORMAL, &x);
+ if (e < 0) {
+ pdf_set_font(pdf, save_font);
+ return e;
+ }
+
+ char X[6];
+ if (string[5] && memcmp(string + 6, "0000", 4) == 0 &&
+ '5' <= string[10] && string[10] <= '9') {
+ memcpy(X, string + 1, 5);
+ X[5] = string[10];
+ } else if (string[4] && memcmp(string + 5, "00000", 5) == 0) {
+ memcpy(X, string + 1, 4);
+ X[4] = string[11];
+ X[5] = 4;
+ } else if ('0' <= string[3] && string[3] <= '2' &&
+ memcmp(string + 4, "0000", 4) == 0) {
+ X[0] = string[1];
+ X[1] = string[2];
+ X[2] = string[8];
+ X[3] = string[9];
+ X[4] = string[10];
+ X[5] = string[3];
+ } else if ('3' <= string[3] && string[3] <= '9' &&
+ memcmp(string + 4, "00000", 5) == 0) {
+ memcpy(X, string + 1, 3);
+ X[3] = string[9];
+ X[4] = string[10];
+ X[5] = 3;
+ } else {
+ pdf_set_font(pdf, save_font);
+ return pdf_set_err(pdf, -EINVAL, "Invalid UPCE string format");
+ }
+
+ for (int i = 0; i != 6; i++) {
+ text[0] = X[i];
+ e = pdf_add_text_wrap(pdf, page, text, font, x, y, colour,
+ 7 * x_width, PDF_ALIGN_CENTER, NULL);
+ if (e < 0) {
+ pdf_set_font(pdf, save_font);
+ return e;
+ }
+
+ int set = (set_upce_encoding[string[11] - '0'] & 1 << i) ? 1 : 0;
+ e = pdf_barcode_eanupc_ch(pdf, page, x, bar_y, x_width, bar_height,
+ colour, X[i], set, &x);
+ if (e < 0) {
+ pdf_set_font(pdf, save_font);
+ return e;
+ }
+ }
+
+ e = pdf_barcode_eanupc_aux(pdf, page, x, bar_y, x_width, bar_height,
+ colour, GUARD_SPECIAL, &x);
+ if (e < 0) {
+ pdf_set_font(pdf, save_font);
+ return e;
+ }
+
+ text[0] = string[11];
+ x += eanupc_dimensions[0].quiet_right * x_width -
+ 604.0f * font * 4.0f / 7.0f / (14.0f * 72.0f);
+ e = pdf_add_text(pdf, page, text, font * 4.0 / 7.0, x, y, colour);
+ if (e < 0) {
+ pdf_set_font(pdf, save_font);
+ return e;
+ }
+
+ pdf_set_font(pdf, save_font);
+ return 0;
+}
+
+int pdf_add_barcode(struct pdf_doc *pdf, struct pdf_object *page, int code,
+ float x, float y, float width, float height,
+ const char *string, uint32_t colour)
+{
+ if (!string || !*string)
+ return 0;
+ switch (code) {
+ case PDF_BARCODE_128A:
+ return pdf_add_barcode_128a(pdf, page, x, y, width, height, string,
+ colour);
+ case PDF_BARCODE_39:
+ return pdf_add_barcode_39(pdf, page, x, y, width, height, string,
+ colour);
+ case PDF_BARCODE_EAN13:
+ return pdf_add_barcode_ean13(pdf, page, x, y, width, height, string,
+ colour);
+ case PDF_BARCODE_UPCA:
+ return pdf_add_barcode_upca(pdf, page, x, y, width, height, string,
+ colour);
+ case PDF_BARCODE_EAN8:
+ return pdf_add_barcode_ean8(pdf, page, x, y, width, height, string,
+ colour);
+ case PDF_BARCODE_UPCE:
+ return pdf_add_barcode_upce(pdf, page, x, y, width, height, string,
+ colour);
+ default:
+ return pdf_set_err(pdf, -EINVAL, "Invalid barcode code %d", code);
+ }
+}
+
+static pdf_object *pdf_add_raw_grayscale8(struct pdf_doc *pdf,
+ const uint8_t *data, uint32_t width,
+ uint32_t height)
+{
+ struct pdf_object *obj;
+ size_t len;
+ const char *endstream = ">\r\nendstream\r\n";
+ struct dstr str = INIT_DSTR;
+ size_t data_len = (size_t)width * (size_t)height;
+
+ dstr_printf(&str,
+ "<<\r\n"
+ " /Type /XObject\r\n"
+ " /Name /Image%d\r\n"
+ " /Subtype /Image\r\n"
+ " /ColorSpace /DeviceGray\r\n"
+ " /Height %d\r\n"
+ " /Width %d\r\n"
+ " /BitsPerComponent 8\r\n"
+ " /Length %zu\r\n"
+ ">>stream\r\n",
+ flexarray_size(&pdf->objects), height, width, data_len + 1);
+
+ len = dstr_len(&str) + data_len + strlen(endstream) + 1;
+ if (dstr_ensure(&str, len) < 0) {
+ dstr_free(&str);
+ pdf_set_err(pdf, -ENOMEM,
+ "Unable to allocate %zu bytes memory for image", len);
+ return NULL;
+ }
+ dstr_append_data(&str, data, data_len);
+ dstr_append(&str, endstream);
+
+ obj = pdf_add_object(pdf, OBJ_image);
+ if (!obj) {
+ dstr_free(&str);
+ return NULL;
+ }
+ obj->stream = str;
+
+ return obj;
+}
+
+static struct pdf_object *pdf_add_raw_rgb24(struct pdf_doc *pdf,
+ const uint8_t *data,
+ uint32_t width, uint32_t height)
+{
+ struct pdf_object *obj;
+ size_t len;
+ const char *endstream = ">\r\nendstream\r\n";
+ struct dstr str = INIT_DSTR;
+ size_t data_len = (size_t)width * (size_t)height * 3;
+
+ dstr_printf(&str,
+ "<<\r\n"
+ " /Type /XObject\r\n"
+ " /Name /Image%d\r\n"
+ " /Subtype /Image\r\n"
+ " /ColorSpace /DeviceRGB\r\n"
+ " /Height %d\r\n"
+ " /Width %d\r\n"
+ " /BitsPerComponent 8\r\n"
+ " /Length %zu\r\n"
+ ">>stream\r\n",
+ flexarray_size(&pdf->objects), height, width, data_len + 1);
+
+ len = dstr_len(&str) + data_len + strlen(endstream) + 1;
+ if (dstr_ensure(&str, len) < 0) {
+ dstr_free(&str);
+ pdf_set_err(pdf, -ENOMEM,
+ "Unable to allocate %zu bytes memory for image", len);
+ return NULL;
+ }
+ dstr_append_data(&str, data, data_len);
+ dstr_append(&str, endstream);
+
+ obj = pdf_add_object(pdf, OBJ_image);
+ if (!obj) {
+ dstr_free(&str);
+ return NULL;
+ }
+ obj->stream = str;
+
+ return obj;
+}
+
+static uint8_t *get_file(struct pdf_doc *pdf, const char *file_name,
+ size_t *length)
+{
+ FILE *fp;
+ uint8_t *file_data;
+ struct stat buf;
+ off_t len;
+
+ if ((fp = fopen(file_name, "rb")) == NULL) {
+ pdf_set_err(pdf, -errno, "Unable to open %s: %s", file_name,
+ strerror(errno));
+ return NULL;
+ }
+
+ if (fstat(fileno(fp), &buf) < 0) {
+ pdf_set_err(pdf, -errno, "Unable to access %s: %s", file_name,
+ strerror(errno));
+ fclose(fp);
+ return NULL;
+ }
+
+ len = buf.st_size;
+
+ file_data = (uint8_t *)malloc(len);
+ if (!file_data) {
+ pdf_set_err(pdf, -ENOMEM, "Unable to allocate: %d", (int)len);
+ fclose(fp);
+ return NULL;
+ }
+
+ if (fread(file_data, len, 1, fp) != 1) {
+ pdf_set_err(pdf, -errno, "Unable to read full data: %s", file_name);
+ free(file_data);
+ fclose(fp);
+ return NULL;
+ }
+
+ fclose(fp);
+
+ *length = len;
+
+ return file_data;
+}
+
+static struct pdf_object *
+pdf_add_raw_jpeg_data(struct pdf_doc *pdf, const struct pdf_img_info *info,
+ const uint8_t *jpeg_data, size_t len)
+{
+ struct pdf_object *obj = pdf_add_object(pdf, OBJ_image);
+ if (!obj)
+ return NULL;
+
+ dstr_printf(&obj->stream,
+ "<<\r\n"
+ " /Type /XObject\r\n"
+ " /Name /Image%d\r\n"
+ " /Subtype /Image\r\n"
+ " /ColorSpace %s\r\n"
+ " /Width %d\r\n"
+ " /Height %d\r\n"
+ " /BitsPerComponent 8\r\n"
+ " /Filter /DCTDecode\r\n"
+ " /Length %zu\r\n"
+ ">>stream\r\n",
+ flexarray_size(&pdf->objects),
+ (info->jpeg.ncolours == 1) ? "/DeviceGray" : "/DeviceRGB",
+ info->width, info->height, len);
+ dstr_append_data(&obj->stream, jpeg_data, len);
+
+ dstr_printf(&obj->stream, "\r\nendstream\r\n");
+
+ return obj;
+}
+
+/**
+ * Get the display dimensions of an image, respecting the images aspect ratio
+ * if only one desired display dimension is defined.
+ * The pdf parameter is only used for setting the error value.
+ */
+static int get_img_display_dimensions(struct pdf_doc *pdf, uint32_t img_width,
+ uint32_t img_height,
+ float *display_width,
+ float *display_height)
+{
+ if (!display_height || !display_width) {
+ return pdf_set_err(
+ pdf, -EINVAL,
+ "display_width and display_height may not be null pointers");
+ }
+
+ const float display_width_in = *display_width;
+ const float display_height_in = *display_height;
+
+ if (display_width_in < 0 && display_height_in < 0) {
+ return pdf_set_err(pdf, -EINVAL,
+ "Unable to determine image display dimensions, "
+ "display_width and display_height are both < 0");
+ }
+ if (img_width == 0 || img_height == 0) {
+ return pdf_set_err(pdf, -EINVAL,
+ "Invalid image dimensions received, the loaded "
+ "image appears to be empty.");
+ }
+
+ if (display_width_in < 0) {
+ // Set width, keeping aspect ratio
+ *display_width = display_height_in * ((float)img_width / img_height);
+ } else if (display_height_in < 0) {
+ // Set height, keeping aspect ratio
+ *display_height = display_width_in * ((float)img_height / img_width);
+ }
+ return 0;
+}
+
+static int pdf_add_image(struct pdf_doc *pdf, struct pdf_object *page,
+ struct pdf_object *image, float x, float y,
+ float width, float height)
+{
+ int ret;
+ struct dstr str = INIT_DSTR;
+
+ dstr_append(&str, "q ");
+ dstr_printf(&str, "%f 0 0 %f %f %f cm ", width, height, x, y);
+ dstr_printf(&str, "/Image%d Do ", image->index);
+ dstr_append(&str, "Q");
+
+ ret = pdf_add_stream(pdf, page, dstr_data(&str));
+ dstr_free(&str);
+ return ret;
+}
+
+// Works like fgets, except it's for a fixed in-memory buffer of data
+static size_t dgets(const uint8_t *data, size_t *pos, size_t len, char *line,
+ size_t line_len)
+{
+ size_t line_pos = 0;
+
+ if (*pos >= len)
+ return 0;
+
+ while ((*pos) < len) {
+ if (line_pos < line_len) {
+ // Reject non-ascii data
+ if (data[*pos] & 0x80) {
+ return 0;
+ }
+ line[line_pos] = data[*pos];
+ line_pos++;
+ }
+ if (data[*pos] == '\n') {
+ (*pos)++;
+ break;
+ }
+ (*pos)++;
+ }
+
+ if (line_pos < line_len) {
+ line[line_pos] = '\0';
+ }
+
+ return *pos;
+}
+
+static int parse_ppm_header(struct pdf_img_info *info, const uint8_t *data,
+ size_t length, char *err_msg,
+ size_t err_msg_length)
+{
+ char line[1024];
+ memset(line, '\0', sizeof(line));
+ size_t pos = 0;
+
+ // Load the PPM file
+ if (!dgets(data, &pos, length, line, sizeof(line) - 1)) {
+ snprintf(err_msg, err_msg_length, "Invalid PPM file");
+ return -EINVAL;
+ }
+
+ // Determine number of color channels (Also, we only support binary ppms)
+ int ncolors;
+ if (strncmp(line, "P6", 2) == 0) {
+ info->ppm.color_space = PPM_BINARY_COLOR_RGB;
+ ncolors = 3;
+ } else if (strncmp(line, "P5", 2) == 0) {
+ info->ppm.color_space = PPM_BINARY_COLOR_GRAY;
+ ncolors = 1;
+ } else {
+ snprintf(err_msg, err_msg_length,
+ "Only binary PPM files (grayscale, RGB) supported");
+ return -EINVAL;
+ }
+
+ // Skip comments before header
+ do {
+ if (!dgets(data, &pos, length, line, sizeof(line) - 1)) {
+ snprintf(err_msg, err_msg_length, "Unable to find PPM header");
+ return -EINVAL;
+ }
+ } while (line[0] == '#');
+
+ // Read image dimensions
+ if (sscanf(line, "%u %u\n", &(info->width), &(info->height)) != 2) {
+ snprintf(err_msg, err_msg_length, "Unable to find PPM size");
+ return -EINVAL;
+ }
+ if (info->width > MAX_IMAGE_WIDTH || info->height > MAX_IMAGE_HEIGHT) {
+ snprintf(err_msg, err_msg_length, "Invalid width/height: %ux%u",
+ info->width, info->height);
+ return -EINVAL;
+ }
+ info->ppm.size = (size_t)(info->width * info->height * ncolors);
+ info->ppm.data_begin_pos = pos;
+
+ return 0;
+}
+
+static int pdf_add_ppm_data(struct pdf_doc *pdf, struct pdf_object *page,
+ float x, float y, float display_width,
+ float display_height,
+ const struct pdf_img_info *info,
+ const uint8_t *ppm_data, size_t len)
+{
+ char line[1024];
+ // We start reading at the position delivered by parse_ppm_header,
+ // since we already parsed the header of the file there.
+ size_t pos = info->ppm.data_begin_pos;
+
+ /* Skip over the byte-size line */
+ if (!dgets(ppm_data, &pos, len, line, sizeof(line) - 1))
+ return pdf_set_err(pdf, -EINVAL, "No byte-size line in PPM file");
+
+ /* Try and limit the memory usage to sane images */
+ if (info->width > MAX_IMAGE_WIDTH || info->height > MAX_IMAGE_HEIGHT) {
+ return pdf_set_err(pdf, -EINVAL,
+ "Invalid width/height in PPM file: %ux%u",
+ info->width, info->height);
+ }
+
+ if (info->ppm.size > len - pos) {
+ return pdf_set_err(pdf, -EINVAL, "Insufficient image data available");
+ }
+
+ switch (info->ppm.color_space) {
+ case PPM_BINARY_COLOR_GRAY:
+ return pdf_add_grayscale8(pdf, page, x, y, display_width,
+ display_height, &ppm_data[pos], info->width,
+ info->height);
+ break;
+
+ case PPM_BINARY_COLOR_RGB:
+ return pdf_add_rgb24(pdf, page, x, y, display_width, display_height,
+ &ppm_data[pos], info->width, info->height);
+ break;
+
+ default:
+ return pdf_set_err(pdf, -EINVAL,
+ "Invalid color space in ppm file: %i",
+ info->ppm.color_space);
+ break;
+ }
+}
+
+static int parse_jpeg_header(struct pdf_img_info *info, const uint8_t *data,
+ size_t length, char *err_msg,
+ size_t err_msg_length)
+{
+ // See http://www.videotechnology.com/jpeg/j1.html for details
+ if (length >= 4 && data[0] == 0xFF && data[1] == 0xD8) {
+ for (size_t i = 2; i < length; i++) {
+ if (data[i] != 0xff) {
+ break;
+ }
+ while (++i < length && data[i] == 0xff)
+ ;
+ if (i + 2 >= length) {
+ break;
+ }
+ int len = data[i + 1] * 256 + data[i + 2];
+ /* Search for SOFn marker and decode jpeg details */
+ if ((data[i] & 0xf4) == 0xc0) {
+ if (len >= 9 && i + len + 1 < length) {
+ info->height = data[i + 4] * 256 + data[i + 5];
+ info->width = data[i + 6] * 256 + data[i + 7];
+ info->jpeg.ncolours = data[i + 8];
+ return 0;
+ }
+ break;
+ }
+ i += len;
+ }
+ }
+ snprintf(err_msg, err_msg_length, "Error parsing JPEG header");
+ return -EINVAL;
+}
+
+static int pdf_add_jpeg_data(struct pdf_doc *pdf, struct pdf_object *page,
+ float x, float y, float display_width,
+ float display_height, struct pdf_img_info *info,
+ const uint8_t *jpeg_data, size_t len)
+{
+ struct pdf_object *obj;
+
+ obj = pdf_add_raw_jpeg_data(pdf, info, jpeg_data, len);
+ if (!obj)
+ return pdf->errval;
+
+ if (get_img_display_dimensions(pdf, info->width, info->height,
+ &display_width, &display_height)) {
+ return pdf->errval;
+ }
+ return pdf_add_image(pdf, page, obj, x, y, display_width, display_height);
+}
+
+int pdf_add_rgb24(struct pdf_doc *pdf, struct pdf_object *page, float x,
+ float y, float display_width, float display_height,
+ const uint8_t *data, uint32_t width, uint32_t height)
+{
+ struct pdf_object *obj;
+
+ obj = pdf_add_raw_rgb24(pdf, data, width, height);
+ if (!obj)
+ return pdf->errval;
+
+ if (get_img_display_dimensions(pdf, width, height, &display_width,
+ &display_height)) {
+ return pdf->errval;
+ }
+ return pdf_add_image(pdf, page, obj, x, y, display_width, display_height);
+}
+
+int pdf_add_grayscale8(struct pdf_doc *pdf, struct pdf_object *page, float x,
+ float y, float display_width, float display_height,
+ const uint8_t *data, uint32_t width, uint32_t height)
+{
+ struct pdf_object *obj;
+
+ obj = pdf_add_raw_grayscale8(pdf, data, width, height);
+ if (!obj)
+ return pdf->errval;
+
+ if (get_img_display_dimensions(pdf, width, height, &display_width,
+ &display_height)) {
+ return pdf->errval;
+ }
+ return pdf_add_image(pdf, page, obj, x, y, display_width, display_height);
+}
+
+static int parse_png_header(struct pdf_img_info *info, const uint8_t *data,
+ size_t length, char *err_msg,
+ size_t err_msg_length)
+{
+ if (length <= sizeof(png_signature)) {
+ snprintf(err_msg, err_msg_length, "PNG file too short");
+ return -EINVAL;
+ }
+
+ if (memcmp(data, png_signature, sizeof(png_signature))) {
+ snprintf(err_msg, err_msg_length, "File is not correct PNG file");
+ return -EINVAL;
+ }
+
+ // process first PNG chunk
+ uint32_t pos = sizeof(png_signature);
+ const struct png_chunk *chunk = (const struct png_chunk *)&data[pos];
+ pos += sizeof(struct png_chunk);
+ if (pos > length) {
+ snprintf(err_msg, err_msg_length, "PNG file too short");
+ return -EINVAL;
+ }
+ if (strncmp(chunk->type, png_chunk_header, 4) == 0) {
+ // header found, process width and height, check errors
+ struct png_header *header = &info->png;
+
+ if (pos + sizeof(struct png_header) > length) {
+ snprintf(err_msg, err_msg_length, "PNG file too short");
+ return -EINVAL;
+ }
+
+ memcpy(header, &data[pos], sizeof(struct png_header));
+ if (header->deflate != 0) {
+ snprintf(err_msg, err_msg_length, "Deflate wrong in PNG header");
+ return -EINVAL;
+ }
+ if (header->bitDepth == 0) {
+ snprintf(err_msg, err_msg_length, "PNG file has zero bit depth");
+ return -EINVAL;
+ }
+ // ensure the width and height values have the proper byte order
+ // and copy them into the info struct.
+ header->width = ntoh32(header->width);
+ header->height = ntoh32(header->height);
+ info->width = header->width;
+ info->height = header->height;
+ return 0;
+ }
+ snprintf(err_msg, err_msg_length, "Failed to read PNG file header");
+ return -EINVAL;
+}
+
+static int pdf_add_png_data(struct pdf_doc *pdf, struct pdf_object *page,
+ float x, float y, float display_width,
+ float display_height,
+ const struct pdf_img_info *img_info,
+ const uint8_t *png_data, size_t png_data_length)
+{
+ // indicates if we return an error or add the img at the
+ // end of the function
+ bool success = false;
+
+ // string stream used for writing color space (and palette) info
+ // into the pdf
+ struct dstr colour_space = INIT_DSTR;
+
+ struct pdf_object *obj = NULL;
+ uint8_t *final_data = NULL;
+ int written = 0;
+ uint32_t pos;
+ uint8_t *png_data_temp = NULL;
+ size_t png_data_total_length = 0;
+ uint8_t ncolours;
+
+ // Stores palette information for indexed PNGs
+ struct rgb_value *palette_buffer = NULL;
+ size_t palette_buffer_length = 0;
+
+ const struct png_header *header = &img_info->png;
+
+ // Father info from png header
+ switch (header->colorType) {
+ case PNG_COLOR_GREYSCALE:
+ ncolours = 1;
+ break;
+ case PNG_COLOR_RGB:
+ ncolours = 3;
+ break;
+ case PNG_COLOR_INDEXED:
+ ncolours = 1;
+ break;
+ // PNG_COLOR_RGBA and PNG_COLOR_GREYSCALE_A are unsupported
+ default:
+ pdf_set_err(pdf, -EINVAL, "PNG has unsupported color type: %d",
+ header->colorType);
+ goto free_buffers;
+ break;
+ }
+
+ /* process PNG chunks */
+ pos = sizeof(png_signature);
+
+ while (1) {
+ const struct png_chunk *chunk;
+
+ chunk = (const struct png_chunk *)&png_data[pos];
+ pos += sizeof(struct png_chunk);
+
+ if (pos > png_data_length - 4) {
+ pdf_set_err(pdf, -EINVAL, "PNG file too short");
+ goto free_buffers;
+ }
+ const uint32_t chunk_length = ntoh32(chunk->length);
+ // chunk length + 4-bytes of CRC
+ if (chunk_length > png_data_length - pos - 4) {
+ pdf_set_err(pdf, -EINVAL, "PNG chunk exceeds file: %d vs %ld",
+ chunk_length, png_data_length - pos - 4);
+ goto free_buffers;
+ }
+ if (strncmp(chunk->type, png_chunk_header, 4) == 0) {
+ // Ignoring the header, since it was parsed
+ // before calling this function.
+ } else if (strncmp(chunk->type, png_chunk_palette, 4) == 0) {
+ // Palette chunk
+ if (header->colorType == PNG_COLOR_INDEXED) {
+ // palette chunk is needed for indexed images
+ if (palette_buffer) {
+ pdf_set_err(pdf, -EINVAL,
+ "PNG contains multiple palette chunks");
+ goto free_buffers;
+ }
+ if (chunk_length % 3 != 0) {
+ pdf_set_err(pdf, -EINVAL,
+ "PNG format error: palette chunk length is "
+ "not divisbly by 3!");
+ goto free_buffers;
+ }
+ palette_buffer_length = (size_t)(chunk_length / 3);
+ if (palette_buffer_length > 256 ||
+ palette_buffer_length == 0) {
+ pdf_set_err(pdf, -EINVAL,
+ "PNG palette length invalid: %zd",
+ palette_buffer_length);
+ goto free_buffers;
+ }
+ palette_buffer = (struct rgb_value *)malloc(
+ palette_buffer_length * sizeof(struct rgb_value));
+ if (!palette_buffer) {
+ pdf_set_err(pdf, -ENOMEM,
+ "Could not allocate memory for indexed color "
+ "palette (%zu bytes)",
+ palette_buffer_length *
+ sizeof(struct rgb_value));
+ goto free_buffers;
+ }
+ for (size_t i = 0; i < palette_buffer_length; i++) {
+ size_t offset = (i * 3) + pos;
+ palette_buffer[i].red = png_data[offset];
+ palette_buffer[i].green = png_data[offset + 1];
+ palette_buffer[i].blue = png_data[offset + 2];
+ }
+ } else if (header->colorType == PNG_COLOR_RGB ||
+ header->colorType == PNG_COLOR_RGBA) {
+ // palette chunk is optional for RGB(A) images
+ // but we do not process them
+ } else {
+ pdf_set_err(pdf, -EINVAL,
+ "Unexpected palette chunk for color type %d",
+ header->colorType);
+ goto free_buffers;
+ }
+ } else if (strncmp(chunk->type, png_chunk_data, 4) == 0) {
+ if (chunk_length > 0 && chunk_length < png_data_length - pos) {
+ uint8_t *data = (uint8_t *)realloc(
+ png_data_temp, png_data_total_length + chunk_length);
+ // (uint8_t *)realloc(info.data, info.length + chunk_length);
+ if (!data) {
+ pdf_set_err(pdf, -ENOMEM, "No memory for PNG data");
+ goto free_buffers;
+ }
+ png_data_temp = data;
+ memcpy(&png_data_temp[png_data_total_length], &png_data[pos],
+ chunk_length);
+ png_data_total_length += chunk_length;
+ }
+ } else if (strncmp(chunk->type, png_chunk_end, 4) == 0) {
+ /* end of file, exit */
+ break;
+ }
+
+ if (chunk_length >= png_data_length) {
+ pdf_set_err(pdf, -EINVAL, "PNG chunk length larger than file");
+ goto free_buffers;
+ }
+
+ pos += chunk_length; // add chunk length
+ pos += sizeof(uint32_t); // add CRC length
+ }
+
+ /* if no length was found */
+ if (png_data_total_length == 0) {
+ pdf_set_err(pdf, -EINVAL, "PNG file has zero length");
+ goto free_buffers;
+ }
+
+ switch (header->colorType) {
+ case PNG_COLOR_GREYSCALE:
+ dstr_append(&colour_space, "/DeviceGray");
+ break;
+ case PNG_COLOR_RGB:
+ dstr_append(&colour_space, "/DeviceRGB");
+ break;
+ case PNG_COLOR_INDEXED:
+ if (palette_buffer_length == 0) {
+ pdf_set_err(pdf, -EINVAL, "Indexed PNG contains no palette");
+ goto free_buffers;
+ }
+ // Write the color palette to the color_palette buffer
+ dstr_printf(&colour_space,
+ "[ /Indexed\r\n"
+ " /DeviceRGB\r\n"
+ " %zu\r\n"
+ " <",
+ palette_buffer_length - 1);
+ // write individual paletter values
+ // the index value for every RGB value is determined by its position
+ // (0, 1, 2, ...)
+ for (size_t i = 0; i < palette_buffer_length; i++) {
+ dstr_printf(&colour_space, "%02X%02X%02X ", palette_buffer[i].red,
+ palette_buffer[i].green, palette_buffer[i].blue);
+ }
+ dstr_append(&colour_space, ">\r\n]");
+ break;
+
+ default:
+ pdf_set_err(pdf, -EINVAL,
+ "Cannot map PNG color type %d to PDF color space",
+ header->colorType);
+ goto free_buffers;
+ break;
+ }
+
+ final_data = (uint8_t *)malloc(png_data_total_length + 1024 +
+ dstr_len(&colour_space));
+ if (!final_data) {
+ pdf_set_err(pdf, -ENOMEM, "Unable to allocate PNG data %zu",
+ png_data_total_length + 1024 + dstr_len(&colour_space));
+ goto free_buffers;
+ }
+
+ // Write image information to PDF
+ written =
+ sprintf((char *)final_data,
+ "<<\r\n"
+ " /Type /XObject\r\n"
+ " /Name /Image%d\r\n"
+ " /Subtype /Image\r\n"
+ " /ColorSpace %s\r\n"
+ " /Width %u\r\n"
+ " /Height %u\r\n"
+ " /Interpolate true\r\n"
+ " /BitsPerComponent %u\r\n"
+ " /Filter /FlateDecode\r\n"
+ " /DecodeParms << /Predictor 15 /Colors %d "
+ "/BitsPerComponent %u /Columns %u >>\r\n"
+ " /Length %zu\r\n"
+ ">>stream\r\n",
+ flexarray_size(&pdf->objects), dstr_data(&colour_space),
+ header->width, header->height, header->bitDepth, ncolours,
+ header->bitDepth, header->width, png_data_total_length);
+
+ memcpy(&final_data[written], png_data_temp, png_data_total_length);
+ written += png_data_total_length;
+ written += sprintf((char *)&final_data[written], "\r\nendstream\r\n");
+
+ obj = pdf_add_object(pdf, OBJ_image);
+ if (!obj) {
+ goto free_buffers;
+ }
+
+ dstr_append_data(&obj->stream, final_data, written);
+
+ if (get_img_display_dimensions(pdf, header->width, header->height,
+ &display_width, &display_height)) {
+ goto free_buffers;
+ }
+ success = true;
+
+free_buffers:
+ if (final_data)
+ free(final_data);
+ if (palette_buffer)
+ free(palette_buffer);
+ if (png_data_temp)
+ free(png_data_temp);
+ dstr_free(&colour_space);
+
+ if (success)
+ return pdf_add_image(pdf, page, obj, x, y, display_width,
+ display_height);
+ else
+ return pdf->errval;
+}
+
+static int parse_bmp_header(struct pdf_img_info *info, const uint8_t *data,
+ size_t data_length, char *err_msg,
+ size_t err_msg_length)
+{
+ if (data_length < sizeof(bmp_signature) + sizeof(struct bmp_header)) {
+ snprintf(err_msg, err_msg_length, "File is too short");
+ return -EINVAL;
+ }
+
+ if (memcmp(data, bmp_signature, sizeof(bmp_signature))) {
+ snprintf(err_msg, err_msg_length, "File is not correct BMP file");
+ return -EINVAL;
+ }
+ memcpy(&info->bmp, &data[sizeof(bmp_signature)],
+ sizeof(struct bmp_header));
+ if (info->bmp.biWidth < 0) {
+ snprintf(err_msg, err_msg_length, "BMP has negative width");
+ return -EINVAL;
+ }
+ if (info->bmp.biHeight == -2147483648) { // 1 << 31
+ snprintf(err_msg, err_msg_length, "BMP height overflow");
+ return -EINVAL;
+ }
+ info->width = info->bmp.biWidth;
+ // biHeight might be negative (positive indicates vertically mirrored
+ // lines)
+ info->height = abs(info->bmp.biHeight);
+ return 0;
+}
+
+static int pdf_add_bmp_data(struct pdf_doc *pdf, struct pdf_object *page,
+ float x, float y, float display_width,
+ float display_height,
+ const struct pdf_img_info *info,
+ const uint8_t *data, const size_t len)
+{
+ const struct bmp_header *header = &info->bmp;
+ uint8_t *bmp_data = NULL;
+ uint8_t row_padding;
+ uint32_t bpp;
+ size_t data_len;
+ int retval;
+ const uint32_t width = info->width;
+ const uint32_t height = info->height;
+
+ if (header->bfSize != len)
+ return pdf_set_err(pdf, -EINVAL,
+ "BMP file seems to have wrong length");
+ if (header->biSize != 40)
+ return pdf_set_err(pdf, -EINVAL, "Wrong BMP header: biSize");
+ if (header->biCompression != 0)
+ return pdf_set_err(pdf, -EINVAL, "Wrong BMP compression value: %d",
+ header->biCompression);
+ if (header->biWidth > MAX_IMAGE_WIDTH || header->biWidth <= 0 ||
+ width > MAX_IMAGE_WIDTH || width == 0)
+ return pdf_set_err(pdf, -EINVAL, "BMP has invalid width: %d",
+ header->biWidth);
+ if (header->biHeight > MAX_IMAGE_HEIGHT ||
+ header->biHeight < -MAX_IMAGE_HEIGHT || header->biHeight == 0 ||
+ height > MAX_IMAGE_HEIGHT || height == 0)
+ return pdf_set_err(pdf, -EINVAL, "BMP has invalid height: %d",
+ header->biHeight);
+ if (header->biBitCount != 24 && header->biBitCount != 32)
+ return pdf_set_err(pdf, -EINVAL, "Unsupported BMP bitdepth: %d",
+ header->biBitCount);
+ bpp = header->biBitCount / 8;
+ /* BMP rows are 4-bytes padded! */
+ row_padding = (width * bpp) & 3;
+ data_len = (size_t)width * (size_t)height * 3;
+
+ if (header->bfOffBits >= len)
+ return pdf_set_err(pdf, -EINVAL, "Invalid BMP image offset");
+
+ if (len - header->bfOffBits <
+ (size_t)height * (width + row_padding) * bpp)
+ return pdf_set_err(pdf, -EINVAL, "Wrong BMP image size");
+
+ if (bpp == 3) {
+ /* 24 bits: change R and B colors */
+ bmp_data = (uint8_t *)malloc(data_len);
+ if (!bmp_data)
+ return pdf_set_err(pdf, -ENOMEM,
+ "Insufficient memory for bitmap");
+ for (uint32_t pos = 0; pos < width * height; pos++) {
+ uint32_t src_pos =
+ header->bfOffBits + 3 * (pos + (pos / width) * row_padding);
+
+ bmp_data[pos * 3] = data[src_pos + 2];
+ bmp_data[pos * 3 + 1] = data[src_pos + 1];
+ bmp_data[pos * 3 + 2] = data[src_pos];
+ }
+ } else if (bpp == 4) {
+ /* 32 bits: change R and B colors, remove key color */
+ int offs = 0;
+ bmp_data = (uint8_t *)malloc(data_len);
+ if (!bmp_data)
+ return pdf_set_err(pdf, -ENOMEM,
+ "Insufficient memory for bitmap");
+
+ for (uint32_t pos = 0; pos < width * height * 4; pos += 4) {
+ bmp_data[offs] = data[header->bfOffBits + pos + 2];
+ bmp_data[offs + 1] = data[header->bfOffBits + pos + 1];
+ bmp_data[offs + 2] = data[header->bfOffBits + pos];
+ offs += 3;
+ }
+ } else {
+ return pdf_set_err(pdf, -EINVAL, "Unsupported BMP bitdepth: %d",
+ header->biBitCount);
+ }
+ if (header->biHeight >= 0) {
+ // BMP has vertically mirrored representation of lines, so swap them
+ uint8_t *line = (uint8_t *)malloc(width * 3);
+ if (!line) {
+ free(bmp_data);
+ return pdf_set_err(pdf, -ENOMEM,
+ "Unable to allocate memory for bitmap mirror");
+ }
+ for (uint32_t pos = 0; pos < (height / 2); pos++) {
+ memcpy(line, &bmp_data[pos * width * 3], width * 3);
+ memcpy(&bmp_data[pos * width * 3],
+ &bmp_data[(height - pos - 1) * width * 3], width * 3);
+ memcpy(&bmp_data[(height - pos - 1) * width * 3], line,
+ width * 3);
+ }
+ free(line);
+ }
+
+ retval = pdf_add_rgb24(pdf, page, x, y, display_width, display_height,
+ bmp_data, width, height);
+ free(bmp_data);
+
+ return retval;
+}
+
+static int determine_image_format(const uint8_t *data, size_t length)
+{
+ if (length >= sizeof(png_signature) &&
+ memcmp(data, png_signature, sizeof(png_signature)) == 0)
+ return IMAGE_PNG;
+ if (length >= sizeof(bmp_signature) &&
+ memcmp(data, bmp_signature, sizeof(bmp_signature)) == 0)
+ return IMAGE_BMP;
+ if (length >= sizeof(jpeg_signature) &&
+ memcmp(data, jpeg_signature, sizeof(jpeg_signature)) == 0)
+ return IMAGE_JPG;
+ if (length >= sizeof(ppm_signature) &&
+ memcmp(data, ppm_signature, sizeof(ppm_signature)) == 0)
+ return IMAGE_PPM;
+ if (length >= sizeof(pgm_signature) &&
+ memcmp(data, pgm_signature, sizeof(pgm_signature)) == 0)
+ return IMAGE_PPM;
+
+ return IMAGE_UNKNOWN;
+}
+
+int pdf_parse_image_header(struct pdf_img_info *info, const uint8_t *data,
+ size_t length, char *err_msg,
+ size_t err_msg_length)
+
+{
+ const int image_format = determine_image_format(data, length);
+ info->image_format = image_format;
+ switch (image_format) {
+ case IMAGE_PNG:
+ return parse_png_header(info, data, length, err_msg, err_msg_length);
+ case IMAGE_BMP:
+ return parse_bmp_header(info, data, length, err_msg, err_msg_length);
+ case IMAGE_JPG:
+ return parse_jpeg_header(info, data, length, err_msg, err_msg_length);
+ case IMAGE_PPM:
+ return parse_ppm_header(info, data, length, err_msg, err_msg_length);
+
+ case IMAGE_UNKNOWN:
+ default:
+ snprintf(err_msg, err_msg_length, "Unknown file format");
+ return -EINVAL;
+ }
+}
+
+int pdf_add_image_data(struct pdf_doc *pdf, struct pdf_object *page, float x,
+ float y, float display_width, float display_height,
+ const uint8_t *data, size_t len)
+{
+ struct pdf_img_info info = {
+ .image_format = IMAGE_UNKNOWN,
+ .width = 0,
+ .height = 0,
+ .jpeg = {0},
+ };
+
+ int ret = pdf_parse_image_header(&info, data, len, pdf->errstr,
+ sizeof(pdf->errstr));
+ if (ret)
+ return ret;
+
+ // Try and determine which image format it is based on the content
+ switch (info.image_format) {
+ case IMAGE_PNG:
+ return pdf_add_png_data(pdf, page, x, y, display_width,
+ display_height, &info, data, len);
+ case IMAGE_BMP:
+ return pdf_add_bmp_data(pdf, page, x, y, display_width,
+ display_height, &info, data, len);
+ case IMAGE_JPG:
+ return pdf_add_jpeg_data(pdf, page, x, y, display_width,
+ display_height, &info, data, len);
+ case IMAGE_PPM:
+ return pdf_add_ppm_data(pdf, page, x, y, display_width,
+ display_height, &info, data, len);
+
+ // This case should be caught in parse_image_header, but is checked
+ // here again for safety
+ case IMAGE_UNKNOWN:
+ default:
+ return pdf_set_err(pdf, -EINVAL, "Unable to determine image format");
+ }
+}
+
+int pdf_add_image_file(struct pdf_doc *pdf, struct pdf_object *page, float x,
+ float y, float display_width, float display_height,
+ const char *image_filename)
+{
+ size_t len;
+ uint8_t *data;
+ int ret = 0;
+
+ data = get_file(pdf, image_filename, &len);
+ if (data == NULL)
+ return pdf_get_errval(pdf);
+
+ ret = pdf_add_image_data(pdf, page, x, y, display_width, display_height,
+ data, len);
+ free(data);
+ return ret;
+}
diff --git a/src/PDFGen/pdfgen.h b/src/PDFGen/pdfgen.h
new file mode 100644
index 0000000..ca0ac6d
--- /dev/null
+++ b/src/PDFGen/pdfgen.h
@@ -0,0 +1,773 @@
+/**
+ * Simple engine for creating PDF files.
+ * It supports text, shapes, images etc...
+ * Capable of handling millions of objects without too much performance
+ * penalty.
+ * Public domain license - no warrenty implied; use at your own risk.
+ * @file pdfgen.h
+ */
+#ifndef PDFGEN_H
+#define PDFGEN_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+#include <stdio.h>
+
+/**
+ * @defgroup subsystem Simple PDF Generation
+ * Allows for quick generation of simple PDF documents.
+ * This is useful for producing easily printed output from C code, where
+ * advanced formatting is not required
+ *
+ * Note: All coordinates/sizes are in points (1/72 of an inch).
+ * All coordinates are based on 0,0 being the bottom left of the page.
+ * All colours are specified as a packed 32-bit value - see @ref PDF_RGB.
+ * Text strings are interpreted as UTF-8 encoded, but only a small subset of
+ * characters beyond 7-bit ascii are supported (see @ref pdf_add_text for
+ * details).
+ *
+ * @par PDF library example:
+ * @code
+#include "pdfgen.h"
+ ...
+struct pdf_info info = {
+ .creator = "My software",
+ .producer = "My software",
+ .title = "My document",
+ .author = "My name",
+ .subject = "My subject",
+ .date = "Today"
+ };
+struct pdf_doc *pdf = pdf_create(PDF_A4_WIDTH, PDF_A4_HEIGHT, &info);
+pdf_set_font(pdf, "Times-Roman");
+pdf_append_page(pdf);
+pdf_add_text(pdf, NULL, "This is text", 12, 50, 20, PDF_BLACK);
+pdf_add_line(pdf, NULL, 50, 24, 150, 24);
+pdf_save(pdf, "output.pdf");
+pdf_destroy(pdf);
+ * @endcode
+ */
+
+struct pdf_doc;
+struct pdf_object;
+
+/**
+ * pdf_info describes the metadata to be inserted into the
+ * header of the output PDF
+ */
+struct pdf_info {
+ char creator[64]; //!< Software used to create the PDF
+ char producer[64]; //!< Software used to create the PDF
+ char title[64]; //!< The title of the PDF (typically displayed in the
+ //!< window bar when viewing)
+ char author[64]; //!< Who created the PDF
+ char subject[64]; //!< What is the PDF about
+ char date[64]; //!< The date the PDF was created
+};
+
+/**
+ * Enum that declares the different image file formats we currently support.
+ * Each value has a corresponding header struct used within
+ * the format_specific_img_info union.
+ */
+enum {
+ IMAGE_PNG,
+ IMAGE_JPG,
+ IMAGE_PPM,
+ IMAGE_BMP,
+
+ IMAGE_UNKNOWN
+};
+
+/**
+ * Since we're casting random areas of memory to these, make sure
+ * they're packed properly to match the image format requirements
+ */
+#pragma pack(push, 1)
+
+/**
+ * Information about color type of PNG format
+ * As defined by https://www.w3.org/TR/2003/REC-PNG-20031110/#6Colour-values
+ */
+enum /* png colortype */ {
+ // Greyscale
+ PNG_COLOR_GREYSCALE = 0,
+ // Truecolour
+ PNG_COLOR_RGB = 2,
+ // Indexed-colour
+ PNG_COLOR_INDEXED = 3,
+ // Greyscale with alpha
+ PNG_COLOR_GREYSCALE_A = 4,
+ // Truecolour with alpha
+ PNG_COLOR_RGBA = 6,
+
+ PNG_COLOR_INVALID = 255
+};
+
+/**
+ * png_header describes the header information extracted from .PNG files
+ */
+struct png_header {
+ uint32_t width; //!< Width in pixels
+ uint32_t height; //!< Height in pixels
+ uint8_t bitDepth; //!< Bit Depth
+ uint8_t colorType; //!< Color type - see PNG_COLOR_xx
+ uint8_t deflate; //!< Deflate setting
+ uint8_t filtering; //!< Filtering
+ uint8_t interlace; //!< Interlacing
+};
+
+/**
+ * bmp_header describes the header information extracted from .BMP files
+ */
+struct bmp_header {
+ uint32_t bfSize; //!< size of BMP in bytes
+ uint16_t bfReserved1; //!< ignore!
+ uint16_t bfReserved2; //!< ignore!
+ uint32_t bfOffBits; //!< Offset to BMP data
+ uint32_t biSize; //!< Size of this header (40)
+ int32_t biWidth; //!< Width in pixels
+ int32_t biHeight; //!< Height in pixels
+ uint16_t biPlanes; //!< Number of colour planes - must be 1
+ uint16_t biBitCount; //!< Bits Per Pixel
+ uint32_t biCompression; //!< Compression Method
+};
+#pragma pack(pop)
+
+/**
+ * jpeg_header describes the header information extracted from .JPG files
+ */
+struct jpeg_header {
+ int ncolours; //!< Number of colours
+};
+
+/**
+ * PPM color spaces
+ */
+enum {
+ PPM_BINARY_COLOR_RGB, //!< binary ppm with RGB colors (magic number P5)
+ PPM_BINARY_COLOR_GRAY, //!< binary ppm with grayscale colors (magic number
+ //!< P6)
+};
+
+/**
+ * ppm_header describes the header information extracted from .PPM files
+ */
+struct ppm_header {
+ size_t size; //!< Indicate the size of the image data
+ size_t data_begin_pos; //!< position in the data where the image starts
+ int color_space; //!< PPM color space
+};
+
+/**
+ * pdf_img_info describes the metadata for an arbitrary image
+ */
+struct pdf_img_info {
+ int image_format; //!< Indicates the image format (IMAGE_PNG, ...)
+ uint32_t width; //!< Width in pixels
+ uint32_t height; //!< Height in pixels
+
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+ // Doxygen doesn't like anonymous unions
+ //!< Image specific details
+ union {
+ struct bmp_header bmp; //!< BMP header info
+ struct jpeg_header jpeg; //!< JPEG header info
+ struct png_header png; //!< PNG header info
+ struct ppm_header ppm; //!< PPM header info
+ };
+#endif
+};
+
+/**
+ * pdf_path_operation holds information about a path
+ * drawing operation.
+ * See PDF reference for detailed usage.
+ */
+struct pdf_path_operation {
+ char op; /*!< Operation command. Possible operators are: m = move to, l =
+ line to, c = cubic bezier curve with two control points, v =
+ cubic bezier curve with one control point fixed at first
+ point, y = cubic bezier curve with one control point fixed
+ at second point, h = close path */
+ float x1; /*!< X offset of the first point. Used with: m, l, c, v, y */
+ float y1; /*!< Y offset of the first point. Used with: m, l, c, v, y */
+ float x2; /*!< X offset of the second point. Used with: c, v, y */
+ float y2; /*!< Y offset of the second point. Used with: c, v, y */
+ float x3; /*!< X offset of the third point. Used with: c */
+ float y3; /*!< Y offset of the third point. Used with: c */
+};
+
+/**
+ * Convert a value in inches into a number of points.
+ * @param inch inches value to convert to points
+ */
+#define PDF_INCH_TO_POINT(inch) ((float)((inch)*72.0f))
+
+/**
+ * Convert a value in milli-meters into a number of points.
+ * @param mm millimeter value to convert to points
+ */
+#define PDF_MM_TO_POINT(mm) ((float)((mm)*72.0f / 25.4f))
+
+/*! Point width of a standard US-Letter page */
+#define PDF_LETTER_WIDTH PDF_INCH_TO_POINT(8.5f)
+
+/*! Point height of a standard US-Letter page */
+#define PDF_LETTER_HEIGHT PDF_INCH_TO_POINT(11.0f)
+
+/*! Point width of a standard A4 page */
+#define PDF_A4_WIDTH PDF_MM_TO_POINT(210.0f)
+
+/*! Point height of a standard A4 page */
+#define PDF_A4_HEIGHT PDF_MM_TO_POINT(297.0f)
+
+/*! Point width of a standard A3 page */
+#define PDF_A3_WIDTH PDF_MM_TO_POINT(297.0f)
+
+/*! Point height of a standard A3 page */
+#define PDF_A3_HEIGHT PDF_MM_TO_POINT(420.0f)
+
+/**
+ * Convert three 8-bit RGB values into a single packed 32-bit
+ * colour. These 32-bit colours are used by various functions
+ * in PDFGen
+ */
+#define PDF_RGB(r, g, b) \
+ (uint32_t)((((r)&0xff) << 16) | (((g)&0xff) << 8) | (((b)&0xff)))
+
+/**
+ * Convert four 8-bit ARGB values into a single packed 32-bit
+ * colour. These 32-bit colours are used by various functions
+ * in PDFGen. Alpha values range from 0 (opaque) to 0xff
+ * (transparent)
+ */
+#define PDF_ARGB(a, r, g, b) \
+ (uint32_t)(((uint32_t)((a)&0xff) << 24) | (((r)&0xff) << 16) | \
+ (((g)&0xff) << 8) | (((b)&0xff)))
+
+/*! Utility macro to provide bright red */
+#define PDF_RED PDF_RGB(0xff, 0, 0)
+
+/*! Utility macro to provide bright green */
+#define PDF_GREEN PDF_RGB(0, 0xff, 0)
+
+/*! Utility macro to provide bright blue */
+#define PDF_BLUE PDF_RGB(0, 0, 0xff)
+
+/*! Utility macro to provide black */
+#define PDF_BLACK PDF_RGB(0, 0, 0)
+
+/*! Utility macro to provide white */
+#define PDF_WHITE PDF_RGB(0xff, 0xff, 0xff)
+
+/*!
+ * Utility macro to provide a transparent colour
+ * This is used in some places for 'fill' colours, where no fill is required
+ */
+#define PDF_TRANSPARENT (uint32_t)(0xffu << 24)
+
+/**
+ * Different alignment options for rendering text
+ */
+enum {
+ PDF_ALIGN_LEFT, //!< Align text to the left
+ PDF_ALIGN_RIGHT, //!< Align text to the right
+ PDF_ALIGN_CENTER, //!< Align text in the center
+ PDF_ALIGN_JUSTIFY, //!< Align text in the center, with padding to fill the
+ //!< available space
+ PDF_ALIGN_JUSTIFY_ALL, //!< Like PDF_ALIGN_JUSTIFY, except even short
+ //!< lines will be fully justified
+ PDF_ALIGN_NO_WRITE, //!< Fake alignment for only checking wrap height with
+ //!< no writes
+};
+
+/**
+ * Create a new PDF object, with the given page
+ * width/height
+ * @param width Width of the page
+ * @param height Height of the page
+ * @param info Optional information to be put into the PDF header
+ * @return PDF document object, or NULL on failure
+ */
+struct pdf_doc *pdf_create(float width, float height,
+ const struct pdf_info *info);
+
+/**
+ * Destroy the pdf object, and all of its associated memory
+ * @param pdf PDF document to clean up
+ */
+void pdf_destroy(struct pdf_doc *pdf);
+
+/**
+ * Retrieve the error message if any operation fails
+ * @param pdf pdf document to retrieve error message from
+ * @param errval optional pointer to an integer to be set to the error code
+ * @return NULL if no error message, string description of error otherwise
+ */
+const char *pdf_get_err(const struct pdf_doc *pdf, int *errval);
+
+/**
+ * Acknowledge an outstanding pdf error
+ * @param pdf pdf document to clear the error message from
+ */
+void pdf_clear_err(struct pdf_doc *pdf);
+
+/**
+ * Sets the font to use for text objects. Default value is Times-Roman if
+ * this function is not called.
+ * Note: The font selection should be done before text is output,
+ * and will remain until pdf_set_font is called again.
+ * @param pdf PDF document to update font on
+ * @param font New font to use. This must be one of the standard PDF fonts:
+ * Courier, Courier-Bold, Courier-BoldOblique, Courier-Oblique,
+ * Helvetica, Helvetica-Bold, Helvetica-BoldOblique, Helvetica-Oblique,
+ * Times-Roman, Times-Bold, Times-Italic, Times-BoldItalic,
+ * Symbol or ZapfDingbats
+ * @return < 0 on failure, 0 on success
+ */
+int pdf_set_font(struct pdf_doc *pdf, const char *font);
+
+/**
+ * Calculate the width of a given string in the current font
+ * @param pdf PDF document
+ * @param font_name Name of the font to get the width of.
+ * This must be one of the standard PDF fonts:
+ * Courier, Courier-Bold, Courier-BoldOblique, Courier-Oblique,
+ * Helvetica, Helvetica-Bold, Helvetica-BoldOblique, Helvetica-Oblique,
+ * Times-Roman, Times-Bold, Times-Italic, Times-BoldItalic,
+ * Symbol or ZapfDingbats
+ * @param text Text to determine width of
+ * @param size Size of the text, in points
+ * @param text_width area to store calculated width in
+ * @return < 0 on failure, 0 on success
+ */
+int pdf_get_font_text_width(struct pdf_doc *pdf, const char *font_name,
+ const char *text, float size, float *text_width);
+
+/**
+ * Retrieves a PDF document height
+ * @param pdf PDF document to get height of
+ * @return height of PDF document (in points)
+ */
+float pdf_height(const struct pdf_doc *pdf);
+
+/**
+ * Retrieves a PDF document width
+ * @param pdf PDF document to get width of
+ * @return width of PDF document (in points)
+ */
+float pdf_width(const struct pdf_doc *pdf);
+
+/**
+ * Retrieves page height
+ * @param page Page object to get height of
+ * @return height of page (in points)
+ */
+float pdf_page_height(const struct pdf_object *page);
+
+/**
+ * Retrieves page width
+ * @param page Page object to get width of
+ * @return width of page (in points)
+ */
+float pdf_page_width(const struct pdf_object *page);
+
+/**
+ * Add a new page to the given pdf
+ * @param pdf PDF document to append page to
+ * @return new page object
+ */
+struct pdf_object *pdf_append_page(struct pdf_doc *pdf);
+
+/**
+ * Adjust the width/height of a specific page
+ * @param pdf PDF document that the page belongs to
+ * @param page object returned from @ref pdf_append_page
+ * @param width Width of the page in points
+ * @param height Height of the page in points
+ * @return < 0 on failure, 0 on success
+ */
+int pdf_page_set_size(struct pdf_doc *pdf, struct pdf_object *page,
+ float width, float height);
+
+/**
+ * Save the given pdf document to the supplied filename.
+ * @param pdf PDF document to save
+ * @param filename Name of the file to store the PDF into (NULL for stdout)
+ * @return < 0 on failure, >= 0 on success
+ */
+int pdf_save(struct pdf_doc *pdf, const char *filename);
+
+/**
+ * Save the given pdf document to the given FILE output
+ * @param pdf PDF document to save
+ * @param fp FILE pointer to store the data into (must be writable)
+ * @return < 0 on failure, >= 0 on success
+ */
+int pdf_save_file(struct pdf_doc *pdf, FILE *fp);
+
+/**
+ * Add a text string to the document
+ * @param pdf PDF document to add to
+ * @param page Page to add object to (NULL => most recently added page)
+ * @param text String to display
+ * @param size Point size of the font
+ * @param xoff X location to put it in
+ * @param yoff Y location to put it in
+ * @param colour Colour to draw the text
+ * @return 0 on success, < 0 on failure
+ */
+int pdf_add_text(struct pdf_doc *pdf, struct pdf_object *page,
+ const char *text, float size, float xoff, float yoff,
+ uint32_t colour);
+
+/**
+ * Add a text string to the document, making it wrap if it is too
+ * long
+ * @param pdf PDF document to add to
+ * @param page Page to add object to (NULL => most recently added page)
+ * @param text String to display
+ * @param size Point size of the font
+ * @param xoff X location to put it in
+ * @param yoff Y location to put it in
+ * @param colour Colour to draw the text
+ * @param wrap_width Width at which to wrap the text
+ * @param align Text alignment (see PDF_ALIGN_xxx)
+ * @param height Store the final height of the wrapped text here (optional)
+ * @return < 0 on failure, >= 0 on success
+ */
+int pdf_add_text_wrap(struct pdf_doc *pdf, struct pdf_object *page,
+ const char *text, float size, float xoff, float yoff,
+ uint32_t colour, float wrap_width, int align,
+ float *height);
+
+/**
+ * Add a line to the document
+ * @param pdf PDF document to add to
+ * @param page Page to add object to (NULL => most recently added page)
+ * @param x1 X offset of start of line
+ * @param y1 Y offset of start of line
+ * @param x2 X offset of end of line
+ * @param y2 Y offset of end of line
+ * @param width Width of the line
+ * @param colour Colour to draw the line
+ * @return 0 on success, < 0 on failure
+ */
+int pdf_add_line(struct pdf_doc *pdf, struct pdf_object *page, float x1,
+ float y1, float x2, float y2, float width, uint32_t colour);
+
+/**
+ * Add a cubic bezier curve to the document
+ * @param pdf PDF document to add to
+ * @param page Page to add object to (NULL => most recently added page)
+ * @param x1 X offset of the initial point of the curve
+ * @param y1 Y offset of the initial point of the curve
+ * @param x2 X offset of the final point of the curve
+ * @param y2 Y offset of the final point of the curve
+ * @param xq1 X offset of the first control point of the curve
+ * @param yq1 Y offset of the first control point of the curve
+ * @param xq2 X offset of the second control of the curve
+ * @param yq2 Y offset of the second control of the curve
+ * @param width Width of the curve
+ * @param colour Colour to draw the curve
+ * @return 0 on success, < 0 on failure
+ */
+int pdf_add_cubic_bezier(struct pdf_doc *pdf, struct pdf_object *page,
+ float x1, float y1, float x2, float y2, float xq1,
+ float yq1, float xq2, float yq2, float width,
+ uint32_t colour);
+
+/**
+ * Add a quadratic bezier curve to the document
+ * @param pdf PDF document to add to
+ * @param page Page to add object to (NULL => most recently added page)
+ * @param x1 X offset of the initial point of the curve
+ * @param y1 Y offset of the initial point of the curve
+ * @param x2 X offset of the final point of the curve
+ * @param y2 Y offset of the final point of the curve
+ * @param xq1 X offset of the control point of the curve
+ * @param yq1 Y offset of the control point of the curve
+ * @param width Width of the curve
+ * @param colour Colour to draw the curve
+ * @return 0 on success, < 0 on failure
+ */
+int pdf_add_quadratic_bezier(struct pdf_doc *pdf, struct pdf_object *page,
+ float x1, float y1, float x2, float y2,
+ float xq1, float yq1, float width,
+ uint32_t colour);
+
+/**
+ * Add a custom path to the document
+ * @param pdf PDF document to add to
+ * @param page Page to add object to (NULL => most recently added page)
+ * @param operations Array of drawing operations
+ * @param operation_count The number of operations
+ * @param stroke_width Width of the stroke
+ * @param stroke_colour Colour to stroke the curve
+ * @param fill_colour Colour to fill the path
+ * @return 0 on success, < 0 on failure
+ */
+int pdf_add_custom_path(struct pdf_doc *pdf, struct pdf_object *page,
+ struct pdf_path_operation *operations,
+ int operation_count, float stroke_width,
+ uint32_t stroke_colour, uint32_t fill_colour);
+
+/**
+ * Add an ellipse to the document
+ * @param pdf PDF document to add to
+ * @param page Page to add object to (NULL => most recently added page)
+ * @param x X offset of the center of the ellipse
+ * @param y Y offset of the center of the ellipse
+ * @param xradius Radius of the ellipse in the X axis
+ * @param yradius Radius of the ellipse in the Y axis
+ * @param width Width of the ellipse outline stroke
+ * @param colour Colour to draw the ellipse outline stroke
+ * @param fill_colour Colour to fill the ellipse
+ * @return 0 on success, < 0 on failure
+ */
+int pdf_add_ellipse(struct pdf_doc *pdf, struct pdf_object *page, float x,
+ float y, float xradius, float yradius, float width,
+ uint32_t colour, uint32_t fill_colour);
+
+/**
+ * Add a circle to the document
+ * @param pdf PDF document to add to
+ * @param page Page to add object to (NULL => most recently added page)
+ * @param x X offset of the center of the circle
+ * @param y Y offset of the center of the circle
+ * @param radius Radius of the circle
+ * @param width Width of the circle outline stroke
+ * @param colour Colour to draw the circle outline stroke
+ * @param fill_colour Colour to fill the circle
+ * @return 0 on success, < 0 on failure
+ */
+int pdf_add_circle(struct pdf_doc *pdf, struct pdf_object *page, float x,
+ float y, float radius, float width, uint32_t colour,
+ uint32_t fill_colour);
+
+/**
+ * Add an outline rectangle to the document
+ * @param pdf PDF document to add to
+ * @param page Page to add object to (NULL => most recently added page)
+ * @param x X offset to start rectangle at
+ * @param y Y offset to start rectangle at
+ * @param width Width of rectangle
+ * @param height Height of rectangle
+ * @param border_width Width of rectangle border
+ * @param colour Colour to draw the rectangle
+ * @return 0 on success, < 0 on failure
+ */
+int pdf_add_rectangle(struct pdf_doc *pdf, struct pdf_object *page, float x,
+ float y, float width, float height, float border_width,
+ uint32_t colour);
+
+/**
+ * Add a filled rectangle to the document
+ * @param pdf PDF document to add to
+ * @param page Page to add object to (NULL => most recently added page)
+ * @param x X offset to start rectangle at
+ * @param y Y offset to start rectangle at
+ * @param width Width of rectangle
+ * @param height Height of rectangle
+ * @param border_width Width of rectangle border
+ * @param colour_fill Colour to fill the rectangle
+ * @param colour_border Colour to draw the rectangle
+ * @return 0 on success, < 0 on failure
+ */
+int pdf_add_filled_rectangle(struct pdf_doc *pdf, struct pdf_object *page,
+ float x, float y, float width, float height,
+ float border_width, uint32_t colour_fill,
+ uint32_t colour_border);
+
+/**
+ * Add an outline polygon to the document
+ * @param pdf PDF document to add to
+ * @param page Page to add object to (NULL => most recently added page)
+ * @param x array of X offsets for points comprising the polygon
+ * @param y array of Y offsets for points comprising the polygon
+ * @param count Number of points comprising the polygon
+ * @param border_width Width of polygon border
+ * @param colour Colour to draw the polygon
+ * @return 0 on success, < 0 on failure
+ */
+int pdf_add_polygon(struct pdf_doc *pdf, struct pdf_object *page, float x[],
+ float y[], int count, float border_width,
+ uint32_t colour);
+
+/**
+ * Add a filled polygon to the document
+ * @param pdf PDF document to add to
+ * @param page Page to add object to (NULL => most recently added page)
+ * @param x array of X offsets of points comprising the polygon
+ * @param y array of Y offsets of points comprising the polygon
+ * @param count Number of points comprising the polygon
+ * @param border_width Width of polygon border
+ * @param colour Colour to draw the polygon
+ * @return 0 on success, < 0 on failure
+ */
+int pdf_add_filled_polygon(struct pdf_doc *pdf, struct pdf_object *page,
+ float x[], float y[], int count,
+ float border_width, uint32_t colour);
+
+/**
+ * Add a bookmark to the document
+ * @param pdf PDF document to add bookmark to
+ * @param page Page to jump to for bookmark
+ (or NULL for the most recently added page)
+ * @param parent ID of a previously created bookmark that is the parent
+ of this one. -1 if this should be a top-level bookmark.
+ * @param name String to associate with the bookmark
+ * @return < 0 on failure, new bookmark id on success
+ */
+int pdf_add_bookmark(struct pdf_doc *pdf, struct pdf_object *page, int parent,
+ const char *name);
+
+/**
+ * Add a link annotation to the document
+ * @param pdf PDF document to add link to
+ * @param page Page that holds the clickable rectangle
+ (or NULL for the most recently added page)
+ * @param x X coordinate of bottom LHS corner of clickable rectangle
+ * @param y Y coordinate of bottom LHS corner of clickable rectangle
+ * @param width width of clickable rectangle
+ * @param height height of clickable rectangle
+ * @param target_page Page to jump to for link
+ * @param target_x X coordinate to position at the left of the view
+ * @param target_y Y coordinate to position at the top of the view
+ * @return < 0 on failure, new bookmark id on success
+ */
+int pdf_add_link(struct pdf_doc *pdf, struct pdf_object *page, float x,
+ float y, float width, float height,
+ struct pdf_object *target_page, float target_x,
+ float target_y);
+
+/**
+ * List of different barcode encodings that are supported
+ */
+enum {
+ PDF_BARCODE_128A, //!< Produce code-128A style barcodes
+ PDF_BARCODE_39, //!< Produce code-39 style barcodes
+ PDF_BARCODE_EAN13, //!< Produce EAN-13 style barcodes
+ PDF_BARCODE_UPCA, //!< Produce UPC-A style barcodes
+ PDF_BARCODE_EAN8, //!< Produce EAN-8 style barcodes
+ PDF_BARCODE_UPCE, //!< Produce UPC-E style barcodes
+};
+
+/**
+ * Add a barcode to the document
+ * @param pdf PDF document to add barcode to
+ * @param page Page to add barcode to (NULL => most recently added page)
+ * @param code Type of barcode to add (PDF_BARCODE_xxx)
+ * @param x X offset to put barcode at
+ * @param y Y offset to put barcode at
+ * @param width Width of barcode
+ * @param height Height of barcode
+ * @param string Barcode contents
+ * @param colour Colour to draw barcode
+ * @return < 0 on failure, >= 0 on success
+ */
+int pdf_add_barcode(struct pdf_doc *pdf, struct pdf_object *page, int code,
+ float x, float y, float width, float height,
+ const char *string, uint32_t colour);
+
+/**
+ * Add image data as an image to the document.
+ * Image data must be one of: JPEG, PNG, PPM, PGM or BMP formats
+ * Passing 0 for either the display width or height will
+ * include the image but not render it visible.
+ * Passing a negative number either the display height or width will
+ * have the image be resized while keeping the original aspect ratio.
+ * @param pdf PDF document to add image to
+ * @param page Page to add image to (NULL => most recently added page)
+ * @param x X offset to put image at
+ * @param y Y offset to put image at
+ * @param display_width Displayed width of image
+ * @param display_height Displayed height of image
+ * @param data Image data bytes
+ * @param len Length of data
+ * @return < 0 on failure, >= 0 on success
+ */
+int pdf_add_image_data(struct pdf_doc *pdf, struct pdf_object *page, float x,
+ float y, float display_width, float display_height,
+ const uint8_t *data, size_t len);
+
+/**
+ * Add a raw 24 bit per pixel RGB buffer as an image to the document
+ * Passing 0 for either the display width or height will
+ * include the image but not render it visible.
+ * Passing a negative number either the display height or width will
+ * have the image be resized while keeping the original aspect ratio.
+ * @param pdf PDF document to add image to
+ * @param page Page to add image to (NULL => most recently added page)
+ * @param x X offset to put image at
+ * @param y Y offset to put image at
+ * @param display_width Displayed width of image
+ * @param display_height Displayed height of image
+ * @param data RGB data to add
+ * @param width width of image in pixels
+ * @param height height of image in pixels
+ * @return < 0 on failure, >= 0 on success
+ */
+int pdf_add_rgb24(struct pdf_doc *pdf, struct pdf_object *page, float x,
+ float y, float display_width, float display_height,
+ const uint8_t *data, uint32_t width, uint32_t height);
+
+/**
+ * Add a raw 8 bit per pixel grayscale buffer as an image to the document
+ * @param pdf PDF document to add image to
+ * @param page Page to add image to (NULL => most recently added page)
+ * @param x X offset to put image at
+ * @param y Y offset to put image at
+ * @param display_width Displayed width of image
+ * @param display_height Displayed height of image
+ * @param data grayscale pixel data to add
+ * @param width width of image in pixels
+ * @param height height of image in pixels
+ * @return < 0 on failure, >= 0 on success
+ */
+int pdf_add_grayscale8(struct pdf_doc *pdf, struct pdf_object *page, float x,
+ float y, float display_width, float display_height,
+ const uint8_t *data, uint32_t width, uint32_t height);
+
+/**
+ * Add an image file as an image to the document.
+ * Passing 0 for either the display width or height will
+ * include the image but not render it visible.
+ * Passing a negative number either the display height or width will
+ * have the image be resized while keeping the original aspect ratio.
+ * Supports image formats: JPEG, PNG, PPM, PGM & BMP
+ * @param pdf PDF document to add bookmark to
+ * @param page Page to add image to (NULL => most recently added page)
+ * @param x X offset to put image at
+ * @param y Y offset to put image at
+ * @param display_width Displayed width of image
+ * @param display_height Displayed height of image
+ * @param image_filename Filename of image file to display
+ * @return < 0 on failure, >= 0 on success
+ */
+int pdf_add_image_file(struct pdf_doc *pdf, struct pdf_object *page, float x,
+ float y, float display_width, float display_height,
+ const char *image_filename);
+
+/**
+ * Parse image data to determine the image type & metadata
+ * @param info structure to hold the parsed metadata
+ * @param data image data to parse
+ * @param length number of bytes in data
+ * @param err_msg area to put any failure details
+ * @param err_msg_length maximum number of bytes to store in err_msg
+ * @return < 0 on failure, >= 0 on success
+ */
+int pdf_parse_image_header(struct pdf_img_info *info, const uint8_t *data,
+ size_t length, char *err_msg,
+ size_t err_msg_length);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // PDFGEN_H
diff --git a/src/conf.c b/src/conf.c
new file mode 100644
index 0000000..13deffa
--- /dev/null
+++ b/src/conf.c
@@ -0,0 +1,620 @@
+/*
+ * conf.c: functions that handle the nwipe.conf configuration file
+ * and the creation of the nwipe_customers.csv file. nwipe.conf uses
+ * libconfig format, while nwipe_customers.csv uses comma separted
+ * values. CSV is used so that the user can build there own customer
+ * listing using spreadsheets rather than enter all the customer
+ * information via the nwipe GUI interface.
+ *
+ * Copyright PartialVolume <https://github.com/PartialVolume>.
+ *
+ * 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, version 2.
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ *
+ *
+ */
+
+#include <libconfig.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include "nwipe.h"
+#include "context.h"
+#include "logging.h"
+#include "method.h"
+#include "options.h"
+#include "conf.h"
+
+config_t nwipe_cfg;
+config_setting_t *nwipe_conf_setting, *group_organisation, *root, *group, *previous_group, *setting;
+const char* nwipe_conf_str;
+char nwipe_config_directory[] = "/etc/nwipe";
+char nwipe_config_file[] = "/etc/nwipe/nwipe.conf";
+char nwipe_customers_file[] = "/etc/nwipe/nwipe_customers.csv";
+char nwipe_customers_file_backup[] = "/etc/nwipe/nwipe_customers.csv.backup";
+char nwipe_customers_file_backup_tmp[] = "/etc/nwipe/nwipe_customers.csv.backup.tmp";
+
+/*
+ * Checks for the existence of nwipe.conf and nwipe_customers.csv
+ * If either one does not exist, they are created and formatted
+ * with a basic configuration.
+ */
+
+int nwipe_conf_init()
+{
+ FILE* fp_config;
+ FILE* fp_customers;
+
+ config_init( &nwipe_cfg );
+ char nwipe_customers_initial_content[] =
+ "\"Customer Name\";\"Contact Name\";\"Customer Address\";\"Contact Phone\"\n"
+ "\"Not Applicable\";\"Not Applicable\";\"Not Applicable\";\"Not Applicable\"\n";
+
+ /* Read /etc/nwipe/nwipe.conf. If there is an error, determine whether
+ * it's because it doesn't exist. If it doesn't exist create it and
+ * populate it with a basic structure.
+ */
+
+ /* Does the /etc/nwipe/nwipe.conf file exist? If not, then create it */
+ if( access( nwipe_config_file, F_OK ) == 0 )
+ {
+ nwipe_log( NWIPE_LOG_INFO, "Nwipes config file %s exists", nwipe_config_file );
+
+ /* Read the nwipe.conf configuration file and report any errors */
+
+ nwipe_log( NWIPE_LOG_INFO, "Reading nwipe's config file %s", nwipe_config_file );
+ if( !config_read_file( &nwipe_cfg, nwipe_config_file ) )
+ {
+ nwipe_log( NWIPE_LOG_ERROR,
+ "Syntax error: %s:%d - %s\n",
+ config_error_file( &nwipe_cfg ),
+ config_error_line( &nwipe_cfg ),
+ config_error_text( &nwipe_cfg ) );
+ }
+ }
+ else
+ {
+ nwipe_log( NWIPE_LOG_WARNING, "%s does not exist", nwipe_config_file );
+
+ /* We assume the /etc/nwipe directory doesn't exist, so try to create it */
+ mkdir( nwipe_config_directory, 0755 );
+
+ /* create the nwipe.conf file */
+ if( !( fp_config = fopen( nwipe_config_file, "w" ) ) )
+ {
+ nwipe_log( NWIPE_LOG_ERROR, "Failed to create %s", nwipe_config_file );
+ }
+ else
+ {
+ nwipe_log( NWIPE_LOG_INFO, "Created %s", nwipe_config_file );
+ fclose( fp_config );
+ }
+ }
+
+ root = config_root_setting( &nwipe_cfg );
+
+ /**
+ * If they don't already exist, populate nwipe.conf with groups, settings and values.
+ * This will also fill in missing group or settings if they have been corrupted or
+ * accidently deleted by the user. It will also update an existing nwipe.conf
+ * file as new groups and settings are added to nwipe. If new settings are added
+ * to nwipes conf file they MUST appear below in this list of groups and settings.
+ */
+
+ nwipe_conf_populate( "Organisation_Details.Business_Name", "Not Applicable (BN)" );
+ nwipe_conf_populate( "Organisation_Details.Business_Address", "Not Applicable (BA)" );
+ nwipe_conf_populate( "Organisation_Details.Contact_Name", "Not Applicable (BCN)" );
+ nwipe_conf_populate( "Organisation_Details.Contact_Phone", "Not Applicable (BCP)" );
+ nwipe_conf_populate( "Organisation_Details.Op_Tech_Name", "Not Applicable (OTN)" );
+
+ /**
+ * Add PDF Certificate/Report settings
+ */
+ nwipe_conf_populate( "PDF_Certificate.PDF_Enable", "ENABLED" );
+ nwipe_conf_populate( "PDF_Certificate.PDF_Preview", "DISABLED" );
+
+ /**
+ * The currently selected customer that will be printed on the report
+ */
+ nwipe_conf_populate( "Selected_Customer.Customer_Name", "Not Applicable (CN)" );
+ nwipe_conf_populate( "Selected_Customer.Customer_Address", "Not Applicable (CA)" );
+ nwipe_conf_populate( "Selected_Customer.Contact_Name", "Not Applicable (CCN)" );
+ nwipe_conf_populate( "Selected_Customer.Contact_Phone", "Not Applicable (CP)" );
+
+ /**
+ * Write out the new configuration.
+ */
+ if( !config_write_file( &nwipe_cfg, nwipe_config_file ) )
+ {
+ nwipe_log( NWIPE_LOG_ERROR, "Failed to write nwipe config to %s", nwipe_config_file );
+ }
+ else
+ {
+ nwipe_log( NWIPE_LOG_INFO, "Sucessfully written nwipe config to %s", nwipe_config_file );
+ }
+
+ /* Read the nwipe.conf configuration file and report any errors */
+ if( !config_read_file( &nwipe_cfg, nwipe_config_file ) )
+ {
+ nwipe_log( NWIPE_LOG_ERROR,
+ "Syntax error: %s:%d - %s\n",
+ config_error_file( &nwipe_cfg ),
+ config_error_line( &nwipe_cfg ),
+ config_error_text( &nwipe_cfg ) );
+ }
+
+ /* -----------------------------------------------------------------------------
+ * Now check nwipe_customers.csv exists, if not create it with a basic structure
+ */
+ if( access( nwipe_customers_file, F_OK ) == 0 )
+ {
+ nwipe_log( NWIPE_LOG_INFO, "Nwipes customer file %s exists", nwipe_customers_file );
+ }
+ else
+ {
+ nwipe_log( NWIPE_LOG_WARNING, "%s does not exist", nwipe_customers_file );
+
+ /* We assume the /etc/nwipe directory doesn't exist, so try to create it */
+ mkdir( nwipe_config_directory, 0755 );
+
+ /* create the nwipe_customers.csv file */
+ if( !( fp_customers = fopen( nwipe_customers_file, "w" ) ) )
+ {
+ nwipe_log( NWIPE_LOG_ERROR, "Failed to create %s", nwipe_customers_file );
+ }
+ else
+ {
+ nwipe_log( NWIPE_LOG_INFO, "Created %s", nwipe_customers_file );
+
+ /* Now populate the csv header and first entry, Lines 1 and 2 */
+ if( fwrite( nwipe_customers_initial_content, sizeof( nwipe_customers_initial_content ), 1, fp_customers )
+ == 1 )
+ {
+ nwipe_log( NWIPE_LOG_INFO, "Populated %s with basic config", nwipe_customers_file );
+ }
+ else
+ {
+ nwipe_log( NWIPE_LOG_ERROR, "Failed to write basic config to %s", nwipe_customers_file );
+ }
+ fclose( fp_customers );
+ }
+ }
+ return ( 0 );
+}
+
+void save_selected_customer( char** customer )
+{
+ /* This function saves the user selected customer
+ * to nwipe's config file /etc/nwipe/nwipe.conf
+ * for later use by the PDF creation functions.
+ */
+
+ int idx;
+ int field_count;
+ int field_idx;
+
+ char field_1[FIELD_LENGTH];
+ char field_2[FIELD_LENGTH];
+ char field_3[FIELD_LENGTH];
+ char field_4[FIELD_LENGTH];
+
+ /* zero the field strings */
+ for( idx = 0; idx < FIELD_LENGTH; idx++ )
+ field_1[idx] = 0;
+ for( idx = 0; idx < FIELD_LENGTH; idx++ )
+ field_2[idx] = 0;
+ for( idx = 0; idx < FIELD_LENGTH; idx++ )
+ field_3[idx] = 0;
+ for( idx = 0; idx < FIELD_LENGTH; idx++ )
+ field_4[idx] = 0;
+
+ /* Extract the field contents from the csv string
+ */
+ idx = 0;
+ field_idx = 0;
+ field_count = 1;
+
+ while( *( *customer + idx ) != 0 && field_count < NUMBER_OF_FIELDS + 1 )
+ {
+ /* Start of a field? */
+ if( *( *customer + idx ) == '\"' )
+ {
+ idx++;
+
+ while( *( *customer + idx ) != '\"' && *( *customer + idx ) != 0 )
+ {
+ if( field_count == 1 && field_idx < ( FIELD_LENGTH - 1 ) )
+ {
+ field_1[field_idx++] = *( *customer + idx );
+ }
+ else
+ {
+ if( field_count == 2 && field_idx < ( FIELD_LENGTH - 1 ) )
+ {
+ field_2[field_idx++] = *( *customer + idx );
+ }
+ else
+ {
+ if( field_count == 3 && field_idx < ( FIELD_LENGTH - 1 ) )
+ {
+ field_3[field_idx++] = *( *customer + idx );
+ }
+ else
+ {
+ if( field_count == 4 && field_idx < ( FIELD_LENGTH - 1 ) )
+ {
+ field_4[field_idx++] = *( *customer + idx );
+ }
+ }
+ }
+ }
+ idx++;
+ }
+ if( *( *customer + idx ) == '\"' )
+ {
+ /* Makesure the field string is terminated */
+ switch( field_count )
+ {
+ case 1:
+ field_1[field_idx] = 0;
+ break;
+ case 2:
+ field_2[field_idx] = 0;
+ break;
+ case 3:
+ field_3[field_idx] = 0;
+ break;
+ case 4:
+ field_4[field_idx] = 0;
+ break;
+ }
+
+ field_count++;
+ field_idx = 0;
+ }
+ }
+ idx++;
+ }
+
+ /* All 4 fields present? */
+ if( field_count != NUMBER_OF_FIELDS + 1 )
+ {
+ nwipe_log( NWIPE_LOG_ERROR,
+ "Insuffient fields in customer entry, expected %i, actual(field_count) %i, idx=%i",
+ NUMBER_OF_FIELDS,
+ field_count,
+ idx );
+ return;
+ }
+
+ /* -------------------------------------------------------------
+ * Write the fields to nwipe's config file /etc/nwipe/nwipe.conf
+ */
+ if( ( setting = config_lookup( &nwipe_cfg, "Selected_Customer.Customer_Name" ) ) )
+ {
+ config_setting_set_string( setting, field_1 );
+ }
+ else
+ {
+ nwipe_log( NWIPE_LOG_ERROR, "Can't find \"Selected Customers.Customer_Name\" in %s", nwipe_config_file );
+ }
+
+ if( ( setting = config_lookup( &nwipe_cfg, "Selected_Customer.Customer_Address" ) ) )
+ {
+ config_setting_set_string( setting, field_2 );
+ }
+ else
+ {
+ nwipe_log( NWIPE_LOG_ERROR, "Can't find \"Selected Customers.Customer_Address\" in %s", nwipe_config_file );
+ }
+
+ if( ( setting = config_lookup( &nwipe_cfg, "Selected_Customer.Contact_Name" ) ) )
+ {
+ config_setting_set_string( setting, field_3 );
+ }
+ else
+ {
+ nwipe_log( NWIPE_LOG_ERROR, "Can't find \"Selected Customers.Contact_Name\" in %s", nwipe_config_file );
+ }
+
+ if( ( setting = config_lookup( &nwipe_cfg, "Selected_Customer.Contact_Phone" ) ) )
+ {
+ config_setting_set_string( setting, field_4 );
+ }
+ else
+ {
+ nwipe_log( NWIPE_LOG_ERROR, "Can't find \"Selected Customers.Contact_Phone\" in %s", nwipe_config_file );
+ }
+
+ /* Write out the new configuration. */
+ if( !config_write_file( &nwipe_cfg, nwipe_config_file ) )
+ {
+ nwipe_log( NWIPE_LOG_ERROR, "Failed to write user selected customer to %s", nwipe_config_file );
+ }
+ else
+ {
+ nwipe_log( NWIPE_LOG_INFO, "Populated %s with user selected customer", nwipe_config_file );
+ }
+}
+
+int nwipe_conf_update_setting( char* group_name_setting_name, char* setting_value )
+{
+ /* You would call this function of you wanted to update an existing setting in nwipe.conf, i.e
+ *
+ * nwipe_conf_update_setting( "PDF_Certificate.PDF_Enable", "ENABLED" )
+ *
+ * It is NOT used for creating a new group or setting name.
+ */
+
+ /* -------------------------------------------------------------
+ * Write the field to nwipe's config file /etc/nwipe/nwipe.conf
+ */
+ if( ( setting = config_lookup( &nwipe_cfg, group_name_setting_name ) ) )
+ {
+ config_setting_set_string( setting, setting_value );
+ }
+ else
+ {
+ nwipe_log(
+ NWIPE_LOG_ERROR, "Can't find group.setting_name %s in %s", group_name_setting_name, nwipe_config_file );
+ return 1;
+ }
+
+ /* Write the new configuration to nwipe.conf
+ */
+ if( !config_write_file( &nwipe_cfg, nwipe_config_file ) )
+ {
+ nwipe_log( NWIPE_LOG_ERROR, "Failed to write %s to %s", group_name_setting_name, nwipe_config_file );
+ return 2;
+ }
+ else
+ {
+ nwipe_log( NWIPE_LOG_INFO,
+ "Updated %s with value %s in %s",
+ group_name_setting_name,
+ setting_value,
+ nwipe_config_file );
+ }
+
+ return 0;
+
+} // end nwipe_conf_update_setting()
+
+int nwipe_conf_read_setting( char* group_name_setting_name, const char** setting_value )
+{
+ /* You would call this function if you wanted to read a settings value in nwipe.conf, i.e
+ *
+ * const char ** pReturnString;
+ * nwipe_conf_read_setting( "PDF_Certificate", "PDF_Enable", pReturnString );
+ *
+ */
+
+ /* Separate group_name_setting_name i.e "PDF_Certificate.PDF_Enable" string
+ * into two separate strings by replacing the period with a NULL.
+ */
+
+ int return_status;
+ int length = strlen( group_name_setting_name );
+
+ char* group_name = malloc( length );
+ char* setting_name = malloc( length );
+
+ int idx = 0;
+
+ while( group_name_setting_name[idx] != 0 && group_name_setting_name[idx] != '.' )
+ {
+ if( group_name_setting_name[idx] == '.' )
+ {
+ break;
+ }
+ idx++;
+ }
+ memcpy( group_name, group_name_setting_name, idx );
+ strcpy( setting_name, &group_name_setting_name[idx + 1] );
+
+ if( !( setting = config_lookup( &nwipe_cfg, group_name ) ) )
+ {
+ nwipe_log( NWIPE_LOG_ERROR, "Can't find group name %s.%s in %s", group_name, setting_name, nwipe_config_file );
+ return_status = -1;
+ }
+ else
+ {
+ /* Retrieve data from nwipe.conf */
+ if( CONFIG_TRUE == config_setting_lookup_string( setting, setting_name, setting_value ) )
+ {
+ return_status = 0; /* Success */
+ }
+ else
+ {
+ nwipe_log(
+ NWIPE_LOG_ERROR, "Can't find setting_name %s.%s in %s", group_name, setting_name, nwipe_config_file );
+ return_status = -2;
+ }
+ }
+
+ free( group_name );
+ free( setting_name );
+ return ( return_status );
+
+} // end nwipe_conf_read_setting()
+
+int nwipe_conf_populate( char* path, char* value )
+{
+ /* This function will check that a path containing a group or multiple groups that lead to a setting all exist,
+ * if they don't exist, the group/s, settings and associated value are created.
+ *
+ * The path, a string consisting of one or more groups separted by a period symbol
+ * '.' with the final element in the path being the setting name. For instance the path might be
+ * PDF_Certificate.PDF_Enable. Where PDF_Certificate is the group name and PDF_Enable is the setting name.
+ * If one or other don't exist then they will be created.
+ *
+ * An arbitary depth of four groups are allowed for nwipe's configuration file, although we only currently, as of
+ * October 2023 use a depth of one group. The number of groups can be increased in the future if required by
+ * changing the definition MAX_GROUP_DEPTH in conf.h
+ */
+
+ char* path_copy;
+ char* path_build;
+
+ char* group_start[MAX_GROUP_DEPTH + 2]; // A pointer to the start of each group string
+ char* setting_start;
+
+ int idx; // General index
+ int group_count; // Counts the number of groups in the path
+
+ /* Initialise the pointers */
+ memset( group_start, 0, MAX_GROUP_DEPTH + 2 );
+ memset( &setting_start, 0, 1 );
+
+ /* Allocate enough memory for a copy of the path and initialise to zero */
+ path_copy = calloc( strlen( path ) + 1, sizeof( char ) );
+ path_build = calloc( strlen( path ) + 1, sizeof( char ) );
+
+ /* Duplicate the path */
+ strcpy( path_copy, path );
+
+ /* Create individual strings by replacing the period '.' with NULL, counting the number of groups. */
+ idx = 0;
+ group_count = 0;
+
+ /* pointer to first group */
+ group_start[group_count] = path_copy;
+
+ while( *( path_copy + idx ) != 0 )
+ {
+ if( group_count > MAX_GROUP_DEPTH )
+ {
+ nwipe_log( NWIPE_LOG_ERROR,
+ "Too many groups in path, specified = %i, allowed = %i ",
+ group_count,
+ MAX_GROUP_DEPTH );
+ return 1;
+ }
+
+ if( *( path_copy + idx ) == '.' )
+ {
+ *( path_copy + idx ) = 0;
+ group_count++;
+ group_start[group_count] = path_copy + idx + 1;
+ }
+ idx++;
+ }
+
+ /* setting_start points to a string that represents the setting to be created */
+ setting_start = group_start[group_count];
+
+ /* Remove the last entry from group_start as that would be the setting and not a group */
+ group_start[group_count] = 0;
+
+ if( group_count == 0 )
+ {
+ nwipe_log( NWIPE_LOG_ERROR, "No groups specified in path, i.e. no period . separator found." );
+ return 2;
+ }
+
+ /**
+ * Now determine whether the group/s in the path exist, if not create them.
+ */
+
+ idx = 0;
+ while( group_start[idx] != 0 )
+ {
+ strcat( path_build, group_start[idx] );
+
+ if( !( group = config_lookup( &nwipe_cfg, path_build ) ) )
+ {
+ if( idx == 0 )
+ {
+ group = config_setting_add( root, path_build, CONFIG_TYPE_GROUP );
+ previous_group = group;
+ }
+ else
+ {
+ group = config_setting_add( previous_group, group_start[idx], CONFIG_TYPE_GROUP );
+ previous_group = group;
+ }
+ if( group )
+ {
+ nwipe_log( NWIPE_LOG_INFO, "Created group [%s] in %s", path_build, nwipe_config_file );
+ }
+ else
+ {
+ nwipe_log( NWIPE_LOG_ERROR, "Failed to create group [%s] in %s", path_build, nwipe_config_file );
+ }
+ }
+ else
+ {
+ previous_group = group;
+ }
+
+ idx++;
+
+ if( group_start[idx] != 0 )
+ {
+ strcat( path_build, "." );
+ }
+ }
+
+ /**
+ * And finally determine whether the setting already exists, if not create it and assign the value to it
+ */
+
+ /* Does the full path exist ? i.e AAA.BBB */
+ if( ( group = config_lookup( &nwipe_cfg, path_build ) ) )
+ {
+ /* Does the path and setting exist? AAA.BBB.SETTING_NAME */
+ if( !( setting = config_lookup( &nwipe_cfg, path ) ) )
+ {
+ /* Add the new SETTING_NAME */
+ if( ( setting = config_setting_add( group, setting_start, CONFIG_TYPE_STRING ) ) )
+ {
+ nwipe_log( NWIPE_LOG_INFO, "Created setting name %s in %s", path, nwipe_config_file );
+ }
+ else
+ {
+ nwipe_log(
+ NWIPE_LOG_ERROR, "Failed to create setting name %s in %s", setting_start, nwipe_config_file );
+ }
+
+ if( config_setting_set_string( setting, value ) == CONFIG_TRUE )
+ {
+ nwipe_log( NWIPE_LOG_INFO, "Set value for %s in %s to %s", path, nwipe_config_file, value );
+ }
+ else
+ {
+ nwipe_log( NWIPE_LOG_ERROR, "Failed to set value for %s in %s to %s", path, nwipe_config_file, value );
+ }
+ }
+ else
+ {
+ if( nwipe_options.verbose )
+ {
+ nwipe_log( NWIPE_LOG_INFO, "Setting already exists [%s] in %s", path, nwipe_config_file );
+ }
+ }
+ }
+ else
+ {
+ nwipe_log( NWIPE_LOG_INFO, "Couldn't find constructed path [%s] in %s", path_build, nwipe_config_file );
+ }
+
+ free( path_copy );
+ free( path_build );
+
+ return 0;
+}
+
+void nwipe_conf_close()
+{
+ config_destroy( &nwipe_cfg );
+}
diff --git a/src/conf.h b/src/conf.h
new file mode 100644
index 0000000..4fce884
--- /dev/null
+++ b/src/conf.h
@@ -0,0 +1,55 @@
+#ifndef CONF_H_
+#define CONF_H_
+
+/**
+ * Initialises the libconfig code, called once at the
+ * start of nwipe, prior to any attempts to access
+ * nwipe's config file /etc/nwipe/nwipe.conf
+ * @param none
+ * @return int
+ * 0 = success
+ * -1 = error
+ */
+int nwipe_conf_init();
+
+/**
+ * Before exiting nwipe, this function should be called
+ * to free up libconfig's memory usage
+ * @param none
+ * @return void
+ */
+void nwipe_conf_close();
+
+void save_selected_customer( char** );
+
+/**
+ * int nwipe_conf_update_setting( char *, char * );
+ * Use this function to update a setting in nwipe.conf
+ * @param char * this is the group name and setting name separated by a period '.'
+ * i.e "PDF_Certificate.PDF_Enable"
+ * @param char * this is the setting, i.e ENABLED
+ * @return int 0 = Success
+ * 1 = Unable to update memory copy
+ * 2 = Unable to write new configuration to /etc/nwipe/nwipe.conf
+ */
+int nwipe_conf_update_setting( char*, char* );
+
+/**
+ * int nwipe_conf_read_setting( char *, char *, const char ** )
+ * Use this function to read a setting value in nwipe.conf
+ * @param char * this is the group name
+ * @param char * this is the setting name
+ * @param char ** this is a pointer to the setting value
+ * @return int 0 = Success
+ * -1 = Unable to find the specified group name
+ * -2 = Unable to find the specified setting name
+ */
+int nwipe_conf_read_setting( char*, const char** );
+
+int nwipe_conf_populate( char* path, char* value );
+
+#define FIELD_LENGTH 256
+#define NUMBER_OF_FIELDS 4
+#define MAX_GROUP_DEPTH 4
+
+#endif /* CONF_H_ */
diff --git a/src/context.h b/src/context.h
new file mode 100644
index 0000000..80edf6e
--- /dev/null
+++ b/src/context.h
@@ -0,0 +1,228 @@
+/*
+ * context.h: The internal state representation of nwipe.
+ *
+ * Copyright Darik Horn <dajhorn-dban@vanadac.com>.
+ *
+ * Modifications to original dwipe Copyright Andy Beverley <andy@andybev.com>
+ *
+ * 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, version 2.
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#ifndef CONTEXT_H_
+#define CONTEXT_H_
+
+#include "prng.h"
+#ifndef __HDDTEMP_H__
+#include "hddtemp_scsi/hddtemp.h"
+#endif /* __HDDTEMP_H__ */
+
+typedef enum nwipe_device_t_ {
+ NWIPE_DEVICE_UNKNOWN = 0, // Unknown device.
+ NWIPE_DEVICE_IDE,
+ NWIPE_DEVICE_SCSI,
+ NWIPE_DEVICE_COMPAQ, // Unimplemented.
+ NWIPE_DEVICE_USB,
+ NWIPE_DEVICE_IEEE1394, // Unimplemented.
+ NWIPE_DEVICE_ATA,
+ NWIPE_DEVICE_NVME,
+ NWIPE_DEVICE_VIRT,
+ NWIPE_DEVICE_SAS,
+ NWIPE_DEVICE_MMC
+} nwipe_device_t;
+
+typedef enum nwipe_pass_t_ {
+ NWIPE_PASS_NONE = 0, // Not running.
+ NWIPE_PASS_WRITE, // Writing patterns to the device.
+ NWIPE_PASS_VERIFY, // Verifying a pass.
+ NWIPE_PASS_FINAL_BLANK, // Filling the device with zeros.
+ NWIPE_PASS_FINAL_OPS2 // Special case for nwipe_ops2.
+} nwipe_pass_t;
+
+typedef enum nwipe_select_t_ {
+ NWIPE_SELECT_NONE = 0, // Unused.
+ NWIPE_SELECT_TRUE, // Wipe this device.
+ NWIPE_SELECT_TRUE_PARENT, // A parent of this device has been selected, so the wipe is implied.
+ NWIPE_SELECT_FALSE, // Do not wipe this device.
+ NWIPE_SELECT_FALSE_CHILD, // A child of this device has been selected, so we can't wipe this device.
+ NWIPE_SELECT_DISABLED // Do not wipe this device and do not allow it to be selected.
+} nwipe_select_t;
+
+#define NWIPE_KNOB_SPEEDRING_SIZE 30
+#define NWIPE_KNOB_SPEEDRING_GRANULARITY 10
+
+typedef struct nwipe_speedring_t_
+{
+ u64 bytes[NWIPE_KNOB_SPEEDRING_SIZE];
+ u64 bytestotal;
+ u64 byteslast;
+ time_t times[NWIPE_KNOB_SPEEDRING_SIZE];
+ time_t timestotal;
+ time_t timeslast;
+ u32 position;
+} nwipe_speedring_t;
+
+#define NWIPE_DEVICE_LABEL_LENGTH 200
+#define NWIPE_DEVICE_SIZE_TXT_LENGTH 8
+
+// Arbitrary length, so far most paths don't exceed about 25 characters
+#define MAX_HWMON_PATH_LENGTH 100
+
+// 20 chracters for serial number plus null Byte
+#define NWIPE_SERIALNUMBER_LENGTH 20
+
+typedef struct nwipe_context_t_
+{
+ /*
+ * Device fields
+ */
+ int device_block_size; // The soft block size reported by the device, as logical
+ int device_sector_size; // The logical sector size reported by libparted
+ int device_phys_sector_size; // The physical sector size reported by libparted
+ int device_bus; // The device bus number.
+ int device_fd; // The file descriptor of the device file being wiped.
+ int device_host; // The host number.
+ struct hd_driveid device_id; // The WIN_IDENTIFY data for IDE drives.
+ int device_lun; // The device logical unit number.
+ int device_major; // The major device number.
+ int device_minor; // The minor device number.
+ int device_part; // The device partition or slice number.
+ char* device_name; // The device file name.
+ char device_name_without_path[100];
+ char gui_device_name[100];
+ unsigned long long device_size; // The device size in bytes.
+ u64 device_size_in_sectors; // The device size in number of logical sectors, this may be 512 or 4096 sectors
+ u64 device_size_in_512byte_sectors; // The device size in number of 512byte sectors, irrespective of logical sector
+ // size reported by libata
+ unsigned long long bytes_erased; // Irrespective of pass, this how much of the drive has been erased, CANNOT be
+ // greater than device_size.
+ char* device_size_text; // The device size in a more (human)readable format.
+ char device_size_txt[NWIPE_DEVICE_SIZE_TXT_LENGTH]; // The device size in a more (human)readable format.
+ char* device_model; // The model of the device.
+ char device_label[NWIPE_DEVICE_LABEL_LENGTH]; // The label (name, model, size and serial) of the device.
+ struct stat device_stat; // The device file state from fstat().
+ nwipe_device_t device_type; // Indicates an IDE, SCSI, or Compaq SMART device in enumerated form (int)
+ char device_type_str[14]; // Indicates an IDE, SCSI, USB etc as per nwipe_device_t but in ascii
+ int device_is_ssd; // 0 = no SSD, 1 = is a SSD
+ char device_serial_no[NWIPE_SERIALNUMBER_LENGTH
+ + 1]; // Serial number(processed, 20 characters plus null termination) of the device.
+ int device_target; // The device target.
+
+ u64 eta; // The estimated number of seconds until method completion.
+ int entropy_fd; // The entropy source. Usually /dev/urandom.
+ int pass_count; // The number of passes performed by the working wipe method.
+ u64 pass_done; // The number of bytes that have already been i/o'd in this pass.
+ u64 pass_errors; // The number of errors across all passes.
+ u64 pass_size; // The total number of i/o bytes across all passes.
+ nwipe_pass_t pass_type; // The type of the current working pass.
+ int pass_working; // The current working pass.
+ nwipe_prng_t* prng; // The PRNG implementation.
+ nwipe_entropy_t prng_seed; // The random data that is used to seed the PRNG.
+ void* prng_state; // The private internal state of the PRNG.
+ int result; // The process return value.
+ int round_count; // The number of rounds requested by the user for the working wipe method.
+ u64 round_done; // The number of bytes that have already been i/o'd.
+ u64 round_errors; // The number of errors across all rounds.
+ u64 round_size; // The total number of i/o bytes across all rounds.
+ double round_percent; // The percentage complete across all rounds.
+ int round_working; // The current working round.
+ nwipe_select_t select; // Indicates whether this device should be wiped.
+ int signal; // Set when the child is killed by a signal.
+ nwipe_speedring_t speedring; // Ring buffer for computing the rolling throughput average.
+ short sync_status; // A flag to indicate when the method is syncing.
+ pthread_t thread; // The ID of the thread.
+ u64 throughput; // Average throughput in bytes per second.
+ char throughput_txt[13]; // Human readable throughput.
+ u64 verify_errors; // The number of verification errors across all passes.
+ int templ_has_hwmon_data; // 0 = no hwmon data available, 1 = hwmon data available
+ int templ_has_scsitemp_data; // 0 = no scsitemp data available, 1 = scsitemp data available
+ char temp1_path[MAX_HWMON_PATH_LENGTH]; // path to temperature variables /sys/class/hwmon/hwmonX/ etc.
+ int temp1_crit; // Critical high drive temperature, 1000000=unitialised, millidegree celsius.
+ int temp1_highest; // Historical highest temperature reached, 1000000=unitialised, millidegree celsius.
+ int temp1_input; // drive temperature, -1=unitialised. 1000000=unitialised, millidegree celsius.
+ int temp1_lcrit; // Critical low drive temperature, 1000000=unitialised, millidegree celsius.
+ int temp1_lowest; // Historically lowest temperature, 1000000=unitialised, millidegree celsius.
+ int temp1_max; // Maximum allowed temperature, 1000000=unitialised, millidegree celsius.
+ int temp1_min; // Minimum allowed temperature, 1000000=unitialised, millidegree celsius.
+ int temp1_monitored_wipe_max;
+ int temp1_monitored_wipe_min;
+ int temp1_monitored_wipe_avg;
+ int temp1_flash_rate; // number relates to one tenth of a second, so 2 means a flash on and off = 0.4s
+ int temp1_flash_rate_counter; // used by the gui for timing the flash rate
+ int temp1_flash_rate_status; // 0=blank 1=visible
+ time_t temp1_time; // The time when temperature was last checked, seconds since epoch
+ struct disk* templ_disk; // Pointer to disk structure for hddtemp SCSI routines
+ int wipe_status; // Wipe finished = 0, wipe in progress = 1, wipe yet to start = -1.
+ char wipe_status_txt[10]; // ERASED, FAILED, ABORTED, INSANITY
+ int spinner_idx; // Index into the spinner character array
+ char spinner_character[1]; // The current spinner character
+ double duration; // Duration of the wipe in seconds
+ char duration_str[20]; // The duration string in hh:mm:ss
+ time_t start_time; // Start time of wipe
+ time_t end_time; // End time of wipe
+ u64 fsyncdata_errors; // The number of fsyncdata errors across all passes.
+ char PDF_filename[FILENAME_MAX]; // The filename of the PDF certificate/report.
+ int HPA_status; // 0 = No HPA found/disabled, 1 = HPA detected, 2 = Unknown, unable to checked,
+ // 3 = Not applicable to this device
+ u64 HPA_reported_set; // the 'HPA set' value reported hdparm -N, i.e the first value of n/n
+ u64 HPA_reported_real; // the 'HPA real' value reported hdparm -N, i.e the second value of n/n
+ int DCO_status; // 0 = No DCO found, 1 = DCO detected, 2 = Unknown, unable to checked
+ u64 DCO_reported_real_max_sectors; // real max sectors as reported by hdparm --dco-identify
+ u64 DCO_reported_real_max_size; // real max sectors in bytes
+ u64 Calculated_real_max_size_in_bytes; // This value is determined from all the possible variations for drives that
+ // don't support DCO/HPA and those that do. Also drives that can't provide
+ // HPA/DCO due to the chips they use (USB adapters)
+ char DCO_reported_real_max_size_text[NWIPE_DEVICE_SIZE_TXT_LENGTH]; // real max size in human readable form i.e 1TB
+ char Calculated_real_max_size_in_bytes_text[NWIPE_DEVICE_SIZE_TXT_LENGTH]; // calculated real max human readable
+ u64 HPA_sectors; // The size of the host protected area in sectors
+ char HPA_size_text[NWIPE_DEVICE_SIZE_TXT_LENGTH]; // Human readable size bytes, KB, MB, GB ..
+ int HPA_display_toggle_state; // 0 or 1 Used to toggle between "[1TB] [ 33C]" and [HDA STATUS]
+ time_t HPA_toggle_time; // records a time, then if for instance 3 seconds has elapsed the display changes
+ int test_use1;
+ int test_use2;
+
+ /*
+ * Identity contains the raw serial number of the drive
+ * (where applicable), however, for use within nwipe use the
+ * processed serial_no[21] string above. To access serial no. use
+ * c[i]->serial_no) and not c[i]->identity.serial_no);
+ */
+ struct hd_driveid identity;
+} nwipe_context_t;
+
+/*
+ * We use 2 data structs to pass data between threads.
+ * The first contains any required values.
+ * Values cannot form part of the second array below, hence the need for this.
+ */
+typedef struct
+{
+ int nwipe_enumerated; // The number of devices available.
+ int nwipe_selected; // The number of devices being wiped.
+ time_t maxeta; // The estimated runtime of the slowest device.
+ u64 throughput; // Total throughput.
+ u64 errors; // The combined number of errors of all processes.
+ pthread_t* gui_thread; // The ID of GUI thread.
+} nwipe_misc_thread_data_t;
+
+/*
+ * The second points to the first structure, as well as the structure of all the devices
+ */
+typedef struct
+{
+ nwipe_context_t** c; // Pointer to the nwipe context structure.
+ nwipe_misc_thread_data_t* nwipe_misc_thread_data; // Pointer to the misc structure above.
+} nwipe_thread_data_ptr_t;
+
+#endif /* CONTEXT_H_ */
diff --git a/src/create_pdf.c b/src/create_pdf.c
new file mode 100644
index 0000000..754543b
--- /dev/null
+++ b/src/create_pdf.c
@@ -0,0 +1,997 @@
+/*
+ * create_pdf.c: Routines that create the PDF erasure certificate
+ *
+ * Copyright PartialVolume <https://github.com/PartialVolume>.
+ *
+ * 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, version 2.
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#ifndef _DEFAULT_SOURCE
+#define _DEFAULT_SOURCE
+#endif
+
+#include <stdint.h>
+#include "stdarg.h"
+#include "stdio.h"
+#include "stdlib.h"
+#include "string.h"
+#include "nwipe.h"
+#include "context.h"
+#include "create_pdf.h"
+#include "PDFGen/pdfgen.h"
+#include "version.h"
+#include "method.h"
+#include "embedded_images/shred_db.jpg.h"
+#include "embedded_images/tick_erased.jpg.h"
+#include "embedded_images/redcross.h"
+#include "embedded_images/nwipe_exclamation.jpg.h"
+#include "logging.h"
+#include "options.h"
+#include "prng.h"
+#include "hpa_dco.h"
+#include "miscellaneous.h"
+#include <libconfig.h>
+#include "conf.h"
+
+#define text_size_data 10
+
+struct pdf_doc* pdf;
+struct pdf_object* page;
+
+char model_header[50] = ""; /* Model text in the header */
+char serial_header[30] = ""; /* Serial number text in the header */
+char barcode[100] = ""; /* Contents of the barcode, i.e model:serial */
+char pdf_footer[MAX_PDF_FOOTER_TEXT_LENGTH];
+float height;
+float page_width;
+int status_icon;
+
+int create_pdf( nwipe_context_t* ptr )
+{
+ extern nwipe_prng_t nwipe_twister;
+ extern nwipe_prng_t nwipe_isaac;
+ extern nwipe_prng_t nwipe_isaac64;
+
+ /* Used by libconfig functions to retrieve data from nwipe.conf defined in conf.c */
+ extern config_t nwipe_cfg;
+ extern char nwipe_config_file[];
+
+ // char pdf_footer[MAX_PDF_FOOTER_TEXT_LENGTH];
+ nwipe_context_t* c;
+ c = ptr;
+ // char model_header[50] = ""; /* Model text in the header */
+ // char serial_header[30] = ""; /* Serial number text in the header */
+ char device_size[100] = ""; /* Device size in the form xMB (xxxx bytes) */
+ // char barcode[100] = ""; /* Contents of the barcode, i.e model:serial */
+ char verify[20] = ""; /* Verify option text */
+ char blank[10] = ""; /* blanking pass, none, zeros, ones */
+ char rounds[50] = ""; /* rounds ASCII numeric */
+ char prng_type[50] = ""; /* type of prng, twister, isaac, isaac64 */
+ char start_time_text[50] = "";
+ char end_time_text[50] = "";
+ char bytes_erased[50] = "";
+ char HPA_status_text[50] = "";
+ char HPA_size_text[50] = "";
+ char errors[50] = "";
+ char throughput_txt[50] = "";
+ char bytes_percent_str[7] = "";
+
+ // int status_icon;
+
+ // float height;
+ // float page_width;
+
+ struct pdf_info info = { .creator = "https://github.com/PartialVolume/shredos.x86_64",
+ .producer = "https://github.com/martijnvanbrummelen/nwipe",
+ .title = "PDF Disk Erasure Certificate",
+ .author = "Nwipe",
+ .subject = "Disk Erase Certificate",
+ .date = "Today" };
+
+ /* A pointer to the system time struct. */
+ struct tm* p;
+
+ /* variables used by libconfig */
+ config_setting_t* setting;
+ const char *business_name, *business_address, *contact_name, *contact_phone, *op_tech_name, *customer_name,
+ *customer_address, *customer_contact_name, *customer_contact_phone;
+
+ /* ------------------ */
+ /* Initialise Various */
+
+ /* Used to display correct icon on page 2 */
+ status_icon = 0; // zero don't display icon, see header STATUS_ICON_..
+
+ // nwipe_log( NWIPE_LOG_NOTICE, "Create the PDF disk erasure certificate" );
+ // struct pdf_doc* pdf = pdf_create( PDF_A4_WIDTH, PDF_A4_HEIGHT, &info );
+ pdf = pdf_create( PDF_A4_WIDTH, PDF_A4_HEIGHT, &info );
+
+ /* Create footer text string and append the version */
+ snprintf( pdf_footer, sizeof( pdf_footer ), "Disc Erasure by NWIPE version %s", version_string );
+
+ pdf_set_font( pdf, "Helvetica" );
+ struct pdf_object* page_1 = pdf_append_page( pdf );
+
+ /* Obtain page page_width */
+ page_width = pdf_page_width( page_1 );
+
+ /*********************************************************************
+ * Create header and footer on page 1, with the exception of the green
+ * tick/red icon which is set from the 'status' section below
+ */
+ pdf_add_text_wrap( pdf, NULL, pdf_footer, 12, 0, 30, PDF_BLACK, page_width, PDF_ALIGN_CENTER, &height );
+ pdf_add_line( pdf, NULL, 50, 50, 550, 50, 3, PDF_BLACK );
+ pdf_add_line( pdf, NULL, 50, 650, 550, 650, 3, PDF_BLACK );
+ pdf_add_image_data( pdf, NULL, 45, 665, 100, 100, bin2c_shred_db_jpg, 27063 );
+ pdf_set_font( pdf, "Helvetica-Bold" );
+ snprintf( model_header, sizeof( model_header ), " %s: %s ", "Model", c->device_model );
+ pdf_add_text_wrap( pdf, NULL, model_header, 14, 0, 755, PDF_BLACK, page_width, PDF_ALIGN_CENTER, &height );
+ snprintf( serial_header, sizeof( serial_header ), " %s: %s ", "S/N", c->device_serial_no );
+ pdf_add_text_wrap( pdf, NULL, serial_header, 14, 0, 735, PDF_BLACK, page_width, PDF_ALIGN_CENTER, &height );
+ pdf_set_font( pdf, "Helvetica" );
+
+ pdf_add_text_wrap( pdf, NULL, "Disk Erasure Report", 24, 0, 695, PDF_BLACK, page_width, PDF_ALIGN_CENTER, &height );
+ snprintf( barcode, sizeof( barcode ), "%s:%s", c->device_model, c->device_serial_no );
+ pdf_add_text_wrap(
+ pdf, NULL, "Page 1 - Erasure Status", 14, 0, 670, PDF_BLACK, page_width, PDF_ALIGN_CENTER, &height );
+ pdf_add_barcode( pdf, NULL, PDF_BARCODE_128A, 100, 790, 400, 25, barcode, PDF_BLACK );
+
+ /* ------------------------ */
+ /* Organisation Information */
+
+ pdf_add_line( pdf, NULL, 50, 550, 550, 550, 1, PDF_GRAY );
+ pdf_add_text( pdf, NULL, "Organisation Performing The Disk Erasure", 12, 50, 630, PDF_BLUE );
+ pdf_add_text( pdf, NULL, "Business Name:", 12, 60, 610, PDF_GRAY );
+ pdf_add_text( pdf, NULL, "Business Address:", 12, 60, 590, PDF_GRAY );
+ pdf_add_text( pdf, NULL, "Contact Name:", 12, 60, 570, PDF_GRAY );
+ pdf_add_text( pdf, NULL, "Contact Phone:", 12, 300, 570, PDF_GRAY );
+
+ /* Obtain organisational details from nwipe.conf - See conf.c */
+ setting = config_lookup( &nwipe_cfg, "Organisation_Details" );
+ if( setting != NULL )
+ {
+ pdf_set_font( pdf, "Helvetica-Bold" );
+ if( config_setting_lookup_string( setting, "Business_Name", &business_name ) )
+ {
+ pdf_add_text( pdf, NULL, business_name, text_size_data, 153, 610, PDF_BLACK );
+ }
+ if( config_setting_lookup_string( setting, "Business_Address", &business_address ) )
+ {
+ pdf_add_text( pdf, NULL, business_address, text_size_data, 165, 590, PDF_BLACK );
+ }
+ if( config_setting_lookup_string( setting, "Contact_Name", &contact_name ) )
+ {
+ pdf_add_text( pdf, NULL, contact_name, text_size_data, 145, 570, PDF_BLACK );
+ }
+ if( config_setting_lookup_string( setting, "Contact_Phone", &contact_phone ) )
+ {
+ pdf_add_text( pdf, NULL, contact_phone, text_size_data, 390, 570, PDF_BLACK );
+ }
+ pdf_set_font( pdf, "Helvetica" );
+ }
+ else
+ {
+ nwipe_log( NWIPE_LOG_ERROR, "Cannot locate group [Organisation_Details] in %s", nwipe_config_file );
+ }
+
+ /* -------------------- */
+ /* Customer Information */
+ pdf_add_line( pdf, NULL, 50, 450, 550, 450, 1, PDF_GRAY );
+ pdf_add_text( pdf, NULL, "Customer Details", 12, 50, 530, PDF_BLUE );
+ pdf_add_text( pdf, NULL, "Name:", 12, 60, 510, PDF_GRAY );
+ pdf_add_text( pdf, NULL, "Address:", 12, 60, 490, PDF_GRAY );
+ pdf_add_text( pdf, NULL, "Contact Name:", 12, 60, 470, PDF_GRAY );
+ pdf_add_text( pdf, NULL, "Contact Phone:", 12, 300, 470, PDF_GRAY );
+
+ /* Obtain current customer details from nwipe.conf - See conf.c */
+ setting = config_lookup( &nwipe_cfg, "Selected_Customer" );
+ if( setting != NULL )
+ {
+ pdf_set_font( pdf, "Helvetica-Bold" );
+ if( config_setting_lookup_string( setting, "Customer_Name", &customer_name ) )
+ {
+ pdf_add_text( pdf, NULL, customer_name, text_size_data, 100, 510, PDF_BLACK );
+ }
+ if( config_setting_lookup_string( setting, "Customer_Address", &customer_address ) )
+ {
+ pdf_add_text( pdf, NULL, customer_address, text_size_data, 110, 490, PDF_BLACK );
+ }
+ if( config_setting_lookup_string( setting, "Contact_Name", &customer_contact_name ) )
+ {
+ pdf_add_text( pdf, NULL, customer_contact_name, text_size_data, 145, 470, PDF_BLACK );
+ }
+ if( config_setting_lookup_string( setting, "Contact_Phone", &customer_contact_phone ) )
+ {
+ pdf_add_text( pdf, NULL, customer_contact_phone, text_size_data, 390, 470, PDF_BLACK );
+ }
+ pdf_set_font( pdf, "Helvetica" );
+ }
+ else
+ {
+ nwipe_log( NWIPE_LOG_ERROR, "Cannot locate group [Selected_Customer] in %s", nwipe_config_file );
+ }
+
+ /******************
+ * Disk Information
+ */
+ pdf_add_line( pdf, NULL, 50, 350, 550, 350, 1, PDF_GRAY );
+ pdf_add_text( pdf, NULL, "Disk Information", 12, 50, 430, PDF_BLUE );
+
+ /************
+ * Make/model
+ */
+ pdf_add_text( pdf, NULL, "Make/Model:", 12, 60, 410, PDF_GRAY );
+ pdf_set_font( pdf, "Helvetica-Bold" );
+ pdf_add_text( pdf, NULL, c->device_model, text_size_data, 135, 410, PDF_BLACK );
+ pdf_set_font( pdf, "Helvetica" );
+
+ /************
+ * Serial no.
+ */
+ pdf_add_text( pdf, NULL, "Serial:", 12, 340, 410, PDF_GRAY );
+ if( c->device_serial_no[0] == 0 )
+ {
+ snprintf( c->device_serial_no, sizeof( c->device_serial_no ), "Unknown" );
+ }
+ pdf_set_font( pdf, "Helvetica-Bold" );
+ pdf_add_text( pdf, NULL, c->device_serial_no, text_size_data, 380, 410, PDF_BLACK );
+ pdf_set_font( pdf, "Helvetica" );
+
+ /******************************
+ * Bus type, ATA, USB, NVME etc
+ */
+ pdf_add_text( pdf, NULL, "Bus:", 12, 340, 390, PDF_GRAY );
+ pdf_set_font( pdf, "Helvetica-Bold" );
+ pdf_add_text( pdf, NULL, c->device_type_str, text_size_data, 370, 390, PDF_BLACK );
+ pdf_set_font( pdf, "Helvetica" );
+
+ /*************************
+ * Capacity (Size) of disk
+ */
+
+ /* Size (Apparent) */
+ pdf_add_text( pdf, NULL, "Size(Apparent): ", 12, 60, 390, PDF_GRAY );
+ pdf_set_font( pdf, "Helvetica-Bold" );
+ snprintf( device_size, sizeof( device_size ), "%s, %lli bytes", c->device_size_text, c->device_size );
+ if( ( c->device_size == c->Calculated_real_max_size_in_bytes ) || c->device_type == NWIPE_DEVICE_NVME
+ || c->device_type == NWIPE_DEVICE_VIRT || c->HPA_status == HPA_NOT_APPLICABLE || c->HPA_status != HPA_UNKNOWN )
+ {
+ pdf_add_text( pdf, NULL, device_size, text_size_data, 145, 390, PDF_DARK_GREEN );
+ }
+ else
+ {
+ pdf_add_text( pdf, NULL, device_size, text_size_data, 145, 390, PDF_RED );
+ }
+ pdf_set_font( pdf, "Helvetica" );
+
+ /* Size (Real) */
+ pdf_add_text( pdf, NULL, "Size(Real):", 12, 60, 370, PDF_GRAY );
+ pdf_set_font( pdf, "Helvetica-Bold" );
+ if( c->device_type == NWIPE_DEVICE_NVME || c->device_type == NWIPE_DEVICE_VIRT
+ || c->HPA_status == HPA_NOT_APPLICABLE )
+ {
+ snprintf( device_size, sizeof( device_size ), "%s, %lli bytes", c->device_size_text, c->device_size );
+ pdf_add_text( pdf, NULL, device_size, text_size_data, 125, 370, PDF_DARK_GREEN );
+ }
+ else
+ {
+ /* If the calculared real max size as determined from HPA/DCO and libata data is larger than
+ * or equal to the apparent device size then display that value in green.
+ */
+ if( c->Calculated_real_max_size_in_bytes >= c->device_size )
+ {
+ /* displays the real max size of the disc from the DCO displayed in Green */
+ snprintf( device_size,
+ sizeof( device_size ),
+ "%s, %lli bytes",
+ c->Calculated_real_max_size_in_bytes_text,
+ c->Calculated_real_max_size_in_bytes );
+ pdf_add_text( pdf, NULL, device_size, text_size_data, 125, 370, PDF_DARK_GREEN );
+ }
+ else
+ {
+ /* If there is no real max size either because the drive or adapter doesn't support it */
+ if( c->HPA_status == HPA_UNKNOWN )
+ {
+ snprintf( device_size, sizeof( device_size ), "Unknown" );
+ pdf_add_text( pdf, NULL, device_size, text_size_data, 125, 370, PDF_RED );
+ }
+ else
+ {
+ /* we are already here because c->DCO_reported_real_max_size < 1 so if HPA enabled then use the
+ * value we determine from whether HPA set, HPA real exist and if not assume libata's value*/
+ if( c->HPA_status == HPA_ENABLED )
+ {
+ snprintf( device_size,
+ sizeof( device_size ),
+ "%s, %lli bytes",
+ c->device_size_text,
+ c->Calculated_real_max_size_in_bytes );
+ pdf_add_text( pdf, NULL, device_size, text_size_data, 125, 370, PDF_DARK_GREEN );
+ }
+ else
+ {
+ /* Sanity check, should never get here! */
+ snprintf( device_size, sizeof( device_size ), "Sanity: HPA_status = %i", c->HPA_status );
+ pdf_add_text( pdf, NULL, device_size, text_size_data, 125, 370, PDF_RED );
+ }
+ }
+ }
+ }
+
+ pdf_set_font( pdf, "Helvetica" );
+
+ /* --------------- */
+ /* Erasure Details */
+ pdf_add_text( pdf, NULL, "Disk Erasure Details", 12, 50, 330, PDF_BLUE );
+
+ /* start time */
+ pdf_add_text( pdf, NULL, "Start time:", 12, 60, 310, PDF_GRAY );
+ p = localtime( &c->start_time );
+ snprintf( start_time_text,
+ sizeof( start_time_text ),
+ "%i/%02i/%02i %02i:%02i:%02i",
+ 1900 + p->tm_year,
+ 1 + p->tm_mon,
+ p->tm_mday,
+ p->tm_hour,
+ p->tm_min,
+ p->tm_sec );
+ pdf_set_font( pdf, "Helvetica-Bold" );
+ pdf_add_text( pdf, NULL, start_time_text, text_size_data, 120, 310, PDF_BLACK );
+ pdf_set_font( pdf, "Helvetica" );
+
+ /* end time */
+ pdf_add_text( pdf, NULL, "End time:", 12, 300, 310, PDF_GRAY );
+ p = localtime( &c->end_time );
+ snprintf( end_time_text,
+ sizeof( end_time_text ),
+ "%i/%02i/%02i %02i:%02i:%02i",
+ 1900 + p->tm_year,
+ 1 + p->tm_mon,
+ p->tm_mday,
+ p->tm_hour,
+ p->tm_min,
+ p->tm_sec );
+ pdf_set_font( pdf, "Helvetica-Bold" );
+ pdf_add_text( pdf, NULL, end_time_text, text_size_data, 360, 310, PDF_BLACK );
+ pdf_set_font( pdf, "Helvetica" );
+
+ /* Duration */
+ pdf_add_text( pdf, NULL, "Duration:", 12, 60, 290, PDF_GRAY );
+ pdf_set_font( pdf, "Helvetica-Bold" );
+ pdf_add_text( pdf, NULL, c->duration_str, text_size_data, 115, 290, PDF_BLACK );
+ pdf_set_font( pdf, "Helvetica" );
+
+ /*******************
+ * Status of erasure
+ */
+ pdf_add_text( pdf, NULL, "Status:", 12, 300, 290, PDF_GRAY );
+ pdf_set_font( pdf, "Helvetica-Bold" );
+
+ if( !strcmp( c->wipe_status_txt, "ERASED" )
+ && ( c->HPA_status == HPA_DISABLED || c->HPA_status == HPA_NOT_APPLICABLE || c->device_type == NWIPE_DEVICE_NVME
+ || c->device_type == NWIPE_DEVICE_VIRT ) )
+ {
+ pdf_add_text( pdf, NULL, c->wipe_status_txt, 12, 365, 290, PDF_DARK_GREEN );
+ pdf_add_ellipse( pdf, NULL, 390, 295, 45, 10, 2, PDF_DARK_GREEN, PDF_TRANSPARENT );
+
+ /* Display the green tick icon in the header */
+ pdf_add_image_data( pdf, NULL, 450, 665, 100, 100, bin2c_te_jpg, 54896 );
+ status_icon = STATUS_ICON_GREEN_TICK; // used later on page 2
+ }
+ else
+ {
+ if( !strcmp( c->wipe_status_txt, "ERASED" )
+ && ( c->HPA_status == HPA_ENABLED || c->HPA_status == HPA_UNKNOWN ) )
+ {
+ pdf_add_ellipse( pdf, NULL, 390, 295, 45, 10, 2, PDF_RED, PDF_BLACK );
+ pdf_add_text( pdf, NULL, c->wipe_status_txt, 12, 365, 290, PDF_YELLOW );
+ pdf_add_text( pdf, NULL, "See Warning !", 12, 450, 290, PDF_RED );
+
+ /* Display the yellow exclamation icon in the header */
+ pdf_add_image_data( pdf, NULL, 450, 665, 100, 100, bin2c_nwipe_exclamation_jpg, 65791 );
+ status_icon = STATUS_ICON_YELLOW_EXCLAMATION; // used later on page 2
+ }
+ else
+ {
+ if( !strcmp( c->wipe_status_txt, "FAILED" ) )
+ {
+ // text shifted left slightly in ellipse due to extra character
+ pdf_add_text( pdf, NULL, c->wipe_status_txt, 12, 370, 290, PDF_RED );
+
+ // Display the red cross in the header
+ pdf_add_image_data( pdf, NULL, 450, 665, 100, 100, bin2c_redcross_jpg, 60331 );
+ status_icon = STATUS_ICON_RED_CROSS; // used later on page 2
+ }
+ else
+ {
+ pdf_add_text( pdf, NULL, c->wipe_status_txt, 12, 360, 290, PDF_RED );
+
+ // Print the red cross
+ pdf_add_image_data( pdf, NULL, 450, 665, 100, 100, bin2c_redcross_jpg, 60331 );
+ status_icon = STATUS_ICON_RED_CROSS; // used later on page 2
+ }
+ pdf_add_ellipse( pdf, NULL, 390, 295, 45, 10, 2, PDF_RED, PDF_TRANSPARENT );
+ }
+ }
+ pdf_set_font( pdf, "Helvetica" );
+
+ /********
+ * Method
+ */
+ pdf_add_text( pdf, NULL, "Method:", 12, 60, 270, PDF_GRAY );
+ pdf_set_font( pdf, "Helvetica-Bold" );
+ pdf_add_text( pdf, NULL, nwipe_method_label( nwipe_options.method ), text_size_data, 110, 270, PDF_BLACK );
+ pdf_set_font( pdf, "Helvetica" );
+
+ /***********
+ * prng type
+ */
+ pdf_add_text( pdf, NULL, "PRNG algorithm:", 12, 300, 270, PDF_GRAY );
+ if( nwipe_options.method == &nwipe_verify_one || nwipe_options.method == &nwipe_verify_zero
+ || nwipe_options.method == &nwipe_zero || nwipe_options.method == &nwipe_one )
+ {
+ snprintf( prng_type, sizeof( prng_type ), "Not applicable to method" );
+ }
+ else
+ {
+ if( nwipe_options.prng == &nwipe_twister )
+ {
+ snprintf( prng_type, sizeof( prng_type ), "Twister" );
+ }
+ else
+ {
+ if( nwipe_options.prng == &nwipe_isaac )
+ {
+ snprintf( prng_type, sizeof( prng_type ), "Isaac" );
+ }
+ else
+ {
+ if( nwipe_options.prng == &nwipe_isaac64 )
+ {
+ snprintf( prng_type, sizeof( prng_type ), "Isaac64" );
+ }
+ else
+ {
+ snprintf( prng_type, sizeof( prng_type ), "Unknown" );
+ }
+ }
+ }
+ }
+ pdf_set_font( pdf, "Helvetica-Bold" );
+ pdf_add_text( pdf, NULL, prng_type, text_size_data, 395, 270, PDF_BLACK );
+ pdf_set_font( pdf, "Helvetica" );
+
+ /******************************************************
+ * Final blanking pass if selected, none, zeros or ones
+ */
+ if( nwipe_options.noblank )
+ {
+ strcpy( blank, "None" );
+ }
+ else
+ {
+ strcpy( blank, "Zeros" );
+ }
+ pdf_add_text( pdf, NULL, "Final Pass(Zeros/Ones/None):", 12, 60, 250, PDF_GRAY );
+ pdf_set_font( pdf, "Helvetica-Bold" );
+ pdf_add_text( pdf, NULL, blank, text_size_data, 230, 250, PDF_BLACK );
+ pdf_set_font( pdf, "Helvetica" );
+
+ /* ***********************************************************************
+ * Create suitable text based on the numeric value of type of verification
+ */
+ switch( nwipe_options.verify )
+ {
+ case NWIPE_VERIFY_NONE:
+ strcpy( verify, "Verify None" );
+ break;
+
+ case NWIPE_VERIFY_LAST:
+ strcpy( verify, "Verify Last" );
+ break;
+
+ case NWIPE_VERIFY_ALL:
+ strcpy( verify, "Verify All" );
+ break;
+ }
+ pdf_add_text( pdf, NULL, "Verify Pass(Last/All/None):", 12, 300, 250, PDF_GRAY );
+ pdf_set_font( pdf, "Helvetica-Bold" );
+ pdf_add_text( pdf, NULL, verify, text_size_data, 450, 250, PDF_BLACK );
+ pdf_set_font( pdf, "Helvetica" );
+
+ /* ************
+ * bytes erased
+ */
+ pdf_add_text( pdf, NULL, "*Bytes Erased:", 12, 60, 230, PDF_GRAY );
+ pdf_set_font( pdf, "Helvetica-Bold" );
+
+ /* Bytes erased is not applicable when user only requested a verify */
+ if( nwipe_options.method == &nwipe_verify_one || nwipe_options.method == &nwipe_verify_zero )
+ {
+ snprintf( bytes_erased, sizeof( bytes_erased ), "Not applicable to method" );
+ pdf_add_text( pdf, NULL, bytes_erased, text_size_data, 145, 230, PDF_BLACK );
+ }
+ else
+ {
+ if( c->device_type == NWIPE_DEVICE_NVME || c->device_type == NWIPE_DEVICE_VIRT
+ || c->HPA_status == HPA_NOT_APPLICABLE )
+ {
+ convert_double_to_string( bytes_percent_str,
+ (double) ( (double) c->bytes_erased / (double) c->device_size ) * 100 );
+
+ snprintf( bytes_erased, sizeof( bytes_erased ), "%lli, (%s%%)", c->bytes_erased, bytes_percent_str );
+
+ if( c->bytes_erased == c->device_size )
+ {
+ pdf_add_text( pdf, NULL, bytes_erased, text_size_data, 145, 230, PDF_DARK_GREEN );
+ }
+ else
+ {
+ pdf_add_text( pdf, NULL, bytes_erased, text_size_data, 145, 230, PDF_RED );
+ }
+ }
+ else
+ {
+
+ convert_double_to_string(
+ bytes_percent_str,
+ (double) ( (double) c->bytes_erased / (double) c->Calculated_real_max_size_in_bytes ) * 100 );
+
+ snprintf( bytes_erased, sizeof( bytes_erased ), "%lli, (%s%%)", c->bytes_erased, bytes_percent_str );
+
+ if( c->bytes_erased == c->Calculated_real_max_size_in_bytes )
+ {
+ pdf_add_text( pdf, NULL, bytes_erased, text_size_data, 145, 230, PDF_DARK_GREEN );
+ }
+ else
+ {
+ pdf_add_text( pdf, NULL, bytes_erased, text_size_data, 145, 230, PDF_RED );
+ }
+ }
+ }
+ pdf_set_font( pdf, "Helvetica" );
+
+ /************************************************
+ * rounds - How many times the method is repeated
+ */
+ pdf_add_text( pdf, NULL, "Rounds(completed/requested):", 12, 300, 230, PDF_GRAY );
+ pdf_set_font( pdf, "Helvetica-Bold" );
+ if( !strcmp( c->wipe_status_txt, "ERASED" ) )
+ {
+ snprintf( rounds, sizeof( rounds ), "%i/%i", c->round_working, nwipe_options.rounds );
+ pdf_add_text( pdf, NULL, rounds, text_size_data, 470, 230, PDF_DARK_GREEN );
+ }
+ else
+ {
+ snprintf( rounds, sizeof( rounds ), "%i/%i", c->round_working - 1, nwipe_options.rounds );
+ pdf_add_text( pdf, NULL, rounds, text_size_data, 470, 230, PDF_RED );
+ }
+ pdf_set_font( pdf, "Helvetica" );
+
+ /*******************
+ * HPA, DCO - LABELS
+ */
+ pdf_add_text( pdf, NULL, "HPA/DCO:", 12, 60, 210, PDF_GRAY );
+ pdf_set_font( pdf, "Helvetica-Bold" );
+ pdf_add_text( pdf, NULL, HPA_status_text, text_size_data, 155, 210, PDF_BLACK );
+ pdf_set_font( pdf, "Helvetica" );
+ pdf_add_text( pdf, NULL, "HPA/DCO Size:", 12, 300, 210, PDF_GRAY );
+
+ /*******************
+ * Populate HPA size
+ */
+
+ pdf_set_font( pdf, "Helvetica-Bold" );
+ if( c->HPA_status == HPA_ENABLED )
+ {
+ snprintf( HPA_size_text, sizeof( HPA_size_text ), "%lli sectors", c->HPA_sectors );
+ pdf_add_text( pdf, NULL, HPA_size_text, text_size_data, 390, 210, PDF_RED );
+ }
+ else
+ {
+ if( c->HPA_status == HPA_DISABLED )
+ {
+ snprintf( HPA_size_text, sizeof( HPA_size_text ), "No hidden sectors" );
+ pdf_add_text( pdf, NULL, HPA_size_text, text_size_data, 390, 210, PDF_DARK_GREEN );
+ }
+ else
+ {
+ if( c->HPA_status == HPA_NOT_APPLICABLE )
+ {
+ snprintf( HPA_size_text, sizeof( HPA_size_text ), "Not Applicable" );
+ pdf_add_text( pdf, NULL, HPA_size_text, text_size_data, 390, 210, PDF_DARK_GREEN );
+ }
+ else
+ {
+ if( c->HPA_status == HPA_UNKNOWN )
+ {
+ snprintf( HPA_size_text, sizeof( HPA_size_text ), "Unknown" );
+ pdf_add_text( pdf, NULL, HPA_size_text, text_size_data, 390, 210, PDF_RED );
+ }
+ }
+ }
+ }
+
+ pdf_set_font( pdf, "Helvetica" );
+
+ /*********************
+ * Populate HPA status (and size if not applicable, NVMe and VIRT)
+ */
+ if( c->device_type == NWIPE_DEVICE_NVME || c->device_type == NWIPE_DEVICE_VIRT
+ || c->HPA_status == HPA_NOT_APPLICABLE )
+ {
+ snprintf( HPA_status_text, sizeof( HPA_status_text ), "Not applicable" );
+ pdf_set_font( pdf, "Helvetica-Bold" );
+ pdf_add_text( pdf, NULL, HPA_status_text, text_size_data, 130, 210, PDF_DARK_GREEN );
+ pdf_set_font( pdf, "Helvetica" );
+ }
+ else
+ {
+ if( c->HPA_status == HPA_ENABLED )
+ {
+ snprintf( HPA_status_text, sizeof( HPA_status_text ), "Hidden sectors found!" );
+ pdf_set_font( pdf, "Helvetica-Bold" );
+ pdf_add_text( pdf, NULL, HPA_status_text, text_size_data, 130, 210, PDF_RED );
+ pdf_set_font( pdf, "Helvetica" );
+ }
+ else
+ {
+ if( c->HPA_status == HPA_DISABLED )
+ {
+ snprintf( HPA_status_text, sizeof( HPA_status_text ), "No hidden sectors" );
+ pdf_set_font( pdf, "Helvetica-Bold" );
+ pdf_add_text( pdf, NULL, HPA_status_text, text_size_data, 130, 210, PDF_DARK_GREEN );
+ pdf_set_font( pdf, "Helvetica" );
+ }
+ else
+ {
+ if( c->HPA_status == HPA_UNKNOWN )
+ {
+ snprintf( HPA_status_text, sizeof( HPA_status_text ), "Unknown" );
+ pdf_set_font( pdf, "Helvetica-Bold" );
+ pdf_add_text( pdf, NULL, HPA_status_text, text_size_data, 130, 210, PDF_RED );
+ pdf_set_font( pdf, "Helvetica" );
+ }
+ else
+ {
+ if( c->HPA_status == HPA_NOT_SUPPORTED_BY_DRIVE )
+ {
+ snprintf( HPA_status_text, sizeof( HPA_status_text ), "No hidden sectors **DDNSHDA" );
+ pdf_set_font( pdf, "Helvetica-Bold" );
+ pdf_add_text( pdf, NULL, HPA_status_text, text_size_data, 130, 210, PDF_DARK_GREEN );
+ pdf_set_font( pdf, "Helvetica" );
+ }
+ }
+ }
+ }
+ }
+
+ /************
+ * Throughput
+ */
+ pdf_add_text( pdf, NULL, "Throughput:", 12, 300, 190, PDF_GRAY );
+ snprintf( throughput_txt, sizeof( throughput_txt ), "%s/sec", c->throughput_txt );
+ pdf_set_font( pdf, "Helvetica-Bold" );
+ pdf_add_text( pdf, NULL, throughput_txt, text_size_data, 370, 190, PDF_BLACK );
+ pdf_set_font( pdf, "Helvetica" );
+
+ /********
+ * Errors
+ */
+ pdf_add_text( pdf, NULL, "Errors(pass/sync/verify):", 12, 60, 190, PDF_GRAY );
+ pdf_set_font( pdf, "Helvetica-Bold" );
+ snprintf( errors, sizeof( errors ), "%llu/%llu/%llu", c->pass_errors, c->fsyncdata_errors, c->verify_errors );
+ if( c->pass_errors != 0 || c->fsyncdata_errors != 0 || c->verify_errors != 0 )
+ {
+ pdf_add_text( pdf, NULL, errors, text_size_data, 195, 190, PDF_RED );
+ }
+ else
+ {
+ pdf_add_text( pdf, NULL, errors, text_size_data, 195, 190, PDF_DARK_GREEN );
+ }
+ pdf_set_font( pdf, "Helvetica" );
+
+ /*************
+ * Information
+ */
+ pdf_add_text( pdf, NULL, "Information:", 12, 60, 170, PDF_GRAY );
+ pdf_set_font( pdf, "Helvetica-Bold" );
+
+ if( !strcmp( c->wipe_status_txt, "ERASED" ) && c->HPA_status == HPA_ENABLED )
+ {
+ pdf_add_ellipse( pdf, NULL, 160, 173, 30, 9, 2, PDF_RED, PDF_BLACK );
+ pdf_add_text( pdf, NULL, "Warning", text_size_data, 140, 170, PDF_YELLOW );
+
+ pdf_add_text( pdf,
+ NULL,
+ "Visible sectors erased as requested, however hidden sectors NOT erased",
+ text_size_data,
+ 200,
+ 170,
+ PDF_RED );
+ }
+ else
+ {
+ if( c->HPA_status == HPA_UNKNOWN )
+ {
+ pdf_add_ellipse( pdf, NULL, 160, 173, 30, 9, 2, PDF_RED, PDF_BLACK );
+ pdf_add_text( pdf, NULL, "Warning", text_size_data, 140, 170, PDF_YELLOW );
+
+ pdf_add_text( pdf,
+ NULL,
+ "HPA/DCO data unavailable, can not determine hidden sector status.",
+ text_size_data,
+ 200,
+ 170,
+ PDF_RED );
+ }
+ }
+
+ /* info descripting what bytes erased actually means */
+ pdf_add_text( pdf,
+ NULL,
+ "* bytes erased: The amount of drive that's been erased at least once",
+ text_size_data,
+ 60,
+ 137,
+ PDF_BLACK );
+
+ /* meaning of abreviation DDNSHPA */
+ if( c->HPA_status == HPA_NOT_SUPPORTED_BY_DRIVE )
+ {
+ pdf_add_text(
+ pdf, NULL, "** DDNSHPA = Drive does not support HPA/DCO", text_size_data, 60, 125, PDF_DARK_GREEN );
+ }
+ pdf_set_font( pdf, "Helvetica" );
+
+ /************************
+ * Technician/Operator ID
+ */
+ pdf_add_line( pdf, NULL, 50, 120, 550, 120, 1, PDF_GRAY );
+ pdf_add_text( pdf, NULL, "Technician/Operator ID", 12, 50, 100, PDF_BLUE );
+ pdf_add_text( pdf, NULL, "Name/ID:", 12, 60, 80, PDF_GRAY );
+ pdf_add_text( pdf, NULL, "Signature:", 12, 300, 100, PDF_BLUE );
+ pdf_add_line( pdf, NULL, 360, 65, 550, 66, 1, PDF_GRAY );
+
+ pdf_set_font( pdf, "Helvetica-Bold" );
+ /* Obtain organisational details from nwipe.conf - See conf.c */
+ setting = config_lookup( &nwipe_cfg, "Organisation_Details" );
+ if( config_setting_lookup_string( setting, "Op_Tech_Name", &op_tech_name ) )
+ {
+ pdf_add_text( pdf, NULL, op_tech_name, text_size_data, 120, 80, PDF_BLACK );
+ }
+ pdf_set_font( pdf, "Helvetica" );
+
+ /***************************************
+ * Populate page 2 and 3 with smart data
+ */
+ nwipe_get_smart_data( c );
+
+ /*****************************
+ * Create the reports filename
+ *
+ * Sanitize the strings that we are going to use to create the report filename
+ * by converting any non alphanumeric characters to an underscore or hyphon
+ */
+ replace_non_alphanumeric( end_time_text, '-' );
+ replace_non_alphanumeric( c->device_model, '_' );
+ replace_non_alphanumeric( c->device_serial_no, '_' );
+ snprintf( c->PDF_filename,
+ sizeof( c->PDF_filename ),
+ "%s/nwipe_report_%s_Model_%s_Serial_%s.pdf",
+ nwipe_options.PDFreportpath,
+ end_time_text,
+ c->device_model,
+ c->device_serial_no );
+
+ pdf_save( pdf, c->PDF_filename );
+ pdf_destroy( pdf );
+ return 0;
+}
+
+int nwipe_get_smart_data( nwipe_context_t* c )
+{
+ FILE* fp;
+
+ char* pdata;
+ char page_title[50];
+
+ char smartctl_command[] = "smartctl -a %s";
+ char smartctl_command2[] = "/sbin/smartctl -a %s";
+ char smartctl_command3[] = "/usr/bin/smartctl -a %s";
+ char final_cmd_smartctl[sizeof( smartctl_command3 ) + 256];
+ char result[512];
+ char smartctl_labels_to_anonymize[][18] = {
+ "serial number:", "lu wwn device id:", "logical unit id:", "" /* Don't remove this empty string !, important */
+ };
+
+ int idx, idx2, idx3;
+ int x, y;
+ int set_return_value;
+ int page_number;
+
+ final_cmd_smartctl[0] = 0;
+
+ /* Determine whether we can access smartctl, required if the PATH environment is not setup ! (Debian sid 'su' as
+ * opposed to 'su -' */
+ if( system( "which smartctl > /dev/null 2>&1" ) )
+ {
+ if( system( "which /sbin/smartctl > /dev/null 2>&1" ) )
+ {
+ if( system( "which /usr/bin/smartctl > /dev/null 2>&1" ) )
+ {
+ nwipe_log( NWIPE_LOG_WARNING, "Command not found. Install smartmontools !" );
+ }
+ else
+ {
+ sprintf( final_cmd_smartctl, smartctl_command3, c->device_name );
+ }
+ }
+ else
+ {
+ sprintf( final_cmd_smartctl, smartctl_command2, c->device_name );
+ }
+ }
+ else
+ {
+ sprintf( final_cmd_smartctl, smartctl_command, c->device_name );
+ }
+
+ if( final_cmd_smartctl[0] != 0 )
+ {
+ fp = popen( final_cmd_smartctl, "r" );
+
+ if( fp == NULL )
+ {
+ nwipe_log( NWIPE_LOG_WARNING, "nwipe_get_smart_data(): Failed to create stream to %s", smartctl_command );
+
+ set_return_value = 3;
+ }
+ else
+ {
+ x = 50; // left side of page
+ y = 630; // top row of page
+ page_number = 2;
+
+ /* Create Page 2 of the report. This shows the drives smart data
+ */
+ page = pdf_append_page( pdf );
+
+ /* Create the header and footer for page 2, the start of the smart data */
+ snprintf( page_title, sizeof( page_title ), "Page %i - Smart Data", page_number );
+ create_header_and_footer( c, page_title );
+
+ /* Read the output a line at a time - output it. */
+ while( fgets( result, sizeof( result ) - 1, fp ) != NULL )
+ {
+ /* Convert the label, i.e everything before the ':' to lower case, it's required to
+ * convert to lower case as smartctl seems to use inconsistent case when labeling
+ * for serial number, i.e mostly it produces labels "Serial Number:" but occasionally
+ * it produces a label "Serial number:" */
+
+ idx = 0;
+
+ while( result[idx] != 0 && result[idx] != ':' )
+ {
+ /* If upper case alpha character, change to lower case */
+ if( result[idx] >= 'A' && result[idx] <= 'Z' )
+ {
+ result[idx] += 32;
+ }
+ idx++;
+ }
+
+ if( nwipe_options.quiet == 1 )
+ {
+ for( idx2 = 0; idx2 < 3; idx2++ )
+ {
+ if( strstr( result, &smartctl_labels_to_anonymize[idx2][0] ) )
+ {
+ if( ( pdata = strstr( result, ":" ) ) )
+ {
+ idx3 = 1;
+ while( pdata[idx3] != 0 )
+ {
+ if( pdata[idx3] != ' ' )
+ {
+ pdata[idx3] = 'X';
+ }
+ idx3++;
+ }
+ }
+ }
+ }
+ }
+
+ pdf_set_font( pdf, "Courier" );
+ pdf_add_text( pdf, NULL, result, 8, x, y, PDF_BLACK );
+ y -= 9;
+
+ /* Have we reached the bottom of the page yet */
+ if( y < 60 )
+ {
+ /* Append an extra page */
+ page = pdf_append_page( pdf );
+ page_number++;
+ y = 630;
+
+ /* create the header and footer for the next page */
+ snprintf( page_title, sizeof( page_title ), "Page %i - Smart Data", page_number );
+ create_header_and_footer( c, page_title );
+ }
+ }
+ set_return_value = 0;
+ }
+ }
+ else
+ {
+ set_return_value = 1;
+ }
+ return set_return_value;
+}
+
+void create_header_and_footer( nwipe_context_t* c, char* page_title )
+{
+ /**************************************************************************
+ * Create header and footer on most recently added page, with the exception
+ * of the green tick/red icon which is set from the 'status' section below.
+ */
+ pdf_add_text_wrap( pdf, NULL, pdf_footer, 12, 0, 30, PDF_BLACK, page_width, PDF_ALIGN_CENTER, &height );
+ pdf_add_line( pdf, NULL, 50, 50, 550, 50, 3, PDF_BLACK );
+ pdf_add_line( pdf, NULL, 50, 650, 550, 650, 3, PDF_BLACK );
+ pdf_add_image_data( pdf, NULL, 45, 665, 100, 100, bin2c_shred_db_jpg, 27063 );
+ pdf_set_font( pdf, "Helvetica-Bold" );
+ snprintf( model_header, sizeof( model_header ), " %s: %s ", "Model", c->device_model );
+ pdf_add_text_wrap( pdf, NULL, model_header, 14, 0, 755, PDF_BLACK, page_width, PDF_ALIGN_CENTER, &height );
+ snprintf( serial_header, sizeof( serial_header ), " %s: %s ", "S/N", c->device_serial_no );
+ pdf_add_text_wrap( pdf, NULL, serial_header, 14, 0, 735, PDF_BLACK, page_width, PDF_ALIGN_CENTER, &height );
+ pdf_set_font( pdf, "Helvetica" );
+
+ pdf_add_text_wrap( pdf, NULL, "Disk Erasure Report", 24, 0, 695, PDF_BLACK, page_width, PDF_ALIGN_CENTER, &height );
+ snprintf( barcode, sizeof( barcode ), "%s:%s", c->device_model, c->device_serial_no );
+ pdf_add_text_wrap( pdf, NULL, page_title, 14, 0, 670, PDF_BLACK, page_width, PDF_ALIGN_CENTER, &height );
+ pdf_add_barcode( pdf, NULL, PDF_BARCODE_128A, 100, 790, 400, 25, barcode, PDF_BLACK );
+
+ /**********************************************************
+ * Display the appropriate status icon, top right on page on
+ * most recently added page.
+ */
+ switch( status_icon )
+ {
+ case STATUS_ICON_GREEN_TICK:
+
+ /* Display the green tick icon in the header */
+ pdf_add_image_data( pdf, NULL, 450, 665, 100, 100, bin2c_te_jpg, 54896 );
+ break;
+
+ case STATUS_ICON_YELLOW_EXCLAMATION:
+
+ /* Display the yellow exclamation icon in the header */
+ pdf_add_image_data( pdf, NULL, 450, 665, 100, 100, bin2c_nwipe_exclamation_jpg, 65791 );
+ break;
+
+ case STATUS_ICON_RED_CROSS:
+
+ // Display the red cross in the header
+ pdf_add_image_data( pdf, NULL, 450, 665, 100, 100, bin2c_redcross_jpg, 60331 );
+ break;
+
+ default:
+
+ break;
+ }
+}
diff --git a/src/create_pdf.h b/src/create_pdf.h
new file mode 100644
index 0000000..0208657
--- /dev/null
+++ b/src/create_pdf.h
@@ -0,0 +1,52 @@
+/*.
+ * create_pdf.h: The header file for the pdf creation routines
+ *
+ * Copyright https://github.com/PartialVolume/shredos.x86_64
+ *
+ * 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, version 2.
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#ifndef CREATE_PDF_H_
+#define CREATE_PDF_H_
+
+#define MAX_PDF_FOOTER_TEXT_LENGTH 100
+
+#define STATUS_ICON_GREEN_TICK 1
+#define STATUS_ICON_YELLOW_EXCLAMATION 2
+#define STATUS_ICON_RED_CROSS 3
+
+/* Additional colors that supplement the standard colors in pdfgen.h
+ */
+/*! Utility macro to provide gray */
+#define PDF_DARK_GREEN PDF_RGB( 0, 0x64, 0 )
+
+/*! Utility macro to provide gray */
+#define PDF_GRAY PDF_RGB( 0x5A, 0x5A, 0x5A )
+
+/*! Utility macro to provide gray */
+#define PDF_YELLOW PDF_RGB( 0xFF, 0xFF, 0x5A )
+
+/**
+ * Create the disk erase report in PDF format
+ * @param pointer to a drive context
+ * @return returns 0 on success < 1 on error
+ */
+int create_pdf( nwipe_context_t* ptr );
+
+int nwipe_get_smart_data( nwipe_context_t* );
+
+void create_header_and_footer( nwipe_context_t*, char* );
+
+#endif /* CREATE_PDF_H_ */
diff --git a/src/customers.c b/src/customers.c
new file mode 100644
index 0000000..9097d24
--- /dev/null
+++ b/src/customers.c
@@ -0,0 +1,713 @@
+/*
+ * ****************************************************************************
+ * customers.c: Functions related to customer processing for the PDF erasure *
+ * certificate. *
+ * ****************************************************************************
+ *
+ * Copyright PartialVolume <https://github.com/PartialVolume>.
+ *
+ * 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, version 2.
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <stdio.h>
+#include "nwipe.h"
+#include "context.h"
+#include "gui.h"
+#include "logging.h"
+#include "conf.h"
+#include "customers.h"
+#include <sys/stat.h>
+
+void customer_processes( int mode )
+{
+ /* This function reads the customers.csv file, counts the number of lines,
+ * converts line feeds to NULL and constructs a array of pointers that point
+ * to the variable length strings.
+ *
+ * Depending on the value of mode the pointer array is passed to either
+ * the select_customers() or delete_customer() functions.
+ */
+
+ int idx;
+ int idx2;
+ FILE* fptr;
+
+ struct stat st;
+ intmax_t size = 0;
+ int lines;
+ int list_idx;
+ int current_list_size;
+
+ size_t result_size;
+
+ extern char nwipe_customers_file[];
+
+ /* Determine size of customers.csv file */
+ stat( nwipe_customers_file, &st );
+ size = st.st_size;
+ current_list_size = 0;
+
+ nwipe_customers_buffer_t raw_buffer = (nwipe_customers_buffer_t) calloc( 1, size + 1 );
+
+ /* Allocate storage for the contents of customers.csv */
+ nwipe_customers_buffer_t buffer = (nwipe_customers_buffer_t) calloc( 1, size + 1 );
+
+ /* Allocate storage for the processed version of customers.csv,
+ * i.e we convert the csv format to strings without the quotes
+ * and semi colon delimiters
+ */
+ nwipe_customers_pointers_t list = (nwipe_customers_pointers_t) calloc( 1, sizeof( char* ) );
+ current_list_size += sizeof( char* );
+
+ /* Open customers.csv */
+ if( ( fptr = fopen( nwipe_customers_file, "rb" ) ) == NULL )
+ {
+ nwipe_log( NWIPE_LOG_ERROR, "Unable to open %s", nwipe_customers_file );
+ free( buffer );
+ free( *list );
+ return;
+ }
+
+ /* Read the customers.csv file and populate the list array with the data */
+ result_size = fread( raw_buffer, size, 1, fptr );
+
+ fclose( fptr );
+
+ /* Validate csv contents. With the exception of line feeds,
+ * remove non printable characters and move to a secondary buffer.
+ */
+ idx = 0;
+ idx2 = 0;
+ while( idx < size )
+ {
+ if( ( raw_buffer[idx] > 0x1F && raw_buffer[idx] < 0x7F ) || raw_buffer[idx] == 0x0A )
+ {
+ /* copy printable characters and line feeds */
+ buffer[idx2++] = raw_buffer[idx];
+ }
+ idx++;
+ }
+
+ /* Construct a array of pointers that point to each line of the csv file
+ */
+ idx = 0;
+ lines = 0;
+ list_idx = 0;
+
+ while( idx < size )
+ {
+ if( buffer[idx] == 0x0A )
+ {
+ buffer[idx] = 0;
+
+ /* increment the line counter, but don't count
+ * the first line as that is the csv header line.
+ */
+ if( idx != 0 )
+ {
+ lines++;
+
+ /* Change the line feed to a NULL, string terminator */
+ buffer[idx] = 0;
+
+ /* Save the pointer to the first data line of the csv. */
+ list[list_idx++] = &buffer[idx + 1];
+
+ current_list_size += sizeof( char* );
+
+ /* Expand allocated memory by the size of one pointer */
+ if( ( list = realloc( list, current_list_size ) ) == NULL )
+ {
+ nwipe_log( NWIPE_LOG_ERROR, "Unable to realloc customer list array, out of memory?" );
+ break;
+ }
+ current_list_size += sizeof( char* );
+ }
+ }
+ else
+ {
+ /* Replace colons with commas */
+ if( buffer[idx] == ';' )
+ {
+ buffer[idx] = ',';
+ }
+ }
+ idx++;
+ }
+
+ /* Sync lines variable to actual number of lines */
+ if( lines > 0 )
+ lines--;
+
+ if( idx == size )
+ {
+ /* makesure the very last entry is NULL terminated */
+ buffer[idx] = 0;
+ }
+
+ /* Select the requested mode, customer or delete customer.
+ */
+ switch( mode )
+ {
+ case SELECT_CUSTOMER:
+ select_customers( lines, list );
+ break;
+
+ case DELETE_CUSTOMER:
+ delete_customer( lines, list );
+ break;
+ }
+
+ free( raw_buffer );
+ free( buffer );
+}
+
+void select_customers( int count, char** customer_list_array )
+{
+ int selected_entry = 0;
+ char window_title[] = " Select Customer For PDF Report ";
+
+ /* Display the customer selection window */
+ nwipe_gui_list( count, window_title, customer_list_array, &selected_entry );
+
+ /* Save the selected customer details to nwipe's config file /etc/nwipe/nwipe.conf
+ * If selected entry equals 0, then the customer did not select an entry so skip save.
+ */
+ if( selected_entry != 0 )
+ {
+ save_selected_customer( &customer_list_array[selected_entry - 1] );
+ }
+}
+
+void delete_customer( int count, char** customer_list_array )
+{
+ char window_title[] = " Delete Customer ";
+ int selected_entry = 0;
+
+ nwipe_gui_list( count, window_title, customer_list_array, &selected_entry );
+
+ if( selected_entry != 0 )
+ {
+ delete_customer_csv_entry( &selected_entry );
+ }
+}
+
+void write_customer_csv_entry( char* customer_name,
+ char* customer_address,
+ char* customer_contact_name,
+ char* customer_contact_phone )
+{
+ /**
+ * Write the attached strings in csv format to the first
+ * line after the header (line 2 of file)
+ */
+
+ FILE* fptr = 0;
+ FILE* fptr2 = 0;
+
+ size_t result_size;
+
+ /* General index variables */
+ int idx1, idx2, idx3;
+
+ /* Length of the new customer line */
+ int csv_line_length;
+
+ /* Size of the new buffer that holds old contents plus new entry */
+ int new_customers_buffer_size;
+
+ struct stat st;
+
+ extern char nwipe_customers_file[];
+ extern char nwipe_customers_file_backup[];
+ extern char nwipe_customers_file_backup_tmp[];
+
+ intmax_t existing_file_size = 0;
+
+ /* pointer to the new customer entry in csv format. */
+ char* csv_buffer = 0;
+
+ /* pointer to the buffer containing the existing customer file */
+ char* customers_buffer = 0;
+
+ /* pointer to the buffer containing the existing customer file plus the new entry */
+ char* new_customers_buffer = 0;
+
+ size_t new_customers_buffer_length;
+
+ /* Determine length of all four strings and malloc sufficient storage + 12 = 8 quotes + three colons + null */
+ csv_line_length = strlen( customer_name ) + strlen( customer_address ) + strlen( customer_contact_name )
+ + strlen( customer_contact_phone ) + 12;
+ if( !( csv_buffer = calloc( 1, csv_line_length == 0 ) ) )
+ {
+ nwipe_log( NWIPE_LOG_ERROR, "func:nwipe_gui_add_customer:csv_buffer, calloc returned NULL " );
+ }
+ else
+ {
+ /* Determine current size of the csv file containing the customers */
+ stat( nwipe_customers_file, &st );
+ existing_file_size = st.st_size;
+
+ /* calloc sufficient storage to hold the existing customers file */
+ if( !( customers_buffer = calloc( 1, existing_file_size + 1 ) ) )
+ {
+ nwipe_log( NWIPE_LOG_ERROR, "func:nwipe_gui_add_customer:customers_buffer, calloc returned NULL " );
+ }
+ else
+ {
+ /* create a third buffer which is the combined size of the previous two, i.e existing file size, plus the
+ * new customer entry + 1 (NULL) */
+ new_customers_buffer_size = existing_file_size + csv_line_length + 1;
+
+ if( !( new_customers_buffer = calloc( 1, new_customers_buffer_size ) ) )
+ {
+ nwipe_log( NWIPE_LOG_ERROR, "func:nwipe_gui_add_customer:customers_buffer, calloc returned NULL " );
+ }
+ else
+ {
+ /* Read the whole of customers.csv file into customers_buffer */
+ if( ( fptr = fopen( nwipe_customers_file, "rb" ) ) == NULL )
+ {
+ nwipe_log( NWIPE_LOG_ERROR, "Unable to open %s", nwipe_customers_file );
+ }
+ else
+ {
+ /* Read the customers.csv file and populate the list array with the data */
+ if( ( result_size = fread( customers_buffer, existing_file_size, 1, fptr ) ) != 1 )
+ {
+ nwipe_log(
+ NWIPE_LOG_ERROR,
+ "func:nwipe_gui_add_customer:Error reading customers file, # bytes read not as expected "
+ "%i bytes",
+ result_size );
+ }
+ else
+ {
+ /* --------------------------------------------------------------------
+ * Read the first line which is the csv header from the existing customer
+ * buffer & write to the new buffer.
+ */
+
+ idx1 = 0; // Index for the current csv buffer
+ idx2 = 0; // Index for the new csv buffer
+ idx3 = 0; // Index for new customer fields
+
+ while( idx1 < existing_file_size && idx2 < new_customers_buffer_size )
+ {
+ if( customers_buffer[idx1] != LINEFEED )
+ {
+ new_customers_buffer[idx2++] = customers_buffer[idx1++];
+ }
+ else
+ {
+ new_customers_buffer[idx2++] = LINEFEED;
+ break;
+ }
+ }
+
+ /* --------------------------------------------------------------------------
+ * Copy the new customer name entry so it is immediately after the csv header
+ */
+
+ /* Start with first entries opening quote */
+ new_customers_buffer[idx2++] = '"';
+
+ /* Copy the customer_name string */
+ while( idx3 < FIELD_LENGTH && idx2 < new_customers_buffer_size && customer_name[idx3] != 0 )
+ {
+ new_customers_buffer[idx2++] = customer_name[idx3++];
+ }
+
+ /* Close customer name field with a quote */
+ new_customers_buffer[idx2++] = '"';
+
+ /* Insert field delimiters, we use a semi-colon, not a comma ';' */
+ new_customers_buffer[idx2++] = ';';
+
+ /* -----------------------------------------------------------------------------
+ * Copy the new customer address entry so it is immediately after the csv header
+ */
+
+ idx3 = 0;
+
+ /* Start with first entries opening quote */
+ new_customers_buffer[idx2++] = '\"';
+
+ /* Copy the customer_name string */
+ while( idx3 < FIELD_LENGTH && idx2 < new_customers_buffer_size && customer_address[idx3] != 0 )
+ {
+ new_customers_buffer[idx2++] = customer_address[idx3++];
+ }
+
+ /* Close customer name field with a quote */
+ new_customers_buffer[idx2++] = '\"';
+
+ /* Insert field delimiters, we use a semi-colon, not a comma ';' */
+ new_customers_buffer[idx2++] = ';';
+
+ /* -----------------------------------------------------------------------------
+ * Copy the new customer contact name entry so it is immediately after the csv header
+ */
+
+ idx3 = 0;
+
+ /* Start with first entries opening quote */
+ new_customers_buffer[idx2++] = '\"';
+
+ /* Copy the customer_name string */
+ while( idx3 < FIELD_LENGTH && idx2 < new_customers_buffer_size
+ && customer_contact_name[idx3] != 0 )
+ {
+ new_customers_buffer[idx2++] = customer_contact_name[idx3++];
+ }
+
+ /* Close customer name field with a quote */
+ new_customers_buffer[idx2++] = '\"';
+
+ /* Insert field delimiters, we use a semi-colon, not a comma ';' */
+ new_customers_buffer[idx2++] = ';';
+
+ /* -----------------------------------------------------------------------------
+ * Copy the new customer contact phone entry so it is immediately after the csv header
+ */
+
+ idx3 = 0;
+
+ /* Start with first entries opening quote */
+ new_customers_buffer[idx2++] = '\"';
+
+ /* Copy the customer_name string */
+ while( idx3 < FIELD_LENGTH && idx2 < new_customers_buffer_size
+ && customer_contact_phone[idx3] != 0 )
+ {
+ new_customers_buffer[idx2++] = customer_contact_phone[idx3++];
+ }
+
+ /* Close customer name field with a quote */
+ new_customers_buffer[idx2++] = '\"';
+
+ /* Insert a line feed to finish the new entry */
+ new_customers_buffer[idx2++] = LINEFEED;
+
+ /* skip any LINEFEEDS in the existing customer entry as we just inserted one */
+ while( customers_buffer[idx1] != 0 && customers_buffer[idx1] == LINEFEED )
+ {
+ idx1++;
+ }
+
+ /* -------------------------------------------------------------------------------
+ * Now copy the existing customer entries, if any, immediately after the new entry
+ */
+
+ while( idx1 < existing_file_size && idx2 < new_customers_buffer_size )
+ {
+ /* Removes any nulls when copying and pasting, which would break this process? */
+ if( customers_buffer[idx1] == 0 )
+ {
+ while( idx1 < existing_file_size && customers_buffer[idx1] == 0 )
+ {
+ idx1++;
+ }
+ }
+ new_customers_buffer[idx2++] = customers_buffer[idx1++];
+ }
+
+ /* Rename the customers.csv file to customers.csv.backup */
+ if( rename( nwipe_customers_file, nwipe_customers_file_backup_tmp ) != 0 )
+ {
+ nwipe_log( NWIPE_LOG_ERROR,
+ "Unable to rename %s to %s",
+ nwipe_customers_file,
+ nwipe_customers_file_backup_tmp );
+ }
+ else
+ {
+ /* Create/open the customers.csv file */
+ if( ( fptr2 = fopen( nwipe_customers_file, "wb" ) ) == NULL )
+ {
+ nwipe_log( NWIPE_LOG_ERROR, "Unable to open %s", nwipe_customers_file );
+ }
+ else
+ {
+ /* write the new customers.csv file */
+ new_customers_buffer_length = strlen( new_customers_buffer );
+
+ if( ( result_size = fwrite(
+ new_customers_buffer, sizeof( char ), new_customers_buffer_length, fptr2 ) )
+ != new_customers_buffer_length )
+ {
+ nwipe_log(
+ NWIPE_LOG_ERROR,
+ "func:write_customer_csv_entry:fwrite: Error result_size = %i not as expected",
+ result_size );
+ }
+ else
+ {
+ /* Remove the customer.csv.backup file if it exists */
+ if( remove( nwipe_customers_file_backup ) != 0 )
+ {
+ nwipe_log(
+ NWIPE_LOG_ERROR, "Unable to remove %s", nwipe_customers_file_backup_tmp );
+ }
+ else
+ {
+ /* Rename the customers.csv.backup.tmp file to customers.csv.backup */
+ if( rename( nwipe_customers_file_backup_tmp, nwipe_customers_file_backup )
+ != 0 )
+ {
+ nwipe_log( NWIPE_LOG_ERROR,
+ "Unable to rename %s to %s",
+ nwipe_customers_file,
+ nwipe_customers_file_backup_tmp );
+ }
+ nwipe_log( NWIPE_LOG_INFO,
+ "Succesfully write new customer entry to %s",
+ nwipe_customers_file );
+ }
+ }
+ fclose( fptr2 );
+ }
+ }
+ fclose( fptr );
+ }
+ }
+ free( new_customers_buffer );
+ }
+ free( customers_buffer );
+ }
+ free( csv_buffer );
+ }
+}
+
+void delete_customer_csv_entry( int* selected_entry )
+{
+ /**
+ * Deletes a line from the csv file. The line to be deleted is determined
+ * by the value of selected_entry
+ */
+ FILE* fptr = 0;
+ FILE* fptr2 = 0;
+
+ size_t result_size;
+
+ struct stat st;
+
+ extern char nwipe_customers_file[];
+ extern char nwipe_customers_file_backup[];
+ extern char nwipe_customers_file_backup_tmp[];
+
+ intmax_t existing_file_size = 0;
+
+ int linecount;
+
+ /* General index variables */
+ int idx1, idx2, idx3;
+
+ /* pointer to the buffer containing the existing customer file */
+ char* customers_buffer = 0;
+
+ /* pointer to the buffer containing the existing customer minus the deleted entry */
+ char* new_customers_buffer = 0;
+
+ int status_flag = 0;
+
+ size_t new_customers_buffer_length;
+
+ /* Determine current size of the csv file containing the customers */
+ stat( nwipe_customers_file, &st );
+ existing_file_size = st.st_size;
+
+ /* calloc sufficient storage to hold the existing customers file */
+ if( !( customers_buffer = calloc( 1, existing_file_size + 1 ) ) )
+ {
+ nwipe_log( NWIPE_LOG_ERROR,
+ "func:nwipe_gui_delete_customer_csv_entry:customers_buffer, calloc returned NULL " );
+ }
+ else
+ {
+ /* create a second buffer which is identical in size to the first, it will store the customer
+ * csv file minus the one selected entry
+ */
+
+ if( !( new_customers_buffer = calloc( 1, existing_file_size + 1 ) ) )
+ {
+ nwipe_log( NWIPE_LOG_ERROR,
+ "func:nwipe_gui_delete_customer_csv_entry:customers_buffer, calloc returned NULL " );
+ }
+ else
+ {
+ /* Read the whole of customers.csv file into customers_buffer */
+ if( ( fptr = fopen( nwipe_customers_file, "rb" ) ) == NULL )
+ {
+ nwipe_log( NWIPE_LOG_ERROR,
+ "func:nwipe_gui_delete_customer_csv_entry:Unable to open %s",
+ nwipe_customers_file );
+ }
+ else
+ {
+ /* Read the customers.csv file and populate the list array with the data */
+ if( ( result_size = fread( customers_buffer, existing_file_size, 1, fptr ) ) != 1 )
+ {
+ nwipe_log( NWIPE_LOG_ERROR,
+ "func:nwipe_gui_delete_customer_csv_entry:Error reading customers file, # elements read "
+ "not as expected "
+ "%i elements",
+ result_size );
+ }
+ else
+ {
+ /* --------------------------------------------------------------------
+ * Read the first line which is the csv header from the existing customer
+ * buffer & write to the new buffer.
+ */
+
+ idx1 = 0; // Index for the current csv buffer
+ idx2 = 0; // Index for the new csv buffer
+
+ linecount = 1; // count the lines in the csv file starting at line 1
+
+ while( idx1 < existing_file_size && idx2 < existing_file_size )
+ {
+ if( customers_buffer[idx1] != LINEFEED )
+ {
+ new_customers_buffer[idx2++] = customers_buffer[idx1++];
+ }
+ else
+ {
+ new_customers_buffer[idx2++] = customers_buffer[idx1++];
+ break;
+ }
+ }
+
+ /* -------------------------------------------------------------------------------
+ * Now copy the existing customer entries, counting the lines as we go and when we
+ * get to the the line selected for deletion we skip over it and then carry on
+ * copying.
+ */
+
+ while( idx1 < existing_file_size && idx2 < existing_file_size )
+ {
+ /* Don't copy nulls */
+ if( customers_buffer[idx1] == 0 )
+ {
+ idx1++;
+ continue;
+ }
+
+ /* Is this the line to delete? */
+ if( linecount == *selected_entry )
+ {
+ /* skip all the characters in this line */
+ while( idx1 < existing_file_size && customers_buffer[idx1] != LINEFEED )
+ {
+ idx1++;
+ }
+
+ /* skip the trailing linefeed if it exists, may not exist if last line */
+ if( customers_buffer[idx1] == LINEFEED )
+ {
+ idx1++;
+ }
+ linecount++;
+ nwipe_log( NWIPE_LOG_INFO, "Deleted customer entry from cache" );
+ status_flag = 1;
+ }
+ else
+ {
+ /* Is the character a LINEFEED? */
+ if( customers_buffer[idx1] == LINEFEED )
+ {
+ linecount++;
+ }
+
+ /* Copy a character */
+ new_customers_buffer[idx2++] = customers_buffer[idx1++];
+ }
+ }
+
+ /* Rename the customers.csv file to customers.csv.backup */
+ if( rename( nwipe_customers_file, nwipe_customers_file_backup_tmp ) != 0 )
+ {
+ nwipe_log( NWIPE_LOG_ERROR,
+ "func:delete_customer_csv_entry:Unable to rename %s to %s",
+ nwipe_customers_file,
+ nwipe_customers_file_backup_tmp );
+ }
+ else
+ {
+ /* Create/open the customers.csv file */
+ if( ( fptr2 = fopen( nwipe_customers_file, "wb" ) ) == NULL )
+ {
+ nwipe_log( NWIPE_LOG_ERROR,
+ "func:delete_customer_csv_entry:Unable to open %s",
+ nwipe_customers_file );
+ }
+ else
+ {
+ /* write the new customers.csv file */
+ new_customers_buffer_length = strlen( new_customers_buffer );
+
+ if( ( result_size = fwrite(
+ new_customers_buffer, sizeof( char ), new_customers_buffer_length, fptr2 ) )
+ != new_customers_buffer_length )
+ {
+ nwipe_log(
+ NWIPE_LOG_ERROR,
+ "func:delete_customer_csv_entry:fwrite: Error result_size = %i not as expected",
+ result_size );
+ }
+ else
+ {
+ /* Remove the customer.csv.backup file if it exists */
+ if( remove( nwipe_customers_file_backup ) != 0 )
+ {
+ nwipe_log( NWIPE_LOG_ERROR,
+ "func:delete_customer_csv_entry:Unable to remove %s",
+ nwipe_customers_file_backup_tmp );
+ }
+ else
+ {
+ /* Rename the customers.csv.backup.tmp file to customers.csv.backup */
+ if( rename( nwipe_customers_file_backup_tmp, nwipe_customers_file_backup ) != 0 )
+ {
+ nwipe_log( NWIPE_LOG_ERROR,
+ "func:delete_customer_csv_entry:Unable to rename %s to %s",
+ nwipe_customers_file,
+ nwipe_customers_file_backup_tmp );
+ }
+ if( status_flag == 1 )
+ {
+ nwipe_log(
+ NWIPE_LOG_INFO, "Deleted customer entry in %s", nwipe_customers_file );
+ }
+ else
+ {
+ nwipe_log( NWIPE_LOG_INFO,
+ "Failed to delete customer entry in %s",
+ nwipe_customers_file );
+ }
+ }
+ }
+ fclose( fptr2 );
+ }
+ }
+ }
+ fclose( fptr );
+ }
+ free( new_customers_buffer );
+ }
+ free( customers_buffer );
+ }
+}
diff --git a/src/customers.h b/src/customers.h
new file mode 100644
index 0000000..353551b
--- /dev/null
+++ b/src/customers.h
@@ -0,0 +1,42 @@
+/*
+ * ****************************************************************************
+ * customers.h: Functions related to customer processing for the PDF erasure *
+ * certificate. *
+ * ****************************************************************************
+ *
+ * Copyright PartialVolume <https://github.com/PartialVolume>.
+ *
+ * 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, version 2.
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#ifndef CUSTOMERS_H_INCLUDED
+#define CUSTOMERS_H_INCLUDED
+
+void customer_processes( int );
+void select_customers( int, char** );
+void delete_customer();
+void add_customer();
+void write_customer_csv_entry( char*, char*, char*, char* );
+void delete_customer_csv_entry( int* );
+
+typedef char* nwipe_customers_buffer_t;
+typedef char** nwipe_customers_pointers_t;
+
+#define SELECT_CUSTOMER 1
+#define DELETE_CUSTOMER 2
+
+#define LINEFEED 0x0A
+
+#endif // CUSTOMERS_H_INCLUDED
diff --git a/src/device.c b/src/device.c
new file mode 100644
index 0000000..8475f30
--- /dev/null
+++ b/src/device.c
@@ -0,0 +1,917 @@
+/*
+ * device.c: Device routines for nwipe.
+ *
+ * Copyright Darik Horn <dajhorn-dban@vanadac.com>.
+ *
+ * Modifications to original dwipe Copyright Andy Beverley <andy@andybev.com>
+ *
+ * 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, version 2.
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#ifndef _DEFAULT_SOURCE
+#define _DEFAULT_SOURCE
+#endif
+
+#include <stdio.h>
+#include <stdint.h>
+#include <ctype.h>
+
+#include "nwipe.h"
+#include "context.h"
+#include "device.h"
+#include "method.h"
+#include "options.h"
+#include "logging.h"
+#include <sys/ioctl.h>
+#include <linux/hdreg.h> // Drive specific defs
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include "hpa_dco.h"
+#include "miscellaneous.h"
+
+#include <parted/parted.h>
+#include <parted/debug.h>
+
+int check_device( nwipe_context_t*** c, PedDevice* dev, int dcount );
+char* trim( char* str );
+
+extern int terminate_signal;
+
+int nwipe_device_scan( nwipe_context_t*** c )
+{
+ /**
+ * Scans the filesystem for storage device names.
+ *
+ * @parameter device_names A reference to a null array pointer.
+ * @modifies device_names Populates device_names with an array of nwipe_context_t
+ * @returns The number of strings in the device_names array.
+ *
+ */
+
+ PedDevice* dev = NULL;
+ ped_device_probe_all();
+
+ int dcount = 0;
+
+ while( ( dev = ped_device_get_next( dev ) ) )
+ {
+ /* to have some progress indication. can help if there are many/slow disks */
+ fprintf( stderr, "." );
+
+ if( check_device( c, dev, dcount ) )
+ dcount++;
+
+ /* Don't bother scanning drives if the terminate signal is active ! as in the case of
+ * the readlink program missing which is required if the --nousb option has been specified */
+ if( terminate_signal == 1 )
+ {
+ break;
+ }
+ }
+
+ /* Return the number of devices that were found. */
+ return dcount;
+
+} /* nwipe_device_scan */
+
+int nwipe_device_get( nwipe_context_t*** c, char** devnamelist, int ndevnames )
+{
+ PedDevice* dev = NULL;
+
+ int i;
+ int dcount = 0;
+
+ for( i = 0; i < ndevnames; i++ )
+ {
+ /* to have some progress indication. can help if there are many/slow disks */
+ fprintf( stderr, "." );
+
+ dev = ped_device_get( devnamelist[i] );
+ if( !dev )
+ {
+ nwipe_log( NWIPE_LOG_WARNING, "Device %s not found", devnamelist[i] );
+ continue;
+ }
+
+ if( check_device( c, dev, dcount ) )
+ dcount++;
+
+ /* Don't bother scanning drives if the terminate signal is active ! as in the case of
+ * the readlink program missing which is required if the --nousb option has been specified */
+ if( terminate_signal == 1 )
+ {
+ break;
+ }
+ }
+
+ /* Return the number of devices that were found. */
+ return dcount;
+
+} /* nwipe_device_get */
+
+int check_device( nwipe_context_t*** c, PedDevice* dev, int dcount )
+{
+ /* Populate this struct, then assign it to overall array of structs. */
+ nwipe_context_t* next_device;
+ int fd;
+ int idx;
+ int r;
+ char tmp_serial[NWIPE_SERIALNUMBER_LENGTH + 1];
+ nwipe_device_t bus;
+ int is_ssd;
+ int check_HPA; // a flag that indicates whether we check for a HPA on this device
+
+ bus = 0;
+
+ /* Check whether this drive is on the excluded drive list ? */
+ idx = 0;
+ while( idx < MAX_NUMBER_EXCLUDED_DRIVES )
+ {
+ if( !strcmp( dev->path, nwipe_options.exclude[idx++] ) )
+ {
+ nwipe_log( NWIPE_LOG_NOTICE, "Device %s excluded as per command line option -e", dev->path );
+ return 0;
+ }
+ }
+
+ /* Check whether the user has specified using the --nousb option
+ * that all USB devices should not be displayed or wiped whether
+ * in GUI, --nogui or --autonuke modes */
+
+ if( nwipe_options.nousb )
+ {
+ /* retrieve bus and drive serial number, HOWEVER we are only interested in the bus at this time */
+ r = nwipe_get_device_bus_type_and_serialno( dev->path, &bus, &is_ssd, tmp_serial );
+
+ /* See nwipe_get_device_bus_type_and_serialno() function for meaning of these codes */
+ if( r == 0 || ( r >= 3 && r <= 6 ) )
+ {
+ if( bus == NWIPE_DEVICE_USB )
+ {
+ nwipe_log( NWIPE_LOG_NOTICE, "Device %s ignored as per command line option --nousb", dev->path );
+ return 0;
+ }
+ }
+ else
+ {
+ if( r == 2 )
+ {
+ nwipe_log(
+ NWIPE_LOG_NOTICE, "--nousb requires the 'readlink' program, please install readlink", dev->path );
+ terminate_signal = 1;
+ return 0;
+ }
+ }
+ }
+
+ /* Try opening the device to see if it's valid. Close on completion. */
+ if( !ped_device_open( dev ) )
+ {
+ nwipe_log( NWIPE_LOG_FATAL, "Unable to open device" );
+ return 0;
+ }
+ ped_device_close( dev );
+
+ /* New device, reallocate memory for additional struct pointer */
+ *c = realloc( *c, ( dcount + 1 ) * sizeof( nwipe_context_t* ) );
+
+ next_device = malloc( sizeof( nwipe_context_t ) );
+
+ /* Check the allocation. */
+ if( !next_device )
+ {
+ nwipe_perror( errno, __FUNCTION__, "malloc" );
+ nwipe_log( NWIPE_LOG_FATAL, "Unable to create the array of enumeration contexts." );
+ return 0;
+ }
+
+ /* Zero the allocation. */
+ memset( next_device, 0, sizeof( nwipe_context_t ) );
+
+ /* Get device information */
+ next_device->device_model = dev->model;
+ remove_ATA_prefix( next_device->device_model );
+
+ /* Some USB adapters have drive model endian swapped, pattern match and fix */
+ fix_endian_model_names( next_device->device_model );
+
+ /* full device name, i.e. /dev/sda */
+ next_device->device_name = dev->path;
+
+ /* remove /dev/ from device, right justify and prefix name so string length is eight characters */
+ nwipe_strip_path( next_device->device_name_without_path, next_device->device_name );
+
+ /* To maintain column alignment in the gui we have to remove /dev/ from device names that
+ * exceed eight characters including the /dev/ path.
+ */
+ if( strlen( next_device->device_name ) > MAX_LENGTH_OF_DEVICE_STRING )
+ {
+ strcpy( next_device->gui_device_name, next_device->device_name_without_path );
+ }
+ else
+ {
+ strcpy( next_device->gui_device_name, next_device->device_name );
+ }
+
+ next_device->device_size = dev->length * dev->sector_size;
+ next_device->device_sector_size = dev->sector_size; // logical sector size
+ next_device->device_block_size = dev->sector_size; // set as logical but could be a multiple of logical sector size
+ next_device->device_phys_sector_size = dev->phys_sector_size; // physical sector size
+ next_device->device_size_in_sectors = next_device->device_size / next_device->device_sector_size;
+ next_device->device_size_in_512byte_sectors = next_device->device_size / 512;
+ Determine_C_B_nomenclature( next_device->device_size, next_device->device_size_txt, NWIPE_DEVICE_SIZE_TXT_LENGTH );
+ next_device->device_size_text = next_device->device_size_txt;
+ next_device->result = -2;
+
+ /* Attempt to get serial number of device.
+ */
+ next_device->device_serial_no[0] = 0; /* initialise the serial number */
+
+ if( ( fd = open( next_device->device_name = dev->path, O_RDONLY ) ) == ERR )
+ {
+ nwipe_log( NWIPE_LOG_WARNING, "Unable to open device %s to obtain serial number", next_device->device_name );
+ }
+
+ /*
+ * We don't check the ioctl return status because there are plenty of situations where a serial number may not be
+ * returned by ioctl such as USB drives, logical volumes, encryted volumes, so the log file would have multiple
+ * benign ioctl errors reported which isn't necessarily a problem.
+ */
+ ioctl( fd, HDIO_GET_IDENTITY, &next_device->identity );
+ close( fd );
+
+ for( idx = 0; idx < NWIPE_SERIALNUMBER_LENGTH; idx++ )
+ {
+ if( isascii( next_device->identity.serial_no[idx] ) && !iscntrl( next_device->identity.serial_no[idx] ) )
+ {
+ next_device->device_serial_no[idx] = next_device->identity.serial_no[idx];
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ // Terminate the string.
+ next_device->device_serial_no[idx] = 0;
+
+ // Remove leading/trailing whitespace from serial number and left justify.
+ trim( (char*) next_device->device_serial_no );
+
+ /* if we couldn't obtain serial number by using the above method .. try this */
+ r = nwipe_get_device_bus_type_and_serialno(
+ next_device->device_name, &next_device->device_type, &next_device->device_is_ssd, tmp_serial );
+
+ /* If serial number & bus retrieved (0) OR unsupported USB bus identified (5) */
+ if( r == 0 || r == 5 )
+ {
+ /* If the serial number hasn't already been populated */
+ if( next_device->device_serial_no[0] == 0 )
+ {
+ strncpy( next_device->device_serial_no, tmp_serial, NWIPE_SERIALNUMBER_LENGTH );
+ }
+ }
+
+ /* Does the user want to anonymize serial numbers ? */
+ if( nwipe_options.quiet )
+ {
+ if( next_device->device_serial_no[0] == 0 )
+ {
+ strncpy( next_device->device_serial_no, "????????????????????", NWIPE_SERIALNUMBER_LENGTH + 1 );
+ }
+ else
+ {
+ strncpy( next_device->device_serial_no, "XXXXXXXXXXXXXXXXXXXX", NWIPE_SERIALNUMBER_LENGTH + 1 );
+ }
+ }
+ /* strncpy would have copied the null terminator BUT just to be sure, just in case somebody changes the length
+ * of those strings we should explicitly terminate the string */
+ next_device->device_serial_no[NWIPE_SERIALNUMBER_LENGTH] = 0;
+
+ /* Initialise the variables that toggle the [size][temp c] with [HPA status]
+ * Not currently used, but may be used in the future or for other purposes
+ */
+ next_device->HPA_toggle_time = time( NULL );
+ next_device->HPA_display_toggle_state = 0;
+
+ /* Initialise the HPA variables for this device
+ */
+ next_device->HPA_reported_set = 0;
+ next_device->HPA_reported_real = 0;
+ next_device->DCO_reported_real_max_sectors = 0;
+ next_device->HPA_status = HPA_NOT_APPLICABLE;
+
+ /* All device strings should be 4 characters, prefix with space if under 4 characters
+ * We also set a switch for certain devices to check for the host protected area (HPA)
+ */
+ check_HPA = 0;
+
+ // WARNING TEMP LINE WARNING
+ // next_device->device_type = NWIPE_DEVICE_ATA;
+
+ switch( next_device->device_type )
+ {
+ case NWIPE_DEVICE_UNKNOWN:
+ strcpy( next_device->device_type_str, " UNK" );
+ check_HPA = 1;
+ break;
+
+ case NWIPE_DEVICE_IDE:
+ strcpy( next_device->device_type_str, " IDE" );
+ check_HPA = 1;
+ break;
+
+ case NWIPE_DEVICE_SCSI:
+ strcpy( next_device->device_type_str, " SCSI" );
+ check_HPA = 1;
+ break;
+
+ case NWIPE_DEVICE_COMPAQ:
+ strcpy( next_device->device_type_str, " CPQ" );
+ break;
+
+ case NWIPE_DEVICE_USB:
+ strcpy( next_device->device_type_str, " USB" );
+ check_HPA = 1;
+ break;
+
+ case NWIPE_DEVICE_IEEE1394:
+ strcpy( next_device->device_type_str, "1394" );
+ break;
+
+ case NWIPE_DEVICE_ATA:
+ strcpy( next_device->device_type_str, " ATA" );
+ check_HPA = 1;
+ break;
+
+ case NWIPE_DEVICE_NVME:
+ strcpy( next_device->device_type_str, "NVME" );
+ break;
+
+ case NWIPE_DEVICE_VIRT:
+ strcpy( next_device->device_type_str, "VIRT" );
+ break;
+
+ case NWIPE_DEVICE_SAS:
+ strcpy( next_device->device_type_str, " SAS" );
+ check_HPA = 1;
+ break;
+
+ case NWIPE_DEVICE_MMC:
+ strcpy( next_device->device_type_str, " MMC" );
+ break;
+ }
+ if( next_device->device_is_ssd )
+ {
+ strcpy( next_device->device_type_str + 4, "-SSD" );
+ }
+ else
+ {
+ strcpy( next_device->device_type_str + 4, " " );
+ }
+
+ if( strlen( (const char*) next_device->device_serial_no ) )
+ {
+ snprintf( next_device->device_label,
+ NWIPE_DEVICE_LABEL_LENGTH,
+ "%s %s [%s] %s/%s",
+ next_device->device_name,
+ next_device->device_type_str,
+ next_device->device_size_text,
+ next_device->device_model,
+ next_device->device_serial_no );
+ }
+ else
+ {
+ snprintf( next_device->device_label,
+ NWIPE_DEVICE_LABEL_LENGTH,
+ "%s %s [%s] %s",
+ next_device->device_name,
+ next_device->device_type_str,
+ next_device->device_size_text,
+ next_device->device_model );
+ }
+
+ nwipe_log( NWIPE_LOG_NOTICE,
+ "Found %s, %s, %s, %s, S/N=%s",
+ next_device->device_name,
+ next_device->device_type_str,
+ next_device->device_model,
+ next_device->device_size_text,
+ next_device->device_serial_no );
+
+ nwipe_log( NWIPE_LOG_INFO,
+ "%s, sector(logical)/block(physical) sizes %i/%i",
+ next_device->device_name,
+ dev->sector_size,
+ dev->phys_sector_size );
+
+ /******************************
+ * Check for hidden sector_size
+ */
+ if( check_HPA == 1 )
+ {
+ hpa_dco_status( next_device );
+ }
+
+ /* print an empty line to separate the drives in the log */
+ nwipe_log( NWIPE_LOG_INFO, " " );
+
+ ( *c )[dcount] = next_device;
+ return 1;
+}
+
+/* Remove leading/trailing whitespace from a string and left justify result */
+char* trim( char* str )
+{
+ size_t len = 0;
+ char* frontp = str;
+ char* endp = NULL;
+
+ if( str == NULL )
+ {
+ return NULL;
+ }
+ if( str[0] == '\0' )
+ {
+ return str;
+ }
+ len = strlen( str );
+ endp = str + len;
+
+ /*
+ * Move the front and back pointers to address the first non-whitespace
+ * characters from each end.
+ */
+ while( isspace( (unsigned char) *frontp ) )
+ {
+ ++frontp;
+ }
+ if( endp != frontp )
+ {
+ while( isspace( (unsigned char) *( --endp ) ) && endp != frontp )
+ {
+ }
+ }
+ if( str + len - 1 != endp )
+ *( endp + 1 ) = '\0';
+ else if( frontp != str && endp == frontp )
+ *str = '\0';
+ /*
+ * Shift the string so that it starts at str so that if it's dynamically
+ * allocated, we can still free it on the returned pointer. Note the reuse
+ * of endp to mean the front of the string buffer now.
+ */
+ endp = str;
+ if( frontp != str )
+ {
+ while( *frontp )
+ {
+ *endp++ = *frontp++;
+ }
+ *endp = '\0';
+ }
+ return str;
+}
+
+int nwipe_get_device_bus_type_and_serialno( char* device, nwipe_device_t* bus, int* is_ssd, char* serialnumber )
+{
+ /* The caller provides a string that contains the device, i.e. /dev/sdc, also a pointer
+ * to an integer (bus type), another pointer to an integer (is_ssd), and finally a 21 byte
+ * character string which this function populates with the serial number (20 characters + null terminator).
+ *
+ * The function populates the bus integer and serial number strings for the given device.
+ * Results for bus would typically be ATA or USB see nwipe_device_t in context.h
+ *
+ * Return Values:
+ * 0 = Success
+ * 1 = popen failed to create stream for readlink
+ * 2 = readlink exit code not 0, see nwipe logs
+ * 3 = popen failed to create stream for smartctl
+ * 4 = smartctl command not found, install smartmontools
+ * 5 = smartctl detected unsupported USB to IDE/SATA adapter
+ * 6 = All other errors !
+ *
+ */
+
+ FILE* fp;
+
+ int r; // A result buffer.
+ int idx_src;
+ int idx_dest;
+ int device_len;
+ int set_return_value;
+ int exit_status;
+ int idx;
+ int idx2;
+
+ char readlink_command[] = "readlink /sys/block/%s";
+ char readlink_command2[] = "/usr/bin/readlink /sys/block/%s";
+ char readlink_command3[] = "/sbin/readlink /sys/block/%s";
+ char smartctl_command[] = "smartctl -i %s";
+ char smartctl_command2[] = "/sbin/smartctl -i %s";
+ char smartctl_command3[] = "/usr/bin/smartctl -i %s";
+ char device_shortform[50];
+ char result[512];
+ char final_cmd_readlink[sizeof( readlink_command ) + sizeof( device_shortform )];
+ char final_cmd_smartctl[sizeof( smartctl_command ) + 256];
+ char* pResult;
+ char smartctl_labels_to_anonymize[][18] = {
+ "serial number:", "lu wwn device id:", "logical unit id:", "" /* Don't remove this empty string !, important */
+ };
+
+ /* Initialise return value */
+ set_return_value = 0;
+
+ *bus = 0;
+
+ /* Scan device name and if device is for instance /dev/sdx then convert to sdx
+ * If already sdx then just copy. */
+
+ idx_dest = 0;
+ device_shortform[idx_dest] = 0;
+ device_len = strlen( device );
+ idx_src = device_len;
+
+ while( idx_src >= 0 )
+ {
+ if( device[idx_src] == '/' || idx_src == 0 )
+ {
+ idx_src++;
+
+ /* Now scan forwards copying the short form device i.e sdc */
+ while( idx_src < device_len )
+ {
+ device_shortform[idx_dest++] = device[idx_src++];
+ }
+ break;
+ }
+ else
+ {
+ idx_src--;
+ }
+ }
+ device_shortform[idx_dest] = 0;
+
+ final_cmd_readlink[0] = 0;
+
+ /* Determine whether we can access readlink, required if the PATH environment is not setup ! (Debian sid 'su' as
+ * opposed to 'su -' */
+ if( system( "which readlink > /dev/null 2>&1" ) )
+ {
+ if( system( "which /sbin/readlink > /dev/null 2>&1" ) )
+ {
+ if( system( "which /usr/bin/readlink > /dev/null 2>&1" ) )
+ {
+ nwipe_log( NWIPE_LOG_WARNING, "Command not found. Install readlink !" );
+ set_return_value = 2;
+
+ /* Return immediately if --nousb specified. Readlink is a requirement for this option. */
+ if( nwipe_options.nousb )
+ {
+ return set_return_value;
+ }
+ }
+ else
+ {
+ sprintf( final_cmd_readlink, readlink_command3, device_shortform );
+ }
+ }
+ else
+ {
+ sprintf( final_cmd_readlink, readlink_command2, device_shortform );
+ }
+ }
+ else
+ {
+ sprintf( final_cmd_readlink, readlink_command, device_shortform );
+ }
+
+ if( final_cmd_readlink[0] != 0 )
+ {
+
+ fp = popen( final_cmd_readlink, "r" );
+
+ if( fp == NULL )
+ {
+ nwipe_log( NWIPE_LOG_WARNING,
+ "nwipe_get_device_bus_type_and_serialno: Failed to create stream to %s",
+ readlink_command );
+
+ set_return_value = 1;
+ }
+
+ if( fp != NULL )
+ {
+ /* Read the output a line at a time - output it. */
+ if( fgets( result, sizeof( result ) - 1, fp ) != NULL )
+ {
+ if( nwipe_options.verbose )
+ {
+ strip_CR_LF( result );
+ nwipe_log( NWIPE_LOG_DEBUG, "Readlink: %s", result );
+ }
+
+ /* Scan the readlink results for bus types, i.e. USB or ATA
+ * Example: readlink
+ * /sys/block/sdd../devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.3/2-1.3:1.0/host6/target6:0:0/6:0:0:0/block/sdd
+ */
+
+ if( strstr( result, "/usb" ) != 0 )
+ {
+ *bus = NWIPE_DEVICE_USB;
+ }
+ else
+ {
+ if( strstr( result, "/ata" ) != 0 )
+ {
+ *bus = NWIPE_DEVICE_ATA;
+ }
+ else
+ {
+ if( strstr( result, "/nvme/" ) != 0 )
+ {
+ *bus = NWIPE_DEVICE_NVME;
+ }
+ else
+ {
+ if( strstr( result, "/virtual/" ) != 0 )
+ {
+ *bus = NWIPE_DEVICE_VIRT;
+ }
+ else
+ {
+ if( strstr( result, "/mmcblk" ) != 0 )
+ {
+ *bus = NWIPE_DEVICE_MMC;
+ }
+ }
+ }
+ }
+ }
+ }
+ /* close */
+ r = pclose( fp );
+
+ if( r > 0 )
+ {
+ exit_status = WEXITSTATUS( r );
+ if( nwipe_options.verbose )
+ {
+ nwipe_log( NWIPE_LOG_WARNING,
+ "nwipe_get_device_bus_type_and_serialno(): readlink failed, \"%s\" exit status = %u",
+ final_cmd_readlink,
+ exit_status );
+ }
+
+ if( exit_status == 127 )
+ {
+ nwipe_log( NWIPE_LOG_WARNING, "Command not found. Install Readlink recommended !" );
+ set_return_value = 2;
+ if( nwipe_options.nousb )
+ {
+ return set_return_value;
+ }
+ }
+ }
+ }
+ }
+
+ /*
+ * Retrieve smartmontools drive information if USB bridge supports it, so we can retrieve the serial number of the
+ * drive that's on the other side of the USB bridge.. */
+
+ final_cmd_smartctl[0] = 0;
+
+ /* Determine whether we can access smartctl, required if the PATH environment is not setup ! (Debian sid 'su' as
+ * opposed to 'su -' */
+ if( system( "which smartctl > /dev/null 2>&1" ) )
+ {
+ if( system( "which /sbin/smartctl > /dev/null 2>&1" ) )
+ {
+ if( system( "which /usr/bin/smartctl > /dev/null 2>&1" ) )
+ {
+ nwipe_log( NWIPE_LOG_WARNING, "Command not found. Install smartmontools !" );
+ }
+ else
+ {
+ sprintf( final_cmd_smartctl, smartctl_command3, device );
+ }
+ }
+ else
+ {
+ sprintf( final_cmd_smartctl, smartctl_command2, device );
+ }
+ }
+ else
+ {
+ sprintf( final_cmd_smartctl, smartctl_command, device );
+ }
+
+ if( final_cmd_smartctl[0] != 0 )
+ {
+ fp = popen( final_cmd_smartctl, "r" );
+
+ if( fp == NULL )
+ {
+ nwipe_log( NWIPE_LOG_WARNING,
+ "nwipe_get_device_bus_type_and_serialno(): Failed to create stream to %s",
+ smartctl_command );
+
+ set_return_value = 3;
+ }
+ else
+ {
+ /* Read the output a line at a time - output it. */
+ while( fgets( result, sizeof( result ) - 1, fp ) != NULL )
+ {
+ /* Convert the label, i.e everything before the ':' to lower case, it's required to
+ * convert to lower case as smartctl seems to use inconsistent case when labeling
+ * for serial number, i.e mostly it produces labels "Serial Number:" but occasionally
+ * it produces a label "Serial number:" */
+
+ idx = 0;
+ while( result[idx] != 0 && result[idx] != ':' )
+ {
+ /* If upper case alpha character, change to lower case */
+ if( result[idx] >= 'A' && result[idx] <= 'Z' )
+ {
+ result[idx] = tolower( result[idx] );
+ }
+
+ idx++;
+ }
+
+ if( nwipe_options.verbose && result[0] != 0x0A )
+ {
+ strip_CR_LF( result );
+
+ /* Remove serial number if -q option specified */
+ if( nwipe_options.quiet )
+ {
+ /* initialise index into string array */
+ idx2 = 0;
+
+ while( smartctl_labels_to_anonymize[idx2][0] != 0 )
+ {
+ if( ( pResult = strstr( result, &smartctl_labels_to_anonymize[idx2][0] ) ) != 0 )
+ {
+ /* set index to character after end of label string */
+ idx = strlen( &smartctl_labels_to_anonymize[idx2][0] );
+
+ /* Ignore spaces, overwrite other characters */
+ while( *( pResult + idx ) != 0x0A && *( pResult + idx ) != 0x0D
+ && *( pResult + idx ) != 0 && idx <= sizeof( result ) - 1 )
+ {
+ if( *( pResult + idx ) == ' ' )
+ {
+ idx++;
+ continue;
+ }
+ else
+ {
+ /* ignore if the serial number has been written over with '?' */
+ if( *( pResult + idx ) != '?' )
+ {
+ *( pResult + idx ) = 'X';
+ }
+ idx++;
+ }
+ }
+ }
+ idx2++;
+ }
+ }
+
+ nwipe_log( NWIPE_LOG_INFO, "smartctl: %s", result );
+ }
+
+ if( strstr( result, "serial number:" ) != 0 )
+ {
+ /* strip any leading or trailing spaces and left justify, +15 is the length of "Serial Number:" */
+ trim( &result[15] );
+
+ strncpy( serialnumber, &result[15], NWIPE_SERIALNUMBER_LENGTH );
+ serialnumber[NWIPE_SERIALNUMBER_LENGTH] = 0;
+ }
+
+ if( *bus == 0 )
+ {
+ if( strstr( result, "transport protocol:" ) != 0 )
+ {
+ /* strip any leading or trailing spaces and left justify, +4 is the length of "bus type:" */
+ trim( &result[19] );
+ for( idx = 19; result[idx]; idx++ )
+ {
+ result[idx] = tolower( result[idx] );
+ }
+
+ if( strncmp( &result[19], "sas", 3 ) == 0 )
+ {
+ *bus = NWIPE_DEVICE_SAS;
+ }
+ }
+
+ if( strstr( result, "sata version is:" ) != 0 )
+ {
+
+ /* strip any leading or trailing spaces and left justify, +4 is the length of "bus type:" */
+ trim( &result[16] );
+ for( idx = 16; result[idx]; idx++ )
+ {
+ result[idx] = tolower( result[idx] );
+ }
+
+ if( strncmp( &result[16], "sata", 4 ) == 0 )
+ {
+ *bus = NWIPE_DEVICE_ATA;
+ }
+ }
+ }
+ if( strstr( result, "rotation rate:" ) != 0 )
+ {
+ /* strip any leading or trailing spaces and left justify, +15 is the length of "Rotation Rate:" */
+ trim( &result[15] );
+ for( idx = 15; result[idx]; idx++ )
+ {
+ result[idx] = tolower( result[idx] );
+ }
+
+ if( strncmp( &result[15], "solid state device", 19 ) == 0 )
+ {
+ *is_ssd = 1;
+ }
+ }
+ }
+
+ /* close */
+ r = pclose( fp );
+
+ if( r > 0 )
+ {
+ exit_status = WEXITSTATUS( r );
+ if( nwipe_options.verbose && exit_status != 1 )
+ {
+ nwipe_log( NWIPE_LOG_WARNING,
+ "nwipe_get_device_bus_type_and_serialno(): smartctl failed, \"%s\" exit status = %u",
+ final_cmd_smartctl,
+ exit_status );
+ }
+ set_return_value = 6;
+
+ if( exit_status == 127 )
+ {
+ nwipe_log( NWIPE_LOG_WARNING, "Command not found. Install Smartctl recommended !" );
+
+ set_return_value = 4;
+ }
+
+ if( exit_status == 1 )
+ {
+ nwipe_log( NWIPE_LOG_WARNING, "Smartctl is unable to provide smart data for %s", device );
+
+ if( *bus == NWIPE_DEVICE_USB || *bus == NWIPE_DEVICE_MMC )
+ {
+ strcpy( serialnumber, "(S/N: unknown)" );
+ set_return_value = 5;
+ }
+ }
+ }
+ }
+ }
+
+ return set_return_value;
+}
+
+void remove_ATA_prefix( char* str )
+{
+ /* Remove "ATA" prefix if present in the model no. string, left justifing string */
+
+ int idx_pre = 4;
+ int idx_post = 0;
+
+ if( !strncmp( str, "ATA ", 4 ) )
+ {
+ while( str[idx_pre] != 0 )
+ {
+ str[idx_post++] = str[idx_pre++];
+ }
+
+ str[idx_post] = 0;
+ }
+}
diff --git a/src/device.h b/src/device.h
new file mode 100644
index 0000000..50c04c1
--- /dev/null
+++ b/src/device.h
@@ -0,0 +1,49 @@
+/*
+ * device.h: Device routines for nwipe.
+ *
+ * Copyright Darik Horn <dajhorn-dban@vanadac.com>.
+ *
+ * Modifications to original dwipe Copyright Andy Beverley <andy@andybev.com>
+ *
+ * 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, version 2.
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#ifndef DEVICE_H_
+#define DEVICE_H_
+
+#define MAX_LENGTH_OF_DEVICE_STRING 8
+
+void nwipe_device_identify( nwipe_context_t* c ); // Get hardware information about the device.
+int nwipe_device_scan( nwipe_context_t*** c ); // Find devices that we can wipe.
+
+/**
+ * Gets information about devices
+ *
+ * @parameter device_names A reference to a null array pointer.
+ * @parameter devnamelist An array of string pointers to the device names
+ * @parameter ndevnames Number of elements in devnamelist
+ * @modifies device_names Populates device_names with an array of nwipe_contect_t
+ * @returns The number of strings in the device_names array.
+ *
+ */
+int nwipe_device_get( nwipe_context_t*** c, char** devnamelist, int ndevnames ); // Get info about devices to wipe.
+
+int nwipe_get_device_bus_type_and_serialno( char*, nwipe_device_t*, int*, char* );
+void strip_CR_LF( char* );
+void determine_disk_capacity_nomenclature( u64, char* );
+void remove_ATA_prefix( char* );
+char* trim( char* );
+
+#endif /* DEVICE_H_ */
diff --git a/src/embedded_images/nwipe_exclamation.jpg b/src/embedded_images/nwipe_exclamation.jpg
new file mode 100644
index 0000000..81354ba
--- /dev/null
+++ b/src/embedded_images/nwipe_exclamation.jpg
Binary files differ
diff --git a/src/embedded_images/nwipe_exclamation.jpg.c b/src/embedded_images/nwipe_exclamation.jpg.c
new file mode 100644
index 0000000..909be88
--- /dev/null
+++ b/src/embedded_images/nwipe_exclamation.jpg.c
@@ -0,0 +1,4 @@
+/* Autogenerated by hxtools bin2c */
+#include "nwipe_exclamation.jpg.h"
+/* Autogenerated from nwipe_exclamation.jpg */
+const unsigned char bin2c_nwipe_exclamation_jpg[63304] = {0377,0330,0377,0340,0,020,'J','F','I','F',0,01,01,02,0,'v',0,'v',0,0,0377,0341,'(',022,'E','x','i','f',0,0,'I','I','*',0,010,0,0,0,07,0,022,01,03,0,01,0,0,0,01,0,0,0,032,01,05,0,01,0,0,0,'b',0,0,0,033,01,05,0,01,0,0,0,'j',0,0,0,'(',01,03,0,01,0,0,0,03,0,0,0,'1',01,02,0,015,0,0,0,'r',0,0,0,'2',01,02,0,024,0,0,0,0200,0,0,0,'i',0207,04,0,01,0,0,0,0224,0,0,0,0246,0,0,0,'v',0,0,0,01,0,0,0,'v',0,0,0,01,0,0,0,'G','I','M','P',' ','2','.','1','0','.','3','0',0,0,'2','0','2','3',':','0','3',':','1','2',' ','1','6',':','1','5',':','4','7',0,01,0,01,0240,03,0,01,0,0,0,01,0,0,0,0,0,0,0,011,0,0376,0,04,0,01,0,0,0,01,0,0,0,0,01,04,0,01,0,0,0,0377,0,0,0,01,01,04,0,01,0,0,0,0,01,0,0,02,01,03,0,03,0,0,0,030,01,0,0,03,01,03,0,01,0,0,0,06,0,0,0,06,01,03,0,01,0,0,0,06,0,0,0,025,01,03,0,01,0,0,0,03,0,0,0,01,02,04,0,01,0,0,0,036,01,0,0,02,02,04,0,01,0,0,0,0354,'&',0,0,0,0,0,0,010,0,010,0,010,0,0377,0330,0377,0340,0,020,'J','F','I','F',0,01,01,0,0,01,0,01,0,0,0377,0333,0,'C',0,010,06,06,07,06,05,010,07,07,07,011,011,010,012,014,024,015,014,013,013,014,031,022,023,017,024,035,032,037,036,035,032,034,034,' ','$','.',047,' ','"',',','#',034,034,'(','7',')',',','0','1','4','4','4',037,047,'9','=','8','2','<','.','3','4','2',0377,0333,0,'C',01,011,011,011,014,013,014,030,015,015,030,'2','!',034,'!','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','2','2','2','2','2','2','2','2','2','2','2','2','2','2','2','2','2','2',0377,0300,0,021,010,01,0,0,0377,03,01,'"',0,02,021,01,03,021,01,0377,0304,0,037,0,0,01,05,01,01,01,01,01,01,0,0,0,0,0,0,0,0,01,02,03,04,05,06,07,010,011,012,013,0377,0304,0,0265,020,0,02,01,03,03,02,04,03,05,05,04,04,0,0,01,'}',01,02,03,0,04,021,05,022,'!','1','A',06,023,'Q','a',07,'"','q',024,'2',0201,0221,0241,010,'#','B',0261,0301,025,'R',0321,0360,'$','3','b','r',0202,011,012,026,027,030,031,032,'%','&',047,'(',')','*','4','5','6','7','8','9',':','C','D','E','F','G','H','I','J','S','T','U','V','W','X','Y','Z','c','d','e','f','g','h','i','j','s','t','u','v','w','x','y','z',0203,0204,0205,0206,0207,0210,0211,0212,0222,0223,0224,0225,0226,0227,0230,0231,0232,0242,0243,0244,0245,0246,0247,0250,0251,0252,0262,0263,0264,0265,0266,0267,0270,0271,0272,0302,0303,0304,0305,0306,0307,0310,0311,0312,0322,0323,0324,0325,0326,0327,0330,0331,0332,0341,0342,0343,0344,0345,0346,0347,0350,0351,0352,0361,0362,0363,0364,0365,0366,0367,0370,0371,0372,0377,0304,0,037,01,0,03,01,01,01,01,01,01,01,01,01,0,0,0,0,0,0,01,02,03,04,05,06,07,010,011,012,013,0377,0304,0,0265,021,0,02,01,02,04,04,03,04,07,05,04,04,0,01,02,'w',0,01,02,03,021,04,05,'!','1',06,022,'A','Q',07,'a','q',023,'"','2',0201,010,024,'B',0221,0241,0261,0301,011,'#','3','R',0360,025,'b','r',0321,012,026,'$','4',0341,'%',0361,027,030,031,032,'&',047,'(',')','*','5','6','7','8','9',':','C','D','E','F','G','H','I','J','S','T','U','V','W','X','Y','Z','c','d','e','f','g','h','i','j','s','t','u','v','w','x','y','z',0202,0203,0204,0205,0206,0207,0210,0211,0212,0222,0223,0224,0225,0226,0227,0230,0231,0232,0242,0243,0244,0245,0246,0247,0250,0251,0252,0262,0263,0264,0265,0266,0267,0270,0271,0272,0302,0303,0304,0305,0306,0307,0310,0311,0312,0322,0323,0324,0325,0326,0327,0330,0331,0332,0342,0343,0344,0345,0346,0347,0350,0351,0352,0362,0363,0364,0365,0366,0367,0370,0371,0372,0377,0332,0,014,03,01,0,02,021,03,021,0,'?',0,0367,0372,'(',0242,0200,012,'(',0242,0200,012,'(',0242,0200,012,'(',0242,0200,012,'(',0242,0200,012,'(',0242,0200,012,'(',0242,0200,012,'(',0242,0200,012,'(',0242,0200,012,'(',0242,0200,012,'(',0242,0200,012,'(',0242,0200,012,'(',0242,0200,012,'(',0242,0200,012,'(',0242,0200,012,'(',0242,0200,012,'(',0242,0200,012,'(',0242,0200,012,'(',0242,0200,012,'(',0250,'.',0357,'m',0254,' ','i',0356,0347,0216,030,0227,0222,0316,0330,024,'l','4',0234,0235,0221,'=','5',0335,'#','R',0316,0312,0252,':',0226,'8',025,0345,0276,'%',0370,0307,'k','j',0315,'o',0241,0301,0366,0227,034,031,0344,0341,07,0320,'w',0257,'/',0326,'|',']',0255,0353,0256,0337,'m',0276,0221,0243,047,'>','R',0235,0252,'?',01,0134,0225,'1','p',0216,0213,'S',0336,0301,0360,0356,'&',0272,'R',0251,0356,'/','=',0376,0357,0363,'=',0353,'V',0370,0213,0341,0235,'!',0231,'%',0324,022,'i','W',0370,' ',033,0317,0351,0305,'q',0372,0207,0306,0333,'u','%','l','4',0307,0177,'F',0225,0261,'^','3','E','r','K',031,'Q',0355,0241,0364,'4','8','w',07,'O',0343,'N','O',0315,0377,0,0221,0350,'7',0177,030,'|','I','?',0372,0205,0266,0267,037,0354,0246,0343,0372,0326,'<',0377,0,021,0274,'Y','8','`','u','y','T',023,0237,0221,'U','q',0372,'W','-','E','b',0353,'T','{',0266,'z','P',0313,0260,0220,0370,'i',0307,0356,'G','C',0377,0,011,0327,0212,'?',0350,'7','w',0377,0,'}',0323,0243,0361,0357,0212,'b',0220,'8',0326,0356,0211,035,0230,0202,'?',',','W','9','E','/','i','.',0346,0277,'U',0241,0374,0213,0356,'G','i','k',0361,'S',0305,'v',0307,0346,0276,'I',0206,'s',0211,'b',07,0371,'b',0267,0354,'~','5',0352,'1',0220,'/','t',0350,'%',035,0314,'l','T',0376,'U',0345,0224,'U','*',0365,026,0314,0346,0251,0225,'`',0252,'|','T',0327,0311,'[',0362,'=',0367,'M',0370,0303,0341,0373,0255,0253,'v',0263,0332,'1',0352,'Y','w',017,0322,0273,'M','7','[',0323,'5','x',0204,0266,027,0320,0134,')',0376,0343,0202,0177,'*',0371,':',0246,0266,0273,0270,0263,0231,'f',0266,0236,'H','d','S',0220,0310,0304,032,0350,0206,'6','k',0342,'W','<',0254,'G',014,0341,0346,0257,'F','N','/',0357,'G',0327,'T','W',0200,'h','?',026,0365,0275,'0',0244,'w',0341,'o',0240,035,'w','p',0370,0372,0327,0255,0370,'w',0307,032,047,0211,'#','_',0262,0334,0210,0347,0357,04,0277,'+',03,0375,'k',0262,0236,'"',025,'4','[',0237,'7',0215,0311,0361,'X','O','z','J',0361,0356,0216,0222,0212,'(',0255,0317,'(','(',0242,0212,0,'(',0242,0212,0,'(',0242,0212,0,'(',0242,0212,0,'(',0242,0212,0,'(',0242,0212,0,'(',0246,0311,'"','E',033,'I','#',05,'E',031,',','N',0,025,0343,0276,'<',0370,0244,0322,'4',0272,'f',0201,'.',020,'|',0262,']','/',0177,'e',0377,0,032,0316,0255,'X',0323,'W',0221,0333,0201,0300,'V',0306,'T',0344,0246,0275,'_','D','v',0372,0367,0216,0354,'4',0313,0237,0354,0373,'&','[',0255,'@',0377,0,02,0237,0226,'?','v','5',0311,'M',0247,0336,0370,0246,'F','{',0334,0334,0200,'y',0363,016,021,'}',0200,0351,'^',0177,0340,0362,'d',0324,0356,'$','r','Y',0312,'d',0261,'9','$',0223,'^',0333,0244,0306,0261,0351,'V',0301,'F','2',0201,0217,0324,0327,0205,0233,'f',0262,0302,'a','U','e',024,0333,'v','W',0331,035,0270,0354,':',0301,'W','T','(',0266,0254,0223,'o',0253,0377,0,'$','r',037,0360,0257,'`',0377,0,0237,'[','O',0326,0250,'j','~',010,0266,0266,0200,0371,0266,'Q',0252,036,'<',0310,0273,032,0364,0272,0212,0346,'5',0232,0326,'X',0334,'e','Y','H','5',0363,'X','n',047,0256,0352,0305,'U',0204,0134,'[',0326,0312,0314,0347,0372,0306,'!','j',0252,'J',0376,0254,0371,0342,0377,0,'D',0272,0265,0324,0276,0311,014,'R','N','_',0230,0366,')','%',0207,0320,'W','E',0244,0374,'+',0361,'.',0244,025,0345,0267,'[','8',0317,'y',0217,'?',0225,'z',0327,0201,'c',0214,0333,0335,'H','Q','K',0207,0,'6','9',0306,'=','k',0260,0257,0271,'X','(','^',0355,0350,'z','r',0342,'l','G',0263,'Q',0214,'W','7','W',0377,0,0,0362,'+','/',0202,'1',0200,015,0356,0252,0304,0367,021,'&','?',0235,'m','A',0360,'s',0303,'q',0217,0336,0265,0334,0247,035,0344,0307,0362,0257,'C',0242,0266,'X','j','K',0241,0347,'T',0316,0261,0323,0336,0243,'^',0232,034,'R','|',')',0360,0222,' ','S','a','#',021,0334,0314,0331,'?',0255,'E','7',0302,'?',012,'J',0304,0255,0274,0361,0361,0321,'f','l',017,0316,0273,0252,'*',0275,0205,'?',0345,'F','K','4',0306,0247,0177,'k','/',0275,0236,'c','u',0360,'W','H',0220,0177,0243,'_',0334,0304,0177,0332,0303,'W','9',0250,0374,026,0325,'`','V','k',033,0330,'n','1',0321,0134,'m','&',0275,0306,0212,0211,'a','i','>',0207,'M',',',0373,035,'M',0374,'w',0365,'G',0312,0372,0277,0205,0265,0275,015,0210,0277,0323,0346,0215,'G',0374,0264,013,0225,'?',0210,0254,'z',0372,0371,0343,'I','P',0244,0212,0254,0247,0250,'a',0220,'k',0220,0327,0276,032,0370,0177,'[','V','e',0267,026,0223,0221,0304,0220,014,'s',0356,';',0327,'4',0360,'M','|',014,0366,0360,0274,'O',011,'Y','b','!','o','5',0376,'G',0316,024,0370,0245,0222,031,026,'H',0235,0221,0324,0344,'2',0234,021,']',0207,0211,'>',032,'k',0232,03,'<',0261,0305,0366,0313,'A',0317,0233,020,0344,017,'q',0134,'i',04,022,010,0301,035,'A',0256,')','B','P','v',0222,'>',0226,0216,'"',0226,'"',034,0324,0244,0232,'=','7',0301,0337,026,'.',0364,0342,0226,'Z',0341,'k',0233,'n',0213,'?',0361,0247,0327,0324,'W',0263,0351,0272,0235,0226,0255,'f',0227,'V','7',011,'<','-',0321,0220,0347,037,'Z',0371,'*',0267,'|','1',0342,0275,'G',0302,0327,0302,'{','9',011,0211,0217,0357,'a',047,0345,'q',0376,'>',0365,0325,'G',027,'(',0351,'-','Q',0341,0346,'y',05,'*',0367,0251,'C',0335,0227,'n',0217,0374,0217,0250,0350,0254,017,013,'x',0267,'O',0361,'V',0236,0263,0332,0270,'I',0200,0375,0354,04,0374,0310,'k','~',0275,'8',0311,'I',']',037,017,'V',0224,0350,0315,0323,0250,0254,0320,'Q','E','s',0332,0337,0212,'m',0364,0274,0305,036,'$',0230,'u',0347,0205,0246,'f','t','4','d','z',0327,0217,0352,'~',':',0270,0221,0310,'7','L','?',0331,0216,0261,0337,0306,0267,0231,0371,'e',0237,0361,'z',0302,'x',0252,'0','v',0224,0214,0335,'X','.',0247,0274,'d','z',0321,'^',014,0276,'5',0276,0335,0363,'K','6','=',0234,0327,0240,0370,'#','T',0272,0275,0225,0326,'G','w',0214,0246,0357,0234,0347,06,0235,'*',0364,0352,0374,016,0343,0214,0343,'-',0216,0342,0212,'(',0255,0213,012,'k',0272,0305,033,';',0260,'U','Q',0222,'O','a','N',0257,036,0370,0245,0343,0306,0336,0372,06,0227,')',0134,'q','s','*',0237,0374,'t','V','u','j',0252,'q',0346,'g','n',03,05,'S',031,'Y','R',0207,0315,0366,'F','g',0304,'_',0210,0257,0253,'K','&',0223,0244,0310,'V',0311,'N',0331,'e',035,'e','>',0203,0332,0274,0316,0212,'U','F','w',010,0212,'Y',0211,0300,0,'d',0232,0361,0252,'T',0225,'I','s','H',0375,'#',013,0205,0245,0205,0244,0251,0323,'V','K',0361,0363,':','O',06,0177,0307,0365,0307,0375,'s',037,0316,0275,0307,'L',0377,0,0220,'e',0257,0375,'r','_',0345,'^','u',0241,0370,036,0357,'C',0320,0177,0265,0357,0330,0307,'=',0301,012,0260,'c',0356,0257,0277,0275,'z','&',0234,'q',0244,0333,037,'H',0227,0371,'W',0215,0304,0321,'q',0300,0323,'O',0371,0277,'F','|','F','o','Z',0235,'l','t',0245,'M',0335,'Y','/',0270,0267,'M',0223,0375,'S',0377,0,0272,'k',0316,07,0210,0276,' ',0334,0331,'M',0250,0330,0351,0232,'D',0266,0,0273,'F',0346,'r',031,0221,'I',0301,0307,0270,025,0253,0340,0377,0,026,0352,032,0343,0334,'Y','j',0326,'p','A','r','-','c',0273,0206,'K','y','7',0307,'4','2',03,0265,0201,0374,010,0374,'+',0346,026,'[','V',0237,0357,'.',0232,0213,'W',0263,'M',0255,'{',036,'o','2',':',0177,02,0177,0307,0245,0347,0375,'t',037,0312,0272,0332,0344,0274,011,0377,0,036,0227,0237,0365,0320,0177,'*',0353,'k',0365,0323,0214,'(',0242,0212,0,'(',0242,0212,0,'(',0242,0212,0,'(',0242,0212,0,'B',03,02,010,04,036,0306,0270,037,030,'|','1',0323,0265,0340,0367,'v',01,'l',0357,0272,0222,0243,0344,0223,0352,'?',0255,'w',0364,'T','N',0234,'f',0255,'$','t','a',0261,'U',0260,0323,0347,0245,'+','3',0344,0375,'c','E',0277,0320,0257,0232,0323,'P',0201,0242,0220,'t',047,0243,017,'P','{',0326,'}','}','M',0342,'O',013,0351,0336,047,0260,'6',0327,0260,0202,0340,037,'.','P','>','d','>',0240,0327,0316,0376,'*',0360,0245,0367,0205,'u','6',0266,0271,'R',0320,0267,'1','L',07,0312,0343,0374,'k',0313,0257,0207,'t',0365,'[',037,'{',0225,'g',024,0361,0253,0222,'Z','O',0267,0177,'B',0216,0215,0255,'^',0350,':',0214,'w',0326,'3',030,0345,'C',0310,0354,0303,0320,0327,0321,0276,020,0361,'}',0227,0212,0364,0321,'4',',',022,0345,0,0363,0241,047,0225,'?',0341,'_','1','W','W',0340,'w',0276,0260,0324,0316,0241,'m','3','B',0210,'0',0336,0216,'=',015,'<','-','Y','F','|',0253,'f','g',0236,0340,0250,'V',0303,0272,0323,'v',0224,'v',0177,0241,0355,0376,'*',0361,034,'z',']',0253,0303,023,0201,'1',034,0237,0356,0212,0361,'m','O','W',0232,0372,'f',0303,0235,0204,0372,0365,0251,0265,0355,'f','M','F',0351,0362,0345,0201,'?','1',0365,'5',0215,'Y','c',0261,0216,'O',0331,0323,'z',037,0231,'V',0253,'w','d',024,'Q','V',',',0355,036,0362,'q',032,0216,';',0237,'J',0363,'a',07,'9','(',0307,'s',04,0256,0354,0213,'Z','F',0232,0367,0327,'+',0362,0222,0200,0376,'f',0275,0313,0303,032,'7',0366,'V',0236,013,0214,'K',' ',0311,0366,036,0225,0211,0340,0277,014,0245,0274,'I','w','2','`',017,0270,0244,'u',0367,0256,0347,0265,'}','&',027,016,0250,'B',0335,'N',0372,'t',0371,020,'Q','E','E','s','q',035,0245,0264,0267,023,'0','X',0343,'R',0314,'O','`','+',0244,0325,'&',0335,0221,0311,0374,'D',0361,'h',0360,0316,0202,0353,03,017,0267,0134,015,0221,017,0356,0372,0265,'|',0345,'$',0217,',',0215,'$',0214,'Y',0330,0345,0230,0365,'&',0266,0374,']',0342,031,0274,'K',0342,013,0213,0351,030,0371,'[',0266,0302,0231,0341,'P','t',0254,'*',0361,0261,025,'}',0244,0374,0217,0322,'r',0214,0275,'`',0350,'$',0376,047,0253,0377,0,'/',0220,'W',0262,0374,'0',0360,0,0204,'&',0273,0252,0304,013,0221,0233,'x',0134,'}',0337,0366,0215,'s',037,014,0274,032,'<','C',0251,0233,0353,0330,0311,0260,0266,'a',0362,0221,0304,0215,0351,0364,0257,0240,'U','B',0250,'U',0,0,'0',0,0355,'[',0341,'(','_',0337,0221,0344,0347,0371,0253,0247,0376,0313,'E',0353,0325,0376,0207,';',0343,'O',0371,01,0217,0372,0352,0265,'_','N',0,0351,'V',0300,0364,'1','/',0362,0253,036,'5',0377,0,0220,030,0377,0,0256,0253,'U',0264,0361,0235,'"',0334,0177,0323,021,0374,0253,0304,0342,0377,0,0367,'Z',0177,0342,0375,031,0362,'4','w','<',0223,'P',0323,0355,'t',0333,0333,0235,'2',0317,0342,'l',032,'v',0226,0316,0333,0354,0134,'#','4','`',0234,0262,0202,'O',03,0223,0217,'J',0364,035,03,0303,0372,']',0230,0207,'T',0323,'.',0232,'k','s',0246,0305,'g',06,010,'*','b','M',0305,'X',036,0344,0356,'5',0346,':','5',0347,0203,0264,'{','{',0213,'-','S',0302,0227,027,0367,'I','u','6','n','e',0260,'2',0231,027,'y',0333,0311,036,0230,037,0205,'{',016,0215,'5',0244,0376,035,0264,0226,0306,0330,0332,0332,'<',0,0305,01,'M',0236,'Z',0343,0201,0267,0267,0322,0274,',',0306,'U','i',0362,0307,0336,0265,0355,'v',0242,0224,0274,0364,0325,0277,']','K',0215,0211,0274,011,0377,0,036,0227,0237,0365,0320,0177,'*',0353,'k',0222,0360,047,0374,'z','^',0177,0327,'A',0374,0253,0255,0257,0322,0216,'`',0242,0212,'(',0,0242,0212,'(',0,0242,0212,'(',0,0242,0212,'(',0,0242,0212,'(',0,0254,0275,0177,'A',0263,0361,016,0225,'-',0215,0334,'`',0206,07,'k','c',0224,'=',0210,0255,'J',0216,'i',0222,010,'Z','Y',030,'*','(',0311,'&',0223,'I',0253,'2',0351,0324,0225,'9',')',0301,0331,0243,0346,'{',0357,06,'j',':','g',0210,037,'L',0271,'O',0225,016,'D',0240,'|',0256,0276,0242,0264,0357,0256,'#',0261,0266,'[',033,0134,0,07,0314,'E','u',036,'/',0327,0332,0366,0361,0232,'!',0271,0260,'V','0',07,' ','W',0,0345,0231,0311,'|',0356,047,0234,0327,0215,0212,0251,034,':','t',0351,0275,'_',0340,0216,0214,0333,':',0255,0214,'Q',0247,'-','-',0275,0273,0367,033,'E',024,0240,022,'@',03,'$',0327,0224,'x',03,0241,0211,0347,0220,'F',0203,'$',0327,0246,'x','/',0302,0302,'g','Y','$','_',0335,047,',',0177,0274,'}','+',033,0302,'~',033,0222,0356,0345,06,0337,0231,0271,'c',0375,0321,'^',0311,'e','g',025,0215,0252,'A',012,0200,0252,'1',0365,0257,0177,03,0205,0366,'Q',0347,0226,0354,0355,0243,'O',0225,'s','=',0311,0221,026,'4',010,0240,05,03,0,012,'u',024,0207,0201,'^',0201,0270,0265,0346,0277,027,0274,'K',0375,0235,0243,0307,0244,'@',0370,0236,0360,022,0370,0376,030,0307,0370,0327,0244,0261,012,0245,0217,'@','2','k',0346,'O',035,0353,047,'[',0361,'u',0355,0310,'b','c','F',0362,0243,0347,'<','/',0371,'5',0313,0213,0251,0311,013,'.',0247,0273,0303,0370,'E','_',025,0317,'-',0243,0257,0317,0241,0315,0325,0315,'/','N',0237,'W',0324,0355,0354,'-',0206,'e',0231,0302,0217,'o','z',0247,'^',0267,0360,'k',0303,0236,'d',0327,032,0364,0353,0362,0247,0356,0240,04,'w',0356,0177,0245,'y',0264,0251,0373,'I',0250,0237,'k',0217,0305,0254,'&',036,'U','_','M',0275,'z',036,0245,0240,'h',0326,0372,06,0215,'o',0247,0333,' ',013,022,0215,0304,0177,023,'w','&',0264,0350,0242,0275,0264,0222,'V','G',0346,023,0234,0252,'I',0316,'N',0355,0234,0347,0215,0177,0344,06,'?',0353,0252,0326,':',0276,0250,0260,0350,0302,0306,'(',0336,0331,0270,0273,'g',' ',025,'M',0234,025,0365,'9',0305,'l','x',0327,0376,'@','c',0376,0272,0255,'V',0323,0363,0375,0223,'o',0203,0203,0344,0214,'~','U',0362,0274,'[','.',0134,'5','7','k',0373,0337,0243,'*',0216,0347,033,'q','y',0361,'-','n','e',020,'i',0272,'9',0204,'9',0362,0313,0334,'`',0225,0317,031,0367,0305,'v',':','{','^',0276,0215,013,'j','I',032,'^',0230,0263,'2',0306,'r',0241,0261,0316,017,0245,'x',0336,0231,0242,'i','Z',0264,'w',027,'^',' ',0361,0336,0241,'m',0250,0375,0246,'T',0222,'(','/',02,'"',0355,'r',06,01,07,0250,0,0376,'5',0353,0332,'4',026,0366,0276,035,0264,0202,0322,0355,0356,0355,0343,0200,',','w',022,'6',0346,0220,01,0367,0211,0356,'O',0255,'|',0276,':',0224,')',0270,'F',')',']','5','{','E',0257,0305,0275,'M','b',0356,'M',0340,'O',0370,0364,0274,0377,0,0256,0203,0371,'W','[',0134,0217,0201,'?',0343,0322,0363,0376,0272,017,0345,']','m','~',0246,'r',0213,'E',024,'P',01,'E',024,'P',01,'E',024,'P',01,'E',024,'P',01,'E',024,'P',0,'H',03,047,0245,'y',0337,0215,'<','L',0270,'{','x',0237,020,0247,0336,'#',0370,0215,'o',0370,0257,0134,032,'}',0241,0267,0211,0300,0231,0307,047,0373,0242,0274,'S','U',0277,'k',0333,0222,0252,'I','@','x',0367,'5',0315,0212,0304,'*',020,0277,'^',0206,'u','j','r','!',0326,'w',0223,0317,0251,0357,012,030,0267,030,'=',0205,'_',0272,0261,0266,0275,'-',0345,0262,0254,0313,0327,025,'K','O',0266,0270,0264,0225,'g','x',0216,0302,'9',0365,02,0257,0210,0255,'#',0234,0336,',',0330,0343,'$','f',0276,':',0275,'G','*',0216,'w',0324,0371,'<','e','K',0327,0366,0224,0336,0266,0321,0255,'n',0373,'3',0237,0270,0267,0222,0332,'R',0222,014,037,0347,'Z',0272,'M',0212,'*',0375,0256,0343,0204,'^','F','j',';','h','d',0325,'o',0214,0262,037,0335,0251,0375,'*',0227,0211,'5',0177,0371,'p',0266,'8','E',0341,0310,0376,'U',0355,'`',0351,'r','C',0333,'U',0371,037,'a',0222,0345,0265,0261,0325,'#',031,'|',0317,0240,0274,'=',0246,0301,'c',0246,'D',0321,'a',0232,'U',014,0134,'w',0315,'k',0327,011,0360,0247,']',':',0267,0204,0226,0336,'V','&','{','&',0362,0211,047,0222,0275,'A',0374,0253,0272,07,'5',0356,0323,0232,0234,'T',0221,0321,0213,0303,0313,017,'Z','T','e',0321,0213,'A',0242,0212,0263,0234,0301,0361,0236,0252,'4','_',011,'j',027,0231,0303,0210,0212,0247,0373,0307,0201,0374,0353,0345,0342,'K','1','b','r','I',0311,'5',0355,0377,0,032,0257,0214,'Z',015,0225,0222,0267,0372,0351,0367,'0',0365,0,0177,0215,'x','}','y','X',0311,'^',0245,0273,037,'{',0303,'t',024,'0',0216,0247,'Y','?',0313,'A',0361,'F',0323,'J',0221,' ',0313,';',05,03,0334,0327,0324,0336,030,0322,0223,'E',0360,0345,0225,0212,014,'y','q',0215,0337,'S',0311,0257,0237,0276,036,'i','?',0332,0376,'5',0260,0211,0200,'1',0304,0336,'s',0347,0321,'y',0376,'x',0257,0246,'+','l',014,'4','r','<',0376,'(',0304,'{',0320,0240,0275,'_',0344,0277,'P',0242,0212,'+',0274,0371,'#',0234,0361,0257,0374,0200,0307,0375,'u','Z',0257,0247,0177,0310,'*',0333,0376,0271,'/',0362,0253,036,'5',0377,0,0220,030,0377,0,0256,0253,'U',0364,0354,0177,'e','[','g',0247,0224,0277,0312,0276,'K',0214,'?',0335,'i',0377,0,0213,0364,'f',0324,'w','8','m','#','K',0360,'f',0252,0227,0222,0352,'z','F',0217,05,0344,'w','r',0306,0352,'d',04,0266,033,0206,0311,'=',0301,07,0361,0256,0346,0322,033,'K','}','.','8','l',026,'5',0264,'H',0366,0304,0261,0234,0250,0134,'q',0217,'j',0362,035,'B',0373,0341,'b',0352,'w','I','&',0205,'s','s','2',0273,0264,0262,0305,03,0262,0223,0270,0206,'9',0317,'L',0347,0232,0365,'M',015,0254,0237,0303,026,'m',0246,0302,0360,'Y',033,'q',0344,'F',0340,0202,0211,0216,01,07,0245,'|',0316,':',0234,0342,0341,'7',0316,0223,'k','I','l',0275,'5','f',0221,'d',0376,04,0377,0,0217,'K',0317,0372,0350,'?',0225,'u',0325,0310,0370,023,0376,'=','.',0377,0,0353,0240,0376,'U',0327,'W',0352,'g','(','Q','E',024,0,'Q','E',024,0,'Q','E',024,0,'Q','E',024,0,'U','=','S','P',0217,'M',0261,'{',0211,017,' ','|',0243,0324,0325,0246,'!','A','b','p',0,0346,0274,0267,0306,0376,'$',022,0312,0311,033,'~',0356,'?',0225,07,0251,0365,0251,0224,0224,'S',0223,0330,'M',0244,0256,0316,'g',0304,0372,0334,0267,'W',022,02,0344,0273,0234,0261,0364,036,0225,0207,0246,'"',0233,0350,0314,0203,0345,0317,031,0365,0250,'S','u',0325,0332,0207,'<',0273,'r','k',0241,0222,0315,'J',0254,'D',04,0205,';',0216,0244,0327,0311,0343,0361,'~',0322,'z',0236,016,'a',0214,0214,'}',0311,'u','O',0344,'I','8',0237,'i','D',0344,0271,0353,0331,'E','d',']','[',0255,0306,0242,0260,'[',0257,'A',0206,'"',0247,0324,'.','.',0255,0245,026,0361,0312,'Y','X','q',0307,'5',0321,'x','K',0303,0262,']',0334,'*',0220,'K','7',0315,'#','z',012,'2',0314,033,0255,'>','g',0360,0230,0345,'8','I',';','T','m','[',0313,0257,0251,0215,0253,0273,'h',':',',','l',0221,0221,0347,'e','Q',0261,0324,0367,0257,'>','v','g','r',0314,'r',0304,0344,0232,0372,03,0342,'v',0207,024,0236,04,0177,'%',0,0373,031,022,016,';','w',0257,0237,0253,0330,0305,0246,0246,0243,0323,0241,0373,017,015,'B',0222,0302,'^',013,'[',0352,'z','?',0301,0335,'W',0354,0276,'&',0226,0301,0230,0204,0272,0217,0201,0376,0320,0257,'y',0257,0225,'|','3','~','t',0317,023,'i',0327,'`',0340,'G',':',0347,0350,'N',017,0350,'k',0352,0225,'!',0224,021,0320,0214,0327,'V',012,'W',0203,0217,'c',0310,0342,'j',034,0230,0230,0325,'_','i','~','+',0372,'B',0321,'E',025,0330,'|',0321,0341,0377,0,032,0356,0213,0353,0226,'6',0337,0303,034,'%',0277,'3','^','_',']',0327,0305,0271,0226,'_',034,0312,0243,'?',0273,0205,024,0347,0327,0223,0134,'-','x',0265,0335,0352,'H',0375,';',')',0207,'&',012,0232,0362,0374,0365,'=','W',0340,0225,0220,'}','S','Q',0275,' ',037,'.','5',0215,'O',0246,'N','M','{',']','y',0207,0301,'H',025,'4',013,0331,0201,0345,0347,0301,0374,05,'z','}','z','X','U','j','H',0370,0214,0366,0247,'>','>','~','V','_',0200,'Q','E',025,0320,'y',07,'9',0343,'_',0371,01,0217,0372,0352,0265,'V',0303,07,'H',0266,'R','@',0335,020,03,'?','J',0265,0343,'_',0371,01,0217,0372,0352,0265,0313,'x',0212,0313,'F',0277,0360,']',0274,'Z',0345,0371,0260,0264,'_','-',0305,0302,0315,0345,0225,'p','>',0134,'7',0255,'|',0267,025,'A','N',0215,'(',0276,0262,0350,0256,0366,'}',':',0232,0322,0335,0234,0344,0376,032,0361,'6',0211,0245,'i',0355,0244,0351,0366,0227,0267,'1',0333,'K','c','<','M','0',0134,0306,0317,0225,0223,047,034,0216,'I',036,0365,0334,0350,026,027,032,'W',0205,0264,0373,013,0271,'|',0333,0213,'{','T',0212,'G',0316,'r',0301,'p','y',0257,036,':',0326,0265,0246,0314,0261,'x','+',0304,'z',0257,0210,020,035,0276,'M',0305,0221,'x',0307,0251,'3',022,'3',0370,'W',0263,'i',023,'_',0134,'x','~',0326,'m','N',021,015,0363,0300,032,'x',0307,'E','|','r','+',0346,'s',010,'V',0202,0207,0264,'q','i',0312,0373,'5','/',0232,'{','-',']',0277,'6','k',033,022,'x',023,0376,'=','/','?',0353,0240,0376,'U',0326,0327,'%',0340,'O',0370,0364,0274,0377,0,0256,0203,0371,'W','[','_',0247,0234,0241,'E',024,'P',01,'G','z',0206,0342,0356,013,'D','/','<',0252,0212,'=','M','a',0134,'x',0317,'M',0205,0310,']',0357,0216,0340,'q','@',035,035,025,0201,0247,0370,0263,'O',0277,0235,'a',033,0221,0233,0200,'[',0241,0255,0372,0,'(',0242,0253,0336,0335,0307,'e','i','%',0304,0207,012,0203,'?','Z',0,0300,0361,0206,0266,0272,'}',0211,0267,'G',0304,0216,'9','>',0202,0274,'G','P',0274,'{',0353,0242,'F','J',0347,012,'+','o',0305,0232,0344,0227,0367,0262,015,0334,0261,0347,0330,'z','V','-',0205,0253,'8','k',0214,0205,021,0362,'3',0320,0327,0207,0231,0342,0327,0360,0343,0363,'8','1','u',0324,021,'z',0333,'G','@',0212,'^','B','&',0306,'F',';','S',0344,0232,'[','i','?',0323,'I','d','_',0270,'T','p','O',0275,'I','g','0',0274,0206,'V','2',04,0221,0217,'>',0302,0230,0352,0372,0215,0350,0201,'N','`',0217,0251,0365,'5',0341,'Q',0245,'R',0275,'_','g',0375,'#',0347,0351,'B',0265,'|','C',0247,'S','^',0376,'^',0214,0261,0244,'X',0313,0252,'_',0254,0354,0204,0263,0234,'F',0265,0354,0372,036,0223,036,0225,'b',0261,0200,'<',0306,0345,0333,0336,0261,0274,'!',0240,0255,0245,0272,0335,0314,0230,'v',037,' ','#',0240,0256,0262,0276,0317,017,'B','4',')',0250,'D',0372,0272,'4',0243,'J',012,021,0331,031,0276,' ',0262,032,0207,0207,0265,013,'F',031,022,0333,0272,0376,'8',0257,0224,0210,'*',0304,036,0240,0342,0276,0276,'a',0271,'H','=',0306,'+',0344,0355,'j',021,'o',0256,'_',0302,016,'B',0134,'H',0240,0377,0,0300,0215,'r',0343,0227,0302,0317,0265,0341,'Z',0232,'T',0247,0350,0377,0,'2',0220,'$',020,'G','Q','_','W',0350,'7','F',0367,'@',0323,0356,'O','Y','-',0321,0217,0344,'+',0344,0372,0372,'s',0300,022,0211,'|',015,0244,0221,0236,' ',013,0317,0265,'N',05,0373,0315,032,0361,'L','/','B',022,0354,0377,0,'5',0377,0,0,0351,'(',0242,0212,0364,0217,0211,'>','s',0370,0251,0377,0,'#',0355,0347,0373,0211,0374,0253,0213,0256,0337,0342,0274,'M',037,0216,0356,'Y',0261,0207,0215,030,'}','1',0212,0342,'+',0303,0255,0374,'I','z',0237,0251,'e',0316,0370,'J','_',0341,'_',0221,0357,0377,0,07,'@',036,014,'$',01,0223,';',0344,0327,0240,0327,0233,'|',030,0233,0177,0205,0356,'"','.',016,0311,0316,027,0323,'"',0275,'&',0275,'l','?',0360,0242,'~',0177,0233,0246,0261,0325,'o',0334,'(',024,036,0224,0202,0266,'<',0323,0235,0361,0257,0374,0200,0307,0375,'u','Z',0344,0374,'O',07,0207,'n','<',017,04,'~',047,'}',0232,'q',0362,0316,0340,0345,'H','|','q',0310,0374,'k',0254,0361,0257,0374,0200,0307,0375,'u','Z',0317,0206,0302,0323,'Q',0321,0255,'"',0275,0266,0212,0342,'5','U','p',0222,0250,'a',0220,'8','8','?','Z',0371,'~','(',0250,0251,0322,0245,'6',0332,0264,0272,'h',0366,'{',032,0322,0325,0263,0304,'`',0325,0256,0354,0212,'[','|','7',0324,'u',0333,0364,'G',013,0344,'I','j',0257,02,0217,'B',0304,014,'W',0267,0350,0362,0352,023,0370,'v',0322,']','Z','%',0213,'P','x',01,0270,0215,'z','+',0343,0220,'9','?',0316,0257,0307,024,'p',0306,'#',0212,'5','D',035,025,'F',0,0374,')','d',0377,0,'T',0377,0,0356,0232,0370,0334,'V','a',034,'T',0340,0224,'-','g',0276,0362,'~',0255,'%',0371,033,'(',0330,0251,0340,'O',0370,0364,0274,0377,0,0256,0203,0371,'W','[',0134,0227,0201,'?',0343,0322,0363,0376,0272,017,0345,']','m','~',0264,'r',05,'b','k',0372,0374,'z','D',';','W',015,';',016,07,0247,0275,'m',0327,0220,0370,0372,0342,'h',0357,0356,'y','9','$','(',0366,025,023,0232,0204,0134,0237,'A','I',0331,0134,0311,0326,0374,'U','5',0304,0315,0231,'L',0257,0237,'^',05,'s','3','_',0134,'L',0331,'y','[',0350,015,'W',0353,'E','|',0355,'l',']','J',0257,'{','#',0202,'u','%','-',0315,']',037,'S',0226,0332,0361,03,'9','*','O',031,'=',015,'{',0366,0217,'x','/',0264,0270,047,07,'$',0256,017,0326,0276,'m',04,0251,04,'u',025,0354,0277,016,0265,'q','q','g',0366,'W','n','q',0271,0177,0255,'z','9','m','w','$',0341,047,0261,0276,036,'w',0367,'Y',0336,'W',0234,0374,'A',0361,022,0304,0246,0322,'&',0316,0336,0270,0356,'k',0261,0327,0365,'T',0322,'t',0311,'%',',',03,0221,0205,025,0340,0327,0372,0237,0333,'5','O','>',0134,0272,06,0316,'=','k',0247,031,0210,0366,024,0333,'[',0364,'.',0275,'N','H',0266,0267,07,0260,'-','i',0347,0314,0307,0317,0220,0374,0253,0353,'V',0355,0256,'"',0362,'~',0305,'t',0236,'Y',0306,'>',0265,'r','6',0202,0354,0244,0361,0235,0314,0203,0201,0351,'T','Z',0333,0211,0356,0257,0207,'=',025,'k',0343,0235,'G','R',0374,0373,0376,0247,0311,'}','c',0333,0336,025,0256,0232,0177,'4',0372,'X',0247,'s',0247,0264,'7','i',024,016,'[',0177,'L',036,0225,0350,'~',013,0360,0330,0231,0326,'I',027,0367,'1',0234,0261,'?',0304,'k',0235,0360,0276,0211,'-',0355,0322,'|',0244,0273,0236,0247,0370,'V',0275,0236,0302,0312,'+',013,'D',0267,0210,0,024,'s',0356,'k',0352,0262,0354,'+',0245,016,'i',0374,'L',0372,0214,'%',031,'B',011,0315,0335,0226,025,'B',0250,'P','0',07,'A','K','E',025,0351,035,'a','_','-',0370,0310,05,0361,0216,0254,0,0,'}',0245,0270,025,0365,'!',0340,'W',0312,'^','#',0230,0334,'x',0227,'S',0224,0270,'}',0327,'/',0363,016,0377,0,'1',0256,034,'w',0302,0217,0252,0341,'d',0375,0255,'G',0344,0214,0312,0372,'[',0341,0307,0374,0210,'z','g',0375,'s',0376,0265,0363,'M','}','3',0360,0362,'6',0213,0300,0272,'P','l','d',0305,0270,'}',015,'c',0202,0370,0337,0241,0350,'q','?',0373,0254,0177,0305,0372,'3',0247,0242,0212,'+',0324,'>',024,0360,0177,0214,0326,0336,'W',0212,0255,0347,0301,0375,0354,03,0234,0365,0301,0377,0,0353,0327,0233,0327,0262,0374,'n',0263,0315,0246,0233,'z',07,0335,'v',0214,0237,0250,0317,0364,0257,032,0257,033,022,0255,'U',0237,0245,'d',0265,'=',0246,06,0233,0354,0255,0367,036,0277,0360,'F',0360,'g','S',0262,047,0237,0226,'@','?','J',0366,032,0371,0343,0341,'F',0246,0272,0177,0215,'a',0211,0333,011,'t',0215,027,0374,013,0250,0376,'U',0364,'=','w',0340,0345,'z','v',0354,'|',0227,021,'Q','p',0306,0271,0177,'2','O',0364,0375,02,0220,'R',0321,']','G',0204,'s',0236,'5',0377,0,0220,030,0377,0,0256,0253,'P','i',0237,0362,014,0265,0377,0,0256,'K',0374,0252,0337,0214,'b','y','t',026,'*','3',0261,0303,037,0245,'f',0350,0267,')','q',0246,'B',0252,'F',0350,0324,'#',017,'L','W',0312,'q','u','9','K',07,031,'%',0242,0226,0277,'s','5',0243,0361,032,'4',0331,'?',0325,'?',0373,0246,0235,'U',0257,0356,'R',0322,0312,'I',0134,0201,0362,0220,0243,0324,0372,'W',0300,0341,0251,0312,0245,'h',0302,012,0355,0264,'t','7','d','G',0340,'O',0370,0364,0274,0377,0,0256,0203,0371,'W','[',0134,0257,0201,0242,'t',0323,0256,'%','#',012,0362,'|',0276,0370,025,0325,'W',0355,047,020,'W',0231,'|','I',0260,0375,0341,0230,016,035,'3',0237,'q','^',0233,0134,0327,0215,',','E',0336,0214,0134,014,0230,0316,0177,012,0231,0307,0232,'.','=',0305,'%','u','c',0300,0250,0251,'n','b','0',0334,'I',031,0354,'M','E','_',047,'(',0270,0266,0231,0346,0205,'t','~',025,0325,0237,'O',0274,033,037,014,0247,'+',0237,0345,0134,0345,'(','%','N','A',' ',0373,'V',0270,'z',0316,0215,'E','4','T','%',0312,0356,'v',036,'/',0361,'4',0372,0233,0210,0331,0200,'8',0306,0325,'<',012,0343,0251,'I',',','r','N','M','%','V',047,020,0353,0317,0231,0204,0346,0346,0356,0311,'`',0270,0222,0335,0303,0306,0304,032,0334,0262,0363,0265,0211,0343,'.',0237,0273,'C',0320,0177,021,0254,';','h',032,0346,'u',0215,'G','$',0327,0256,'x','#',0303,0252,0252,0267,'R',047,0356,0343,0373,0200,0367,'>',0265,0276,03,07,032,0263,0366,0262,'Z','!','R',0303,'S',0235,'E','R','K','T','t','~',031,0321,'W','L',0262,022,':',0217,'>','A',0223,0354,'=','+','z',0212,'+',0350,017,'H','(',0242,0212,0,0255,0250,0334,0255,0236,0233,'u','r',0307,02,'(',0231,0311,0372,012,0371,'.','i',014,0323,0311,'+','u','v',',',0177,023,'_','I','|','G',0324,06,0237,0340,'}','D',0356,0303,'L',0236,'J',0375,'[',0212,0371,0252,0274,0334,'t',0275,0344,0217,0266,0341,'z','V',0243,':',0235,0335,0276,0357,0370,'p',0257,0252,'<',')','m',0366,'O',011,0351,'p','c',05,'m',0223,'#','9',0355,'_','1','i','v',0215,0177,0253,'Z','Z','(',0311,0232,'e','O',0314,0327,0326,'P',0306,'!',0205,'#','^',0210,0241,'G',0341,'O',03,035,'[','3',0342,0232,0236,0345,':','~',0254,'}',024,'Q','^',0211,0361,0247,'!',0361,'/','J',':',0257,0202,'/','B','.','d',0267,0304,0353,0377,0,01,0353,0372,'f',0276,'n',0257,0257,047,0205,'n','-',0344,0205,0306,'V','E','*','G',0261,0257,0225,0274,'C',0245,0276,0215,0257,0336,'X','8',0307,0225,'!',013,0376,0357,'o',0322,0274,0334,'l','5','R','>',0323,0205,0361,'7',0247,':',017,0246,0253,0346,'U',0323,0356,0336,0303,'R',0266,0273,0214,0220,0360,0312,0262,02,'=',0216,'k',0352,0355,':',0362,'=','C','N',0267,0273,0214,0202,0222,0306,030,021,0356,'+',0344,0212,0367,0177,0203,0376,'!',027,0372,023,0351,'3','8',0363,0354,0317,0310,';',0230,0317,'O',0313,0245,',',024,0355,047,027,0324,0327,0211,'p',0256,0245,010,0326,0217,0331,0337,0321,0236,0223,'E',024,'W',0246,'|','0',0311,'#','I',0243,'h',0344,'P',0310,0303,04,036,0342,0270,0333,0377,0,011,0315,'i','p','f',0323,0257,'R','%','c',0302,0310,0373,'H',0366,0317,'z',0351,'u',0235,'H','i','Z','l',0227,'8',05,0307,010,017,'r','k',0210,0267,0323,0357,'u',0262,'n',0357,'.',0134,'+',0236,'3',0316,'~',0203,0260,0256,'l','^','&',0206,032,0223,0251,']',0332,'#','I',0267,0241,'k',0373,047,'[',0377,0,0240,0235,0277,0375,0377,0,0377,0,0353,'T',0226,0336,025,0273,0277,0234,'5',0366,0241,033,0306,0247,0221,033,0357,'?',0375,'j',0213,0376,021,0210,0177,0347,0346,'O',0310,'U','{',0235,'&',0357,'J','_',0265,0331,']','9',0362,0371,'8',0340,0217,0361,0257,'3',015,0234,'e','u','*','(','R',0222,'R','{','i','o',0320,0247,011,'%',0251,0350,'6',0326,0321,'Z','[',0244,020,0240,'X',0320,'`',01,'R',0326,'^',0201,0252,0377,0,'k','i',0253,'3',0,'%','S',0265,0300,0365,0255,'J',0367,010,012,0202,0362,01,'s','g',',',',','2',035,'H',0251,0350,0240,017,0236,'<','M','d','m','5',047,04,'c',0222,017,0324,'V','%','z','g',0304,0215,047,'e',0303,'N',0213,0303,0215,0337,0217,'z',0363,':',0371,0354,0302,0227,'%','k',0367,'8','k','F',0323,012,'(',0242,0270,'L','B',0200,'2','q','E','k','h','z','k','^',0335,0251,0332,'H',07,0,'z',0232,0326,0215,047,'V','j',010,0250,0305,0311,0331,035,027,0203,0274,'<',0367,0227,'(',012,0234,0267,',','}',05,'{','-',0275,0274,'v',0266,0351,014,'J',02,'(',0300,025,0231,0341,0355,'!','4',0253,05,05,'G',0232,0343,',',0177,0245,'l','W',0323,0323,0247,032,'q','Q',0217,'C',0320,0214,'T','U',0220,'Q','E',025,'e',05,024,'T','w',023,0245,0265,0274,0223,0312,0301,'R','5',',',0304,0366,02,0201,0244,0333,0262,'<',0207,0343,'V',0262,013,0331,'h',0361,0277,'O',0337,'J',01,0374,05,'y',015,'k',0370,0237,'X','}','w',0304,'w',0272,0203,0266,'D',0222,020,0236,0312,'8',037,0245,'d','W',0211,'Z','|',0363,'r','?','O',0313,'p',0277,'U',0302,0302,0227,'[','k',0352,0367,';','o',0205,'z','_',0366,0207,0215,' ',0224,0256,'R',0325,'L',0247,0353,0320,'W',0321,'U',0345,0377,0,06,'4','f',0267,0321,'n',0265,'I','S',015,'s','&',0310,0317,0373,'+',0377,0,0327,0315,'z',0205,'z','X','H','r',0323,0277,'s',0342,0270,0203,021,0355,0261,0256,'+','h',0351,0376,'a','E',024,'W','I',0342,05,'x',0307,0306,'o',016,0264,'w','v',0372,0354,011,0373,0271,07,0225,'>',';',021,0320,0377,0,'J',0366,'z',0315,0327,0264,0230,0265,0315,026,0353,'O',0230,02,0262,0241,0,0236,0307,0261,0254,0253,'S',0366,0220,'q',';',0362,0314,'c',0302,'b','c','S',0246,0317,0320,0371,'F',0267,0274,035,0257,0267,0207,'<','I','m','}',0223,0345,'g','d',0240,'w','C',0326,0262,0365,033,011,0364,0315,'F','{','+',0224,')','4','.','Q',0201,0252,0265,0342,0246,0342,0357,0325,037,0245,0316,020,0255,'M',0305,0352,0244,0277,'3',0353,0313,'y',0343,0272,0267,0216,'x','X','4','r','(','e','a',0334,032,0222,0274,0227,0341,037,0214,'<',0350,'?',0260,'/','e',0371,0343,0346,0331,0230,0365,'_',0356,0327,0255,'W',0267,'J',0242,0251,036,'d','~','c',0217,0301,0317,011,']',0322,0227,0313,0315,034,0307,0216,'?',0344,021,017,0375,'v',037,0310,0322,'X',0,'4',0353,'`',06,07,0224,0277,0312,0264,'<','M',0247,'>',0243,0244,':','D','3','*',035,0352,'=','q',0134,0276,0225,0255,0303,015,0272,0333,']',0223,033,0307,0362,0206,'#',0267,0275,'|',0367,023,0340,0253,0342,0260,0321,0366,'*',0356,'.',0366,0371,030,'R',0222,'O','S',0240,0246,'L',03,'A',' ','#',' ',0251,07,0362,0252,0237,0333,':',0177,0374,0374,0247,0353,'T','5','=','z',017,0263,'<','6',0254,'d',0221,0306,0335,0300,'p','+',0342,0360,0231,'F',':',0245,'x',0257,'f',0326,0253,'V',0255,'c','g','8',0333,'s','C',0300,0237,0361,0351,'w',0377,0,']',07,0362,0256,0266,0260,'<',047,0246,0311,0247,0351,'E',0246,'R',0262,'L',0333,0312,0236,0240,'v',0255,0372,0375,'d',0344,012,'(',0242,0200,'0','<','Y',0246,0177,'h','i',016,'U','r',0361,0362,'>',0225,0340,0332,0205,0253,'Z',0335,0274,'d','`','g',0212,0372,']',0200,'e','*','F','A',0340,0327,0233,0370,0247,0300,0322,0335,0134,0264,0226,0261,0356,'F','9',033,'z',0255,'r','c','0',0336,0336,026,'[',0243,'*',0264,0371,0326,0233,0236,'M','E','u',0315,0340,035,'H',0177,0313,')','?',0357,0232,'X',0274,01,0251,'H',0330,'1',0311,0377,0,'|',0342,0274,0217,0354,0372,0375,0216,'_','c','>',0307,'+','o',03,0134,'L',0261,0240,0344,0232,0366,037,03,'x','u',' ',0205,'n',0345,'O',0225,'~',0340,'#',0251,0365,0252,'z',017,0303,0306,0267,0221,'d',0271,0302,'/','~',0344,0327,0242,0303,012,'A',012,0305,032,0205,'E',030,0,'W',0253,0203,0302,'{',05,'y','n',0316,0232,'T',0271,'5','{',0217,0242,0212,'+',0264,0330,'(',0242,0212,0,'+',0315,'~','.','x',0240,'i',0272,':',0350,0366,0357,0376,0225,'v','>','|',0177,014,0177,0375,'z',0356,0265,0255,'^',0327,'C',0322,0247,0324,'.',0334,',','q','.','}',0330,0366,02,0276,'b',0327,0365,0273,0237,020,'k','3',0352,027,',','w','H',0337,'*',0377,0,'u','{',012,0344,0305,0325,0344,0217,'*',0335,0237,'C',0303,0371,'{',0257,'[',0333,'M','{',0261,0374,'_',0374,03,'2',0255,0351,0226,023,'j',0232,0225,0275,0214,013,0231,'&','p',0203,0374,'j',0245,'z',0337,0301,0337,013,0227,0270,0223,'_',0271,'O',0221,01,0216,0334,021,0324,0367,'o',0351,'^','u','*','n',0244,0324,'O',0261,0307,0342,0343,0204,0303,0312,0253,0351,0267,0257,'C',0326,'4','}',':','-','#','H',0265,0260,0205,'B',0244,021,0205,0,'U',0332,'(',0257,'m','+','+','#',0362,0371,0311,0316,'N','R',0335,0205,024,'Q','L',0220,0242,0212,'(',03,0312,'>','.','x','@',0134,'[',0177,'o',0331,0305,0231,0243,0,0134,05,037,'y',0177,0275,0370,'W',0213,'W',0327,0262,0304,0223,0304,0361,'H',0241,0221,0301,'V','S',0320,0203,'_',':','|','A',0360,'l',0276,030,0325,0232,'h',020,0235,':',0341,0211,0211,0277,0272,0177,0272,'k',0315,0305,0321,0263,0347,'G',0332,0360,0366,'f',0247,037,0252,0324,'z',0255,0275,';','|',0216,'J',0336,0342,'k','K',0230,0356,'-',0344,'h',0346,0215,0203,'#',0251,0301,04,'W',0321,0276,04,0361,0245,0277,0212,0264,0300,0256,0312,0272,0204,'*','<',0350,0375,0177,0332,036,0325,0363,'m','_',0321,0365,0213,0315,013,'R',0212,0372,0306,'R',0222,0241,0374,030,'z',037,'j',0302,0205,'g','J','^','G',0255,0232,'e',0260,0307,'R',0266,0322,'[','?',0353,0241,0365,0215,'f','^',0350,032,'m',0374,0206,'Y',0355,0227,0314,'=','Y','N',011,0372,0342,0262,0374,037,0343,'K',017,025,0330,'+','#',010,0357,'T','~',0366,02,'y',07,0324,'z',0212,0351,0353,0327,0214,0224,0225,0321,0371,0315,'j',025,'(','M',0323,0250,0254,0321,0205,0377,0,010,0216,0221,0377,0,'<',037,0376,0373,'5','f',0323,0303,0272,']',0224,0242,'X',0255,0224,0270,0344,'3',0235,0330,0374,0353,'R',0212,0243,' ',0242,0212,'(',0,0242,0212,'(',0,0242,0212,'(',0,0242,0212,'(',0,0242,0212,'(',0,0242,0212,'(',0,0250,0346,0232,';','x','^','i',0234,'$','h',013,'3',036,0200,'Q','<',0361,'[','@',0363,'L',0352,0221,0240,0313,'3',034,0,'+',0303,'>','!',0374,'F',0223,'Y',0226,'M','/','J',0223,'e',0202,0360,0362,0203,0314,0247,0374,'+','*',0325,0243,'J','7','g',0241,0227,'e',0325,'q',0265,'9','a',0262,0335,0366,'3',0276,'!',0370,0341,0274,'Q',0177,0366,'k','F','#','N',0201,0276,'N',0336,'a',0376,0361,025,0303,0321,'R','[',0301,'-',0325,0304,'p','@',0205,0345,0221,0202,0252,0216,0244,0327,0215,'9','9',0312,0354,0375,037,017,'B',0236,032,0222,0247,'M','Y','#','O',0303,'Z',05,0317,0211,'5',0270,'4',0373,'p','p',0307,'2','>','8','E',0356,'k',0351,0355,'3','N',0267,0322,'t',0330,',','m','P',',','0',0250,'U',02,0271,0217,0207,0276,016,036,027,0321,0203,0334,05,':',0205,0307,0315,')',037,0303,0350,0277,0205,'v','U',0352,'a','h',0373,'8',0335,0356,0317,0205,0317,'3','/',0255,0326,0366,'t',0337,0271,037,0305,0367,0377,0,' ',0242,0212,'+',0250,0360,0202,0212,'(',0240,02,0212,'(',0240,02,0250,'k',032,'E',0246,0271,0246,'M','a','y',022,0274,'R',014,'r',':',037,'Q',0357,'W',0350,0244,0322,'j',0314,0250,'N','P',0222,0224,']',0232,'>','_',0361,'o',0204,0357,'|',')',0252,'5',0275,0302,0226,0267,'s',0230,'f',03,0207,037,0343,0134,0375,'}',']',0256,'h','v','^',' ',0323,'d',0261,0276,0214,'4','n','8','n',0352,'}','E','|',0363,0343,017,04,0337,0370,'N',0360,0371,0200,0315,'d',0307,021,0334,01,0301,0366,'>',0206,0274,0254,'F',035,0323,'w',0216,0307,0337,0345,031,0314,'1','q','T',0352,'i','?',0317,0323,0374,0214,013,015,'B',0357,'L',0273,'K',0253,')',0336,031,0220,0344,'2',034,'W',0272,'x','7',0342,0205,0216,0264,0221,0332,'j',0216,0226,0267,0374,'(','$',0341,'$','>',0336,0225,0340,'T',02,'A',0310,0340,0326,'T',0253,'J',0233,0320,0355,0307,0345,0264,'q',0260,0265,'E',0252,0331,0365,'>',0300,04,021,0220,'r','(',0257,0236,0374,'-',0361,'G','U',0320,0225,'-',0257,'3','{','f',0274,05,'c',0363,0250,0366,'5',0354,'^',036,0361,0266,0211,0342,'H','A',0264,0272,011,'6','>','h','e',0371,0134,0177,0215,'z',0224,0261,020,0251,0352,'|','6',';',047,0304,0341,033,'m','^','=',0327,0365,0241,0321,'Q','E',025,0271,0345,05,024,'Q','@',05,024,'Q','@',05,024,'Q','@',05,024,'T',027,'W',0226,0326,'P',0264,0327,'S',0307,014,'j','2','Y',0333,02,0215,0206,0223,'n',0310,0236,0263,'u',0255,'{','N',0320,',',0315,0326,0243,'r',0221,047,'`','O',',','}',0,0357,'^',0177,0342,'o',0214,'6','V',0233,0355,0264,'H',0215,0314,0243,0217,'=',0270,'@','}',0275,'k',0310,'u','}','o','P',0327,'o','Z',0353,'P',0271,'y',0244,'=',01,'<','(',0364,03,0265,'q',0325,0305,0306,':','C','V','}',026,'_',0303,0325,0253,'5',':',0376,0354,0177,027,0376,'G','K',0343,'o',0210,'W',0276,'(',0224,0333,0333,0227,0266,0323,0324,0361,030,'8','2','{',0267,0370,'W',025,'E','K','o','o','5',0335,0302,'A','o',033,'I','+',0234,'*','(',0311,'&',0274,0331,'J','S','w','g',0332,0320,0241,'K',015,'M','B',0232,0262,'D','j',0254,0356,021,024,0263,'1',0300,03,0251,'5',0355,0377,0,016,'>',037,'>',0220,0321,'j',0372,0234,'j','n',']','r',0221,'0',0377,0,'T','=','~',0265,047,0303,0377,0,0206,0261,0351,013,026,0251,0253,0240,'{',0342,'2',0220,0221,0221,027,0377,0,'^',0275,'2',0273,0360,0330,'k','{',0363,'>','K',':',0316,0324,0357,0207,0303,0275,':',0276,0376,'H','(',0242,0212,0357,'>','P','(',0242,0212,0,'(',0242,0212,0,'(',0242,0212,0,'(',0242,0212,0,'*',0275,0355,0225,0266,0241,'j',0366,0327,'p',0244,0320,0270,0303,'+',014,0212,0261,'E',026,0270,0323,'q','w','[',0236,037,0343,017,0204,0327,'6',06,'[',0335,014,0264,0366,0374,0261,0267,'?','y',07,0267,0255,'y',0203,'+','#',0224,'u','*',0312,'p','A',030,' ',0327,0327,0365,0311,0370,0233,0341,0356,0213,0342,'@',0362,0274,'"',0336,0354,0377,0,0313,'x',0206,011,'>',0376,0265,0301,'[',07,'}','`','}','V',']',0304,'n',013,0331,0342,0265,']',0377,0,0314,0371,0256,0235,034,0217,023,0207,0215,0331,030,'t','e','8','"',0272,0377,0,022,'|','6',0327,'t',07,'g','H',015,0345,0247,'i','a',031,'#',0352,':',0212,0343,0331,'Y',030,0253,02,0254,'8',' ',0214,021,0134,022,0204,0242,0355,'$','}','m',032,0364,0253,0307,0232,0234,0223,'G','a',0242,0374,'L',0361,'&',0216,'U','~',0324,'.',0341,037,0362,0316,0340,'g',0217,'c',0326,0275,07,'I',0370,0317,0245,0134,0205,'M','J',0322,'[','W',0356,0311,0363,0257,0370,0327,0206,0321,'Z',0303,021,'R',033,'3',0213,023,0223,0340,0361,032,0312,026,'}',0326,0207,0323,0326,'^',':',0360,0335,0360,036,'V',0253,0,047,0263,0266,0323,0372,0326,0304,':',0215,0224,0343,'1',']',0300,0343,031,0371,'d',06,0276,'I',0245,'V','e',0373,0254,'G',0320,0326,0353,035,'.',0250,0362,'g',0302,0324,'_',0301,'Q',0257,0225,0377,0,0310,0372,0364,':','0',04,'2',0220,'{',0203,'M','{',0210,'c',0316,0371,'c',0134,'z',0260,025,0362,'b',0352,'7',0250,0241,'R',0362,0341,'T','p',0,0225,0200,037,0255,'G','%',0314,0363,'6',0351,'g',0221,0333,0246,'Y',0311,'5','_','^',0376,0351,0212,0341,']','u',0253,0370,0177,0301,'>',0247,0271,0361,036,0215,'f','3','>',0247,'j',0237,'Y',05,'s',0272,0207,0305,'O',014,'X',0251,0362,0356,0232,0345,0307,'E',0211,'s',0237,0306,0276,'u',0242,0242,'X',0331,0275,0221,0325,'K',0206,'0',0321,'~',0374,0233,0374,017,'R',0326,0276,'4','j',027,01,0243,0322,'l',0343,0266,'C',0322,'I','~','f',0374,0272,012,0363,0355,'O','^',0325,'u',0227,0337,0250,'_','M','9',0364,'f',0343,0362,0254,0352,'+',0232,'u','g','?',0211,0236,0326,033,01,0206,0303,'/',0335,'A','/','>',0277,'x','Q','W',0364,0335,027,'R',0326,047,'X','l',',',0345,0235,0330,0343,0345,'^','?','>',0225,0352,0236,031,0370,'8',0251,0262,0347,'_',0230,';','u',0373,'4','G',0217,0241,'=',0351,0323,0243,':',0217,0335,'D',0342,0363,034,'>',021,'^',0254,0265,0355,0324,0363,0177,016,'x','W','T',0361,'=',0330,0206,0306,03,0345,0203,0363,0314,0303,0344,'O',0306,0275,0337,0301,0376,0,0323,'|','+',027,0233,0217,0264,0337,'7',0336,0231,0307,'O','e',035,0253,0245,0261,0323,0355,'4',0313,'U',0266,0262,0267,0216,010,'W',0242,' ',0300,0253,'5',0351,'Q',0303,'F',0236,0257,'V','|','V','e',0236,'V',0305,0336,020,0367,'a',0333,0253,0365,012,'(',0242,0272,'O',014,'(',0242,0212,0,'(',0242,0212,0,'(',0242,0212,0,'(',0242,0212,0,'(',0242,0212,0,'(',0242,0212,0,'(',0242,0212,0,010,04,'`',0214,0212,0347,'5',0257,03,'x',0177,']','V','7','V','(',0262,0237,0371,'k',027,0312,0337,0230,0256,0216,0212,0231,'E','I','Y',0243,'Z','U',0252,'Q',0227,'5','9','4',0374,0217,031,0325,'~',011,0316,0254,0317,0245,'j','J',0351,0332,'9',0327,07,0363,025,0310,'j',037,016,'|','O',0247,0223,0273,'N','y',0224,0177,024,047,'u','}','+','E','s','K',07,'M',0355,0241,0355,0320,0342,'<','e','=',047,'i','z',0377,0,0300,'>','H',0270,0323,0257,'m',016,'.','-',047,0210,0377,0,0267,031,025,'Z',0276,0274,'x','"',0224,'b','H',0221,0307,0373,'J',015,'Q',0233,0303,0332,'5',0312,0262,0315,0245,0331,0270,'c',0223,0230,'W',0237,0322,0261,'x',027,0322,'G',0243,016,'*',0217,0333,0247,0367,'?',0370,07,0312,'4','W',0324,0277,0360,0207,'x','o',0376,0200,0226,'?',0367,0344,'S',0242,0360,0237,0207,0341,0220,'I',036,0215,'d',0254,':',021,010,0245,0365,031,'w','5',0377,0,'Z','h',0177,'#',0374,017,0226,0222,'7',0221,0202,0306,0214,0344,0364,012,'3','Z',0326,'^',025,0327,'u',02,'>',0315,0245,']','8','=',011,'B',0243,0365,0257,0247,0241,0323,'l','m',0277,0324,'Y',0333,0307,0316,'~','H',0300,0253,' ',01,0320,01,0364,0252,'X',036,0354,0347,0251,0305,'/',0376,']',0323,0373,0331,0340,':','o',0301,0377,0,020,0336,'`',0334,0274,026,0212,0177,0274,'w',021,0370,012,0356,0264,'O',0203,0372,'&',0236,'V','M','B','I','/',0345,034,0341,0276,'T',0374,0253,0321,'h',0256,0210,'a','i',0307,0245,0317,'+',021,0237,'c','k','+','s','r',0257,'-','?',035,0312,0326,'v',026,0232,'|','"','+','K','x',0341,0214,'t','T','P','*',0315,024,'W','B','V','<','y','I',0311,0335,0260,0242,0212,'(',020,'Q','E',024,0,'Q','E',024,0,'Q','E',024,0,'Q','E',024,01,0377,0331,0377,0341,015,'7','h','t','t','p',':','/','/','n','s','.','a','d','o','b','e','.','c','o','m','/','x','a','p','/','1','.','0','/',0,'<','?','x','p','a','c','k','e','t',' ','b','e','g','i','n','=','"',0357,0273,0277,'"',' ','i','d','=','"','W','5','M','0','M','p','C','e','h','i','H','z','r','e','S','z','N','T','c','z','k','c','9','d','"','?','>',' ','<','x',':','x','m','p','m','e','t','a',' ','x','m','l','n','s',':','x','=','"','a','d','o','b','e',':','n','s',':','m','e','t','a','/','"',' ','x',':','x','m','p','t','k','=','"','X','M','P',' ','C','o','r','e',' ','4','.','4','.','0','-','E','x','i','v','2','"','>',' ','<','r','d','f',':','R','D','F',' ','x','m','l','n','s',':','r','d','f','=','"','h','t','t','p',':','/','/','w','w','w','.','w','3','.','o','r','g','/','1','9','9','9','/','0','2','/','2','2','-','r','d','f','-','s','y','n','t','a','x','-','n','s','#','"','>',' ','<','r','d','f',':','D','e','s','c','r','i','p','t','i','o','n',' ','r','d','f',':','a','b','o','u','t','=','"','"',' ','x','m','l','n','s',':','x','m','p','M','M','=','"','h','t','t','p',':','/','/','n','s','.','a','d','o','b','e','.','c','o','m','/','x','a','p','/','1','.','0','/','m','m','/','"',' ','x','m','l','n','s',':','s','t','E','v','t','=','"','h','t','t','p',':','/','/','n','s','.','a','d','o','b','e','.','c','o','m','/','x','a','p','/','1','.','0','/','s','T','y','p','e','/','R','e','s','o','u','r','c','e','E','v','e','n','t','#','"',' ','x','m','l','n','s',':','d','c','=','"','h','t','t','p',':','/','/','p','u','r','l','.','o','r','g','/','d','c','/','e','l','e','m','e','n','t','s','/','1','.','1','/','"',' ','x','m','l','n','s',':','G','I','M','P','=','"','h','t','t','p',':','/','/','w','w','w','.','g','i','m','p','.','o','r','g','/','x','m','p','/','"',' ','x','m','l','n','s',':','x','m','p','=','"','h','t','t','p',':','/','/','n','s','.','a','d','o','b','e','.','c','o','m','/','x','a','p','/','1','.','0','/','"',' ','x','m','p','M','M',':','D','o','c','u','m','e','n','t','I','D','=','"','g','i','m','p',':','d','o','c','i','d',':','g','i','m','p',':','e','5','9','4','4','0','5','e','-','8','d','f','0','-','4','a','b','a','-','a','2','d','a','-','c','d','f','c','8','0','0','2','8','5','9','e','"',' ','x','m','p','M','M',':','I','n','s','t','a','n','c','e','I','D','=','"','x','m','p','.','i','i','d',':','6','5','e','5','2','f','1','b','-','1','7','2','0','-','4','4','b','d','-','9','1','c','4','-','e','d','8','7','9','1','a','d','8','6','1','3','"',' ','x','m','p','M','M',':','O','r','i','g','i','n','a','l','D','o','c','u','m','e','n','t','I','D','=','"','x','m','p','.','d','i','d',':','8','0','6','1','1','f','0','9','-','1','5','8','1','-','4','b','9','c','-','b','4','5','8','-','9','8','4','f','e','e','c','e','1','2','6','0','"',' ','d','c',':','F','o','r','m','a','t','=','"','i','m','a','g','e','/','j','p','e','g','"',' ','G','I','M','P',':','A','P','I','=','"','2','.','0','"',' ','G','I','M','P',':','P','l','a','t','f','o','r','m','=','"','L','i','n','u','x','"',' ','G','I','M','P',':','T','i','m','e','S','t','a','m','p','=','"','1','6','7','8','6','3','7','7','4','7','6','9','4','3','5','9','"',' ','G','I','M','P',':','V','e','r','s','i','o','n','=','"','2','.','1','0','.','3','0','"',' ','x','m','p',':','C','r','e','a','t','o','r','T','o','o','l','=','"','G','I','M','P',' ','2','.','1','0','"','>',' ','<','x','m','p','M','M',':','H','i','s','t','o','r','y','>',' ','<','r','d','f',':','S','e','q','>',' ','<','r','d','f',':','l','i',' ','s','t','E','v','t',':','a','c','t','i','o','n','=','"','s','a','v','e','d','"',' ','s','t','E','v','t',':','c','h','a','n','g','e','d','=','"','/','"',' ','s','t','E','v','t',':','i','n','s','t','a','n','c','e','I','D','=','"','x','m','p','.','i','i','d',':','9','2','f','7','c','d','9','7','-','d','a','8','4','-','4','9','3','e','-','b','e','0','8','-','b','c','6','3','2','3','4','5','b','d','7','2','"',' ','s','t','E','v','t',':','s','o','f','t','w','a','r','e','A','g','e','n','t','=','"','G','i','m','p',' ','2','.','1','0',' ','(','L','i','n','u','x',')','"',' ','s','t','E','v','t',':','w','h','e','n','=','"','2','0','2','3','-','0','2','-','1','2','T','2','0',':','5','9',':','4','9','+','0','0',':','0','0','"','/','>',' ','<','r','d','f',':','l','i',' ','s','t','E','v','t',':','a','c','t','i','o','n','=','"','s','a','v','e','d','"',' ','s','t','E','v','t',':','c','h','a','n','g','e','d','=','"','/','"',' ','s','t','E','v','t',':','i','n','s','t','a','n','c','e','I','D','=','"','x','m','p','.','i','i','d',':','9','3','6','3','e','3','2','f','-','1','e','b','b','-','4','c','7','2','-','b','5','7','4','-','1','b','b','3','4','a','8','0','b','3','9','9','"',' ','s','t','E','v','t',':','s','o','f','t','w','a','r','e','A','g','e','n','t','=','"','G','i','m','p',' ','2','.','1','0',' ','(','L','i','n','u','x',')','"',' ','s','t','E','v','t',':','w','h','e','n','=','"','2','0','2','3','-','0','3','-','1','2','T','1','6',':','1','5',':','4','7','+','0','0',':','0','0','"','/','>',' ','<','/','r','d','f',':','S','e','q','>',' ','<','/','x','m','p','M','M',':','H','i','s','t','o','r','y','>',' ','<','/','r','d','f',':','D','e','s','c','r','i','p','t','i','o','n','>',' ','<','/','r','d','f',':','R','D','F','>',' ','<','/','x',':','x','m','p','m','e','t','a','>',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','<','?','x','p','a','c','k','e','t',' ','e','n','d','=','"','w','"','?','>',0377,0342,02,0260,'I','C','C','_','P','R','O','F','I','L','E',0,01,01,0,0,02,0240,'l','c','m','s',04,'0',0,0,'m','n','t','r','R','G','B',' ','X','Y','Z',' ',07,0347,0,03,0,014,0,016,0,'&',0,'7','a','c','s','p','A','P','P','L',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0366,0326,0,01,0,0,0,0,0323,'-','l','c','m','s',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,015,'d','e','s','c',0,0,01,' ',0,0,0,'@','c','p','r','t',0,0,01,'`',0,0,0,'6','w','t','p','t',0,0,01,0230,0,0,0,024,'c','h','a','d',0,0,01,0254,0,0,0,',','r','X','Y','Z',0,0,01,0330,0,0,0,024,'b','X','Y','Z',0,0,01,0354,0,0,0,024,'g','X','Y','Z',0,0,02,0,0,0,0,024,'r','T','R','C',0,0,02,024,0,0,0,' ','g','T','R','C',0,0,02,024,0,0,0,' ','b','T','R','C',0,0,02,024,0,0,0,' ','c','h','r','m',0,0,02,'4',0,0,0,'$','d','m','n','d',0,0,02,'X',0,0,0,'$','d','m','d','d',0,0,02,'|',0,0,0,'$','m','l','u','c',0,0,0,0,0,0,0,01,0,0,0,014,'e','n','U','S',0,0,0,'$',0,0,0,034,0,'G',0,'I',0,'M',0,'P',0,' ',0,'b',0,'u',0,'i',0,'l',0,'t',0,'-',0,'i',0,'n',0,' ',0,'s',0,'R',0,'G',0,'B','m','l','u','c',0,0,0,0,0,0,0,01,0,0,0,014,'e','n','U','S',0,0,0,032,0,0,0,034,0,'P',0,'u',0,'b',0,'l',0,'i',0,'c',0,' ',0,'D',0,'o',0,'m',0,'a',0,'i',0,'n',0,0,'X','Y','Z',' ',0,0,0,0,0,0,0366,0326,0,01,0,0,0,0,0323,'-','s','f','3','2',0,0,0,0,0,01,014,'B',0,0,05,0336,0377,0377,0363,'%',0,0,07,0223,0,0,0375,0220,0377,0377,0373,0241,0377,0377,0375,0242,0,0,03,0334,0,0,0300,'n','X','Y','Z',' ',0,0,0,0,0,0,'o',0240,0,0,'8',0365,0,0,03,0220,'X','Y','Z',' ',0,0,0,0,0,0,'$',0237,0,0,017,0204,0,0,0266,0304,'X','Y','Z',' ',0,0,0,0,0,0,'b',0227,0,0,0267,0207,0,0,030,0331,'p','a','r','a',0,0,0,0,0,03,0,0,0,02,'f','f',0,0,0362,0247,0,0,015,'Y',0,0,023,0320,0,0,012,'[','c','h','r','m',0,0,0,0,0,03,0,0,0,0,0243,0327,0,0,'T','|',0,0,'L',0315,0,0,0231,0232,0,0,'&','g',0,0,017,0134,'m','l','u','c',0,0,0,0,0,0,0,01,0,0,0,014,'e','n','U','S',0,0,0,010,0,0,0,034,0,'G',0,'I',0,'M',0,'P','m','l','u','c',0,0,0,0,0,0,0,01,0,0,0,014,'e','n','U','S',0,0,0,010,0,0,0,034,0,'s',0,'R',0,'G',0,'B',0377,0333,0,'C',0,03,02,02,03,02,02,03,03,03,03,04,03,03,04,05,010,05,05,04,04,05,012,07,07,06,010,014,012,014,014,013,012,013,013,015,016,022,020,015,016,021,016,013,013,020,026,020,021,023,024,025,025,025,014,017,027,030,026,024,030,022,024,025,024,0377,0333,0,'C',01,03,04,04,05,04,05,011,05,05,011,024,015,013,015,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,0377,0302,0,021,010,02,'Z',02,'X',03,01,021,0,02,021,01,03,021,01,0377,0304,0,034,0,01,0,02,02,03,01,0,0,0,0,0,0,0,0,0,0,0,06,07,05,010,01,03,04,02,0377,0304,0,034,01,01,0,01,05,01,01,0,0,0,0,0,0,0,0,0,0,0,05,02,03,04,06,07,01,010,0377,0332,0,014,03,01,0,02,020,03,020,0,0,01,0332,0220,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,01,0361,0345,'@',01,0367,0355,' ',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0362,'Q','~',')',0213,';',021,0307,0232,0210,0343,'L',0307,0254,0311,0341,0255,'f',0342,0255,0345,0364,'y','p',01,0231,0273,0205,0220,0257,032,'A','z',':','Q','~','"','a',0223,013,'.',0312,0202,0314,']',0217,0347,0337,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,03,0203,01,0217,'-','X','`','m',0325,0276,016,0321,06,0305,0235,0307,0371,'p',0,0,0,0,0,0356,'y',')',0275,033,'c','g','k',026,0206,'~',0245,'5',0313,0327,'{',0252,0266,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,017,'-',027,0352,0370,0375,0306,0234,0214,0335,0340,'X',0263,0275,'/','@',0,0,0,0,0,0,03,0222,'G','s',06,0332,0221,0323,0256,'I','M',027,'=','~',',',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,01,0345,0242,0375,'A',031,0275,'Q','1',';',0374,'z',0336,'h',0,0,0,0,0,0,0,0,0,036,0237,'i',0267,0244,'4',0373,0336,'_',0235,0310,0262,'"',0,0,0,0,0,0,0,0,0,0,0,0,0,0,01,0361,0345,'U',0204,'~',0337,'@',0304,0364,010,0375,0274,0320,'>','<',0257,0305,'o','0',0,0,0,'v','=',0372,07,'K',0300,0,0,0,0,036,0237,'i',0271,0244,'t',0273,0356,'g',0233,0345,0256,0340,0,0,0,0,0,0,0,0,0,0,0,0,0,04,'s',036,'^',0230,0303,0330,0376,'d','t',0316,0345,0237,0272,03,0212,0235,'U',0373,020,0213,0337,'+',0230,'N',0235,0362,0364,'{','=',0246,']','~','"','_',0223,013,',',0310,0206,0222,'_',0214,0315,0335,0300,0310,0334,0303,0364,0325,'`',0367,015,'j','G',037,'o','&','?','f','J','-','b','Z',033,0213,'9',020,0307,0231,0302,0321,0223,0300,0,0,01,0232,0257,027,'d',0246,0371,0215,0247,'#',0245,0363,0357,0200,0,0,0,0,0,0,0,0,0,0,0,037,'>','U','N','E','o',0224,'<','n',0357,'i','l',0334,':','Y',0241,0327,'0',0344,0271,0235,0230,0236,0216,'.',0243,0373,0376,'4',027,0256,0342,'@',' ',0372,0274,0202,0214,0273,'Z','C','O',0236,0346,'k','Y','+',0270,'@',0,0,0,01,0323,'M',0310,0216,',',0375,']',0201,0266,0325,'q',0373,'l','f',0324,0207,0,0,01,0364,'[',031,0332,0256,0311,0316,0362,0234,0235,0334,' ',0,0,0,':','N',0260,0,'9','=',07,' ',0,0,0,03,0313,'E',0375,'g',0201,0353,'U',026,06,0331,'1',0222,0322,'l',0351,'N','S','j','|',0323,047,0316,'?',0240,01,0,0357,0221,0371,0215,0256,0213,'K',0332,'@',0,0,0,0,0,0,0352,0362,0270,'.',026,0315,'I','E','o',0225,0206,026,0317,0324,0364,0,0,0222,']',0300,0332,0335,0203,0220,'K',0362,0340,0,0,016,014,'1',011,'#','~','<',036,'y',0327,'E','=','T',0322,0363,0316,0332,0353,0365,0334,0256,0334,0365,' ',0,0,0,0,0302,0331,0221,0325,'m','{',0260,0300,0261,0246,0304,0326,'O','E',0265,'/',0362,0353,'3',0346,0331,'0',0,020,0276,0321,0203,0354,0350,026,'-','0',0,0,0,0,0,0,0,04,'G',022,'z',0204,0210,0350,0325,'^',016,0323,0360,0,0,0367,0373,'o','i',0247,0271,'5',0227,'#',0247,0200,07,'Y',01,'+',0312,'i',0301,'`','c',0341,'b','q','|',030,026,'z',',',0323,0363,'o',0307,0216,0313,0365,'M',0366,0371,'K',0217,'.',0374,0330,0,0,0,030,'+',022,'z',0213,0256,'v',0230,0265,0231,020,'&',0262,'z','-',0251,0177,0227,'Y',0237,'6',0311,0200,0,0205,0366,0214,037,'g','@',0261,'i',0200,0,0,0,0,0,0,0,07,04,013,07,'h',0326,0270,'>',0245,021,0263,'&',0,03,0275,0346,0317,0316,0362,0273,'j','O','G',03,0340,0255,0312,0307,016,0304,'G','Z',0216,0307,0305,0332,0342,0237,0,03,0333,'#','r',0306,0334,0245,0357,033,0225,0345,0,0,0,014,025,0211,'=','E',0327,';','L','Z',0314,0210,02,'k',047,0242,0332,0227,0371,'u',0231,0363,'l',0230,0,010,'_','h',0301,0366,'t',013,026,0230,0,0,0,0,0,0,0,0,01,0344,0242,0375,01,013,0323,'h',0350,0315,0343,0251,0350,0,'w',0274,0331,0371,0336,'W','m','I',0350,0374,020,'"',0241,0301,0306,0205,'j',021,0235,030,0264,0200,0,0354,0277,'T',0337,'r',0223,0267,'3',0262,'l',03,0220,0,0,030,0373,'y','z','}',0255,'v',0350,0205,0211,'@',0,0232,0311,0350,0266,0245,0376,']','f','|',0333,'&',0,02,027,0332,'0','}',0235,02,0305,0246,0,0,0,0,0,0,0,0,0,'p','q',0347,0265,0254,'~',0337,0253,0220,']','c',035,'M',0320,0,0357,'y',0266,';',07,' ',0315,'H','j','T',0346,'5',0230,016,0235,025,0346,0302,0240,0,07,0325,'~',0313,'6',0314,0373,':','_',':',0327,0365,0332,0,0,03,0242,0233,0272,0243,0256,'v','j',0323,023,'b',0,016,0367,0227,'T',0357,'+',0314,'Q',0244,0331,0237,'6',0311,0341,0263,0251,0325,016,0307,0206,'=','6',0233,'!',0313,'r',0372,0272,'v',047,0263,0240,'X',0264,0300,0,0,0,0,0,0,0,0,0,04,'G',026,'{','T','u',0356,0301,033,0265,0236,0,023,0331,035,'.','y','3',0311,'+',0355,'J','#',037,027,'h',0,07,'>',0244,033,'&','e',0215,0260,0310,0334,0236,0373,0355,0,0,0,'5',0346,017,0250,0320,0261,'{',0340,0,'w',0274,0331,0371,0336,'W',')',0223,0321,0353,0230,0232,0254,0317,0233,'d',0342,'S','4','S',0233,0305,0213,'s','K',0275,0256,0335,';',027,'+',0207,'U',0351,0223,'O',0263,0240,'X',0264,0300,0,0,0,0,0,0,0,0,0,0,0301,'X',0223,0324,']','s',0264,0305,0254,0310,0201,0230,0277,023,'j','l','<','Z',')',0256,0353,0230,'h',',','`',0,03,'-','7',0221,'`',0355,'2',0227,'-','u','g',0,0,0,012,0373,07,'i',0324,']','w',0261,0365,'=',0,'}',033,'?','7',0313,'m',0371,']',022,034,'W','1','5','Y',0237,'6',0311,0341,0363,0251,0322,016,0375,037,0352,0263,0355,0317,0242,0337,0371,0365,'j','M',0333,0366,'t',013,026,0230,0,0,0,0,0,0,0,0,0,0,03,05,'b','O','O','5',0256,0333,037,0267,0233,0355,0273,0203,'i',0354,034,'w',033,031,0251,'F',0265,'|',036,'<',0,0,0366,0310,0334,0236,0356,022,0326,0355,0353,0223,020,0,0,03,0303,'o',047,'L',0365,0216,0351,031,0265,' ',0,033,01,'+',0240,0337,0363,'|',0303,0222,034,'W','1','5','Y',0237,'6',0311,0212,0327,'j',0265,034,0224,0242,'w',0256,0334,0231,'A',0334,0205,0366,0214,037,'g','@',0261,'i',0200,0,0,0,0,0,0,0,0,0,0,04,'C',022,0177,'P',0365,0356,0307,'?',0234,0345,']',0364,'h',0360,0375,'>','7',0342,0337,0200,0,';',0262,'j',0234,'n','R',0266,0216,'e',0373,034,0372,0,0,0,'5',0276,07,0253,'Q',0361,0273,0270,0,'Z',0271,0272,0306,0324,'l',0134,'o',0357,0332,'D','8',0256,'b','j',0263,'>','m',0223,0206,0316,'[',0323,0336,0335,0205,'g',0352,'W','g',0332,0345,0333,0237,'E',0277,013,0355,030,'>',0316,0201,'b',0323,0,0,0,0,0,0,'>','O',0240,0,0,0,0,'>','J','r',0304,0244,'*',0334,',','#','M',0211,0350,0305,0244,0,07,0325,0317,'f',033,'t',0215,0213,'+',0231,'m',0235,0300,0,0,04,'O',026,'w','M','5',0256,0335,0346,0362,0240,04,0202,0346,026,0346,0354,0374,'3',047,'w',010,010,'q',0134,0304,0325,'f','|',0333,047,0207,0316,0247,'Q',0373,'F',025,0265,0246,'_',0226,0303,'W','d',0352,0267,'a','}',0243,07,0331,0320,',','Z','`',0,0,0,0,0,0,0,0,0,0,01,032,')',0273,'t','@','5',010,0234,'|',']',0240,0,034,0372,0221,0354,0331,0263,0351,0371,013,0227,0337,'r',' ',0,'p','r',0,'5','[',']',0354,0225,'F',016,0322,0,0373,'6',0317,'`',0344,'6','T',0206,0240,0,0207,025,0314,'M','V','g',0315,0262,'~',014,0237,')',015,0373,034,'L','!','+',0263,0265,';',0320,0276,0321,0203,0354,0350,026,'-','0',0,0,0,0,0,0,0,0,0,0,06,'0',0246,0374,0362,01,0254,0304,0372,0264,0235,027,05,0235,0262,'x','2','3',0300,03,'/','7',0221,'=',0332,'e','.','*',0352,0221,0,0,'8','9',0,0210,0342,'O','i',0226,0265,0333,0272,0236,0200,'-',0214,0355,'W','i',0366,'.','9',0310,0,0207,025,0314,'M','Y',0276,023,'!',017,0233,0267,'P','n',0326,05,0221,0253,']',0276,'9',0336,'L','/',0264,'`',0373,':',05,0213,'L',0,0,07,07,' ',0,0,0,0,0,0,03,0316,'T',05,'{',021,0205,025,0324,'"',0345,0220,0272,'N','O',027,'_',0216,0311,'n',0230,011,015,0217,0343,0332,0207,0262,'B',0344,0357,'p',0226,0266,0357,0334,0233,0,0,07,07,' ',032,0273,0257,'v',012,0207,03,'m',0,'z','}',0247,'u','v','~',027,'"',0310,0210,0,010,'q',0134,0304,0325,'=',0371,0326,'K',0303,0221,0346,033,'>',0236,0272,0231,'l','/','d',0221,'u',0302,0373,'F',017,0263,0240,'X',0264,0300,0,0,0,0,0,0,0,0,0,07,0311,'Z',0225,0226,'%',0210,'6',0231,025,0325,0213,'D',0316,033,'D',0363,0134,0313,0313,'b','k',0336,'+',0362,0220,0371,0215,0357,0234,0233,0263,'m',0316,'S',0317,'N',0345,0261,0371,'P','Y','k',0270,'@',0,0,03,017,'f','G','H','u','n',0365,0345,0362,0240,05,0317,'#',0246,0354,0314,0377,0,'$',0,01,016,'+',0230,0232,0254,0317,0233,'d',0343,'r',0224,'h',0327,0320,'Q',0371,0254,012,0255,0255,'2',0366,0312,0362,0274,0270,'_','h',0301,0366,'t',013,026,0231,0300,07,' ',0,0,0,0,0,0,0,0,'"','E','C','n',0210,036,0243,025,0216,0212,0263,0367,'M',023,0330,036,'g',0200,0317,0332,0373,0350,0307,0356,0243,026,')','3',0272,0312,0266,0311,034,0214,0216,0307,'T',0353,0275,0237,'`',0244,0364,'k',0272,'_',0236,0,0,0,012,'>',047,0240,'k','|','/','M',0,'w','<',0335,'m',0237,0205,0311,'2','a',0300,0,'C',0212,0346,'&',0253,'3',0346,0331,'<',024,0205,':',0341,0324,'q','v',07,0233,0344,0344,'1','}',0315,0340,'U',013,0355,030,'>',0316,0201,'b',0323,'8',0,0344,0,0,0,0,0,0,0,0,'1',0205,'3',0347,0220,035,'z',':','=',0255,0341,0372,'-','X',0312,'b',0304,'z',0354,0341,0347,'0','u','O',0237,'n','B',0247,':','.','G','f',0226,0225,0354,'3',0265,'v',0277,0331,'0',030,0233,04,0322,0374,'N',0345,'m',034,'%',0357,0237,'@',0,01,0301,0247,'z',0307,'q',0202,'c','N',0,',',0354,0315,'k','l',0266,'N','.',0,0,'C',0212,0346,'&',0253,'3',0346,0331,'<','6','u',':',0241,0330,0360,0305,0227,0252,0335,0277,'y',0306,'L','/',0264,'`',0373,':',05,0213,'L',0,0,0,0,0,0,0,0,0,035,'E','J','W','q',0270,0220,0315,':','7',0233,'T','L',0341,'t','/',']',0230,0317,06,'D',0316,047,'.','{',025,0227,'+',0351,0227,0313,0234,'m',0322,0361,'(',0276,0227,'^',0304,'t','N',017,0263,'u','6','n',033,'&',0311,0205,0373,0367,0300,0,03,015,'f','K','G',0265,'^',0365,0322,0364,01,0265,0323,0334,0232,0322,0222,0322,0300,0,010,'q',0134,0304,0325,'f','|',0333,047,0341,0310,0362,0201,0350,0370,0333,031,0314,'2',0200,0205,0366,0214,'/','g','@',0307,0264,0300,0,0,0,0,0,0,0,0,01,04,'*',0213,026,0240,'z','l','O',0227,06,0217,']',0234,'Y',0254,'/',';',0350,0257,'3',027,0225,'7',0351,0267,0203,027,0330,'7','y',0316,0347,'+',0321,'F',0337,'T','k',0375,'{',0315,'M',0360,'6','2','c',0234,'^',0223,034,0344,0,0,025,'$','^',0363,0253,'P','}','P',01,0223,0252,0326,0360,'m','|',03,0333,'^','0',0,01,016,'+',0230,0232,0254,0317,0233,'d',0360,0371,0264,0351,'/','|',0300,0231,'A',0327,'d','j',0327,'o',0216,'w',0223,012,0355,030,'~',0336,0201,0215,'i',0200,0,0,0,0,0,0,0,07,036,'9',0365,0210,')','j','|',0200,'j',0361,'x','h',',','o',']',0254,'_','e',0234,'<',0374,'~',0247,0356,0261,023,0321,'^','|','B','s','}',0220,0356,0223,0263,0331,'l',0330,'|',017,'V',0246,0343,'w','p',05,0215,0227,0257,0355,0326,0313,0304,0200,0,01,0254,0260,035,'n',0231,0216,0334,0200,026,0266,'v',0257,0265,'[',027,032,0,0,04,'8',0256,'b','j',0263,'>','m',0223,0351,0255,022,0232,0266,'2','x',0225,'H','#','j',0204,'v',0234,'?','~',0377,0,0215,'i',0200,0,0,0,0,0,0,0,07,0307,0236,'|',0373,0355,'D','V',0360,0370,'1','-','K',03,0356,0335,0271,0244,'.',0203,0341,0277,')',0331,'M',0214,'v','L',0276,'/','*','W','+',0263,'I',0316,0366,'I','+',0223,0337,'a',0361,0373,0206,0245,'k',0375,'t',01,0220,0366,0336,0363,'m',0237,'>',0372,0253,0260,0,03,0203,'L',0265,'n',0361,015,0261,'*',0,0331,0331,0276,'_','q',0312,0350,'@',0,0,0207,025,0314,'M','V','g',0315,0262,'x',0274,0277,'4','w',0350,010,0357,'=',0305,0273,0245,0337,0332,036,'I',0227,012,0355,030,'>',0336,0201,'b',0323,0,0,0,0,0,0,0,0,01,05,'*','L','k','0','=','.',047,0243,026,0237,'}',0214,031,0244,047,'8',0371,0366,0347,'*','#',022,0233,0277,0217,'a',0330,0247,0233,'|',0265,0261,'r',0271,0251,0340,0267,0225,0243,'Z',0247,0320,'>','?','*',03,0223,'t','6','^','%','/',0313,0327,0300,0,'y','h',0277,0243,032,0237,0320,'~',017,'+',03,0223,'t','6','^','%','/',0313,0327,0300,0,01,016,'+',0230,0232,0254,0317,0233,'d',0340,';',035,0272,'c','z',0307,0227,'B',0327,017,0233,0243,'i',0371,016,'d','/',0264,'`',0373,':',05,0213,'L',0,0,0,03,0203,0220,0,0,0,014,'Y','J','S','M','w',0252,'E','b',0241,'l','w','Q','k',0327,'k',023,'1',0205,01,0312,0254,0226,'4',',','K','a',0350,022,0375,0342,'z',0306,0314,0277,'h',0237,'g',06,0231,'j',0335,0342,033,'b','T',01,0264,0263,0234,0256,0333,0224,0321,0200,0,'F',0261,0246,0264,0233,'X',0356,0177,' ',036,0337,'i',0336,'}',0263,0347,0317,'e','x',0340,0,0,0207,025,0314,'M','V','g',0315,0262,'x',0254,0312,'`','[',035,0272,'3',0240,'c',0335,'Z',035,0373,'o','L',0277,013,0355,030,'>',0316,0201,'b',0323,0,0,0,0,0343,0307,'>',0200,0,0,07,0301,'T',025,'d','.',04,'W','T',0301,0355,0265,'f','g',013,0240,'{',',',0304,0341,'s','v',0217,025,0371,'?','.','F','f','g','v',0330,0347,0263,031,0327,031,0353,0,0326,015,0177,0257,0323,0321,0373,'p',02,0375,0225,0320,'v',016,'o',0230,0,0,0257,'0','6',0315,'B',0327,0273,010,02,'U','v',';','u','v',0256,011,0317,0276,0,0,02,034,'W','1','5','Y',0237,'6',0311,0351,0217,'t',0300,0264,'5','+',0326,0206,0245,'z',']',013,'X',0205,0366,0214,037,'g','@',0261,'i',0200,0,0,0,'q',0343,0237,'@',0,0,02,'&','R',0330,0266,' ','Z',0134,'W','N','5','2','(',0355,'k','!',0217,033,0351,0265,0205,0335,'F',06,06,'C','m',0363,'m',0373,'|',0363,'g',0224,0270,0352,0367,'>',0,'(',0210,'n',0221,0256,0321,035,024,01,'n',0347,0352,'{','G',0260,0361,0340,0,025,'d','n',0353,0252,'P','=','`',01,'d',0345,0353,0333,'o',0262,0361,'0',0,034,034,0202,034,'W','1','5','Y',0237,'6',0311,0301,0266,013,'u','V',0341,'g','_','z','V','-',0307,0243,0344,'m','o',035,0314,0205,0366,0214,037,'g','@',0261,'i',0200,0,0,03,0316,'b','O','1',0330,'g',016,0360,0,07,0220,0245,')',0246,0262,0324,0342,0261,'P',0326,'=',0226,'p',0362,0230,0261,'}',0224,0331,0314,'a',0353,'}','U','e',0306,'6',0235,0376,'q',0272,'M','Z','w',0256,'O',016,'@',05,'Q',031,0273,0352,0264,027,'V',0,'Y',0271,0232,0346,0331,0354,0234,'T',0,05,047,021,0320,0265,0256,033,0245,0200,'-',0234,0355,'W','i',0366,'.','8',0,03,0203,0220,'C',0212,0346,'&',0253,'3',0346,0331,'=',027,0372,016,';',0242,0342,0353,0320,0362,'-',0315,'.',0364,0242,'"',0270,'_','h',0301,0366,'t',013,026,0230,0,0,017,021,07,'!',0236,'y',0205,0263,'o',0307,'b',0337,0262,0375,0311,0265,0373,0266,0311,0366,0,'8','+',0202,0234,0206,0301,0211,0352,'x',037,'v',0355,0313,'"','t',0274,'n','L',0276,'/','*','^','I',033,0251,'b','d',0266,031,06,0371,0264,0330,'r',031,'V',0311,0330,0,05,'q',037,0267,'j','6',0277,0327,0300,023,'K',0361,';',0231,0264,0360,0200,0,024,0234,'G','B',0326,0270,'n',0226,0,0275,'$',0364,'m',0214,0235,0345,'`',0,'8','9',04,'8',0256,'b','j',0263,'>','m',0223,0244,0367,0334,'z',0277,'m',0265,033,0224,0242,0357,0320,'r','/',036,0177,0221,013,0355,030,'>',0316,0201,'b',0323,0,0,'p','F',012,0313,0317,'#','8','v','#','P','8','8',0270,0253,037,026,'|',0356,0312,0252,'w',0270,0312,0337,'9','w',0363,0240,03,014,'Q','X',0326,'`',032,'L','6','B','*',023,0305,'{','>','g',013,0316,'|','w',0244,0342,0322,0273,0257,0222,0376,0134,0223,'g',0315,0236,'O','H',0134,0336,0373,0221,0,0,'D',0361,047,0364,0257,'Z',0355,0300,011,0245,0370,0235,0314,0332,'x','@',0,012,'N','#',0241,'k',0134,'7','K',0,'^',0222,'z','6',0306,'N',0362,0260,0,0340,0344,02,034,'W','1','5','Y',0237,'6',0311,0352,'o','e',0302,0201,'l','V',0375,0370,0336,0337,'|',0353,'&',0345,0321,0257,0302,0373,'F',017,0263,0240,'X',0264,0300,0,021,'b',0245,0247,0310,0214,'&',04,'s','[',0302,0353,0263,0340,03,'1','9',0221,'z',0355,'R',0266,'_',0276,0201,0360,'T','T',0371,'O',0352,0261,'^',0375,'G','K',0365,'Y',0302,0305,'e',0316,0370,'o',0347,0311,'c','4',0376,'^',0307,0267,016,0203,'<',0333,0245,0356,012,0352,0224,0200,0,04,'O',022,0177,'J',0365,0256,0334,0,0232,'_',0211,0334,0315,0247,0204,0,0,0244,0342,':',026,0265,0303,'t',0260,05,0313,'!',0247,'l',0336,0301,0310,0300,03,0203,0220,010,'q',0134,0304,0325,'f','|',0333,047,'S','n','V','l',0255,'Z',0357,0277,033,0320,'!','}',0243,07,0331,0320,',','Z','`',0,'u',0224,0271,'^',0302,0340,'E',0265,'L',036,')',0360,0,';','r','=',0265,'7',0211,0213,0342,0375,0336,0360,'E',012,026,047,012,'=',0244,'k',0263,'(','>','s',0205,0315,0332,0362,030,0360,0261,')','}',0353,0335,0217,037,0341,0223,0230,0231,0356,0363,'v','f',']',0373,',',0344,0,0,' ',0230,';','>',0235,'k',0275,0230,01,'4',0277,023,0271,0233,'O',010,0,01,'P','E',0357,'z',0275,07,0324,0200,026,0246,'n',0261,0265,0233,037,031,0,016,016,017,0240,'C',0212,0346,'&',0253,'3',0346,0331,'0',0,020,0276,0321,0203,0354,0350,026,'-','0',0,':',012,'6',0237,'*',0355,'6',037,0303,031,'h',0,0,0226,0355,0322,027,0374,0316,'t',0260,0363,024,'u',0213,'U','V',0231,023,0365,033,0205,',',0210,0321,0262,'8',0360,030,014,0375,0327,0321,'o',013,0311,0221,'!',0335,0277,0357,'V',04,0316,'u',0306,'z','@',0,0,'V','q',0333,0216,0245,0300,'u',0300,05,0203,0225,05,0267,0373,'7',017,0,01,'Y',0307,'n',':',0227,01,0327,0,023,'[',0361,';',0227,0264,0360,0200,0,0340,0371,'>',0301,016,'+',0230,0232,0254,0317,0233,'d',0300,0,'B',0373,'F',017,0263,0240,'X',0264,0300,0,0371,')',0177,'<',0250,0264,0370,0214,'t','M',0220,0,03,'#','+','z',0336,0334,'%',0356,':',0275,0256,0374,0362,0216,0326,0243,'0','Z',0366,'(',0373,0362,0231,04,'v',0263,'"',0216,0323,'|','w',0245,'0',';',0267,'J',0235,'m',0223,'7','5','^',0347,'@',0,0,012,'R','#',0241,'k','T','7','K',0,'Z',0231,0272,0306,0326,'l','|','d',0,04,'C',023,'a',0322,0355,'k',0266,0,'2',0265,'Y',0336,'M',0263,0347,0336,0372,0255,0,'8','8','>',0201,016,'+',0230,0232,0254,0317,0233,'d',0300,0,'B',0373,'F',017,0263,0240,'X',0264,0300,0,025,0251,'H',0353,0221,0221,0375,'o',020,0,0,0372,0271,0354,0353,'v',0224,0232,0346,0344,0302,'c',0261,'`',0332,'l','g',0315,0277,0,031,014,'|',016,0334,0353,0263,015,0347,'c',0265,0262,'.',0317,'@',0,0,01,0255,0260,'=','^',0221,0215,0335,0200,027,0214,0236,0221,0261,0363,0274,0244,0,06,'6',0326,'n',0214,0352,0237,'@','y',0300,'>',0336,0356,0376,0321,0301,0263,0331,021,'@',01,0301,0310,'!',0305,'s',023,'U',0231,0363,'l',0230,0,010,'_','h',0301,0366,'t',013,026,0230,0,02,'.','P',0221,0230,0220,0255,'2','1',0340,0,0,0312,'L','_',0222,'l',031,0261,'-','Z','?',0311,0201,'l',0,'9',0257,0331,0206,0337,'#','c',0312,0346,'[','g','h',0,0,0,'5',023,'Y',0355,0325,0326,',',0370,03,'e','f','y',0235,0327,'/',0317,0200,0,'|',0371,0356,0222,'j',0235,0376,'9','o','4',01,0266,'3',0374,0222,0317,0221,0323,'@',0,01,016,'+','h',0357,'l',0377,0,0232,0245,024,0200,02,017,0333,0360,'2',033,0315,0233,'L',0,01,0344,'(','[','6',0353,035,032,037,0253,036,0220,0,01,0353,0352,0257,'~','h',0360,0,06,'n','{','&',0300,0332,'%','.',0237,'}',0312,0,0,0,03,0246,0233,0232,';',0251,0375,05,0207,0246,0370,03,'p','6','.','9','`','g','j',0240,0,06,0250,'k',0235,0232,0256,0302,0331,0300,027,0214,0236,0217,0261,0363,0274,0250,0,0,030,'R',0222,0361,'b','q',014,0354,0306,0217,'|',01,0346,0230,0242,0264,0372,022,':',0304,0314,0362,'v',0,0,0340,0250,0374,0362,0231,0324,'b','q','P',0366,0,0,0,0,0,'z','s','k',0237,'n','r',0326,0365,0373,0263,'0',0,0,0,010,'~','&',0301,0246,':',0327,'m',0371,0,0357,'7',0217,'k',0340,'9','k',0270,0,0,05,031,017,0321,0265,0312,037,0243,0200,'%','7','c',0267,'S','j',0340,0277,'~',0322,0,0,012,0270,0205,0320,0316,'j',0227,'}',030,025,016,0273,0376,'a','v',0273,'y',',',0272,'n','c',0274,0,0,'!',0305,013,015,0203,022,0324,0243,0336,0,0,0,0,037,'U',0373,'1',0334,'$','l',0331,'L',0313,'T',0373,0,0,0,0,0241,'a',0272,'V',0274,0304,'t','0',04,0276,0364,'f',0350,'m','<',033,0237,'|',0,0,'!',0370,0233,06,0230,0353,']',0267,0344,03,0350,0334,0335,0227,0211,'L','r',0365,0360,0,0,'p','D',0310,0351,0362,01,0227,'&',0207,'p',0,0,017,011,'A','c',0332,0255,0264,'x','~',0273,036,0,0,0,0,'3',0233,06,'U',0213,0263,'I',0335,036,0373,0357,0,0,0,0,0371,0363,0335,'4',0325,0373,0274,'2',0304,0250,02,0361,0223,0321,0366,'>','w',0225,0,0,03,0246,0233,0232,'K',0252,0367,0330,0375,0274,0300,05,0345,047,0243,0354,'t',0357,'*',0,0,07,'A',02,'0',047,0,031,'r','|','{',0200,0,0,'p','T','>','y','M','j','1','8',0270,'{',0,0,0,0,'z','s','k',0260,'7','I','{',0222,0365,0311,'X',0,0,0,0,0207,0342,'l',032,'c',0255,'v',0337,0220,01,0267,'{',017,037,0261,'s',0365,' ',0,0,015,'p',0202,0352,0324,'t','f',0356,0,0313,0327,0217,0273,0273,'W',03,0366,'W',0214,0,03,0254,0246,'L','f',017,0262,'-',032,0377,0,'v',035,'C',0342,0377,0,0221,0375,0332,0307,'2',0324,0335,0207,0254,0,0,04,'D',0241,'b','0',0241,0372,0204,'s',0300,0,0,07,0325,'~',0313,0366,0371,033,'V','W','2',0320,'>',0200,0,0,0,06,0265,'@',0365,0212,'R','7','u',0,'f','k',0307,0335,0375,0257,0200,'z','*',0262,0,0,01,013,0303,0330,0264,0327,'[',0355,0177,' ',03,'e',0346,0271,0235,0323,'-',0317,0200,0,'D',0312,0242,0322,0316,0371,0342,'G',0325,07,'p',01,0327,0235,'M','g',0364,'L','t',0352,'F',0233,04,0,0,07,0220,0241,0354,0333,0254,'t',0210,'~',0214,'Z','@',0,0,'3','3',0271,'6','V',0321,')','t','U',0357,0264,0,0,0,0,'a',0354,0310,0351,'6',0255,0335,0374,036,']',0,']',0322,'Z','N',0311,'O','r',0200,0,0,01,0361,0345,'Z','q',0254,'w','8','N','<',0300,02,'C','s',013,'u','v',0236,015,0352,0256,0300,0,'@','H','n',0273,'r',0305,0371,0346,'D',0,04,'#',0266,0340,0373,0367,0273,026,0230,0,0,01,'X','x',0245,'u',0310,0314,06,0271,0210,0,0,017,'F',']','v',06,0351,'-','t','d',']',0224,0,0,0,0,01,0256,0320,'}','J',0210,0213,0336,0300,037,'f',0346,0354,0274,'F','a',0227,0,0,0,0,05,'M',031,0274,'j',0304,027,'U',0,015,0216,0230,0346,0367,0224,0307,':',0,010,011,017,0326,'n','X',0177,'>',0310,0200,0,0205,0366,0214,037,'g','@',0261,'i',0200,0,0,030,'r',0207,0306,0263,'^','i','1','=','X',0364,0200,0,0346,0257,'e',0273,'t',0205,0271,'/',0233,'f',037,'@',0,0,0,02,'?',0217,'+',0245,0332,0307,'s',0360,'y','t',01,'d','e',0353,0333,'o',0262,'q','N','}',0360,0,0,0,016,0232,'n','i',0216,0257,0335,0242,'v','d',0300,031,'J',0254,'n',0206,0317,0303,'3',0227,0343,0,020,'r',0274,0217,0366,0317,0371,0262,'O',0233,'^',0200,036,0253,0337,0240,'c',0244,033,'5',0273,'4',0,0,0,0340,0254,'|',0362,0240,0205,0301,0214,'k',030,'?','4','x',0363,0336,016,'}',0363,0262,0365,'Y',0351,0354,0253,022,'~','F',0346,0253,0337,'X',0,0,0,0,0343,0317,'u','g','^',0354,'U','6',016,0324,0,0372,'7',013,'c',0343,023,0314,0335,'d',0,0,0,0,'*','h',0315,0343,'V',' ',0272,0250,0,'Z',031,0232,0326,0327,'l','|','g',0353,0332,'@',0307,024,'q','!',0321,'/',0313,'9','v','W','v','/',0243,0342,0362,'7',0320,0261,0241,0375,'7',032,0345,0365,'!',0,0,0,07,0220,0251,0374,0362,'%',0215,'k',0313,'n',0216,0273,'t',0374,0322,0364,']',0367,'1',0221,'z','_','z',0273,03,0337,'r',' ',0,0,0,0,'U','q',0273,0256,0252,0300,0365,0217,0220,01,'d','e',0353,0333,'o',0262,'q','N','}',0360,0,0,0,0,'>','<',0253,'O','u',0236,0343,04,0306,0232,0,'r','l',0304,0327,'2',0271,0345,0271,0370,02,'0','U',0247,0207,0300,03,0321,0352,0311,'&',0300,0,0,0,03,0340,0361,0235,'G',0301,0301,0334,'{',016,0323,0220,0,0,0,0,'0','6','%','4',0327,'Y',0356,030,'z','2','@',035,0317,'7','/','e',0342,'2',0374,0270,020,0,0,0,0,02,037,0211,'?',0247,032,0337,'l',0363,'y','P',03,0331,0355,0275,0271,0330,0270,0344,0357,'7','Y',0,'|',0236,03,0200,017,'q',0330,0,0,0,0,0,0,0,0,0,0,0,0,'y','h',0277,0250,0232,0337,'l',0202,'c','M',0,05,0363,')',0242,'l','<',0347,'-',0,0,0,0,0,0,0240,0341,'z','f',0275,0304,0364,020,0,0315,'W',0213,0270,'{','/',023,0221,0344,'D',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'|','y','V',0256,'k',0335,0206,0245,0301,0332,0200,02,'[','z','3','r','v','~',031,0352,0256,0300,0,0,0,0,0,07,0236,0233,0272,0203,0254,0366,0370,'.','<',0310,0,'J','o','G','m',0326,0307,0305,0263,0267,0343,0,0,0,0,0,0,0,0,0,0,0,0,0,0,017,0217,'*',0327,030,'.',0255,'H','F',0356,0240,01,0351,0366,0235,0277,0330,0370,0274,0337,'3',0134,0344,0,0,0,0,0,0,021,0314,'y','}',';',0326,0273,'f',032,0214,0240,0,0224,0336,0216,0333,015,0207,0216,'I',0362,'a',0200,0,0,0,0,0,0,0,0,0,0,0,0,03,0317,'M',0335,'q',0202,0352,0324,0254,'v',0344,0,037,'F',0311,0314,0363,'K',0256,'_',0236,0374,0370,0372,0364,0,0,0,0,0,0,04,03,07,'h',0324,0255,'{',0260,0370,0374,0270,0,031,0232,0361,'v',0242,0177,0222,'O',0363,0265,'p',0,0,0,0,0,0,0,0,0,0,0,0,'1',0326,0362,0365,'o','^',0354,025,0246,'&',0304,0,02,0355,0222,0322,0266,'J','w',0224,'s',0357,0200,0,0,0,0,0,0,0,'*','h',0315,0343,'W',0340,0272,0247,'C',0320,0,0364,0373,'N',0304,'K',0363,0213,0262,'_',0236,0375,0373,'H',0,0,0,0,0,0,0,0,0,0,0,'!',0230,'{',016,0256,'@',0365,0270,0255,0231,020,0,026,0226,'n',0263,0262,0263,0374,0223,'3','s',014,0,0,0,0,'8','9',0,0,0,05,'K',031,0274,'k',04,027,'T',0350,'z',0,02,0307,0313,0327,0366,'Z','w',0224,0310,0362,'"',0,0,0,0,0,0,0,0,0,0,03,0317,'M',0332,'.',037,0243,0320,0221,'[',0377,0,0233,0312,0200,0,'Z',0271,0272,0306,0321,0354,'<','{',0321,'U',0240,0,0,0,0,0,0,0,0,01,'T','F','n',0332,0311,07,0324,0374,'>',0134,0,01,0357,0366,0325,0343,')',0242,'^',022,0374,0367,'!','s',020,0,0,0,0,0,0,0,0,03,0343,0312,0253,'|',015,0267,']',0241,'z','d','N',0314,0220,0,01,'o','g',0352,'{','5','?',0310,0375,025,'Z',0,0,0,0,0,0,0,0,0,0,'A','0',0266,'m','T',0327,0372,0356,026,0214,0240,0,03,'/','^','=',0341,')',0241,0334,'r',0232,'.','Z',0356,0,0,0,0,0,0,0,0,'t',0323,'r',0270,0300,0333,'h','h',0216,0205,06,0307,0232,0340,0,01,0366,'_',0262,0272,015,0373,'5',0314,0276,0375,0244,0,0,0,0,0,0,0,0,0,0,06,06,0304,0246,0257,'@','u',0312,0373,026,'t',0,0,036,0377,0,'m','Y',0371,0332,0275,0273,'%',0246,'O','s','u',0217,'E','V','@',0,0,0,0,017,0237,'=',0214,0343,'M','U','1',0373,0215,'I',035,0270,0306,'-','H',0,0,03,'+','U',0215,0232,0234,0345,0226,0204,0226,0231,0310,0,0,0,0,0,0,0,0,0,0,0,016,0252,'k',0244,0242,'z',036,0277,'D','t','?',027,0227,0,0,0,'3','5',0343,0330,'9','z',0344,0377,0,'3',0134,0230,'d',0302,0311,'r','!',0375,0367,'1','>',0375,0244,0,'>','|',0367,0317,'M',0354,'%',0231,'8','v',',',0344,'+',026,'v',0271,0302,0331,0342,'v','d',0376,0,0,0,'r','Y',0371,0232,0326,0312,'N',0362,0254,0345,0370,0300,0,0,0,0,0,0,0,0,0,0,0,0,04,'[',032,'o','[',0340,0372,0225,'s',0211,0260,'p',0,0,0,01,0350,'2',0225,0343,'f','.','a','e','n','b','z',0352,0262,'0','6',0244,'<',024,'d','a',0355,0346,'x',0274,0257,0344,0,0,0,03,'=','s',017,'a','f','9',0305,0267,047,0243,0375,'{','H',0,0,0,0,0,0,0,0,0,0,0,0,0,017,0237,'*',0255,0360,'6',0332,016,037,0242,'A',0261,0346,0270,0,0,0,0,0,0,0,0,0,0,031,'z',0361,0357,011,'M',016,0351,0225,0320,'r',027,'1',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,07,0307,0225,'W',030,033,'m','!',025,0276,0327,0330,0233,07,'K',0320,0,0,0,0,0,0,0,03,0222,'G','s',06,0347,0224,0321,0356,'Y','M',017,'!','s',020,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,07,04,'s',036,'b',0251,0216,0334,0252,0310,0375,0272,'%','b','S',0245,0350,0,0,0,0,0,07,'&','n',0274,'k','+',';','W',0265,0344,'4',0351,0356,'n',0261,0335,'U',0260,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'8',0363,0334,035,0231,'8',016,026,0313,04,0304,0330,'"','8',0363,'Q',0213,022,0230,0372,'o',0365,'<',0,0,'>',0217,'_',0264,0347,'n','`','K',0362,'a',0246,0231,'P','V',06,'f',0267,'-',0312,0201,0356,0252,0330,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,035,024,0335,0307,'[',0314,0305,0332,0316,0304,'[',0315,0363,'S','t',01,0235,0275,037,0356,0257,033,'+','v','?',0327,'U',0216,'}',0360,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'p','r',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,07,0377,0304,0,'3',020,0,01,04,01,02,05,03,03,05,01,01,0,01,05,0,0,04,01,02,03,05,06,0,' ',07,022,023,'0','4',021,024,'@',020,025,'3',026,027,'!','1','P','5','6','#','%','2','`','p',0200,0377,0332,0,010,01,01,0,01,05,02,0377,0,0371,'3',0231,023,0134,0355,0327,';','u',0316,0335,'s',0267,0377,0,0300,'e','.',030,'4','V','c','V','+','H',0342,'X','Q',0310,'G',023,0210,'{',047,0342,015,0244,0321,0376,0261,0266,0324,0367,0307,0221,047,0335,0315,0327,0335,0315,0327,0335,0315,0327,0335,0315,0323,'2',0353,'V','4','l',0346,0324,'w',0213,0304,0243,0243,'x',0334,'O',0213,0220,'L',0362,0254,0225,032,0340,'2',0321,025,027,0375,'o','_','M',035,'z',025,'{','l','x',0231,04,'J','~','{','d','[',0247,0260,'$',0237,0201,034,0317,0205,'B',0312,0354,0201,0320,034,'M',0221,0272,0256,0314,0353,'l','u',034,0254,0225,'?',0320,' ',0250,0205,'e',0317,020,0203,07,'V',0271,0275,0215,0222,'I','+',0346,'w',0305,0376,0265,']',0220,035,'X',0372,0256,'%',0257,'0',027,0201,0331,047,0371,0204,025,020,0254,0273,0342,',',03,'j',0316,0364,0313,'g',0374,0270,010,0224,'W',0322,'q',026,'q','u','Y','~',035,0263,'?',0310,'{',0333,033,'n','3','h',0207,'e',0231,0326,0266,0317,0366,04,'k',0330,021,0257,'`','F',0234,024,0354,'N',0323,'X',0347,'k',0243,'&',0272,'2','w',0340,'"','Q','_',0217,0361,016,'A',0365,'_','h','5',0244,'?',0342,0333,0336,0213,'M',011,'W','E','d','.','d','m',0215,0250,0305,'v',0272,'/',0327,'E',0372,'X',0334,0335,'z','z',0352,0326,0225,0263,'5','Q','Z',0277,'H',04,0230,0235,011,0205,0332,024,0341,0270,'d',0134,0214,037,0206,'B',0263,'Q',0340,'5','Q',0277,0364,0265,'f',0240,0245,010,'f','}',0264,']','}',0264,']','.','/','X',0252,'V',025,'V','V',0212,0341,0275,'|',0332,'#',0205,0353,0324,'+',0,0264,031,0245,'R',0232,022,0377,0,']',0252,0313,'r','j','f',0307,'s',0221,0355,021,027,0325,'?',0301,'s',0221,0215,0311,0263,0310,0300,'X',0345,0236,0356,0305,0255,'F','6',0266,0257,0252,0214,0215,0261,0247,0321,'Z',0213,0243,0252,0231,'+','U','9','V',0326,0232,'R',014,0255,0341,0321,0305,'j',0267,0,0256,013,'B',0327,014,033,'{',0262,'B',0311,'P',0354,'F',0264,0344,0262,0341,0223,0223,'V','8',0331,0365,'z',0376,0273,010,0252,0325,0307,'3',0271,0353,'T',033,010,',','a',0355,0254,0255,'n',0275,0324,'z',0367,'Q',0353,0335,'G',0257,'u',036,0275,0324,'z','B','#','^',0361,04,0306,'$','y','N','o','1',0357,0376,0365,0215,'5',024,0221,0343,0352,0316,0211,0312,0233,'n','b',0351,0227,0213,'y',0177,016,'H',0231,'+','m',0260,0272,0373,'6',0333,0360,0374,0332,0366,0311,023,0341,'~',0372,'k',0342,0251,'&',0240,0310,0307,0273,033,0177,0257,0246,0212,0264,0210,'d','+','"','s',0265,'-',0224,0322,'i','K','v',0224,0365,'M','}',0313,'_','s',0323,'O','W','j','2',0344,'c',0252,014,0367,'P','v',0355,'-',0207,0250,037,047,0312,'e',0275,'#',0351,0214,0376,'j',0377,0,'7','u',0367,0345,0305,'|',0337,0215,'o',0213,0203,'n',0313,0274,0,0272,0375,'9',0252,0307,'n',010,0331,'@',0237,031,0314,0207,0270,'f',0327,0275,030,0226,0227,0236,0232,0234,0245,'r',0315,'b',0326,'j','K',047,'.',0234,'S',0335,0256,0253,0265,0316,0272,0347,']','6','g','"',0204,0365,'|','x',0343,025,0260,0366,0255,0355,0340,0247,026,0376,0376,'{',0322,0276,0270,0317,0346,0257,0363,'w','_','~',0134,'W',0315,0370,0377,0,0336,0256,0360,0340,0255,0231,'y',0215,027,'I','&',0350,'f','x',0362,'a',0371,0203,'-','#',0372,0271,0310,0304,0271,0267,0346,'R',0215,'H',0365,'9',0216,0225,'}','w',015,012,0312,0360,'F',0346,'Z',0361,0375,0270,0375,0233,'{','x',')',0305,0277,0277,0236,0364,0255,0230,0317,0346,0257,0363,'w','_','~',0134,'W',0315,0371,'$',0215,021,'Q',0344,0330,07,'E','$',0215,0321,'?','l','3','<','y','0',0374,0301,0226,0221,0351,'W',0323,'W','V',0334,0250,'q',0274,0232,0226,'U',0221,0333,0230,0305,'{',0201,023,0246,0332,'*',0317,'N',0321,0307,'E','^','>','K','}','%',0331,0373,'q',0237,0315,'_',0346,0356,0276,0374,0270,0257,0233,0362,0275,'5',0222,0341,0243,0333,0260,0320,047,0257,0233,'l','3','<','y','1',014,0232,';','p',0356,'-',020,'v',034,'g',0246,0247,0231,'e','v',0344,'O','U',0,'?','M','S','V',',',0357,0215,0211,033,'{',023,0316,0301,0342,0314,0262,0177,0275,025,0266,030,'^','D',0225,0370,0264,0264,0300,'W',0371,0272,0272,')',0340,0323,0376,0352,0336,'k',0367,'V',0363,'P','q','b',0342,'7',0343,031,'$',031,'=','u',0367,0345,0305,'|',0337,0231,0220,'c',0320,'^',013,'u','B','M','$',0373,'h',05,0222,011,'L','5',']',0242,0210,'Y',0237,0274,0,0371,0226,0260,05,'&','A','F','h',0361,0366,'s',0334,0247,0235,'v',0303,013,0310,0223,017,0303,0331,'W',036,'Q',0377,0,'>',0277,0315,0326,'Z','g',0260,0306,0270,'_','K','[','g','L','F',037,'I','(',0374,'?',0255,07,'"',027,0203,0323,0253,'l',0357,0277,'.','+',0346,0374,0333,'z',0210,'.',05,0277,0240,0236,0210,0257,0255,'p','j','D',0254,'j',017,021,0245,0254,0216,0336,020,0253,'+',0202,021,'d','u','h',010,',',']',0234,0303,' ','J','Z',0371,'$','t',0262,'l','k','U',0356,0302,0261,'8',0300,037,'Y','G',0201,'_',0346,0352,0342,0271,0266,0365,'|',0367,030,'U',0221,0231,0325,0351,0343,0360,0326,0200,0252,0220,'8',']','@','u','Q',0227,0337,0227,025,0363,'~','u',0275,'D',027,02,0335,0324,'I','L','v',0205,031,'H',0220,'!',032,',','v',06,'v',06,0201,'f','x',0243,'r',0352,0232,0261,' ','g','d',0322,0330,010,0331,05,0303,0356,0254,'v',0340,0230,0242,'*','"','z','}','2',0217,02,0277,0315,0372,0134,'e',0370,0334,'n',033,'-',0303,'B','}','6','_','W','~','V',0257,0277,'.','+',0346,0374,0374,0216,0202,033,0260,0347,0255,0230,'b',0353,0,'H',030,'q','}','6',0271,0334,0313,0272,'(',0326,'G',06,'/','I',0264,'u','~',0272,'D',0364,'N',0317,020,0262,'%',0232,'m',0270,'F','4',0266,0245,'1',0211,033,'~',0231,'G',0201,'_',0346,0353,'2','"','Q','1','|','c',024,'/',')',0233,0366,'x',0275,'a',0230,024,0330,0265,0246,0257,0277,'.','+',0346,0374,037,'_',0347,0272,0253,0312,0231,',','p',024,'Y','S',0244,014,0226,'E',0221,0333,0232,0234,0312,0,0234,0211,'Q','Z',0244,0311,024,'i',023,';','9','5',0302,'R',0326,021,';',0212,0237,'e',035,'L',0227,047,0327,0203,035,'p',0277,0134,0243,0300,0257,0363,'u','r',0350,'c',0250,0302,0362,0341,'q','C',0177,'w',0252,0365,0215,'g','a',0344,0347,'j',0373,0362,0342,0276,'o',0317,0267,'=',0243,0302,'Q',032,',',0205,0231,0373,0353,0303,0365,0320,01,')','2',04,'+','E',0213,'j',0355,0317,0356,0375,0375,0216,0306,0265,'^',0354,'+',036,'J',0200,'6','e',036,05,0177,0233,0253,0,0322,0300,017,0331,0360,'u',0373,'>',016,0261,0214,014,'l',0134,0375,'_','~',0134,'W',0315,0371,0307,030,0321,'b','<',0325,'"','C',010,'Y',0244,'m','z',',','S',0212,0350,0227,'h','B',0365,0134,'(',0312,0365,0252,0256,'A','b',0334,0273,'2',0213,'v',0324,'U','K','#',0246,0223,'f',01,'F',0247,0330,0177,'[','r',0217,02,0277,0315,0313,0355,'f',0244,0307,'q',0274,0254,0333,'L','C',0367,'f',0353,'_',0273,'7','Z',0300,0363,0213,014,0226,0337,'W',0337,0227,025,0363,'~','l',0322,0244,',',0266,0260,'R',0245,0260,'/',0225,033,'"',0243,0304,'3',0253,0247,0261,036,0205,'W',0372,'i',0315,'V',0257,0320,'x',026,'g',0215,02,'F',0224,'u',0251,033,';','|','D',0266,'R',0254,0266,017,03,0212,0236,0202,0261,0265,'5',0233,'r',0217,02,0277,0315,'8',030,',',0205,016,0214,0,02,'v',037,'B',0315,'~',0223,0307,0365,'[','G','W','[','>',0257,0277,'.','+',0346,0374,0305,'^','T',0274,0264,0347,'S',010,0351,'2','Y',026,'G','"','z',0250,'p','$','q',0276,0303,0222,'X',0345,'l',0250,'H','m',0221,'$','o','#',0243,'b',0275,0301,0214,0221,'0',0353,'$',021,'j',0310,'i','@','v',0255,0213,'@','k',0211,0235,0304,0221,0263,0207,'T',0236,0350,0275,0331,'G',0201,'_',0346,0353,'$',0263,'u','5',022,'6',0313,'$','7',0364,'=',0356,0270,'i',0216,'Y','S',0336,0352,0373,0362,0342,0276,'o',0314,0272,0261,'H','#','"','}',026,'B',0314,0375,'3',0370,'t',013,0315,021,0201,'/','5','|','/','b',0232,'_','M','?',0373,0335,'^',047,0246,0214,'%',0243,0306,'a','J','L',0234,'5',0263,'Y',0204,0355,'q','*',0315,'b',027,'d','Q',0254,0322,0343,0325,0255,0253,0253,0335,0224,'x',025,0376,'n',0257,'D',0210,0372,'n',031,'Z',0205,'F','w',0352,0312,']',05,'w','_','e','.',0257,0277,'.','+',0346,0374,0263,0213,'A','a','8',0265,'"','K',02,0371,0227,'Q','B',0351,'W',0355,0256,0364,0202,'G',013,0246,0312,0327,0243,0345,'l','m','&','^',0254,0200,011,0316,0347,0271,' ',0216,0314,0345,'"','M','b','6','+',']','t',0213,0314,0235,0205,0376,023,'3',0260,0373,0205,0346,0314,06,0255,017,0270,0337,0224,'x',025,0376,'n',0256,0205,'y',0324,0377,0,0265,'w',0232,0375,0253,0274,0326,01,0205,'X',0343,0227,032,0276,0374,0270,0257,0233,0362,0244,'z','F',0333,0233,025,'"','S',0312,0351,0265,0313,0314,0255,'N','e',026,024,0206,'8',0346,'I',024,0307,'#',0265,',','2','@',0216,0231,0316,0320,0243,0254,0317,0212,'4',0211,0227,'G',0362,0267,0350,0307,'+',035,0215,0330,'%',0215,'?',0247,'b',0340,0244,016,0266,'i',026,'i','v','p',0376,0251,02,0250,0337,0224,'x',025,0376,'n',0216,'I','T',036,035,'e','6',0267,'W',0277,'[',0357,0313,0212,0371,0277,'*',0366,0313,0221,0244,0317,0323,'l',0362,0254,0257,0320,0337,0225,0354,0347,0217,0321,06,0210,'v',0272,'y',0254,'d','F',0307,034,'k','#',0204,033,0244,0333,022,0375,0254,'s',0314,0263,0311,0365,0341,0221,0334,0343,'v','8',0216,'g','B',0237,'e','h',0252,'i',0301,0300,0202,0213,0277,'(',0360,'+',0374,0335,0134,0226,0360,'*',04,0313,0213,02,0363,0367,'f',0353,'X',036,'q','a',0222,0333,0352,0373,0363,'b',0276,'o',0311,0261,'1',05,0204,0262,'V','W',0236,'W','Q',0332,0202,05,0231,'d',021,0303,0350,'R','R','F',0275,0210,0364,'{',0330,';',010,0225,'g',0222,0274,'N','T',0253,01,'J',0227,0210,'U',0252,034,0373,'0','3','=',0255,0367,'c',0211,'e',0365,',',0266,'p',0354,037,'s','s',0330,0312,'<',012,0377,0,'7','S','B',0302,'a',0375,031,'G',0257,0321,0224,'z',0257,0307,0353,0252,0246,0325,0357,0346,0305,'|',0337,0216,0211,0351,0251,036,0221,0266,0344,0365,'"','[',02,0271,'Q','W',0327,'L','o','3',0207,0211,' ',0212,'E','q','n','@','9',022,'B',0245,0205,'d',0235,0322,0350,01,'9',0324,'Q',0226,'g',0326,0202,0202,0303,0237,01,0356,0350,0366,'W',0317,0355,0216,026,'N',0260,0333,0227,0370,'L',0270,0257,'w','{',0263,0206,0301,'t','j',0373,031,'G',0201,'_',0346,0352,0324,0366,0325,0326,0231,0221,0335,'[','J',0331,0357,'$',033,0205,'s',0330,023,'s',0253,0357,0313,0212,0371,0277,'"',0366,0307,0221,0246,021,0323,'l',0262,'u',035,0240,0377,0,'3',0231,0316,0326,0265,0260,0266,'9','R','M','Y',0372,'u',05,035,'f','x',0360,'r',0245,'%','g','I',0232,':',017,'r',031,0220,0373,'r',0276,0277,0326,0261,'B',0220,0272,'=',0305,'I',0321,030,0371,'z',0346,0375,0177,0275,'b',0202,0240,0224,'}',0214,0243,0300,0257,0363,'u',0236,'#',0227,021,0304,0207,'l',0334,'7',0341,0365,0365,'{','1',036,020,033,'*','Y','j',0373,0362,0342,0276,'o',0307,'<',0244,032,023,'J',0352,0274,0322,':',0257,0323,'"','t',0232,0366,0362,'B',0261,0331,'"','7',0235,0345,0257,0360,'4','O','U','&','`',0306,0351,'2',0222,0263,0252,0346,0267,0225,'4',0277,0312,'e',0302,0373,'K',0335,0234,'7','+',0255,'M',0273,'#',0231,0320,'S','*',0363,'/',0324,'(',0226,'r',0303,0215,'b',027,0261,0224,'x',025,0376,'n',0255,0200,'K','J',0314,'C',015,0227,030,'m',0317,014,'m',0205,'7',0,0303,0244,0306,'`',0325,0367,0345,0305,'|',0337,0214,0347,'r',0245,0345,0207,'Z','K',022,0275,'5',0375,0352,'8',0372,0216,034,'v',0302,0317,'D','r',022,02,';','P',0220,0242,0350,0202,0226,'u',0257,023,0321,'+','A','R',0245,032,04,0202,'?',0257,022,0205,'t','v',0273,'8','_',';',0371,0267,'g','S',0276,014,0177,'f','.',';','I',0274,'D',0364,'N',0306,'Q',0340,'W',0371,0272,0256,0342,'}',0330,'Z',0256,0342,0350,'R',0352,0273,'-',0250,0265,0372,0337,'~',0134,'W',0315,0370,0327,'g',0364,'"','8',0256,'D',0225,0375,'G','j',0266,'$',0321,022,'+',0244,0222,'O','o',034,'R',0365,031,'d',0366,0350,01,'z',0216,030,'u',0221,0325,'`',' ',0260,0354,0342,'x',0315,0350,'l',0341,0264,0357,'e',0306,0356,'"','N',0310,0350,0266,'`','#','4',0213,0376,0316,'Q',0340,'W',0371,0272,';',010,0243,0261,0325,0337,014,'j',04,0216,0320,030,0253,0311,0341,')',0223,0276,0363,'W',0337,0227,025,0363,'{','O',0235,0221,0352,'K',0210,030,0277,'}',0203,'L',0273,0201,0313,024,0315,0225,'7',023,':','A',025,0241,0275,'y','M','#',0252,0375,015,07,'Y',0317,0215,0301,0350,027,0365,'e',0234,'~',0266,0246,0377,0,0340,0202,'6','8',0231,'F',0203,0221,0264,'U',0234,0211,0267,0210,0303,'6','Z','M',0234,'>',0235,0220,0337,'n',0342,'w',0374,0375,0234,'8',0215,0316,0272,0354,0345,036,05,0177,0233,0253,014,0366,0362,0307,'T',0230,0301,0331,'T',0265,0334,' ',032,'=','S',0343,025,0224,'+',0253,0357,0313,0212,0371,0275,0211,0312,'d',015,';','!',0324,0366,022,'J',0262,030,0324,0327,0334,'Y',0250,0213,'k',0365,'O','d',0350,0245,'c',0271,0233,0266,0376,0303,'V','%','z','k',0373,0323,031,0316,0340,0305,0350,0352,0306,'o','W','A','2',0302,0346,0331,'7',0224,0202,'T',0227,02,'/','M',0264,0325,0312,'D',0221,0261,'#','n',0334,0372,'7','I',0217,0354,0303,0277,0364,033,0270,0235,0377,0,'?','g',013,0374,0236,0316,'Q',0340,'W',0371,0272,0261,0341,'=','Q',':',0260,0341,'E',0250,0272,'[',0274,0227,024,0233,02,0316,'L',0311,'O',0325,0367,0345,0305,'|',0335,0366,'V',0354,031,013,'>','B',035,'9',0255,0213,'S',0236,0351,'4',0347,0253,0276,0221,'J',0261,0270,02,0271,0365,'R','G','X','m',0226,'E',0240,0320,'Y',031,0247,'*',0221,',',0225,0352,0330,0306,'z','E',',','S','6','D','$','4',0227,'S',016,0350,0227,'U',0342,'z',0350,021,024,0231,'B',025,06,0213,'v','S',0377,0,013,'f',035,0377,0,0240,0335,0304,0357,0371,0373,'8','_',0344,0366,'r',0217,02,0277,0315,0321,0274,']',0261,0233,'D','e',0227,0327,017,07,01,0276,0265,'v',021,0201,'K',0213,0233,0253,0357,0313,0212,0371,0273,0256,',',0275,0254,'d',020,0257,'S',',','4',0347,0253,0327,'e','|',0334,0222,'c','e',0354,'s',0271,'[','}','a',0325,0220,0342,':',0257,0257,027,'F',0317,0312,0324,01,'_',033,'e','p',0357,030,0326,0313,0243,0134,0316,0230,0203,0365,0244,036,015,'S','W',' ',0361,'o',0312,0177,0341,'l',0303,0277,0364,033,0270,0235,0377,0,'?','g',015,012,0351,0331,0366,'r',0217,02,0277,0315,0325,'w',014,'i',01,0320,'u',0343,'W',0307,0365,0276,0374,0270,0257,0233,0265,0353,0312,0313,'i',0326,'R','l','J',0364,0322,0257,0256,0350,0335,0312,0352,'B',0371,037,013,0372,0221,0375,'.',0317,0366,0360,0331,0231,0241,'!',0353,'H',0215,0345,'c','F','t',0223,0223,'*','A',033,'X',0244,0313,'0','k',02,'"',0276,'w',06,'?','I',0224,'U',0274,0312,0211,0350,0233,0363,'R',0275,0255,06,0314,';',0377,0,'A',0273,0211,'"',0272,'Z',0215,0234,':',0377,0,0273,0331,0312,'<',012,0377,0,'7','u',0367,0345,0305,'|',0335,0263,0177,'1','X',0247,',',0305,0257,0254,0273,0352,0247,0325,021,'}','h','4','D',0311,014,'w','v','=','G',0317,'/','U',0360,'N',0260,0270,'s',033,'*','h',0370,037,'"',0202,'7','I',0266,04,'j',0274,'_','M','U',0202,0245,'M',04,')',014,'}',0216,' ',0377,0,0347,0366,'`',0303,'8',0213,0375,0334,'A',0377,0,0317,0354,0303,0227,0227,' ',0334,0237,0134,0243,0300,0257,0363,'w','_','~',0134,'W',0315,0332,0344,0365,'K',0310,0271,011,'5',0274,0262,0357,012,'^',0234,0264,0207,'t','%','d',0210,0346,'_',0332,'j',0300,0256,0243,0376,0215,'r',0265,'E',0260,0323,'^',0217,'B',0247,'H','X',',','*','D',0242,0217,0324,'u','X','(',',','=',0236,047,';',0377,0,0247,0354,0341,0317,0375,0335,0331,'c',032,0372,035,0225,'r',0244,'6','0','=','$',0207,'j','}','r',0217,02,0277,0315,0335,'}',0371,'q','_','7','v','I',017,0242,0332,'G',0350,0375,0355,'_','E',0257,0237,0231,0214,0271,0225,0220,0331,0231,0247,'/','2',0354,0200,0267,'E',0245,'{',0214,0220,'Q',0372,'m',0242,0255,0345,'N',0327,023,0210,'k',0315,0331,0303,06,'5','K',0335,'e',037,'T',02,030,0261,0317,0365,'c',0271,'_','D','G',0272,0251,0354,'e',036,05,0177,0233,0272,0373,0362,0342,0276,'n',0353,0310,':',0203,'Z','E',0352,0336,0300,'D',0364,'_','!',0355,'F',021,'2',0314,0355,0250,0236,0253,'^','/','+','i',0253,0324,0211,'c','b','F',0336,0326,'x','W',0271,0277,0331,0303,'!',0371,'+',0367,'9','=','[',0220,0214,0242,0334,0354,0341,0361,'~',0342,0217,0261,0224,'x',01,';',0220,0275,0327,0256,0365,'#',025,0363,'w',023,037,'R','+','H','=',035,'#','y','_',0330,0365,0336,0,0335,'G',04,'*',0315,' ',02,' ',0260,0366,0246,'z','G',025,0271,036,0352,0313,'f',020,'+',0206,0240,0337,0304,'!',':',027,'{','8','g','c',0310,'O','b',0340,0177,'u','_',0375,'-','q',0310,'T','[',010,'!',0203,'F','D',0312,'D',0330,0260,0312,0310,'w','/',0362,0231,0,0334,0223,0237,037,'$',0275,0350,'"','Y','^',' ',0374,0215,0243,0255,0350,0263,0267,0225,0237,0366,0352,'U','^','e',0372,0301,037,'V','j',0250,020,'j',0375,0374,'M',07,0234,'m',0230,0311,0353,']','p',0307,'#',0331,0330,0275,0246,'X','^',0327,'+',035,035,0324,0354,0327,0337,'e',0327,0337,'e',0323,0257,'&','T',0230,0211,010,'u','e','d',0226,'2',0303,013,'`',0213,'~','@','7','R',033,'H',0277,0216,0352,047,0252,0327,0213,0310,0332,'J',0356,0264,0214,'o','#','{','|','L',0262,0364,0217,'f','(',027,0276,0274,'D',0364,'M',0371,'X',037,'q',0245,'T',0345,'_',0252,'/','*',0342,0226,037,'q',0245,0354,'*','z',0241,0230,0340,0344,0253,0261,'I',0275,0177,'J',0317,0257,0322,0263,0353,0364,0254,0372,033,026,0215,0213,024,',',0201,0235,0203,'"',0352,0301,'h','?',0242,0275,0274,0256,0356,0,'7',';',0253,0303,'R','%',014,'d',032,'.',0332,0257,'*','e',0266,037,'q',0273,0331,0303,':',0356,'r','{',022,0306,0222,0305,'|',017,0333,0355,0266,'p',0322,0315,'X','G','b','i',0330,';',013,0312,0177,0225,0310,0215,0365,0375,'D','n',0277,'Q',033,0257,0324,'F',0350,'l',0246,'D','Q',015,0210,0330,0373,012,0236,0251,0220,013,0311,'9',0361,'r','K',0333,0202,'.',0253,0304,037,0225,')','k',0320,'x',0273,0231,']',0217,0333,'i',0234,0345,'s',0266,'`',0265,0276,0302,0223,0263,0304,0312,0336,0231,';','*','O','u','m',0200,'d','!','C','n','{',0322,'6','[','Z','>',0302,'a',0306,0220,0227,'G','C',0374,'}',0205,0272,0373,013,'t',0352,037,0340,0240,'e',021,'A',':','@','f',024,0226,0227,07,'b',0374,'^',0254,026,0220,0377,0,035,0244,'O','U',0257,027,0225,'(',0253,'z',0216,'j','r',0247,'s',0211,026,0353,'1','{',')',0303,'S',0354,0207,0211,' ',0203,0263,0226,0326,'6',0316,0231,'S',0225,'v','p',0352,0357,0334,0211,0273,'$','#',0242,04,'l','Y','d',030,'v',0215,026,0311,'#','l',0254,'0',0177,'m','>','*','G',0252,'v',012,0217,0253,015,0250,0334,0257,0225,0234,0217,0354,0200,'7','Q',0365,0241,')',022,0212,':',017,027,'r',0332,0301,0225,0200,034,'[',0317,'/','g',015,'j',0222,'b',0273,'O','j','=',0271,'}','W',0332,0256,'v','Q','Y',0272,0246,0314,'R',032,'T',033,'r',0317,0305,'R',0324,'q',0333,0257,'[',0351,'>','+',0346,0366,'r',' ',0371,']','e',07,'+',0273,020,0307,0324,'x','B',0372,'%','5','z',015,027,'w',0211,027,'|',0362,'l','c','U',0356,0305,0252,0233,'S','S',0333,0342,05,047,0277,0256,0333,0303,0314,0203,0257,026,0334,0263,0361,'T','y',0333,0257,0277,'.','+',0346,0366,'l',0306,0367,03,0330,0213,0251,031,0310,0355,0311,0374,0352,0270,'_','D',0242,0255,0347,'r',047,0242,'w','/','-',031,'Q',0134,'q','o','<',0275,0230,'=','/',0335,'-','Q','=',023,0267,',','I','4','y','=','J',0324,'[','l',0254,'>','J',0303,'i',0255,'#',0266,07,'f','U',032,0270,'`',0345,0350,022,0213,0352,0233,'m',0346,0353,027,0212,'F',0252,'G','e','S',0325,'/',0353,0371,']','`',037,0256,0225,0252,0232,0364,0372,0372,'i',0261,0271,0332,022,0275,'U','j',0353,0226,'y',06,0201,' ',0217,0270,0253,0350,0231,0346,'E',0367,'"',0366,'5',0252,0347,'a',0324,0311,'Q','S',0335,0342,05,047,0277,0256,0333,0203,0344,'K','T','k',0134,0217,'o',0324,0361,'}',0340,0222,0304,0350,'$',0257,0265,0350,0243,012,0212,'T',0352,0263,']','V','i',0304,'F',0304,':',0341,'9',0177,0227,'-',' ','>',0304,'>',0321,'#',0241,021,0330,0323,'>',047,0311,'W',0352,0253,'S',0245,0251,'M','}',0241,'4',0312,0215,017,'L',0345,0320,'x',0353,0227,'B',0204,0301,'[',0335,0316,'r','$',0253,011,'U',0134,0273,'0','z','U',0263,0265,'D',0364,'N',0353,0330,0222,'7','0',0245,'u','E',0256,0317,0353,'X','.','S',0357,'a',0331,'o','L',0333,04,'$',031,0204,'v',0310,'F',0224,0207,'S',0320,0373,'e',0356,'9',0210,0355,'8','(',0235,0257,0267,'C',0257,0266,'A',0257,0265,0301,0246,0201,023,'t',0221,'5',0275,0373,0253,'h',0251,0302,0265,0263,0226,0330,0315,0220,0302,0342,'%',0305,0352,033,'Q','U',0337,0311,0350,0331,'w',0134,'@',0357,026,'}',0202,025,' ','D','b',0231,033,'o',0203,0330,0346,0243,0265,0366,0341,0227,'_','m',027,'_','m',027,'_','n',027,'L',0215,0261,0247,0370,'$',0220,0301,' ',0312,'r','Y','/',0211,0333,0303,0354,'o',0235,0337,07,0210,'8',0337,'2','m',0252,0265,0236,0240,0252,'+',0210,0356,'@',0377,0,'%',0357,'H',0333,0232,'e','R','X',0223,0267,032,0244,'}',0335,0200,0243,0260,'X','>',014,0360,'0',0230,0262,0274,'y',0364,'g','m',0240,0277,0236,0210,0252,0213,'x','.',05,0377,0,035,0357,'H',0333,0231,0346,'j','K',0266,0216,';',0312,0237,026,0307,0333,'F',02,'|',';',0332,'x',0356,'@',0265,0252,0232,0240,0275,0264,027,0363,0321,025,'I',0220,013,'y',017,0370,0263,0316,0301,0242,0313,0263,0177,'|',0233,'Q',025,0313,0203,'b',0212,023,'u',0351,0361,'2',0274,'e',0227,0303,026,',',0201,021,0266,0252,0326,'z',0202,0261,0234,0256,033,0330,0277,0303,'8',0370,'k',0307,0311,0363,')',0256,0227,'v',021,0210,0373,0267,'"','z',047,0306,0314,'1',06,0332,0307,'4','/',036,'M',0243,0221,' ',0262,'b',0371,0353,011,0323,036,0331,033,0376,05,0366,'N','-','$','7',0231,021,'7',0223,0356,0304,0361,027,0332,0276,0270,'_','j',0317,0221,0230,'a',0354,0264,0216,'h','^','<',0233,0261,0354,0320,0252,'w','T','^',0213,'s',017,0315,0236,'v',015,036,'I',0304,036,']',020,'D',0205,'I',0273,020,0304,037,'m',',',020,'0','x',0276,'V','[',0206,0266,0335,0246,07,'(',023,0356,020,0351,0300,0223,037,0342,'*','z',010,'|',07,'G',0362,0134,0364,'b',']',0347,'!','V','%',0276,'L','u',0303,0267,0342,0330,'D',0307,0276,010,030,'4','_','3','#',0305,07,0276,0216,0326,0246,'z',0202,0267,0201,'j','U','d',0224,0234,'H','k',0264,025,0240,0266,015,0370,0262,0312,0310,'[','q',0236,01,0134,0333,0214,0300,0373,'u',0376,0367,0265,0252,0367,'b',0230,'"','*','1',0211,033,'~','u',0265,'(',0367,03,0344,'X',0201,'4',0262,0366,04,':','p','d',0252,0342,')',0202,0272,0267,':',0255,'?','Q',021,034,0351,0337,'U','F',0350,0374,0226,0276,0271,0266,0334,'K','O','K',034,0220,0373,'N',0315,'}','a',026,'s','c','8','T',025,'-',0376,0277,0301,0222,'&','L',0314,0223,0207,0354,0237,'E',0207,'0','3','v','C',0270,'0',07,'W','q',024,0361,'P','N','%',0207,'"',0215,0231,'U',0222,0310,0354,0205,0233,0134,0355,0334,0256,'D',0324,0205,'C',016,0247,0310,0353,0207,'y',0134,'E',0255,0205,0247,'q','6','G','h',0334,0252,0310,0355,'9',0352,0365,0354,0177,'z',0307,'p','r','m','V',0256,0230,'j',0230,0177,0304,0271,0307,'D',0272,0212,0357,010,'6',0253,'_',0327,'q',0204,'I',032,0307,'v','t','O',0375,'c','m',0241,0263,0353,'A',0331,0373,0215,'g',0257,0334,'[','=','.','c','l',0252,'N','K','b','^',0245,'<',0211,0364,0252,0256,'^',0335,']','!','w',022,'c',0230,034,025,0272,'k','Q',0215,0377,0,031,0315,'G',0266,0353,06,012,0323,'W',030,'y',0365,013,0375,'|',0320,'*',0212,0263,0222,0223,0206,0355,'n',0204,02,0,'c',0377,0,')',0314,'G',0245,0326,014,025,0246,0256,'0','c',0353,'5','$','O',0205,0337,037,0373,0325,'v','>','u',0233,0352,'8','k',034,'n',020,010,01,0217,0374,0357,'O',']','X',0343,0340,0331,0262,0313,0206,'l',']','X','b','V','U',0353,'$','O',0205,0337,07,0373,0320,0224,0246,0232,0265,0274,'6','*','}','U',0341,025,0325,0253,034,'L',0211,'?',0323,'T','E',0321,'t',0201,033,0243,'8','q','_','>',0212,0341,0221,'Q',0264,0214,'&',0326,011,'%',0253,'.',024,'X',0336,0324,0336,0215,'W','j',' ',0247,0235,'G',0305,0354,0312,'`',0234,'9',0260,0231,'A',0341,0224,',',0320,'X',0215,'`',':',0216,026,'D',0237,0355,0276,010,0344,'l',0265,01,0314,0317,0322,0325,0232,0237,04,0252,0236,'O',0333,0352,0255,'~',0337,'U','k',0366,0372,0253,'_',0267,0325,'Z','f',047,'V',0306,017,0217,0327,0214,0350,0303,0202,'%','D','D',0377,0,0365,0317,0377,0304,0,'=',021,0,01,03,02,03,05,06,04,04,05,04,02,03,0,0,0,01,02,03,04,0,021,05,022,'!',06,023,' ','0','1',020,024,'"','2','A','Q','@','P','a','q','3','4',0221,0261,'#','B','R','S',0201,025,026,'5',0241,'$','C','`','p',0200,0377,0332,0,010,01,03,01,01,'?',01,0377,0,0362,'f','R','k','*',0275,0253,'*',0275,0253,'*',0275,0253,'*',0275,0277,0370,013,'l',':',0357,0221,'7',0246,'v','z','{',0247,0311,'o',0275,'5',0262,'O',021,'w',026,01,0246,0366,'V','*','H','+','Q','4',0215,0236,0303,0333,'V','l',0225,0376,0223,07,0373,'B',0233,0211,035,0241,0225,010,026,0373,'V',0341,0257,0350,037,0240,0255,0303,'_',0320,'?','A','[',0206,0277,0240,'~',0202,0267,015,0177,'@',0375,05,034,'.',012,0215,0313,'B',0235,0301,' ',':',',','[',0267,0332,0235,0331,0210,'+',026,'E',0323,'N',0354,0220,0277,0360,0234,0323,0353,'O','l',0314,0346,0301,')',027,0247,0360,0351,'Q',0315,0234,'l',0325,0210,0371,0267,'Z',0215,0205,0313,0227,0370,'h',0250,0333,'&',0243,0254,0205,0333,0355,'Q',0360,030,'1',0307,0223,'7',0336,0220,0332,033,0362,013,'|',01,0324,'X',0323,0370,'d','9',037,0210,0330,0251,033,'(',0302,0356,'Y',']',0252,'V',0317,'M',0215,0250,'N','a',0364,0245,0266,0266,0315,0224,'-',0363,026,0230,'q',0365,'e','l',0134,0324,035,0227,'}',0337,024,0237,010,0377,0,0272,0211,0202,'C',0207,0252,'S','s',0365,0240,'-',0240,0370,'y','0',0243,0313,'N','W',0221,'z',0231,0262,0255,0250,025,'F','U',0217,0261,0251,'X','d',0250,0177,0212,0217,0226,0264,0303,0217,0253,'+','b',0346,0240,'l',0262,0325,0343,0226,'m',0364,0250,0320,0330,0206,0234,0254,0246,0337,030,0244,0245,'c','*',0205,0305,'O',0331,0250,0362,'<','L','x','O',0375,'T',0314,'.','T',023,0374,'T',0351,0362,0224,0244,0254,0331,'5',017,02,0324,'*','j',0302,07,0267,0255,'F','V',035,015,'9','Y','P',025,0337,0342,0377,0,'p','W',0177,0213,0375,0301,']',0376,'/',0367,05,'&','d','u',0234,0251,'X',0346,'Z',0255,0317,'R','R',0261,0225,'B',0342,0261,'-',0232,'j','G',0216,'/',0204,0324,0270,'/',0302,'^','G',0223,'o',0223,'@',0303,'$','b',013,0263,'c','O','z',0220,0270,0330,032,'w','L','x',0235,0365,'>',0324,0363,0356,'>',0254,0356,033,0236,0315,'k','Z',0326,0202,0212,'M',0305,'`',0333,'@',0246,0324,030,0224,'t',0367,0240,'B',0205,0307,'j',0224,0224,'j',0243,'j','{',030,0202,0310,'9',0234,0247,'v',0246,032,015,0220,0222,'i',0315,0255,'p',0334,'6',0335,'+','i',0347,0250,'[','O',0322,0277,0327,'q',037,0356,0232,'w',022,0226,0362,0263,'-',0303,']',0362,'G',0365,0232,0357,0262,0177,0254,0320,0307,'q',01,0246,0364,0323,'[','G',0210,'5',0325,'W',0373,0323,'[','W','%','?',0210,0220,'i',0255,0255,'d',0217,0342,'7',0255,'3',0217,0300,'x',0333,'=',0276,0364,0324,0226,037,027,'m','`',0362,0344,'F','j','Z','7','o',013,0212,0305,'6','u',0330,0327,'r','?',0211,'?',0265,021,'n',0277,'"',011,'*','6',025,0204,0354,0331,'t',07,0245,'h','=',0252,'c',0255,0341,0220,0324,0246,0305,0255,'N','8',0247,'V','V',0276,0246,0233,'o','6',0246,0202,'@',0253,'U',0252,0302,0226,0327,0250,0354,0331,0374,'L','.',')','K',0352,0362,'T',0255,0244,0206,0306,0210,0361,032,0225,0264,0362,0336,0321,0257,015,'?','2','D',0223,'w','W','~','r',034,'Z',015,0322,'j','>','?',':','?',0363,0334,'}','j','.',0325,0264,0275,'$','"',0337,'j',0215,'>','4',0277,0301,']',0371,'X',0236,0,0304,0357,033,'~',025,0324,0250,0217,'C','Y','m',0321,'o',0220,'2',0303,0222,025,0221,0261,'s','X','N',04,0324,' ',035,'t',']',0177,0267,'f',0325,',',0246,'*','R','=','M',015,'h',013,016,047,05,0225,'A','D',013,'|','"',026,0246,0315,0320,'m','P',0366,0206,0134,'c','e',0234,0303,0353,'P',0266,0206,'$',0263,0225,'^',023,0365,0240,'A',0324,'r','&','B','f','r','7','o',012,0305,'0',0207,0260,0367,016,0227,'O',0277,'2',0365,0230,'V',0360,'V',0360,'P','P','<',0350,'P',036,0236,0346,0355,0221,'X','^',026,0326,032,0325,0207,0230,0365,'=',0273,'Y',0371,'d','}',0351,'=','x',0336,0363,'|','D',034,'j','T',035,022,0253,0217,'j',0201,0264,'1','e',0370,'W',0341,'W','!',0306,0320,0362,013,'n',013,0203,'X',0276,02,0344,'2','^','g','T','r',011,0265,')',0360,'(',0276,'M',025,0250,0325,0317,'h','Q',024,0321,'$','k',0314,0201,01,0334,'A',0320,0333,'b',0240,0301,'k',017,'k','t',0327,06,0326,'~','Y',037,'z','O','^','7',0274,0337,023,0322,0260,0374,'~','L','2',022,0263,0231,'5',07,021,'b','z','3','4','u',0366,0343,' ','(','X',0326,'9',0201,0230,0304,0310,0216,'<','?',0267,022,0334,011,0245,'8','U',0306,0204,0346,'4',0221,'a',0313,0201,01,0334,'A',0320,0333,'b',0240,0301,'k',017,'k','t',0327,016,0326,'~','Y',037,'z','O','^','7',0274,0337,026,0313,0356,'0',0254,0355,0233,032,0302,0266,0215,'2',010,'f','V',0212,0367,0240,'o',0250,0342,' ','(','X',0326,'9',0201,0230,0304,0310,0216,'<','?',0267,03,0256,'e',0242,'o',0306,05,0351,0246,0362,0216,0134,'X',0256,'K','p','4',0320,0326,0260,0350,'(',0303,0330,015,047,0257,0257,026,0326,'~','Y',037,'z','O','^','7',0274,0337,031,'z',0302,'q',0367,'!',0220,0323,0332,0242,0232,'u',017,047,';','f',0343,0210,0200,0241,'c','X',0356,020,0250,'n',0227,0233,036,03,0330,0265,0345,024,0245,'f','<',0206,'[',0365,'<',0266,0232,'S',0313,010,'@',0324,0326,015,0205,0214,'5',0237,027,0234,0361,022,022,'.','k','h',0261,'f','f',0,0303,'>',0236,0264,0236,0275,0212,'6',027,0255,0362,0253,'|',0252,0337,'*',0220,0254,0342,0364,0367,0233,0343,0260,0254,'U',0334,'9',0313,0215,'S',0352,'*',034,0326,'g','#','x',0311,0342,0306,0261,'$',0266,0202,0325,'-','A',':',0322,0327,0230,0362,031,'n',0372,0232,0351,0313,0331,0334,'#','r',0236,0364,0370,0361,'z','q',022,022,'.','k',034,0307,014,0222,'c',0307,'>',037,0337,0261,'=','{',026,'l',0223,'L',0244,021,0255,'d','O',0265,'6',02,0256,015,'1',0326,0236,0363,'|','|',011,0356,0341,0356,0207,033,'5',06,'s','X',0203,'[',0326,0270,'1',011,0211,0214,0331,0251,0262,'K',0356,025,032,'u',0314,0307,0220,0332,'3',032,02,0334,0274,07,014,0357,0317,0347,'_',0221,'4',05,0264,034,'X',0376,'4',0247,'V','b',0260,'|','#',0257,0327,0265,'=','{',024,'.','-','^','$',032,'.','(',0372,0323,'I',' ','^',0231,'I',035,'i',0357,'7',0310,' ','O','w',017,'t','8',0331,0250,'r',0221,'1',0220,0362,'=','{','%',0311,'L','V',0312,0215,'b','X',0212,0345,',',0353,0245,'<',0345,0364,034,0204,047,'1',0244,047,'(',0345,0306,0216,0271,'N',0206,0233,0352,'j',014,'4','A','`','2',0216,'-',0241,0306,013,'C',0272,0307,':',0372,0327,'^',0324,0365,0355,'R',0321,0320,0326,'v',0305,'%','i','W','N',0307,0274,0337,'!',0302,'1','G','0',0367,0206,0276,023,0326,0214,0246,0267,'[',0340,'t',0254,'_',024,'2','T','R',0236,0224,0363,0226,0344,01,'z','m',031,'G','3','f',0360,0320,0303,']',0351,'}','U',0323,0213,035,0305,04,06,'w','i',0363,0252,0224,0242,0263,0230,0360,047,0257,'b',0364,'I',0244,' ',0257,0245,'n',017,0275,'!',0274,0206,0375,0217,'y',0276,'D',0334,0347,'[','d',0261,'}',')',0327,'-','D',0337,0220,0313,'v',0324,0363,'0','x',035,0376,'H','A',0362,0216,0264,0224,0204,'$','%','=',07,014,0311,'H',0206,0312,0236,'_',0245,'K',0224,0271,0217,027,0134,'=','x','S',0327,0261,']','5',0246,0326,020,'k','~',')','.',05,0233,'v','=',0346,0371,022,0225,0224,'R',0327,0230,0362,031,'n',0372,0236,'n',0317,'A',0356,0221,'s',0253,0314,0256,'-',0241,0305,';',0343,0333,0226,0374,0211,0342,'O','^',0302,'.','-','[',0201,'[',0201,'H','o','!',0277,'c',0336,'o',0221,'I','z',0302,0227,0213,024,0275,'j',0217,'-',017,0215,'8',0232,'F','c','@','[',0231,0202,0301,0357,0322,0222,0223,0320,'u',0240,',',',','8','v',0206,0177,'t',0215,0273,'I',0361,'*',0272,0361,047,0255,',',0345,'M',0305,'%','d',0244,0232,0337,'*',0267,0312,0246,0334,'*','6','=',0217,'y',0276,'B',0363,0226,0322,0226,0234,0342,0306,0261,034,'4','#',0306,')',0231,013,'a','Z','T',',','Q','.','h',0252,'J',0202,0265,035,0250,'N','c','H','E',0207,'7','f',0341,0367,'x',0233,0322,'5','W',012,0224,020,0222,0245,'t',025,0212,'L','3',0245,')',0337,'N','4',0365,0242,'/',0241,0240,0220,05,0253,'"','}',0253,'"','(','%','#',0247,'c',0336,'o',0220,'8',0274,0242,0211,0275,023,'j',0304,'e',')',0307,'7','b',0233,0302,0203,0215,'_',0326,0237,0212,0344,'U','^',0240,'b','K','I',0312,'i',012,0316,0233,0320,027,0246,0233,0312,'+',06,0303,'{',0323,0227,'W','J',0230,0301,0214,0372,0232,'>',0234,0274,'>','9',0225,'%',015,017,'S','I','H','B','B','S',0320,'p',0355,',',0376,0357,037,'p',0236,0252,0375,0271,011,0353,0330,0243,0224,'^',0265,'U','n',0325,0355,'M','$',0203,0257,'c',0336,'o',0217,'Q',0312,')',0305,0346,'=',0213,0325,'5','4',026,0237,0315,'P','1',04,')',031,'U','X',0244,0206,0324,0233,012,0303,'`',05,0177,020,0320,026,0322,0231,'o',0324,0324,010,'J',0224,0340,02,0241,'D','L','V',0302,'E','m',0134,'K',')',022,'S',0353,0247,'/','e','"',0346,'q','r','O',0246,0234,'$',0201,0251,0254,'Z','Y',0231,')','N',036,'B','z',0366,'(',0134,'S','D','$',0233,0326,'t',0373,0320,'P','=',';',036,0363,'|','{',0316,'_','A',0330,0353,0311,'h',']','T','q','f',0257,'j',0224,0303,'s','S',0231,024,0270,0257,'2','t',0246,'c',':',0372,0365,0250,0254,0356,'[',0313,'M','7',0233,'Z',0214,0301,'y','a',011,0254,'+',017,'L','V',0301,'#','^',0314,'V','(',0227,021,'m',0372,0321,026,'6',0344,0365,0254,032,'/','t',0204,0204,0372,0235,'x','v',0202,'_','u',0204,'r',0365,'V',0234,0224,0365,0354,'P',0270,0265,'n','U','[',0225,'S','m',0224,0233,0236,0307,0274,0337,034,0363,0226,0323,0261,'G','(',0275,'N',0220,0271,016,0356,0323,'R','!',0226,'S','s','X','b',026,0204,0357,015,'3','!',0231,047,')',024,0226,'P',0216,0202,0220,0214,0306,0220,0217,'A','X',06,033,'o',0342,0257,0203,031,0213,0335,'f','-',036,0234,0234,'9',0203,'&','R',033,036,0365,'`','4',034,';','O',',',0273,'+','r',016,0211,0344,0247,0257,'a',0351,'M','-','J','6','<',017,'y',0276,'5','j',0312,')','J',0314,'{','$',0337,'v','m','L',0272,033,'|',0225,'U',0314,0347,0276,0225,'-',0304,'E','c',' ',0254,'!',0222,0245,'g','4',05,0351,0264,'e',025,0204,'a',0306,'B',0302,0215,'2',0320,'e',031,'G',06,0326,'G',0374,'7',0307,0333,0223,0262,0214,'g',0222,0247,'}',0207,013,0256,06,'[','S',0207,0322,0244,0272,'_','u','N',037,'^','J','z',0366,'(',0330,'^',0202,0310,'9',0253,'|',0252,'m',0302,0243,'c',0330,0357,'_',0214,'&',0324,0352,0363,036,0307,0344,'%',0201,'s','L',0314,'D',0257,015,'b','0',024,0205,'f','M','2',0363,0221,0316,0224,0323,'o','M','_',0212,0242,0307,014,'#','(',0246,'[',0266,0247,0263,'e',0335,'K',0221,0312,'=','S',0303,0216,0261,0277,0200,0277,0246,0274,0235,0227,'g','w',014,0271,0375,'G',0207,'h',0344,'n',' ',0224,0203,0252,0271,'I',0353,0333,0273,'O',0265,'n',0323,0355,'A',' ','t',0354,'{',0257,0306,'<',0347,0240,0354,'Z',0262,0213,0324,0307,0327,')',0354,0211,0246,0222,0334,04,'f','W','Z',0377,0,'V','K',0212,0312,0252,'j',033,017,0214,0342,0232,'a',015,'y','E','2',0335,0365,0355,0331,0211,033,0251,0233,0262,'t','W',013,0210,0336,' ',0243,0336,0237,'F',0355,0325,'#',0333,0217,0255,'a','L',0356,'!','6',0213,'[','N',035,0254,0177,'3',0310,'g',0330,'r',0223,0327,0260,0233,013,0321,'R',0216,0265,0342,0246,'o','~',0307,0274,0337,026,0342,0362,0212,'&',0347,0262,'e',0367,'F',0324,0333,0373,0227,'J',0215,'-',0307,'&',0256,0324,0374,'M',0305,0253,011,0276,0353,'Z','B','s',032,'J','r',0216,0330,0256,0356,'_','K',0236,0306,0222,0254,0351,012,0367,0341,0307,'Y',0334,0317,'X',0265,0201,0343,'a',033,0307,'R',0217,'z','B','r','$',047,0333,0207,035,'{','}','=','f',0367,03,0224,0236,0275,0216,'y','M','#',0360,0315,'6',0241,0226,0306,0230,':',0333,0261,0357,'7',0305,023,'j','u','y',0217,'b',0335,'K','z',0252,0273,0313,'/','x','o','O','`',0341,0305,'f','M',06,0232,0200,0233,0236,0264,'3',0316,'{',0351,'Q',0231,0335,0240,'$','S','h',0312,'8',':','V',024,0366,0376,023,'k',0275,0364,0341,0332,0266,0262,0311,'K',0236,0343,0217,06,'m','.',0316,'m',012,0367,0341,'R',0262,'$',0253,0332,0244,0254,'8',0362,0226,'=','O',')','=','{',010,0270,0265,'!',031,')','M','(',035,')',0264,'d',0353,0330,0367,0233,0342,0236,'s',0323,0261,0307,03,'i',0314,'j','d',0267,'$','/','*','+',';',0254,'+','Z',0203,0213,0177,'*',0352,'D','T','M',031,0222,'j','$','$',0307,024,0313,'~',0274,'[','0',0350,0134,034,0203,0371,'O',016,0326,0266,0235,0323,'n','z',0361,0354,0313,'i','r','x',0277,0247,016,'"',0341,'f','#',0253,'O',0265,035,'O',')','=','{',03,0252,024,036,036,0264,026,0223,0332,0367,0233,0342,0134,'^','Q','D',0337,0263,027,'y','I','N','Q','P',0331,015,'4',']',']','4',0307,'}','t',0324,0250,0245,0207,',',0232,0302,'[','p','&',0352,0246,0233,0314,'h',013,'q','l',0223,0244,0207,032,0364,0353,0303,0264,0315,0245,'p','3',037,'C',0307,0262,0250,'Q',0230,'V',':',01,0303,0217,0272,'Z',0303,0326,'G',0256,0234,0264,0365,0354,'-',0244,0372,'R',0232,'H',0365,0242,'-','L',0235,'{',036,0363,'|','A','6',0247,027,0230,0366,'I','|','0',0214,0324,0323,0255,0317,0320,0326,'$',0215,0313,'9','S','P',0246,0367,'z',0212,'L',0271,031,0215,'2',0325,0274,'"',0220,0234,0243,0217,'e',035,')',0226,0246,0375,010,0341,0332,024,')',0314,'=','y','x',0366,'G',0316,0357,0333,0207,'i','T',04,02,017,0251,0345,0247,0257,'a','q','F',0222,0202,0272,014,017,'Z','J',02,'z','v','=',0346,0346,0227,022,'+','|',0232,'K',0201,0134,0227,0234,0364,0354,'Z',0302,05,0315,'b',023,0203,0376,01,'X','T','p',0332,'3','S',0354,07,0323,0224,0322,0360,'S',0233,'J',0205,04,'G',024,0323,'y','G','#','f',024,023,'?','_','c',0303,0213,'~','E',0337,0267,036,0310,0371,0335,0373,'p',0355,'g',0345,0233,0373,0362,0323,0327,0260,0262,0237,'J',',',0250,'t',0254,0313,'E','6',0341,'Q',0261,0354,'{',0315,0313,'[',0241,'4',0247,'I',0253,0366,03,'j','i','y',0207,033,0212,0312,')',0327,'2',0214,0306,0232,0304,0220,0267,'2','T',0226,0313,0315,0331,'5','&','#',0214,'*',0365,013,023,'-','x','U','L','I','C',0302,0343,0261,0226,0357,0257,047,02,0377,0,0221,'k',0357,0303,0213,'~','E',0337,0267,036,0310,0371,0335,0373,'p',0355,'g',0345,0233,0373,0362,0323,0327,0260,0274,'}','+',':',0215,06,0324,'i',0266,0362,033,0366,'=',0346,0344,0223,'j','q',0357,'A','W',0277,013,'*',0261,0342,'&',0324,0352,0363,032,0305,'f',0345,031,023,'X',0134,'B',0243,0275,'U','+',022,'B',034,0335,0322,0332,'n','J','5',0251,0270,'Q','G',0211,025,0205,0266,0360,']','4',0214,0324,05,0271,'8',027,0374,0213,'_','~',034,'[',0362,'.',0375,0270,0366,'G',0316,0357,0333,0207,'j',032,0317,010,'/',0372,'O','-','=','{',03,'I',024,0,035,'8',036,0363,'r','_','s',0323,0214,'i','M',0233,0216,027,0227,'m','*','l',0215,0313,'w',0242,0356,0365,0353,0256,0234,0230,0333,'1',0354,0212,0204,0302,0245,';',0234,0322,0326,0230,0315,0324,'y',0350,0222,'r',0232,'m',0224,0244,0370,'E','6',0214,0243,0225,0263,0215,'o','q',04,0237,'m','x','q','o',0310,0273,0366,0343,0331,'7','B','d','-',0277,'q',0303,0264,0177,0361,0312,0373,0216,'Z','z',0361,0275,0346,0344,032,'s',0315,0310,0216,0257,'N',05,0253,'(',0245,034,0306,0237,'a','/',0246,0306,0246,0341,'j','h',0335,'4','s',015,025,'X',0134,0226,0220,0233,'V',047,'7','z','r','"',0260,0250,'y','F','u','S','-',0372,0362,0366,'[',0363,0337,0340,0360,0343,'n',0206,0240,'8','O',0256,0234,'{','-',0371,0357,0360,'x','q','a','x','.',0375,0271,'i',0353,0306,0367,0233,0220,'i',0301,'e','r',033,'V','U','P',0327,0265,0345,0334,0333,0265,'I',012,026,'5','7',012,'J',0374,'H',0247,031,'v','9',0254,':','*',0244,'/','2',0252,';','6',0322,0200,0267,'/','d','G',0361,035,'?','N',035,0244,0377,0,0216,'W',0334,'q',0340,'*','#',020,'n',0334,'/',047,';','j','O',0320,0323,0251,'(','Y','I',0345,047,0257,033,0336,'n','K',0343,'^','K','*',0270,0354,'y','v',034,'R','!',0266,0377,0,'Z',0213,025,',',0370,'S','H','N','Q',0314,0331,'V',0312,'b',0251,'d','u','<',';','V',0242,'"',0240,017,'~','8','K',0335,0310,'B',0257,'m','j',0340,0352,'8','q','F','w',023,034,'G',0327,0224,0236,0274,'o','y',0271,'/',0246,0343,0222,0332,0362,0232,'/',013,'R',0225,0230,0361,0262,0335,0265,0346,0340,'M','n','p',0366,0307,0276,0274,';','Z',0345,0334,'m',0260,'x',0306,0206,0241,'8',036,0214,0333,0203,0324,'p',0355,'C','9','&','g',0266,0204,'r',0223,0327,0215,0323,0342,0344,0254,0134,'Q',0323,0340,032,'F','c','C','N','c','i','+','X','H',0246,033,0335,'4',0224,'{',016,035,0244,'t','9','=','@','z','i',0310,0331,0327,0367,0320,0,047,'Q',0303,0265,'Q',0267,0221,0322,0370,0376,'^','[','k',0270,0261,0341,'R',0202,'E',023,'s','~','S',0251,0262,0271,0351,031,0215,'!','9','G','7',02,0215,0336,0246,0241,047,0240,0327,0206,0340,'j','j','{',0245,0351,'+','Y','7',0327,0221,0262,'O',0352,0343,07,0357,0303,0210,'G',022,0242,0255,0257,0245,')','%','$',0244,0362,0303,0252,025,0276,'U','o',0225,'E',0325,'Q','7',0353,0313,0220,0237,'^','{','-',0333,'^','v',0311,0305,0260,0134,0203,0366,0341,0304,0337,0356,0360,0334,'s',0351,'G','S',0310,0300,0244,0367,'Y',0250,'Q',0350,'t',0342,0307,'b',0367,'Y',0253,03,0241,0327,0342,0234,027,024,'y',0254,0243,'1',0277,'8',013,0233,'V',027,033,0272,0303,'C','|',';','U',047,'w',035,',',017,0346,0344,0266,0262,0332,0302,0207,0245,'C','|','I',0216,0207,'G',0250,0341,0332,0230,0233,0310,0351,0220,':',0217,0213,'t','Y',0134,0304,0247,'1',0244,047,'(',0347,'`','q','{',0324,0324,0244,0364,032,0361,'m',',',0255,0374,0302,0201,0321,':','r',0266,'Z','V',0362,':',0230,'?',0313,0303,'!',0204,0311,'i','M','+',0326,0244,'2','X','u','M',0237,'N','H',04,0364,0240,0311,0255,0305,'n','(',0262,'i','I',')',0353,0314,0220,0237,'^','c','-',0333,'^','~',0314,'B',014,0307,0357,07,0252,0270,'d','<','#',0262,0247,'O',0240,0247,0234,'.',0270,'V','}','y','X',024,0276,0353,'1',':',0350,'t','<','[','S',03,'*',0304,0264,'z',0365,0344,0241,'9','G',011,031,0205,0251,'B',0306,0334,0265,0213,0212,'"',0307,0224,0322,'3',032,032,'s',0240,'E','T',0311,011,'e','4',0323,'i','e',01,0264,0364,034,';','Q',',',0263,035,',','$',0352,0256,'Z','T','R','s',012,0302,'e',0367,0330,0211,'p',0365,0341,0231,031,'3',030,'S','*',0365,0247,0332,'S',016,026,0325,0324,'q',0243,0315,0306,0367,0233,0230,0372,'l','o',0311,'H',0314,'i',0264,0345,034,0375,0227,0201,0272,'h',0312,'_','S',0323,0213,033,0232,'f','K','Q',0364,032,'s','6','b','~',0345,0356,0354,0276,0212,0375,0370,0266,0237,015,0267,0376,'b','?',0317,033,'~','n','7',0274,0334,0307,023,0230,'Q',026,0344,'2',0337,0257,'?',014,0204,0251,0362,'R',0320,0246,0233,'K','(',015,0243,0240,0341,0307,047,'w','(',0206,0336,'e','i','D',0337,'^','c','k','-',0250,'-','>',0225,0207,'K',023,'c','%',0341,0376,'x','_','e',022,'[','-','9',0320,0326,'#',011,'p',037,'-','+',0210,033,032,032,0361,'8','n',0256,'k',0315,0372,0216,' ','/','M',0263,0352,'h',013,'s',0200,0276,0225,0200,'a',0235,0311,0215,0342,0374,0352,0342,0307,0347,0367,0311,'D',047,0312,0235,'9',0333,'1','?','r',0367,'w','_','E','~',0374,'X',0346,032,047,0307,0314,0237,'2','i','@',0244,0330,0361,'!',0314,0272,032,013,'I',0254,0311,0254,0311,0254,0351,0245,0273,'}',07,'<',0264,0223,'[',0204,0326,0340,'W','w',024,030,024,020,07,0300,'l',0356,027,0336,']',0357,016,017,012,0177,0357,0213,034,0235,0334,0242,033,'y',0225,0240,0242,'o',0257,'9','*','(','9',0205,'a',023,0204,0350,0251,'_',0250,0353,0305,0264,'X','F',0354,0367,0246,06,0236,0277,'.',0303,0240,'9','=',0360,0332,07,0336,0243,'G','n','+','A',0246,0206,0203,0204,0220,0221,'s','X',0324,0343,':','R',0224,'<',0243,0247,0300,'`',0330,0211,0303,0344,02,'|',0247,0255,'%','A','i',012,'O','C',0302,0343,'i','u','%',013,032,032,0306,0360,0243,0207,';','t','y','O',0313,030,'e','R',034,015,0243,0251,0254,'/',014,'o',015,'k','(',0363,036,0247,0213,'i','q','M',0312,';',0243,'g','S',0327,0340,0266,'k',024,0314,';',0233,0247,0355,0305,'&','3','r',0333,'-',':','4',0254,'O',017,'^',036,0371,'l',0364,0364,0371,'R','R','V','r',0212,0300,0360,0204,'A','h',':',0261,0343,'?',0365,0305,0210,0316,'D',06,013,0252,0353,0351,'O',0274,0247,0334,'.','/',0251,0370,'&',0235,'S','+',016,' ',0352,'+',012,0304,'Q',0210,0261,0234,'u',035,'x',0247,'A','k',020,'k','t',0355,'O',0200,0356,036,0351,'m',0301,0362,0204,0244,0254,0345,'M','`','x',030,0212,04,0211,03,0307,0373,'q',')','A',011,'*','W','A','X',0326,'(','q',07,0374,'>','A',0323,0341,'0',0314,'A','x','{',0341,0301,0323,0326,0243,'I','j','[','a',0326,0216,0207,0212,'t',026,0261,06,0267,'N',0326,'#',0205,0277,0207,'*',0316,015,'=',0376,'L',0323,'K','y','a',010,027,'&',0260,'l',04,'B',0376,'4',0215,'W',0373,'q',0355,016,'0',034,0377,0,0305,0216,'~',0377,0,015,0202,0343,07,016,'^','E',0371,015,'6',0342,']','H','Z',016,0207,0212,'L','f',0345,0264,'Z','t',0134,'V','/',0202,'/',016,'V','t','j',0217,0222,'E',0212,0354,0267,03,'M',015,'M','a','x','3','8','h',0315,0325,'|','x',0356,'4','"',0244,0306,'g',0314,'z',0375,'(',0233,0353,0360,0370,026,'8','c',021,036,'G',0223,0366,0240,'B',0205,0307,022,0222,0225,0214,0252,027,025,0213,'l',0335,0256,0364,'?',0322,0224,0222,0203,'e','|',0207,014,0301,0237,0304,024,015,0254,0217,'z',0205,01,0230,015,0344,'h',0177,0236,'<','c',030,'F',036,0235,0332,'5','Y',0377,0,0252,0222,0361,'}','Y',0325,0327,0342,'p','<','p',0306,'"','<',0203,0341,0375,0250,020,0241,'q',0307,0211,'`',0261,0361,01,'~',0212,0367,0251,0370,'d',0214,'=','v','p','i',0357,0361,0315,'4',0267,0224,020,0330,0271,0254,'/','f',0202,'l',0354,0317,0322,0222,0224,0240,'e','H',0260,0343,0306,'q',0224,'a',0310,0310,0215,0134,'?',0365,'N',0272,0267,0226,'V',0263,'r','~','/',04,0307,0214,'O',0340,'H',0362,'~',0324,0333,0210,'y',01,0304,033,0203,0306,0343,'H','y','9',034,027,025,0211,'l',0302,'W',0374,'H','z','}','*','D','G',0242,0253,'+',0251,0267,0305,'%','%','F',0311,0250,033,'9','&','M',0226,0357,0205,'5',023,016,0215,04,'Y',0224,0377,0,0236,'F','-',0216,0265,010,026,0232,'7','_',0355,'N',0272,0267,0226,'V',0263,'r','~','7',012,0306,0335,0303,0216,'C',0252,'j','4',0226,0345,0266,035,'h',0351,0310,'~',';','R','S',0221,0324,0334,'T',0375,0226,07,0307,020,0377,0,0212,0223,06,'D','C','g',0221,'o',0206,'C','j','p',0345,'@',0275,'B',0331,0251,'O',0233,0275,0341,025,013,010,0213,04,'x',023,'s',0357,0311,0306,'6',0204,'4','L','x',0275,'}',0351,'J','+','9',0225,0361,0360,'1',027,0340,'8',026,0331,0377,0,025,0206,0342,0354,'b',')',026,'6','_',0267,'%',0306,0220,0362,'r',0270,'.','*','^',0315,0304,0220,011,'k',0302,'j','V',0315,'L','c','T',014,0303,0351,'N','2',0343,'F',0313,026,0370,0,011,0351,'Q','p','i',0222,0274,0210,0322,0241,0354,0252,'S',0342,0224,0253,0375,05,'F',0201,032,047,0340,0242,0334,0227,0237,'j',':','7',0216,0233,012,0305,0266,0201,0311,0177,0302,'c','D',0376,0365,0327,0344,'(','Z',0233,'9',0220,'l','k',013,0332,'^',0215,'L',0375,'i',0267,020,0352,'B',0320,'n','9','O','G','f','@',0263,0251,06,0244,0354,0334,047,0315,0323,0341,0247,0266,'M',0320,011,'i','`',0323,0273,'=',0210,'6','l',021,0177,0265,'.',024,0206,0357,0231,07,'J',0312,'}',0270,0200,047,0245,'!',0207,0134,0362,'&',0364,0326,015,'9',0344,0346,'C','f',0231,0331,'Y','k','?',0304,' ',012,0217,0262,0214,'"',0305,0345,0336,0230,0303,'b','G',0374,'6',0307,'/',022,0307,'#',0300,031,'S',0342,'U','M',0304,'_',0236,0274,0316,0237,0222,0341,0370,0264,0214,'=','^',03,0247,0265,'@',0306,0342,0316,0360,0203,'e','{','s','N',0242,0306,0225,031,0205,0214,0252,'@',0375,'+',0375,'&',017,0366,0205,';',0200,'a',0356,0233,0224,'[',0355,'_',0355,0274,';',0372,'O',0353,'_',0355,0314,';',0372,'O',0353,'C',011,0202,'?',0365,012,'j',04,'V',0177,015,0260,')','(','J','<',0242,0334,0331,'2',0331,0206,0234,0357,'*',0325,0212,'m','"',0337,0273,'Q','t','M',025,025,033,0237,0223,0205,024,0233,0212,0201,0264,'r','b',0370,035,0361,'&',0241,'b',0361,'g',017,02,0254,'}',0276,'9',0371,015,'F','N','w','U','a','S',0366,0244,017,04,'A',0376,'j','D',0247,0245,'+',';',0312,0277,0312,0322,0242,0223,'t',0324,015,0243,0223,027,0302,0357,0211,'5',013,034,0211,'7','@','r',0237,0255,03,'}','G',0304,0311,0233,036,'"','s','<',0273,'T',0335,0252,0323,',','T',0377,0,0223,'R','%',0275,')','Y',0235,'U',0376,'_',0322,0242,'b',0322,0341,0233,0266,0252,0213,0265,0200,0351,'!',037,0245,'F',0305,'!',0312,0374,'5',0320,'7',0324,'|',033,0262,'X','`',']',0305,0201,'R',0266,0236,'+',':','4','3',032,0231,0264,'r',0344,0202,0224,0370,'G',0322,0226,0342,0334,'7','Q',0277,0315,01,0267,'J',0217,0211,0313,0215,0370,'N',032,'c','j',0344,0243,0361,'R',015,'3',0265,'1',026,'l',0264,0221,'M',0343,'0',035,031,0203,0226,0373,0322,'^','m','~','U',017,0327,0224,0245,'%',036,'c','j','s',021,0210,0311,0312,0267,05,'=',0264,0260,'Z',07,')','$',0323,0373,'Z','z','0',0337,0353,'R','1',0371,0322,'?',0236,0303,0351,'K','q','k','7','Q',0371,0342,']','Z',015,0322,'i',0274,'B','S','j',0314,0227,015,0177,0256,0342,'?',0335,'4',0336,0323,'O','m','6',0275,0353,0375,0323,';',0351,0372,'W',0373,0246,'w',0323,0364,0257,0367,'L',0357,0247,0351,'_',0356,0231,0337,'O',0322,0225,0217,0342,04,0337,'y','N',0342,0363,'_',026,'[',0206,0227,'%',0347,05,0226,0242,'j',0344,0377,0,0365,0317,0377,0304,0,'D',021,0,01,03,02,02,06,06,07,06,06,01,02,07,0,0,0,01,02,03,04,0,021,05,022,06,023,' ','!','0','1',020,'4','@','A','Q','q',024,'"','2','P','a',0221,0241,026,'3','B','R','S',0321,025,'#',0201,0261,0301,0341,'b','5',0360,'C','T','`','p','r',0200,0361,0377,0332,0,010,01,02,01,01,'?',01,0377,0,0352,'m',0352,0365,'z',0277,0376,0201,'*','H',0346,'i','R',0332,'O','}',031,0351,0356,024,'g',',',0362,024,'e',0272,'{',0353,'^',0357,0346,0242,0342,0216,0362,'k','2',0274,'k','2',0274,'k','2',0274,'k','2',0274,'k','^',0347,0346,0244,0311,'u','=',0364,0231,0256,016,'t',0231,0376,'"',0223,'5',0263,0316,0222,0362,027,0310,0373,0341,'o',0266,0216,'f',0227,'?',0362,012,0134,0247,'W',0337,'D',0223,0317,0260,0245,0347,021,0310,0322,047,'(','{','B',0221,'-',0265,0320,' ',0362,0367,0212,0224,023,0274,0323,0223,'R',0237,'c','}','9','%',0307,';',0373,'B',034,'Z','=',0223,'M',0316,'?',0216,0220,0362,034,0366,'O',0273,'T',0240,0235,0346,0235,0234,06,0344,'R',0334,'S',0236,0321,0355,0200,0333,0225,'5','1','H',0334,0255,0364,0333,0350,'w',0221,0367,'I','6',0247,'%','w','6','/','K',017,'9',0355,'V',0251,'~',025,0252,'_',0205,'j',0227,0341,'E',0265,0216,0356,0330,015,0271,'S','3',024,0235,0313,0246,0334,'K',0202,0351,0367,'3',0257,'%',0241,0276,0220,027,'$',0335,'^',0315,'%','!','"',0302,0212,0222,0236,'f',0265,0315,0376,'a','Z',0346,0377,0,'0',0240,0342,025,0310,0364,'H',0210,017,0254,0215,0200,'/',0312,0223,035,0325,'w','R','`',0270,'y',0320,0200,';',0315,010,'M',012,0364,'V',0274,')',',',0266,0236,'B',0265,'h',0360,0255,'Z','<','+',0321,0232,0360,0245,'C','h',0367,'R',0240,' ',0362,'4','`','+',0270,0322,0242,0272,0236,0352,'R',024,0236,'c',0206,0205,0251,06,0351,0246,'&',05,0356,'_','?','q',0362,0247,0346,'e',0365,'Q','M',0202,0363,0226,'4',0,'H',0260,0254,0177,'H',0275,0,0372,'<',0177,'o',0373,'S',0370,0204,0251,'&',0356,0270,'M','k',0134,0374,0306,0265,0256,'~','c','M',0312,'}',0243,'t',',',0212,0301,'4',0241,0304,',','1','0',0334,036,0372,04,'(',0134,'T',0266,'l',0273,0247,0276,0221,015,0305,'s',0335,'H',0204,0204,0373,'[',0351,'-',0245,034,0207,030,0200,'y',0322,0342,0264,0256,0352,0134,025,017,'d',0322,0332,'[','~',0320,0341,'3',')','M',0356,'<',0251,016,'%',0301,'t',0373,0201,'J',011,027,'4',0374,0242,0346,0344,0362,0350,0202,'=','s','R',']',014,'2',0247,017,'p',0251,016,0251,0367,'T',0342,0273,0366,0264,'f','Q',0223,0207,0246,0347,'x',0335,0331,'H',07,0235,'9',021,0265,0362,0335,'N','D','q',0275,0374,0370,'-',0270,0246,0215,0323,'L','>',0227,'G',0307,0211,'q','Z',0304,0326,0271,025,0256,'E',07,022,'x',0316,':',0226,0205,0325,'O',0276,'^','?',016,0230,036,0331,0254,'W',0250,0273,0345,0267,0241,0275,'I','^','}',0241,0330,0310,'r',0235,0210,0266,0367,0215,0374,0,'J','M',0305,'G',0224,034,0365,'U',0317,0200,'T',05,';','1',010,0247,'1','/',012,'T',0327,017,'*','2',0134,'=',0365,0256,'_',0215,'k','W',0343,'H',0222,0340,'5',035,'E','I',0271,0342,':',0352,'Z','M',0315,':',0352,0235,'U',0316,0304,017,'l',0326,'+',0324,']',0362,0333,0320,0336,0244,0257,'>',0324,0354,'T','9',0313,0235,'8',0312,0232,'>',0267,02,'4',0234,0376,0242,0371,0355,'<',0372,'[',024,0374,0325,'/','p',0242,'I',0347,0265,021,0202,0342,0257,'H','N','Q','n',033,0256,0245,0244,0334,0323,0256,0251,0325,0134,0354,0300,0366,0315,'b',0275,'E',0337,'-',0275,015,0352,'J',0363,0355,'j','H','P',0261,0247,0341,0345,0365,0221,0300,0215,047,'?',0250,0276,'{',022,'$',06,0205,'<',0372,0235,';','m','6',0134,'U',0205,'F','`','4',0236,032,0326,033,027,'4',0363,0245,0325,'_','j',07,0266,'k',025,0352,'.',0371,'m',0350,'o','R','W',0237,'m','~','(','s',0326,'O',':','R','J','M',0216,0334,'Y',01,0301,0224,0363,0350,'y',0320,0332,'j','C',0345,0325,'m',0245,'%','f',0302,0241,0306,015,0213,0236,031,'!','"',0346,0244,'?',0256,'V',0356,'[','q',030,'S','~',0262,0253,025,0352,'.',0371,'t','A','e','2','%',0264,0312,0371,')','@','|',0315,'}',0217,0303,'?',0345,0363,0257,0261,0370,'g',0374,0276,'t',0346,0206,0341,0352,036,0241,'P','>',0177,0352,0261,'l','-',0334,'&','F',0245,0315,0343,0270,0370,0212,0320,0336,0244,0257,'>',0334,0373,01,0341,0361,0247,033,'S','F',0312,0332,0214,0311,047,'5','-','y',05,0315,'K',0222,0134,'6',033,'`','^',0240,0305,0267,0254,0252,02,0334,'9','r','3','z',0211,0333,0215,033,047,0256,0276,'}',030,0257,'Q','w',0313,0243,06,'c',0322,'1',026,'[',0370,0377,0,'m',0365,0245,0263,0246,'D',0226,0204,0260,0341,'J','J','{',0217,'}',0315,'5',0215,0342,'H','Z','U',0257,'Q',0376,0265,0244,0222,0245,'a',0216,0263,'1',0205,0234,0267,0261,035,0307,0376,0367,0326,0233,0267,'x',0314,0271,0340,'m',0363,037,0352,0264,'7',0251,'+',0317,0267,0272,0322,']','M',0215,':',0322,0232,'U',0216,0303,'M',0225,0232,'H',015,0246,0246,0312,0277,0252,0236,04,'(',0271,0216,'c','I','N','Q','n',034,0247,0265,'i',0260,0347,0267,026,'8','H',0316,0256,0234,'W',0250,0273,0345,0321,06,'Q',0205,'%',0271,03,0360,0233,0325,0260,0374,'z','8','Q',01,'i',0372,0217,0362,')',0215,036,0303,'#',',','8',0333,';',0307,0304,0237,0356,'k','J',0261,026,'&','>',0324,'6',0225,0271,047,'y',0356,0377,0,0261,0337,'Z',']',0210,0306,0230,0323,'H',0216,0340,'V',0363,0312,0264,'7',0251,'+',0317,0334,016,0264,0227,'S','c','N',' ',0266,0254,0247,0241,0264,025,0233,'S','M',06,0305,'M',0225,0224,'e',024,'M',0315,0366,0342,0307,'.',0252,0346,0233,'@','@',0267,015,'k',010,'N','c','N',0270,']','V','c',0265,022,'>','o',']','[',030,0257,'Q','w',0313,0246,016,011,0214,'(',07,0243,' ',0247,0343,'|',0277,0344,032,'w',05,0322,047,0306,'W','I','#',0342,0277,0367,'S',0260,'Y',0330,'k','a',0331,'(',0260,'&',0334,0307,'F',0206,0365,'%','y',0373,0205,0366,03,0251,0370,0326,'C',0233,'-','G','c',' ',0271,0251,'R',03,'I',0245,0254,0254,0334,0355,0260,0321,'u','V',0246,031,015,0247,0211,'1',0354,0312,0310,'6',0242,0261,0255,'U',0317,'*',033,0266,'1','^',0242,0357,0227,'F',06,0322,036,0304,0231,'C',0234,0257,'X',0266,'2',0306,020,0204,0251,0340,'I','W','+','W',0333,'v','?','D',0374,0305,'c',0272,'D',0336,'/',031,',','!',0262,0233,033,0375,017,0357,0321,0241,0275,'I','^','~',0342,'-',02,0254,0324,0363,0241,0244,0323,0357,027,'U',0266,0204,025,0233,012,0211,030,'6',0236,'$',0207,'u','H',0275,023,'}',0373,'-',0240,0270,0254,0242,0233,'@','m','9','F',0316,'+',0324,']',0362,0350,0202,034,'T',0246,0222,0311,0262,0212,0205,0274,0357,'X',0366,012,0376,'0',0313,'V','P',013,'O','>','v',0337,0317,0373,'W',0330,0251,0277,0250,0237,0257,0355,'X',0256,0217,'H',0302,'Y',017,0272,0260,'A','6',0335,0177,0217,0303,0341,0321,0241,0275,'I','^','~',0342,'Z',0202,05,0352,'d',0235,'b',0254,'6',0300,0275,'B',0213,'o','X',0327,'.','$',0267,'u',0213,0267,0206,0324,'F','u','i',0314,'y',0355,'b',0275,'E',0337,'.',0210,0317,0230,0317,0241,0361,0370,'H','?','*',0373,'m','+',0364,0223,0365,0257,0266,0322,0277,'I','?','Z',0305,0264,0211,0354,']',0200,0303,0210,02,0306,0373,0277,0257,0357,0321,0241,0275,'I','^','~',0341,'&',0325,0210,'K',0312,',','(',0311,'9',0251,0267,'B',0366,0241,'E',0314,'s',032,'H',0312,'-',0304,0222,0356,0255,033,'Q',032,0326,'.',0347,0220,0333,0305,'z',0213,0276,'U',0202,0303,'n','|',0366,0343,'=',0354,0233,0377,0,'b','k',025,0301,0343,'C',0305,'X',0204,0335,0362,0257,'-',0374,'w',0250,0212,0373,033,0207,'x',0253,0346,'?','j',0373,033,0207,'x',0253,0346,'?','j',0322,',',02,'&',025,025,'/','0','M',0312,0255,0277,0310,0374,'>',035,032,033,0324,0225,0347,0356,031,0222,'u','b',0324,0341,0326,'s',0247,0231,0311,0276,0202,0212,'i',0251,035,0306,0201,0277,'L','X',0372,0325,'S','h',010,026,0342,0314,'s',';',0226,0360,0331,02,0373,0251,0206,0365,'H',03,'o',025,0352,'.',0371,'T','y',016,0305,'t','<',0312,0254,0241,'O',0342,022,0244,0274,0231,016,0256,0353,'O','#',0345,0276,0206,'7',0212,036,'O',0252,0277,0214,0342,0337,0254,0252,0225,0210,'N',0224,0214,0222,'V','H',0370,0364,'h','o','R','W',0237,0270,037,'t','6',0232,'}',0322,0352,0257,0320,0363,0205,'J',0265,'&','>','d',0336,0224,0202,0212,'i',0342,0232,'I',0270,0275,'4',0321,'u','V',025,035,0200,0322,'j','C',0331,06,0352,'m','Y',0322,025,0303,'u','y',020,'U','D',0337,'~',0314,'6',0263,0253,'1',0356,0340,'b',0275,'E',0337,'.',0214,'.',' ',0235,'5',0250,0352,0344,0243,0277,0313,0276,0257,017,013,'d',03,0225,0264,0374,0277,0375,0257,0264,030,'_',0353,0212,0322,0254,'R',034,0330,'I','n',';',0201,'G','0','?','C',0321,0241,0275,'I','^','}',0275,'j',0312,'/','S','$',027,025,'a',0320,'y','R',0367,'*',0231,'|','Z',0306,0244,'-',047,0225,'0',0316,'m',0364,0204,0337,'p',0250,'q',0262,013,0232,'u',0300,0330,0247,034,'+','5',01,'|',0321,0303,0236,0275,0301,033,'L','7',0253,'@',034,014,'W',0250,0273,0345,0321,0207,0274,0344,'y','m',':',0320,0272,0201,033,0274,'~',037,0326,0264,0262,034,0234,'A',0226,035,0216,0331,'6',0276,0353,'o',0337,'n',0357,0351,'_',0301,0261,037,0374,0272,0276,'F',0244,'a',0362,0342,0247,';',0355,024,0217,0210,0350,0320,0336,0244,0257,'>',0337,':','U',0275,'Q',0320,0245,0204,'s',0257,'J',024,0264,0207,'w',0246,0212,'J','i',')','*','4',0322,'2',0213,'T',030,0277,0211,'T','H','B','i',0367,'J',0317,'C',013,0310,0340,'<','9',013,0326,'8','N',0314,'F',0363,0271,0301,0305,'z',0213,0276,']',020,'^','L','y','m','<',0276,'I','P','?','#','_','l','0',0317,0371,'|',0253,0355,0206,031,0377,0,'/',0225,'i',036,'=',017,024,0210,0226,'c',0336,0341,'W',0336,'>',07,0367,0350,0320,0336,0244,0257,'>',0335,'.','@','m','4',0265,0225,0233,0232,'&',0324,0352,0312,0325,'J','F','Z','d',021,0353,'R',026,0227,015,0215,04,01,0312,0241,0307,'.','*',0346,0220,0220,0201,'R',0236,0356,033,021,0327,0235,0260,'x','/','+','"',011,0332,0204,0336,'T','f',0361,0340,0342,0275,'E',0337,'.',0210,0372,0275,'r','5',0276,0315,0305,0374,0273,0353,'I',0360,0210,'0','!','!',0350,0255,0330,0225,01,0314,0362,0261,0370,0354,'h','o','R','W',0237,'m','u',0300,0201,'z',0224,0361,'u',']',016,0373,'4',015,0225,'[',0334,'U','8','C','i',0312,'*',':','n',0253,0323,014,0227,'U','j','a',0240,0332,'i',0367,0262,0213,'R',0225,0230,0337,'b',02,0271,0247,0203,'9','V','@','N',0312,'F','c','j','B','r',0244,016,016,'+',0324,']',0362,0350,0202,0312,'d',0312,'i',0225,0362,'R',0200,0371,0232,'{',05,0217,'"',022,'`',0272,0245,024,0247,0226,0375,0377,0,0332,0276,0306,0341,0336,'*',0371,0217,0332,0264,0213,0,0211,0205,'E','K',0314,023,'r',0253,'o',0362,'?',017,0207,'F',0206,0365,'%','y',0366,0302,'m','S',0244,0334,0345,035,013,'p','"',0222,0350,'s','u',':',0321,'I',0240,'J','h',05,'8','i',0206,'{',0205,'C',0215,0253,'M',0372,047,'$',0205,0337,'f','*',0262,0272,'8','3',0225,'w','-',0263,015,031,0235,0341,'b',0275,'E',0337,'.',0206,0326,0246,0226,034,'A',0261,025,0374,'w',023,0375,'u','W',0361,0334,'O',0365,0325,'R','q',')',0223,021,0253,0220,0341,'P',0370,0364,'h','o','R','W',0237,'l',0233,'#','V',0233,'R',0216,'c','s','D',0330,'^',0234,'Y','q','T',0233,'4','.','k',0322,'3','s',0244,0264,0205,0357,0244,'6',023,0312,0240,0305,0374,'F',0200,0267,'D',0324,'f','n',0373,' ',0330,0336,0222,'n',0220,'x',017,0253,'3',0204,0354,0300,'O',0252,'U',0302,0305,'z',0213,0276,']',020,0343,0231,'r',021,035,'?',0210,0201,'L','a','x','l','$',0245,0220,0332,0177,0255,0256,'~','|',0350,0267,0206,'%',0315,'Q',010,0315,0341,0352,0337,0345,'Z','`',0334,'F',0242,'!',015,04,0205,0346,034,0255,'{','Y','_','N',0215,015,0352,'J',0363,0355,'o','8',033,'M',0351,0367,'K',0252,0350,'{',0331,0240,'l','o','D',0225,0232,'R','r',0324,'o','f',0241,0307,0326,'*',0346,0220,0234,0202,0335,'+',031,0222,'E',021,'m',0333,'1','U',0231,0241,0266,0243,'d',0223,'D',0334,0337,'f','*','r',0264,'8','X',0257,'Q','w',0313,0243,'G','m',0374,'U',0214,0336,'?',0340,0326,'2',0341,'F',0220,'C',0360,0335,0365,'Q',025,0244,0270,'t',0265,'b',0272,0346,'P','N','k','X',0217,021,0273,0374,'V',0233,'0',0203,035,0251,037,0212,0366,0376,0226,047,0374,'t','h','o','R','W',0237,'j','Q',0260,0251,0322,'s',034,0243,0241,'J',011,0347,'Z',0304,'/','u','*','1',047,'u','e','K',0134,0353,'{',0212,0250,0254,025,'Y','"',0230,'d','6',0235,0227,0323,0225,0302,'6','`','*',0350,'#','n','A',0312,0321,'#','d',013,0356,0244,013,'$',016,026,'+',0324,']',0362,0350,0207,'#',0321,'$',0267,' ',013,0345,' ',0326,'7',0216,'7',0213,026,0326,0206,0362,')',035,0367,0377,0,'U',07,'K',' ','<',0310,'2','U',0221,'}',0373,0217,0322,0325,0244,'x',0342,'1','U',0245,0266,07,0250,0237,0251,0350,0320,0336,0244,0257,'>',0325,'6','F','A','a','D',0346,'7','4',0245,'e',027,0247,034,'+','5',0274,'S','R','-',0270,0322,0333,016,0357,024,0304,'{',033,'T','(',0331,05,0366,0246,0246,0316,'_','f',01,0365,0210,0333,0232,'l',0326,0313,'C','3',0200,'p',0361,'^',0242,0357,0227,'D',0235,022,0303,'_',0336,0200,'P','~',07,0367,0275,'J',0320,0251,'(',0337,031,0300,0257,'=',0337,0275,'J',0301,0261,010,0177,'|',0311,0267,0217,'1',0363,035,':',033,0324,0225,0347,0332,'_','t','6',0233,0323,0356,0227,'U','~',0211,'*',0356,0246,0323,'a',0230,0322,'S',0254,'U','-','9','M',0252,'0','5',06,'5',0375,'c','@','[','j','z','y',035,0230,'F',0316,0333,'n','q',0376,']',0266,'b',0247,'3',0243,0207,0212,0365,027,'|',0272,'#',0343,0370,0234,'o','a',0342,'|',0367,0377,0,'z',0201,0245,0223,0336,'P','A',0217,0254,0377,0,0343,'{',0377,0,0232,0211,'!',0311,'-',0347,'q',0242,0203,0340,'m',0376,'?',0315,0253,'L',0330,'i','0',0322,0350,'H',0315,0234,'o',0266,0376,'J',0350,0320,0336,0244,0257,'>',0320,0243,'a','S',0244,'g','9','G','C',0213,0310,'/','I','P','{',0235,'>',0234,0211,0260,0246,0334,0311,'H',0376,'b',0352,034,'l',0346,0233,'@','@',0266,0334,0344,0372,0227,0331,0210,'l',0350,0333,0237,0311,';','0',0307,0363,'x','x',0257,'Q','w',0313,0242,'6',0216,'a',0221,'w',0206,0256,'~',';',0377,0,0327,0322,0247,0342,0321,'p','t',0204,0251,07,0372,047,'w',0317,'p',0251,':','l',0362,0267,'F','h',017,'=',0377,0,0265,'N',0305,0246,0342,';',0244,0271,'q',0341,0335,0321,0241,0275,'I','^','|','K',0322,0236,'J','k',0322,0321,'H','u','+',0345,0301,0233,'#','"','m','D',0334,0336,0224,'r',0213,0323,0316,0347,0335,'Q',0221,'a','z','q',031,0305,030,0306,0365,026,'6',0373,'T','V',03,'i',0340,'M',027,'k','e',0217,0275,'N',0334,0376,'I',0331,0201,0355,0236,036,'+',0324,']',0362,0350,0215,0246,'S',0232,0334,0360,013,0372,037,0246,0357,0245,'F',0323,030,'/','n','}','%',037,'Q',0377,0,0177,0322,0204,014,033,030,'F',0261,0264,'%','_',021,0270,0375,'-',0365,0255,'"',0321,0370,0370,'S',011,0220,0302,0216,0363,'k',037,'"',0177,0307,'F',0206,0365,'%','y',0360,0335,0222,0226,0351,0354,'@',0235,0311,0245,0274,0265,0363,'5','s','L',0276,0246,0315,'G','t','8',0235,0267,0234,015,0246,0244,0277,0235,'W','4',0231,0,0233,'S',0211,0316,0235,0324,0244,024,0232,'m',0342,0212,'C',0201,'t',05,0352,014,'l',0243,'1',0340,0312,0373,0243,0262,0307,0336,0247,'n',0177,'$',0354,0300,0366,0317,017,025,0352,'.',0371,'t','G',0320,0270,'h',0373,0347,012,0276,0237,0277,0367,0246,0360,'l','*',010,0317,0252,'H',0370,0235,0377,0,0336,0244,'i',036,027,014,'e',016,'^',0335,0311,0337,0376,0276,0265,0217,0351,023,'x',0263,'"',';','M',0330,03,'{',0237,0352,'9',0177,'_',036,0215,015,0352,'J',0363,0340,0223,'j',0225,'4','#','p',0245,0272,0247,016,0375,0234,'=',0353,034,0273,'D',0332,0247,'I',0314,'r',0212,0220,0357,'p',0246,033,0271,0271,0243,' ',03,'j',')','K',0202,0234,'`',0246,0230,012,0315,'P','c',0347,'9',0215,'%','9','E',0270,'2',0276,0350,0354,0261,0367,0251,0333,0237,0311,';','0','U','g','-',0303,0305,'z',0213,0276,']',022,0264,0263,022,0221,0271,04,' ','|',07,0371,'7',0247,0344,0275,'%','Y',0236,'Y','Q',0370,0235,0215,015,0352,'J',0363,0340,0316,0221,0220,'X','Q','%','F',0347,'i',0225,0344,']',0351,0245,'f','N',0314,0311,032,0264,0324,0207,'H',0337,'W',0271,0337,'E',0320,0224,'X','S','h','+','U',022,033,'M','!',0340,0275,0306,0243,0307,0316,0255,0324,0303,'A',0264,0360,0246,'*',0315,035,0226,'>',0365,';','s',0323,'t',0203,0263,017,0357,'G',017,025,0352,'.',0371,'m',0350,'o','R','W',0237,0,0324,0345,']','|',010,016,0346,'E',0266,035,'^','D',0336,0244,0275,0255,']','8',0330,'X',0247,031,'(',0350,0216,0342,'S',0316,0237,'s','1',0260,0250,0254,0225,032,0205,033,' ',0277,016,'w',0335,'l',0306,'N','g','F',0334,0357,0272,0331,'c',0357,'S',0303,0305,'z',0213,0276,'[','z',033,0324,0225,0347,0300,'5','=','6','_',02,03,0271,027,'j',06,0343,0240,0356,0251,0362,0177,010,0351,'"',0364,0354,'~',0361,'D',024,0323,'H',0316,'k',017,0213,0336,'h',013,016,034,0376,'C','f',037,0336,0215,0271,'_','t','v','R','l','A',0240,'n','8','X',0257,'Q','w',0313,'o','C','z',0222,0274,0370,'8',0223,'}',0374,04,'+','*',0257,'Q',0234,0316,0216,0211,'O',0352,0323,'K','Y','Y',0271,0331,'[','A','u',012,'%',0325,'M','6',033,'M',0270,0223,0215,0326,06,0314,017,'l',0355,0270,'.',0202,'6',0230,'V','f',0301,0341,'b',0275,'E',0337,'-',0275,015,0352,'J',0363,0340,0315,'F','t','Q',0335,0300,0205,047,'V','l','i','r',0220,0224,0336,0244,0310,'/','+','i',')','*','6',025,016,'>',0255,'<','Y','J',0314,0351,0331,0200,'7',023,0300,'p','e','Y',033,'0',0225,'v',0355,0302,0304,033,'.',0304,'q',03,0274,'R',0206,'S','m',0255,020,'h',0267,03,'1',0357,'<',027,'F','d',0323,0350,0310,0262,'8','%','j',';','p','#','_',0326,'4',05,0270,0204,0330,'^',0224,'n',0242,'v','a',0246,0315,'p','&',047,'+',0273,'0','W','e',0345,0341,'s',0255,'"',0301,034,0206,0361,'}',0241,0352,035,0234,'3',014,'{',022,'x','6',0330,0335,0343,'Q','#','"','#',')','e',034,0207,04,0326,' ',0336,'U','_',0217,031,0222,0352,0251,0246,0302,023,'n',',',0245,0344,'l',0355,'4',0234,0250,03,0201,'=','<',0225,0262,0322,0362,',',032,033,0370,'K','B',0134,031,'T','.','*','N',0213,0341,0322,'7',0345,0313,0345,'_','c',0240,'x',0232,0373,035,03,0304,0323,'Z','%',0207,0266,'n','n','j','<','V','b',0247,'#',')',0260,0341,0317,'o','2','/',0306,'J','s',033,012,0207,034,'6',0236,'4',0365,0362,'F',0313,')',0316,0340,034,031,'H',0316,0331,0332,0212,0274,0355,0216,0324,0362,'s','&',0236,'N','U',0236,',',010,0337,0210,0320,026,0343,'>',0274,0356,023,0263,05,027,'^','n',011,027,026,0247,023,0221,'E',';','0','W','e','e',0355,'F',0261,06,0262,0253,'7',022,'3','%',0325,'S','H',0310,0233,'q',0245,'/','#','g','j',032,'2',0267,0177,036,024,0344,'Y','y',0266,'P',0242,0205,'f',024,0205,'f','M',0370,'2',0346,0261,011,0275,'c',0352,0260,0247,0364,0322,':',015,0231,'A','5',0366,0330,'~',0217,0326,0276,0333,017,0321,0372,0323,'Z','j',0321,'?',0314,'n',0302,0240,'b',0221,0261,024,0346,'a',0134,'I',0315,'f','E',0370,'i','N','c','a','P',0330,0325,0247,0217,'5',0314,0313,0313,0262,0204,0346,'P','M','$','e',026,0341,'J','o',';','g','j',013,0267,031,017,01,0327,03,'H','.','+',0272,0261,'|','M',0334,'J','A','Z',0216,0356,0355,0230,'s',035,0202,0360,'y',0243,'c','X','|',0264,0316,0214,0227,0323,0337,0303,'q','9',0223,'j',0220,0214,0213,0341,'@',0217,'s',0230,0320,026,0343,':',0275,'Z',012,0250,0234,0306,0347,'f',023,'y',0225,0233,0207,0316,0237,'o','V',0341,033,'-',0257,'V',0240,0252,'J',0263,013,0215,0274,'}',0305,'7',0206,0272,0244,0370,'m',0350,'{',0212,0134,02,017,'q',0342,'b',014,0376,'!',0301,0216,0311,'u','T',0313,'a',0264,0333,0217,'5',0333,0234,0203,'j','3','z',0266,0370,0223,'Z',0314,0234,0343,'j',023,0337,0370,'g','o','H',0377,0,0351,0216,0355,0350,'o','R','W',0237,021,0366,0363,0246,0236,'o','V',0253,'m',0244,'f','6',0250,'Q',0365,'i',0277,035,0347,'5','H',0315,'D',0346,'7',';','1',0232,0326,'/',0212,'E',0305,0251,0346,0365,'k',')',0331,'J',0212,016,'a','M','8',035,'N','a',0265,'>','?',0245,'E','[','^','"',0235,'m','L',0254,0266,0276,'c','d',013,0356,025,0243,0321,014,'8',010,'J',0206,0363,0277,0213,'6','.',0260,0134,'R',0233,'R','M',0255,'Y','M','[',0243,')',0244,'0',0265,0362,025,026,016,'_','Y','T',05,0270,0362,0236,0326,'*',0303,0226,0324,'V',0265,'h',0343,'M','k','2','s',0215,0250,0317,'j',0225,0277,0225,'s',0332,0307,'t','m','3',0316,0276,'>',0345,0377,0,'z',0177,04,0237,034,0331,'m',032,0376,033,'3',0364,0217,0312,0277,0206,0314,0375,'#',0362,0246,0260,0211,0317,034,0251,'h',0326,013,0242,0245,0225,0207,0346,0363,0360,0256,0134,'[','^',0224,0302,025,'^',0212,0212,0364,'&',0315,'z',03,'t','"',' ','R','Z','J','{',04,0307,0362,014,0203,0236,0324,'f',0265,0213,0343,0221,'z',0220,0336,0251,'v',0332,0207,'"',0376,0242,0275,0334,0363,0241,0244,0334,0322,0326,'V','s',035,0236,'u',031,0255,'R',';',04,0206,'u',0251,0370,0321,026,0335,0262,011,'I',0270,0250,0317,0353,0223,0277,0237,0273,024,0240,0221,'s','O',0274,'^','V',0324,'6','3',034,0347,0261,'L','b',0337,0314,033,'H','Y','l',0346,'M','2',0350,'u','7',0367,'Q',0335,'R','_','.',034,0243,0226,0323,'-',0227,'U',0226,0222,0220,0221,'a',0330,0224,02,0205,0215,'>',0311,'e','V',0332,'i',0325,'4',0253,0212,'i',0324,0272,0233,0217,'t','r',0251,'2','s',0372,0251,0345,0264,05,0367,'T','f','5','I',0337,0317,0262,'<',0320,'u','6',0245,0240,0266,'r',0253,'i',0247,'T',0322,0256,')',0227,0322,0360,0335,0356,'e','(','$',0134,0324,0211,'Z',0317,'U',034,0266,0342,'G',0267,0256,0256,0315,'&','>',0264,0134,'s',0242,012,'M',0216,0322,026,'[','9',0223,'Q',0344,0207,'w',036,'~',0344,'Z',0303,'b',0346,0237,0220,0247,0267,'w','m',0305,0215,0237,0327,'W','.',0321,'*','6',0177,']','<',0366,0301,0267,'*','b','g',0341,'r',0201,0277,0270,'^',0220,0226,0207,0306,0234,'u','N',0233,0253,'n','<','r',0351,0271,0345,'H','N','Q','n',0323,'&','6',0177,']',034,0370,014,0311,'S','^','T',0323,0311,'t','n',0355,0305,'A','"',0346,0237,0231,'}',0315,0321,'7',0347,0267,036,'9','t',0334,0362,0244,0244,'$','X','v',0271,'1','s',0372,0310,0347,'D',024,0233,035,0260,'J','w',0212,'f','m',0267,'9','H','Z','V','.',0223,0332,0271,'S',0263,020,0215,0311,0337,'N','<',0267,'=',0243,0300,'b',')','s','z',0271,'R','R',022,',',';','k',0361,0203,0333,0373,0351,'h','-',0234,0252,0340,'%','j','A',0272,'i',0251,0335,0316,'R',035,'C',0236,0311,0354,0304,0201,0316,0234,0230,0204,0373,';',0351,0311,013,'w',0237,06,'<','L',0336,0262,0353,0227,'o','u',0224,0272,',','i',0350,0352,'h',0374,'8',' ',0224,0357,024,0334,0307,023,0317,'}','"','c','j',0347,0272,0202,0202,0271,'v',025,0310,'m',034,0315,'9','8',0237,'`','R',0335,'[',0236,0321,0340,0245,'%','f',0311,0246,'"',04,'z',0312,0347,0356,'"',01,0334,'i',0370,0177,0211,0272,' ',0244,0330,0360,0222,0265,047,0331,'4',0211,0216,047,0236,0372,'L',0364,0376,'!','I',0226,0321,0357,0240,0342,017,'#','W',0333,'*','H',0346,'h',0310,'i',';',0211,0245,'N','@',0345,'K',0234,0243,0354,0212,'S',0316,'/',0231,0341,0263,031,'N',0357,0356,0246,0332,'K','B',0311,0367,'+',0254,'!',0336,'t',0354,'e',0265,0306,013,'P',0357,0255,'{',0277,0232,0223,')',0324,0367,0327,0246,';',0343,'^',0230,0357,0215,'k',0335,0374,0324,']','Z',0271,0232,'$',0236,'|','T','6',0247,015,0223,'L',0303,011,0336,0272,0345,0356,'~','t',0354,'4','/','z','w','S',0221,0326,0327,'>',0334,0224,')','f',0311,0246,0240,0367,0271,'H','B','P',',',0237,'u',0363,0247,'a',0241,'{',0323,0272,0234,0214,0343,'}',0251,015,0255,'~',0310,0246,0340,0367,0254,0322,020,0224,013,'$','{',0301,0306,020,0347,'1','K',0201,0371,015,'-',0207,021,0314,'v','D',0241,'J',0344,')',020,0226,0257,'k','u','7',015,0264,'o',';',0350,0,'9','{',0325,'L',0266,0276,'b',0225,05,07,0331,'4',0250,'.',016,'T','c',0272,';',0250,0244,0216,'c',0204,05,0371,'P','i',0305,'r',024,0230,'n',0232,'L',017,0314,'i','1','Z','O','u',0,07,'/','~',020,015,026,0220,'w','Z',0275,025,0257,012,'0',0232,'5',0350,'-','W',0240,0265,'^',0202,0325,'z',013,'U',0350,0255,'x','R','c',0266,0236,'B',0202,022,'9',017,0375,0272,0377,0304,0,'F',020,0,01,03,01,04,06,07,05,06,04,05,02,07,0,0,0,01,0,02,03,021,04,022,' ','!',020,'0','1','A','Q','q',023,'"','2','@','a','r',0221,024,'#','3','B','R',05,'4','P',0201,0241,0301,'5','b',0223,0321,'$','D',0222,0261,0341,'S',0202,'C','`','c','d','p',0200,0361,0377,0332,0,010,01,01,0,06,'?',02,0377,0,0352,'f',0320,0273,'A','v',0202,0355,05,0332,037,0371,07,0257,'#','[',0314,0252,0233,'@','v','t',0240,'E',0261,0306,0351,07,024,0341,024,01,0247,'q','(',0266,0363,'[',']',0341,'}',0351,0310,0275,0366,0227,0327,0232,0373,0314,0236,0253,0357,'2','z',0257,0274,0311,0352,0276,0363,047,0252,015,026,0247,'P','"','L',0335,047,0231,036,0226,'6',0310,027,0276,0263,0220,0357,04,0326,0364,0235,031,'?','R',0254,'V',0206,';','v',0337,0306,011,0232,'v',0217,012,0252,'Y','b','2',0370,0225,0324,0177,'B',0336,01,'{',0311,0336,0377,0,0317,0270,'U',0217,'-','>',05,013,0226,0202,0341,'Z',0321,0311,0242,0323,05,0356,'%',0250,01,'5',0307,'p','r',0253,034,034,'<','?',021,'/',0225,0341,0215,0361,'W','l',0377,0,0342,'$',0360,0330,0213,'C',0372,026,'p','j',0274,0367,027,0237,036,0356,035,024,0356,0240,0371,'J','k','-',0261,'e',0365,0265,'{',0211,0232,0343,0303,0360,0322,0371,'^',030,0337,024,0350,0354,'c',0246,0177,0325,0271,023,'<',0244,0217,0244,'l',0357,0227,0342,'y',0215,0334,'B',021,0333,033,0322,0263,0352,033,'P','t',022,0202,'x','~',022,0134,0343,'@',0236,0313,023,035,'h',0227,0213,'v',04,'L',0375,'!',037,'H',0330,0276,013,0275,027,0301,'w',0242,0370,'.',0364,'U','1','8',016,'Z',0274,0232,'O','%',0330,'w',0242,0354,';',0323,'_','~',047,0230,0335,0304,'!',025,0277,0256,0337,0254,'.',0222,0317,' ','x',0374,030,0276,'i',05,'w','7',0212,'$',0326,033,037,0312,0321,0264,0252,'4','P',',',0205,'W','`',0372,'.',0301,0364,'Y',0264,0215,06,'H','E',037,0303,0212,0241,0310,0351,0367,'Q','9',0374,0202,'o',0270,0270,035,0275,0312,0262,0314,0326,';',0202,'a',0222,'g',':',0233,'B',016,0350,0211,0247,025,0367,'F','+',0254,0263,0260,016,'K',0340,'G',0376,0225,0360,'#',0377,0,'J',0257,0262,0261,012,0300,031,'O',0245,016,0214,0272,'$','z',033,'O','S',0305,'T','0','I',0236,0344,'D',0266,'w',0212,'x','j',0304,0220,'H','G',0362,0356,'M',0212,'s',0320,0317,0343,0275,'e',0370,025,0134,'h',023,0254,0366,'O','y','7',0325,0270,'&',0231,0336,'_','S','R',0203,'F','@','!',',',0275,0235,0301,'u','Z',07,'-','9',0212,0242,0350,0205,0327,0360,0342,0250,'S','}',0232,'2',0363,'&',0340,0203,0247,'"',06,0240,'d','o','L',0357,0346,'T',0206,026,0263,0362,0327,'Q',0355,016,036,'(',0336,0200,'4',0223,'Z',0204,']','d',0232,0277,0312,0344,'z','h',015,0337,0250,'j','j',015,012,'l','V',0257,'{',017,035,0351,0262,'B',0360,0340,'F',0263,'2',0273,'A','v',0302,0355,0205,0333,013,0266,027,'h','k',0213,0345,'x','c','G',025,'%',0236,0312,'n','Y',0366,'W',0216,0211,016,0360,023,033,0304,0252,014,'U',0334,0341,'U',' ',0247,0313,0335,'(',0366,0207,017,024,'}',0337,'E',047,0324,0325,'~',037,0361,014,0360,0332,0213,'^',0322,0327,015,0307,'Q','~',07,'u','w',0264,0246,0271,0256,015,0227,'{','5','9',0234,0325,030,0263,'r',0370,0213,0342,'/',0210,0276,'!','_',021,03,'|',0241,']',0272,0303,',',0357,0272,021,015,'%',0266,'q',0261,0272,'f',0344,0242,0347,0216,'>','J','O',047,'w','=','$','@','?',0352,011,0317,0263,0373,0370,0277,'T','C',0205,010,0335,0215,0263,'B',0342,0327,04,0310,0244,'7','-',';',0301,0305,'R',0213,'#','U','{',0226,'K','%',0265,'m','[','V',0325,0265,'g',0254,'t',0323,':',0234,07,024,'^',0363,'H',0376,'V',0340,0233,0222,0213,0236,'8',0371,')','<',0235,0345,0307,0243,021,0315,0271,0301,036,0221,0245,0321,'n',0220,'c','l',0221,0272,0353,0206,0302,020,0263,0332,015,0333,'@',0375,'p','T',0243,033,012,0333,0232,0333,0250,'k',02,'h',0325,':','i',0235,'N',03,0212,'/','y',0244,0177,'+','p',0315,0311,'E',0317,034,'|',0224,0236,'N',0364,'Y','+',03,0332,'x',0247,0332,',','=',0221,0231,0215,026,0275,0245,0256,033,0216,'&',0311,033,0256,0270,'l','!',013,'=',0240,0335,0264,017,0327,'I',0215,0205,034,0363,'U','8',0350,0207,025,0322,'8','j',0235,'4',0316,0272,0320,0236,0373,0307,0241,07,0250,0334,'S','r','Q','s',0307,037,'%',047,0223,0276,'>','X',0305,0313,'N',0342,021,0216,'f',026,021,0211,0262,'F',0353,0256,033,010,'M',0215,0357,0377,0,022,0301,0230,';',0321,'k','N','h',0270,0234,0325,'u',027,0212,016,'p',0352,0240,06,0245,0322,'H','n',0265,0273,'J',0350,0341,'$','Y',0331,0372,0342,'l','q',0266,0363,0216,0300,021,0264,'Z','>','$',0204,013,0252,'.','z','-',0326,0230,0251,0322,'C',03,0344,'m','x',0206,0325,0177,0227,0376,0232,0377,0,'/',0375,'4',014,0221,0331,0245,'n',0361,'p',0217,0335,'{','L','-','1',0275,0246,0354,0221,037,0224,0250,0371,')','<',0235,0364,0261,0302,0222,'|',0256,'W',047,'n','G','c',0261,'6','p','K','J','.','y',0317,'S','x',0240,0,0311,06,0215,'Q',0260,0331,'_',0325,0377,0,0304,'#',023,'c',0215,0267,0234,'v',0,0205,0242,0320,'/','Z',017,0350,0233,0346,'Q','s',0321,0366,0214,0327,'C',0275,0311,'m',034,'*','3',0313,0367,'V',0227,0332,0254,0220,'Z','f','m',0242,0236,0361,0200,0220,'.',0217,0371,'R','0',0375,0233,'f',0214,'9',0244,'^','l','`',020,0276,0320,0373,'6',0327,'g','a',0224,'7',0244,0212,'p',')','#','w','m',0364,'V',0370,'w','>',020,0377,0,'C',0377,0,'*','>','J','O',047,'~','t','3','6',0274,017,04,'X',0361,'X',0376,'W','`',034,'4','P','l',0324,'W','r',014,'h','C',0216,0250,0206,0237,'~',0374,0232,0234,0367,032,0271,0306,0244,0341,0,012,0222,0231,'k',0235,0265,0235,0333,0216,0355,015,0363,0250,0271,0350,0265,'X',0334,'n',0211,0230,'[','^',012,'F','5',0362,'X',0247,0330,'i',0331,'x',0377,0,'b',0237,04,0337,'h','8',0306,0341,'B',032,0306,0266,0276,0201,'[',0276,0322,0236,027,0265,0362,0307,'H',0242,0247,'X',0201,0237,0352,0255,0322,0333,',',0262,'Y',0252,0306,0265,0275,' ',0245,'s','Q',0362,'R','y',';',0373,0241,0231,0265,0340,'x',047,0300,0360,'n',0216,0313,0270,0350,0,'h',0272,'5','!',0240,'f',0203,0334,'3',0325,'>','i',015,032,0320,0237,'3',0217,'W','c','F','/','m',0265,0307,0237,0310,0323,0245,0276,'u',027,'=','.',0262,0333,'m',020,0332,')',0223,0231,0321,0364,0240,'~',0212,0374,014,0202,027,0375,'L',0261,0220,0177,0331,':',0317,'b',0234,0313,'+','Y','|',0202,0302,'2',0374,0371,0350,0217,0222,0223,0311,0370,03,0232,0346,0373,0320,':',0256,'N',0263,0310,0302,0327,0202,0252,'v',0253,0241,'W',035,020,0342,0272,'G',0215,'_',0260,'D','z',0255,0355,0221,0210,'Z','e',036,0342,'3',0260,0357,'A',0255,024,03,'K','|',0352,'.','z','>',0321,0222,032,0211,04,'T',04,'m',033,0224,0254,0263,':','8',0333,025,'/',0276,'C',0262,0277,0376,'/',0342,'0',0377,0,0240,0251,'m','R','Z',0231,'8','|','&','+',0255,'m','7',0203,0373,'h',0217,0222,0223,0311,0370,013,'^',0320,'/',0267,'i',0321,']','E','J',04,0216,0252,0,'j',0244,0233,0347,0330,0320,0237,'+',0363,'s',0315,'N',030,0340,'`',0313,0346,'<',023,'!',0215,0240,06,0215,0330,033,0347,'Q','s',0321,'m','}',0245,0206,'[',';','a','{',0244,'`',0336,0332,'f',0255,0367,0242,0232,'k',',',0364,0273,'J','_',0310,0232,'W',0325,'}',0322,0327,0350,0337,0356,0237,'e',0263,0301,'<','O','d','f','J',0311,'J','R',0240,'q',0361,0321,037,'%',047,0223,0360,02,'+',0232,'.','+',0303,'Q','x',0240,0320,'2','@',015,'_',0263,0261,0336,0352,'/',0367,0302,032,05,'I','B','G',0217,0177,'&','d',0341,'o',0235,'E',0317,'E',0246,0312,0342,'Z',0331,0343,'t','d',0215,0325,024,'_','~',0264,'z',05,0367,0353,'G',0240,'O',0265,'C','i',0226,'g','>','3',035,036,07,020,0177,'m',021,0362,'R','y',';',0371,'%',027,023,0222,0270,0325,0343,0216,0273,0220,'c','B',025,035,'m',0134,0262,'W',0256,'E',032,0234,0367,032,0271,0306,0247,017,0264,0310,0337,'s',036,0316,'x',0233,0347,'Q','s','V',0273,'m',0236,0357,'M',025,0332,'_',025,031,0270,017,0335,'}',0251,0366,0234,0342,'/','h',0263,'t',0267,'.',0267,0253,0325,0214,'8','/',0207,'d',0376,0231,0376,0353,0341,0331,'?',0246,0177,0272,0226,0315,'k','l',02,'6',0300,'d',035,033,'H','5',0274,0321,0307,0307,'D','|',0224,0236,'N',0374,0134,'Q',0317,0252,0256,0205,'y','Q','f',0252,0325,0236,0,025,0367,014,0365,0202,0314,0327,'V','8',0377,0,0337,013,'"','g','i',0346,0212,'(',0,0314,014,0361,'7',0316,0242,0346,0237,'f',0264,0306,'&',0201,0375,0246,035,0373,0324,0266,'8',',',0314,0216,0315,'-','o',0307,0271,0325,024,'+','?',0263,'l',0303,0233,'W',0360,0373,047,0242,'2','X',0254,0260,0303,')','m',0322,0350,0366,0323,'D','|',0224,0236,'N',0373,'U',0321,0260,0243,0305,'W','E','U','7',',',0225,'U',025,'4',03,0300,0250,'d','f','`',0267,'v',0256,'y',0217,0312,0325,'$',0256,'5','.','5',0302,0353,'d',0203,0251,037,'g',033,'|',0352,'.','z','-',0266,0306,'R',0374,'Q',0365,'k',0307,'`','N',' ','O','o',0264,034,0315,'*',0352,0177,'e',0374,'2','o',0321,'O','5',0262,0310,0373,'<','F',0314,'Z',034,0356,'7',0233,0242,'>','J','O',047,'}','-',07,'4',0134,0345,0341,0240,' ',0257,05,'R',0256,0350,0274,'t',023,0271,'I','e','q',0251,'f',0315,0134,'v','V',0222,'/',0355,0302,0326,'7','k',0215,024,'1','4','S',',',0361,0267,0316,0242,0347,0242,0333,04,0357,021,'B',0370,0235,'z','C',0261,0276,'+',0355,'H','-',0226,0250,'b','.',0273,'v','B',0376,0243,0256,0223,'Z',037,0315,0177,025,0262,0177,'X','#',035,0222,0333,05,0242,'@','/',026,0306,0360,'M','4','G',0311,'I',0344,0357,0204,0242,0342,0256,0215,031,'h',0272,0375,0213,'n',0232,0235,04,03,0226,0210,']','Z','5',0346,0351,'@',0352,0245,' ',0325,0254,0352,0214,'=','#',0333,'V','E',0236,0241,0276,'u',027,'=',026,0353,'4','T',0351,'&',0201,0361,0266,0274,'K','h',0266,'Y',0377,0,0250,0266,'Y',0377,0,0250,0246,0264,0332,0372,'.',0215,0320,030,0305,0307,0327,';',0315,'?',0266,0210,0371,')','<',0235,0354,0222,0213,'A',0352,0252,015,024,'U','Y','+',0233,0325,'A',0311,'f','t',0334,'n',0335,'!',0303,'h',0315,'A','(','5','4',0241,0324,0317,')','4',0243,'w',047,0274,0346,0134,'k',0236,021,'1','m','$',0227,':',0352,033,0347,'Q','s',0321,'h',0366,'s','v','~',0215,0335,031,0340,0352,'d',0247,0263,0333,0355,'F','h',0333,'g','s',0256,026,'5',0264,'u',0346,0360,036,047,04,'|',0224,0236,'N',0367,0321,0264,0346,0211,'*',0272,02,0240,0321,'|',0354,'T','T','^','(',0327,'j','.','8','&',0263,022,'j',0323,'Q',0251,021,0177,0324,'8','a',0204,012,0336,'r',0216,'!',0261,0242,0232,0206,0371,0324,0134,0364,'[',0255,'1',0323,0244,0206,07,0310,0333,0333,'*',033,'U','7',0332,0260,'E',04,'v',0211,'A',016,'`','i',0271,0236,0334,0252,0276,035,0223,0372,'g',0373,0251,'l',0326,0266,0300,'#','l',06,'A',0321,0264,0203,'[',0315,034,'|','t','G',0311,'I',0344,0357,'D',0357,'N','{',0212,0240,0331,0243,'%','y','x',0254,0364,0336,'(','e',0325,'P',0310,0337,0206,0341,'M',0230,'c',033,0237,0226,0246,'(',')',0330,025,0303,0322,0226,0325,0261,0215,0272,0226,0371,0324,0134,0364,'>',')','Z',037,033,0332,'Z',0346,0235,0341,0177,014,0263,0377,0,0245,0177,014,0263,0377,0,0245,031,'l',0226,'8',0254,0362,026,0335,'.','`',0335,0242,'>','J','O',047,'y','$',0242,01,0352,0205,'t','h',0242,0252,0240,0354,0256,0251,0315,']','+','2',0257,024,030,0320,0207,024,0367,06,0336,'t','y',0341,0202,'O',0245,0301,'F',0375,0227,0233,']','E',0241,0301,0367,0332,015,06,027,0316,'i','Y',016,0245,0276,'u',027,'=',026,0233,'[',0205,0341,014,'e',0364,0342,0246,0264,0272,0325,'i',0272,0334,0335,0321,'8',0206,'0','~','[',027,0264,'6','O',0264,035,'g',0245,'z','P','_','w',0325,'O','$',0362,'Z','e',0263,'{',';',0200,'t',0205,0305,0227,0257,'7',0365,0321,037,'%',047,0223,0274,0364,'m','9',0242,'w',0252,0350,012,0232,'2',0322,032,025,0367,014,0364,'K',036,'F',0363,'w',0251,'c',0372,0134,'F','X','l',0356,0275,'|',0322,0204,0343,0221,0373,'n',0266,0252,'i',')','K',0316,'8','l',0355,0273,'p',0322,0244,'j','[',0347,'Q','s',0321,0366,0225,0315,0267,07,0245,'E','W',0333,0371,'u',0357,'<',0237,0311,0215,'+',0331,0355,'V',0230,0242,'0','_',017,'d',0216,02,0255,'&',0277,0272,0266,0331,'*','z',07,'E',0322,0323,'p','p',' ','~',0377,0,0246,0210,0371,')','<',0235,0340,0224,0347,0225,0341,0243,' ',0257,'Q','Q',0333,'V','Y','7','O',0212,0351,034,'2','T',032,'m',015,014,0270,0322,'j','0',0230,0351,'K',0216,0307,'j','{','6',0335,'U',';','p','B',0301,0265,0316,012,'&',035,0241,0240,'j','[',0347,'Q','s',0321,'j',0262,027,0134,0351,0243,'s','/','p',0250,'V',0270,0244,0266,'6',0327,'g',0264,014,0343,0350,0356,0320,0372,0247,0213,014,'"',0327,'f',047,0250,0340,0360,010,034,015,'T',0323,'Z',0234,015,0256,'z',02,0326,0354,'`',0321,037,'%',047,0223,0273,0325,026,03,0220,'W','F',0212,'i',0253,'U',0327,05,'@',0257,024,'8',' ',0321,0202,'9','k',0223,0333,0206,0323,025,'z',0233,'q',0315,'p',0322,0271,'a',0262,0261,0333,'/','j',0233,0347,'Q','s',0320,04,0262,'G','l','o',0376,0263,'3',0365,010,013,'m',0222,'[','9',0372,0243,'7',0302,036,0317,'o',0210,0270,0374,0217,'7',']',0350,'t',0307,0311,'I',0344,0356,0345,0240,0346,'Q','$',0346,0253,0242,0362,014,032,'*',0250,'6',0252,0224,030,0324,'8',0341,0263,0315,0363,'V',0230,']',030,'=','W','7','<','e',0204,0365,0234,'r',0302,0313,0337,' ',0250,0325,'7',0316,0242,0347,0243,0336,'}',0237,023,016,0333,0320,0365,017,0350,0235,'#','~',0325,'6',01,0377,0,0271,'-','-',0375,0221,0216,033,'l','6',0346,0375,'p',0203,0373,0205,'<',016,0236,'G','@',0333,'+',0210,0210,0270,0335,06,0363,'w','h',0217,0222,0223,0311,0253,0314,0256,0322,0332,0273,'J',0255,'5',0306,0134,'S',0234,'N','Z','h',0252,'6','"','N',0214,0264,0,027,'H',0341,0236,'.',0220,0355,'a',0313,013,'o',032,'^',024,030,0354,0376,'l','%',0300,'u','C','s',':',0246,0371,0324,0134,0364,'8',':',0334,0350,'X','~','X',':',0237,0362,0234,0370,0355,020,'^',0336,0351,0347,0353,'z','m','@',0333,'m',0262,'L','~',0230,'E',0321,0373,0242,'l','V','V',0304,0362,'.',0231,'+','W',021,0314,0350,0217,0222,0223,0311,0251,0253,0212,0244,'k',0254,0345,0233,0226,0325,0221,'A',0244,0344,'U','q','t','m','*',0350,0321,'E','U','u','W','E',02,0256,0364,034,'F','H',01,0212,'[',0242,0264,0317,015,0227,0236,';','?',0233,015,0253,0226,0251,0276,'u',027,'=',04,0331,'d',0232,0306,0356,0,0337,'o',0353,0375,0321,'u',0226,'X','m',0215,034,015,0307,'~',0277,0335,010,'e',0236,0323,01,0333,'r','~',0273,'O','*',0376,0312,'[',035,0256,030,0201,'d','F','Q','$','u',033,0300,0331,0371,0350,0217,0222,0223,0311,0250,0240,0332,0252,0347,'e',0243,'%',0231,0321,0222,07,'z','n',022,'w',0247,'8',0235,025,'Y',0254,0227,0216,0233,0305,06,0215,0210,01,0216,0327,0345,0303,'e',0347,0216,0317,0346,0303,'j',0345,0252,'o',0235,'E',0317,'G',0370,'k',',',026,'a',0374,0325,'y',0375,0220,0217,0333,0255,022,'8',0354,0216,016,0255,0177,'&',0253,0346,0312,0350,'C',0263,0351,'-','.',0273,0377,0,')',0366,0271,0355,'L',0226,'G',0305,0321,0364,'q',0267,'!',0230,';',0177,'-',021,0362,'R','y','1',0320,'v',0221,'s',0212,0243,'V','x','h',0256,'`',0252,'"',0271,05,'@',0257,025,'t','m','W',0264,'P',0355,0321,0340,0203,'Z',0203,0210,0317,'Q','k',0362,0341,0262,0363,0307,'g',0363,'a',0226,032,'v',0333,0267,'T',0337,':',0213,0236,0200,'d',0216,'K','c',0306,0371,0235,0227,0240,'W',',',0326,'x',0254,0354,0341,033,03,'p','G',0311,'I',0344,0304,'J','w',05,'t','c',05,'0',0240,'t',0226,0203,0231,'G','<',0326,'j',0201,'U',0333,'4',0336,010,'5',016,'+',0244,'p',0324,0316,'i','[',0331,'a',0262,0363,0306,0311,01,0311,0216,0303,0377,0,'n',0251,0276,'u',027,'<','q',0362,'R','y','1','9','=',035,'E',025,0335,0343,'A','q','N','5',0311,'U','d',0274,'t','e',0261,'T',0355,'W',02,0274,'P',0313,'$',032,'5','/',0347,0206,032,036,0306,'x',0344,0347,0206,0315,0235,'3',0325,'7',0316,0242,0347,0216,'>','J','O','&','7','#',0251,'o',02,0257,'"',0306,0225,'M','9','*','9','e',0242,0361,'M','c','P',0343,0252,0200,'W',0346,0303,0377,0,'n',';','U',0341,'^',0256,033,';',0311,0240,017,031,0246,'8','f',010,0324,0267,0316,0242,0347,0216,'>','J','O','&','0',0345,']','H',0342,0256,'#',0236,'x',0302,0,'m',']','#',0206,0256,010,0203,0263,'h',0314,'a',0264,0270,0214,0300,0333,0216,'v',0335,0275,'V',0234,0224,0215,'"',0355,035,0263,0,'<',012,0263,'I','J','U',0272,0226,0371,0324,0134,0361,0307,0311,'I',0344,0306,'N',0253,0303,'E','q',0324,0240,0342,':',0250,01,0253,0224,'R',0227,'2',0303,'4',0205,0224,0274,0354,0235,0214,0253,'S',035,0231,0275,0205,0255,0277,'y',0314,'4',0324,0267,0316,0242,047,0216,'6',0216,01,'I',0344,0306,0340,0236,0324,'G','p',0251,'M','`',010,015,'[',0334,'r',0,'+','D',0227,0257,0325,0373,'p',0301,'x',0326,0366,'z',0202,0360,0313,0241,0343,'o',034,'3','Y',016,0307,'f','5','2',0264,'m',0332,'4',0,'O',0274,033,'F',022,0367,0224,0347,0235,0352,'I',0210,0355,'d','5',04,0356,'=',0302,0210,'4','m','W',0334,'3',0326,'O','&',0362,'(',025,'N',0334,014,'`',025,0251,0245,024,021,0206,0335,0243,'v','j','!',0264,0,'I','i',0241,0303,'g',0222,0271,'V',0207,'4',034,'6',035,'I',0236,'!','X',0316,0320,'7','*',0264,0320,0254,0350,0376,'k',0341,0265,'|','6',0254,0232,0326,0252,0275,0327,0225,06,'Q',0215,0256,'M','c',05,032,'5',027,0270,'*',0353,0352,'U',0367,014,0225,06,0262,033,'#','N',0334,0316,'x','l',0354,0241,' ',032,0232,'*','j',047,0217,'x',025,012,0207,'n',0,'F',0325,03,0367,0201,'C',0251,0315,027,'3',0335,';',0303,'b',0312,'V',0321,'|','V','/',0212,0305,0361,'X',0201,0232,'B',0377,0,0,0203,030,0320,0326,0216,032,0227,04,0366,0243,0255,0251,'A',0240,'d',0203,'F',0260,0225,';',0253,0325,'i',0272,'0',0315,'k',';',033,0220,0324,0271,0207,'a',024,'V',0210,'x',':',0243,014,0266,'G',036,0251,0314,015,'I','|',0216,015,'o',0212,0244,021,0345,0365,'9','v',0307,0242,0370,0203,0321,'|','A',0350,0276,' ',0364,'@','M',030,'p',0342,0325,'~',047,'W',0303,'T','N',0343,0255,0242,015,033,'P','q',031,0353,'g',0222,0264,'q',024,010,0223,0231,'8','c','$','u',0344,0353,035,'T','V',0260,'2','v','G',014,'3',0260,0322,0351,0315,'G','(',0371,0205,'q',0227,';',' ',021,0317,0335,016,0310,'W','X','*',0272,0362,'z','/',0212,'}',027,0305,'>',0213,0253,'.','~','!','u',0306,0134,'B',017,'a',0346,'8',0246,0312,0315,0207,'S','{',0202,0256,0262,0245,'t',0216,031,'*','k','Y','c','i',0352,'3','3',0206,010,'F','w',0234,0231,030,0330,0321,'M','T',0315,0273,'W','4','T','"',016,0334,'&',0307,'#',0272,0361,0354,0345,0216,0350,'9',0274,0321,'5',0243,'i','A',0215,0374,0360,0226,0270,'T',024,0346,')','a',047,0304,'j',0134,0324,0366,0242,'5','U','M','h',031,' ',0321,0255,0226,'w',0232,']',012,'I',0336,'s','y',0256,031,'m','o','m','C','2','n',0254,0264,0354,'*','V','7',0260,0376,0260,0303,024,0340,0345,'^',0267,'$',0311,'X','j',0327,012,0342,0203,0232,'e','q',0260,0361,012,'O','&',0252,0370,'U',0324,0321,06,0215,0250,022,'3',0327,'6',0303,033,0262,031,0277,010,'h',0332,'r','Q','0','v',0210,0251,':',0317,'h','`',0367,0261,'g',0213,0330,'%','=','f','v','N','(','9',0246,'~','x',0343,0344,0244,0362,'j',0210,'N','i',0332,0251,0250,0274,'W','H',0341,0256,0222,0320,0355,0303,' ',0244,0235,0375,0247,0232,0341,017,'p',0367,'Q','f','U','5',0216,'c',0263,016,024,'R',0305,'N',0241,'7',0233,0206,';','D','}',0246,0225,034,0361,0232,0324,'g',0206,047,0356,016,'Q',0277,0305,'W',021,0241,0250,'n','J','W',0356,02,0232,0276,0221,0243,'%','x','b',0310,'*',0271,01,'N',0252,015,032,0332,0257,'e',0210,0373,0230,0377,0,0337,010,03,'2','T','`',0217,'x',0374,0335,0256,0366,0210,0307,0275,0213,'<','B',011,017,0270,0223,0216,0344,034,'3',07,03,0342,0343,0261,'9',0216,024,'p','B','9','s','n',0343,0301,'u','d','i',0374,0327,'h','z',0256,0320,0365,'U','/','h',0374,0321,'d',031,0237,0251,'q','%',013,0335,0267,'f','u','e',0245,032,'6',0255,']',0205,0331,'[',026,0305,0331,'Y','F',0205,0374,0202,0243,'F',0270,0301,023,0275,0374,0231,'r','D',0234,0311,0302,0331,034,'=',0324,'Y',0252,'k',0213,0134,'*',012,0222,0215,0367,'2',032,0264,0342,0366,'K','K',0375,0363,'{','>','8','o','7',0253,'(',0337,0305,'R','H',0310,0303,'v','6',027,037,04,'%',0237,'9','7','7',0206,0267,'0',0273,'+',0262,0273,'+',0262,0273,'+','!',0257,'|',0322,':',0231,'d',0237,'h',0230,0324,0235,0230,'[',033,05,0347,';',' ',024,'Q',0323,0256,'E',']',0334,037,035,'=',0340,0315,0245,'>',047,0212,'9',0246,0230,'Y','4','F',0353,0332,'j',025,'N','S','3',047,014,'9',0212,0257,0200,0317,'E',0360,031,0350,0276,03,'=',027,0300,'g',0242,0352,0264,'7',0227,0340,'O',0225,0346,0215,'h',0252,0246,0310,030,'z',0243,027,0267,0316,0334,0276,'N',0345,0355,0360,'7','1',0333,03,023,'f',0205,0324,0246,0321,0305,'G','3',010,0274,'F','c',0207,0341,'E',0316,'4',01,'>',0313,03,0251,'g','n','D',0215,0370,0233,035,'=',0323,'s','y','L',0211,0202,0215,'h',0247,'r','t','o',027,0232,0355,0310,0357,0205,0371,0264,0342,017,'a',0254,0177,'3','S','f',0205,0325,0342,'8','~',020,0134,0343,'@',023,0254,'v','7','R','?',0231,0343,'~','&','D',0301,'W','8',0321,06,0220,014,0316,0315,0316,0356,0217,0205,0340,'^','#','#',0301,':',011,0233,'C',0270,0361,0304,036,0303,'X',0376,'f',0253,0320,0273,0255,0275,0277,0203,':','I',035,'u',0243,'z','6','k',023,0210,0217,0346,'v',' ',06,'d',0257,'l',0265,'4','t',0216,0354,0264,0356,0356,0325,031,'N',0316,0311,'O',0206,'Q','u',0355,'4','8',0233,'4',016,0241,033,'G',025,'C','H',0347,033,'[',0370,'#',0245,0231,0301,0255,'j','0',0307,0356,0354,0343,0206,0374,'m',0266,0332,'G','P','f',0326,0252,'w','s','i',0263,0213,0266,0201,0372,0247,'G','#','n',0270,'m',07,020,'|','O',',','p',0336,023,'l',0366,0342,'#','}','2',0177,024,034,0323,'Q',0370,013,0257,'<',':','m',0314,'E',0322,'8',0266,'=',0314,07,',','b',0321,'8',0273,'g',037,0252,0270,0321,'F',015,0203,0274,0233,'E',0234,']',0264,017,0325,':','9',033,'u',0303,'h','8',0303,'$','&','h','8',035,0250,'>',031,05,'w',0267,0207,'~','/',0221,0301,0255,033,0312,'t',026,014,0316,0316,0221,027,0312,0362,0367,035,0347,030,0264,'Z',05,0333,'0',0375,'S','c',0215,0267,'Z','7','w',0276,0236,0314,03,'-',037,0356,0235,014,0315,0270,0361,0216,0374,022,030,0335,0340,0204,'_','h','m',0377,0,0250,020,'|','2',07,0217,016,0365,'W',032,017,024,0346,'F',0356,0232,'n',015,'G',0244,0224,0210,0376,0206,0352,'#',0264,'Z',0205,0313,'>',0332,'q','M',0216,'6',0335,'h',0335,0337,'j','z',0223,015,0216,011,0320,0314,0332,'S','a',0343,0250,0275,'g',0224,0263,0303,'r',021,0333,0233,'t',0375,'a','V',011,0232,0376,0355,'y',0356,015,036,'(',0210,0235,0323,0311,0301,0250,0202,0376,0212,'/',0245,0272,0200,032,'*','N',0345,035,0256,0333,0267,'h',0215,06,0264,'P',016,0376,0350,0346,'`',0317,0346,'N','s',032,'e',0263,0375,'Z',0233,0360,'J',0350,0317,0202,'k','m','#',0246,'g',035,0350,03,047,'D',0376,016,'U',0215,0341,0343,0300,0367,014,0315,021,'2',0316,0332,0360,010,0266,0305,036,0177,'S',0221,0351,0247,'7','~',0221,0251,021,0331,0343,'/','(','K','7',0275,0234,0215,0373,0277,02,'-','{','C',0233,0300,0247,'O','a',0352,'?','i','g',024,'b',0231,0205,0217,033,0216,0252,0260,0316,0366,0376,'j',0354,0300,'N',023,'D',0321,0272,'>','%','^',026,0200,0336,'i',0267,'g','a',0256,0314,0326,0321,0213,'2',0272,0362,'5',0274,0312,'-','}',0245,0225,013,0335,0336,0224,0327,'`','N',026,'h','.',0360,'.','F',0375,0240,0206,0223,'Z','5','U',0304,0270,0370,0352,0204,0223,0216,0206,017,035,0350,'G',04,'`','S',0177,0340,0245,0262,0263,0255,0365,04,0347,0260,'t',0320,0215,0343,'Z',013,'d','s','i',0262,0205,07,013,'L',0225,036,'+',0357,'N','W','o',0211,'<','J',0371,027,0310,0276,0362,'P',0277,'j','~',0134,020,0351,'&','{',0251,0342,0252,'s',':',0313,0266,'x',0313,0206,0367,'n','M',0232,0325,0357,'f',0375,025,032,'(','?',07,0243,0205,'B','/','c','z',031,'x',0265,022,'Y',0322,0305,0365,'7',0277,']',0263,0304,'_',0343,0271,011,'-',0316,0274,'~',0200,0256,'A',030,'`',0360,0374,'.',0216,025,036,'(',0271,0215,0350,'e',0342,0325,'y',0215,0351,0343,0342,0325,'u',0355,',','>','=',0344,'6','(',035,'C',0363,024,037,'m',0223,0244,0376,'@',0203,'!',0214,'0','x','~',' ','[','4','-',0346,021,'u',0222,'k',0277,0312,0345,0326,0200,0275,0274,'Z',0256,0275,0245,0207,0307,0271,0201,025,0235,0346,0276,010,':',0323,' ',0211,0274,02,'k',0256,'t',0262,015,0356,'T','c','C','G',0207,0342,0231,0257,'{','g','c',0277,'%',0356,0213,0242,0344,0253,014,0315,'y',0256,0302,0213,'z',013,0364,0336,021,'/',0263,0310,0,0360,'U','-',' ','r',0324,'d','+',0311,'Q',0220,0275,0307,0300,'+',0314,0262,0272,0236,')',0275,'!','l','m','(',033,'D',0305,0346,0273,02,027,'l',0340,0220,'k','R',0250,0306,0206,0217,017,0307,'(',0346,02,'9','"',0307,'Y',0331,'C',0340,0276,0350,0304,'_',0320,0335,0360,013,0341,0237,'U',0360,0317,0252,0370,'g',0325,'|','3',0352,0203,'}',0225,0271,'*',0307,'f','`','<',0225,'Y',023,032,'|',07,0377,0,035,0177,0377,0304,0,',',020,01,0,02,01,02,05,03,05,01,01,01,01,01,01,0,0,01,0,021,'!','1','A',020,' ','0','Q','a','q',0241,0360,'@',0201,0221,0261,0361,'P',0301,0321,'`',0200,0341,0377,0332,0,010,01,01,0,01,'?','!',0377,0,0362,'b',0352,'I',0365,0237,0330,0237,0330,0237,0330,0237,0330,0202,'?',0374,0,03,0177,0340,0237,'y',0350,'a','i',0272,'J','Q',0372,0255,' ',035,0273,',',0234,02,0330,'?','j','r',0336,0367,0255,016,05,023,'2','A','T','"','E','W',01,0210,0347,0236,0372,'j',';',0230,0355,0245,04,047,'`','i',032,'*','>',0237,0353,' ','Z',0321,0346,'.',034,0276,0344,'H',0300,'{','D',0305,0313,'Z',0354,0304,06,0253,'9',0372,011,0321,'f',0314,'Y',0224,'Q',0273,0205,0312,'~',023,020,023,0326,0326,'X','-',0273,'E','_',0372,' ','F','n',0240,014,0302,0324,0233,0345,0250,0341,'w','6',0357,0351,0205,'B','4',0233,0316,0353,0240,0331,035,'@',0320,0300,0272,0250,0274,0263,0376,'h',021,0233,0251,0217,'w',035,0210,0375,0356,0365,'}','`',0310,0356,0355,'N',0305,'^',0322,'5',031,0275,0317,0371,'"',04,0362,0254,' ',0213,025,'Z','B',0355,0273,'U',0310,0326,0264,0264,0363,'W',0246,036,0313,0356,'.',0177,'K','?',0245,0353,0231,035,0335,0250,0345,0235,'3','c',0326,030,'M',0354,0351,0376,'0',0302,'V',0354,0314,'-','w',0262,0253,0325,0361,0,024,0266,'&',0265,0372,016,'"',021,0226,07,0222,' ','R','Y',04,'9',0345,':','C',0220,0243,010,0361,'^',0267,'=','h',032,'3',0134,0230,0333,'Z',';','D',0232,0351,0332,'f','o',0225,0322,0304,0376,'d',0315,'=',0272,'o','?',0204,0213,0225,0355,'"',0245,'s',0235,'!','_',0212,020,0254,'{','Z','u',0227,'Z','6',07,'3','h',0355,'O','3','~',0333,'K',021,024,0211,'I',0267,'J',0277,'K','j','r',0373,'B',0245,'@','e',0207,0342,05,0312,0316,0347,0370,'O',010,'y','V','V',0250,'m',0202,0243,0362,032,04,'4','h','P',020,0214,0345,0367,0345,'0',037,016,' ',0320,017,'$','4',0207,0263,'H','t',012,'L','$','c',0327,'!',0321,0357,05,'D',032,'o',024,'P',0367,0321,05,022,012,0304,05,'i',0325,'J',0331,0250,'.','v','c',0243,'n',03,0241,'1',026,0225,033,'T',0331,021,'H',0224,0233,'t',015,0250,'2','#','I',026,047,'L',0330,0227,0227,'E',016,'N',0235,0324,0323,0222,0177,'Z',0177,'R',0177,'R',0177,'R',0177,'R','+','C','>',0262,0354,0307,'W','~',0240,')',0276,0334,035,'a','T',0253,'k',0273,034,'f',0306,'x',',','!',0206,0,0301,0314,'4','J',0374,0244,'b',0300,0353,0264,0363,0364,0210,0232,0366,027,')',0262,0231,'0','f','4','j','2',0220,0326,014,0203,'O','C',']',0227,0204,0315,'7',0241,0234,0337,'A',02,0330,0302,0227,0262,'&',0215,'y',0227,0266,0220,'z',0257,0314,0334,0237,0230,0263,'w',0346,'Y',0377,0,0322,'h','K',0363,016,0251,'^','b','X','X',0352,034,'a',0320,0335,0215,0205,0373,'W',0353,0307,0333,'z',027,0356,0377,0,0271,0360,'<',0375,'2','Y',0230,010,0313,'C',0244,0232,'3',0263,'F',0202,'>','L','e','m',0316,0236,'^',0360,0353,'0',027,0273,07,0231,'Z',0320,'K',0211,0373,0306,'W','&',031,'_',017,'o',0330,0276,0371,'~',0351,0345,'C',0364,0241,0346,0134,'.',0217,'L',0330,'P',0362,0250,0207,0301,0360,0207,047,0266,0364,'/',0335,0377,0,'s',0340,'y',0372,0204,05,'%',0220,0271,'E',0263,0314,027,0265,'`','c',0235,010,'>',0340,07,0343,0241,'y','.','R',0202,'>',02,0335,0201,'+','h','I',0240,0212,'u',0346,'6','V','"',0215,0203,'C',0232,0351,033,012,036,'U',020,0370,'>',020,0345,0366,0336,0205,0373,0277,0356,'|',017,'?','U',0260,0320,04,0324,'X','n',037,0264,0303,0221,020,0247,0231,010,'>',0340,07,0343,0241,'x','@','[',013,'0','o','R',0256,0243,0211,0223,0234,0220,'&',0220,0312,'U','3',0266,0201,'E','t','O',0311,0357,';',0315,04,021,';','s','{','o','B',0375,0337,0367,'>',07,0237,0252,'c','|','2',0266,0361,0330,'1',0337,0325,'Z','a',0346,'B',017,0270,')',0365,'A',0221,0220,0246,'j','i','D','i',0347,'j',0211,'T',034,0314,'m',020,'Z',0240,0350,0203,'Q','Z','D',0322,0203,0256,0276,'d',' ',0372,0201,0276,02,03,'c',0215,0350,0370,0267,'e',0241,0177,'r','y','>','~',0263,0311,0363,0365,0226,0330,0272,0241,'<','&',020,0375,0204,'m',0276,0276,0347,'f','{',0277,0356,'|',017,'?','Z','n',0314,0302,'d','g',0203,033,0321,0345,013,'`',02,'9',0262,'f',0326,034,'g',034,0341,'r',0250,'q','0',0334,0353,011,0232,0256,0212,0321,'s','(',0264,0257,0320,0346,'B',017,0250,01,0370,0354,031,0366,036,'6',011,'S',025,0341,0307,'f',0344,'1',0242,'_',022,']','v',0270,'q',020,'1',0227,'Q','4','|',0317,'?',0332,0330,'<',0201,0265,'=',0331,0224,0366,'/','Z',0377,0,0356,'{',0277,0356,'|',017,'?',0134,'l',',','y','T','C',0350,0370,'S',0221,':',0261,0205,0315,'(',0214,0233,0240,06,0216,05,0326,'.',0317,0245,0212,0333,'@',0351,031,025,0300,0357,0312,0201,0225,'@','o','(',05,0354,016,037,'l',0343,'~',0271,'c','m',0237,0263,'Q',01,'.','a','{',04,0262,0231,'r','+','+',035,0254,0231,'a','F','g',0236,0265,'y','U',036,047,0220,'U',0265,'-','~',011,0356,0377,0,0271,0360,'<',0364,'/',0351,0315,0205,0217,'*',0227,030,031,015,034,03,',','o',014,0342,0232,0226,'e',0213,'o','8',0202,0261,'+','f',0223,'9',017,'K',',',0356,'Y',0217,'E',0364,034,0241,'m',020,0303,'2',0262,0373,0300,0,024,034,'=',0263,0222,0334,0226,0233,'5','n','4',0213,0237,0336,0364,0221,',',0256,030,0257,'B',0354,033,0216,036,0357,0373,0237,03,0317,0370,03,0357,'(',0265,0271,'W','!','d',0203,0312,011,027,'1',025,'s',0213,04,',',014,0245,0263,0322,'C',' ',0323,0245,0201,0375,'a',0257,0216,'c',0251,0262,'5',0300,0240,'j',03,0217,0266,'q',0265,035,'"','4',012,'$','|',012,0301,'r',0351,0252,0311,'T',03,'n','_',0210,'U',0233,'>','[',0302,0217,'A','H','[',0331,0341,0367,0177,0334,0370,036,'~',0213,'c',0254,'h',0272,'B','Z',0300,06,0261,'H','k',035,0247,0235,'@','!',0211,'f','b',',',0207,035,07,'J',0332,027,',','n',0307,0236,0321,'9','P',0245,0334,'&',0210,'w',0220,'4',0325,0344,0366,0316,'7',0253,'$',' ','&',0203,0315,'M','p','I',0302,0322,'W','Z',';',0247,'^',026,0323,0300,'S','4','P',0302,0316,'<','=',0337,0367,'>',07,0237,0242,0256,0261,047,',',0316,0374,0261,07,'8','.','Y',06,'y',0255,' ',0205,'O','7',0375,'r','-',027,'5',0275,0253,'N',036,'S',0342,'U',01,0274,034,0365,'z',016,'_','l',0343,'z',0335,0332,0260,0311,'?','3',0347,0377,0,0362,'|',0377,0,0376,'F','8','T',0300,033,030,0341,0367,0177,0334,0370,036,'~',0275,022,0317,017,'R',0304,'b','d',0232,0243,0254,'Y',0314,0202,0210,0275,0324,'l',0310,0347,0331,0310,0215,0245,03,0273,'-',014,011,0347,0224,'M',0334,0251,'0',0300,0,014,07,'/',0266,'p',0273,'s',0371,'0','{','=',024,0323,0340,0204,'5',0,0276,0353,0306,0335,0266,'z',0255,'X','n',026,')','p',0367,0177,0334,0370,036,'~',0271,0351,0240,0203,06,011,'`',0271,0235,0304,0225,06,'f','S',0205,0304,0264,0351,'B',0270,0220,'+',020,036,'e',0204,'s',0335,0340,'W',';',0311,'R',0363,'h','r',0216,0353,'(',0224,0312,013,0353,0346,0366,0316,027,'_',0260,0330,0321,07,0270,'L','.',0337,0333,'A','e',0367,0,0236,0311,'a','?',0230,0211,0273,0,0226,0270,0327,0245,0207,017,'w',0375,0317,0201,0347,0353,'M',026,0221,027,023,'x','9',031,'D',')',0225,04,'<',0314,0315,'/',031,0332,0343,'Q',0206,'Z',0366,'A','B',032,'S','0',0224,0344,030,0321,01,'=','3',0241,'V','U','w',0216,0231,'t',0274,0271,025,0305,'m',0336,0177,'l',0343,'b',022,'c',']',0227,0324,0373,0244,0313,027,0300,0242,0375,0236,0320,'s',0340,0367,0224,0303,0226,'i','o','Z',0366,037,0307,017,'w',0375,0317,0201,0347,0353,'r',0266,0312,07,'f','1',0317,02,0265,02,0267,'h',0227,0226,'(',0264,'%',0300,0325,0212,0367,026,'S',06,'+','V',0232,0211,'G',0302,'1',0364,0335,0373,'p',0276,0213,'o',035,0223,'D',0345,'9',0224,0202,0213,0226,'^','U',0266,0267,0317,0355,0234,'l',0343,0240,0232,032,0273,0372,'U',0375,0241,012,0322,'v',03,0261,'n',0251,0336,'|',0227,0376,0312,0332,'$',0352,0301,'t','m','i',0371,0341,0356,0377,0,0271,0360,'<',0312,0225,0365,'I','7','5',020,0374,'K',05,0307,012,01,0206,'v',0361,'*','=','R',0314,06,'2','l','%',0333,020,0213,021,'+',0240,023,035,'O',013,'}',0244,'M',0341,0211,0242,0134,'Z',0350,'+',027,'B','R','B',0354,'9','L','P',0277,0224,012,'+',0237,0333,'8',0336,0257,0213,'t','Z',027,0367,'g',0311,0377,0,0344,0371,'?',0374,0201,0310,0317,0226,0335,0207,'e',0303,0335,0377,0,'s',0340,'y',0372,0265,'*',0202,'b','D',0204,0215,0230,0250,0262,0264,'A',0353,'X',0200,0331,'*',0206,0251,'{',0361,0213,'F',020,0306,'!',0240,0304,047,'l',0242,0333,0303,'U',0304,020,04,'Q',0243,0274,',',0364,033,0325,0274,0352,0216,'R',0215,0265,'r',0352,':',037,016,0207,0266,'q',0262,0213,'|',015,0344,'Z',0275,'j',034,0351,'4',0300,'6',035,'9',017,'w',0375,0317,0201,0347,0352,0226,0245,0326,'S','X','j',0263,034,047,0205,'s',0367,0215,0341,'%',0237,'x',0347,'E',0244,0356,0214,'&','!',036,' ',0201,0205,'#','A','|',0200,0227,0240,':',07,'F',0204,'-',0246,0307,'N','U',0374,0210,'h',0274,'C',0224,03,0201,']',017,'l',0343,'`',0333,026,027,'d','/',0305,0222,0352,'Z',',',0301,']',0305,'/',']','x','[','g',0252,0325,0206,0341,'b',0227,017,'s',0375,0317,0201,0347,0351,0334,'A',0276,015,'g','G',010,0202,';','a',0302,0232,'#',047,'d','>',',','Q',0262,'"',012,0242,'x','T',0243,034,0315,0216,'!','B',0345,'+',013,0345,0301,026,';',0353,0242,0367,0,0312,0275,'y','m',0262,0357,'-',036,0217,0266,'q',0275,'9',0256,'{',024,0217,0333,0215,0356,0233,0315,'W','[',015,'~','C',0361,0300,'~','o',0356,'|',017,'?','O',0254,0265,034,0252,016,013,'B',0361,'3',',','[',020,0206,0362,0374,0326,0242,0223,0256,0351,0226,'B',035,0244,'z',014,'a',0216,021,0202,0312,0234,0221,0252,0267,0247,'+',02,'-',0252,032,0230,0317,02,0234,0352,0305,0320,0226,0261,0222,0266,0345,0254,032,'J','k',0321,0366,0316,'6',0213,0251,0277,'a','u',0367,0216,0235,0200,'o','8',0306,03,'l',0307,02,012,0317,'S','W','f',')',0235,0246,0316,'*','b',0343,013,0367,0341,0356,0377,0,0271,0360,'<',0375,'C',0211,0177,0224,0202,0243,0224,'e',0134,010,0333,013,0266,0235,0227,',','v','E','M','B',030,0304,0240,'9',0204,'Y',0330,025,011,'2',0300,0354,0210,0375,'_','h','k',0310,'*',021,0244,0336,032,0330,0134,'w',0347,0306,'y',024,0373,'M',0332,025,'}',0371,01,'@',026,0273,'C','[','K',0216,0375,037,'l',0343,'~','k','/',0211,0245,0312,0200,0253,0272,'Q','?',0366,'>','`',0265,010,0134,035,'F',0304,0275,047,0303,04,'N',07,0273,0376,0347,0300,0363,0365,017,0366,0232,0214,0256,':',03,0300,0305,']',03,'6','%',010,0302,'5',020,'2','`',0231,0314,02,0304,'1',0341,'4',0204,'a','A',0300,'X',':','2',0326,'2','V',0374,0275,0223,0253,0275,'y',0327,0212,'.',':',0344,'M',0274,0212,'X',05,'o',0254,'W','-','E','z','t','}',0263,0215,0320,0361,0215,']',0350,032,0361,016,0221,'r','6',027,0253,'Q',0257,0261,035,0343,0335,0253,0202,0214,0371,' ',0270,0344,'6',035,0257,'u',0274,0372,034,'=',0337,0367,'>',07,0237,0247,047,'Z',021,'&','U','+','b',0332,0343,010,0204,0254,'.','y','b','^','b','b',0320,'m',0362,'Q',016,'a','}','c',0254,036,'j',0216,'D','(','j',06,0374,0252,0323,0232,'|',0363,0272,'X',0317,0351,0313,'j','&','L','J',0243,0261,0321,0366,0316,'7',0220,013,0245,024,0354,'S',0357,'r',0367,'[','H',0217,0313,0243,0354,0312,0276,0227,0335,0277,013,06,0313,'2','p',0367,0177,0334,0370,036,'~',0234,'f',0326,0243,0210,0252,0270,'1','V',0263,0356,0247,04,0356,021,'i',0211,0320,0204,'2',0304,'#','&',',',0317,0225,0366,0332,0372,'5',0313,'J',0313,0303,0236,0201,'c',0247,'~','W',0224,0344,0253,0277,'K',0333,'8',0335,0342,0345,0300,'n',0374,'o',0357,'-',012,0254,0255,'x','5','~',0341,'T',';','I',0367,0240,0374,',','|',0220,'e','S',0263,'E',0345,0374,0360,0367,0177,0334,0370,036,'z','Z','C',0254,0311,'F','L','g',0257,'E',034,016,'v','R',0250,0213,0201,032,'G','P','q',0301,'&',0315,0261,0277,0270,'D',0266,'V',0265,'U','3',014,0346,01,',',0312,034,0347,'x',024,'W','-',0271,'k',0264,0345,02,0274,0276,'G',0251,0257,'u',0360,0203,0234,0343,0355,0234,'o',0266,0224,0341,'>',0347,0375,'K',',',0216,'!','{','i',0275,0273,0223,'$',0352,0352,0372,'J',0331,0366,0227,'&','8','W',03,'V','-','X','c',0307,017,'w',0375,0317,0201,0347,0242,0370,'$',0310,0376,'x',0361,'S',0357,'7','Q',0365,0211,'1',0336,'Y','o',0371,01,015,0371,'V',0213,0227,'/',0250,0225,0224,0314,'[',0134,'b',033,0305,'6',0217,'S','H','d','E',0345,0254,031,0247,0,0321,030,0313,'`',0225,'A',0315,'n','w',012,0273,'}',012,0357,0266,0363,'0',0343,0355,0234,'l','*',035,0214,0223,0323,'(','|',' ',0260,0261,'}','0',0215,':','a',0210,036,0241,'P','6','"',';','*',0262,0227,0357,0341,0356,0377,0,0271,0360,'<',0363,0255,026,0305,'o','r',0351,036,020,0347,'6',0305,0221,'Q',026,0310,0313,'`',0345,'J',0327,'a',017,'2',0320,0345,0275,'Z','"',0256,'Q',0235,0311,'l',0240,'`',0244,0300,'j',0203,0251,012,0254,0304,';','K',0201,0303,0222,03,0252,'k',0352,'5','w',0333,'y',0236,'O','l',0343,'m','W','z',0373,0336,036,0321,':',0247,0377,0,0,027,015,'f',0233,0335,07,0376,'b','M','{','f','u','7',0327,0355,'8','{',0277,0356,'|',017,'<',0340,0217,0224,0261,0267,0254,'K','^',0134,025,0362,0251,'[',014,0315,'w','^','B','e',0241,')',030,0225,0323,022,0202,0270,'{',0322,'w',0347,'Y','V',0275,' ',0202,'T',031,0335,',','K','+','T',0322,0331,0231,021,'~',0247,'W','u',0343,0223,'-','9',0236,'O','l',0343,'w',0236,027,0377,0,0214,037,0233,0201,016,'6',0307,0267,047,0273,0376,0347,0300,0363,0315,0343,02,'^',0316,021,0363,'b','+','y',0314,0311,0376,0260,0317,0315,0234,'B',01,'i',014,0223,047,0242,'z',023,026,0351,0242,0223,'y',0353,'I','m','b',0201,'X','8',021,'O',0215,0265,0302,' ',0323,0241,0262,'[',0177,'Q','A','!','i',035,0371,'X','g',0337,0314,0301,0343,0355,0235,013,0367,0177,0334,0370,036,'y',0215,017,021,0343,'{',0335,012,0202,0331,'!',02,'x',010,0345,'Q',',',0374,032,'D','D',0313,'t',012,026,0240,0247,'2',0331,0224,'a','h',0267,0273,'+','W','6',0301,0205,0215,'Q',0321,'e','>','<',0252,0304,'j',0337,'?',0262,0362,0256,0354,0243,0314,0315,0134,'}',0263,0241,'~',0357,0373,0237,03,0317,'5',0211,023,017,017,'D','T','^','f','D',0310,'x','q','P','6','C','X',0310,0134,'q',0275,'*',0224,'W',0347,0227,'d','1',0217,'y',0256,0222,0343,0210,0230,'3','O','J',0242,'j',0314,0345,0375,0276,'q','A',0255,0213,'9','s',';',0207,'d','@',0351,'Q',0345,'f',0256,'>',0331,0320,0277,'w',0375,0317,0201,0347,0236,0264,':','!',0243,'f',0252,0302,'W',0256,';',0305,0253,'Y',0305,'E',0344,0322,'/',0255,0220,'k','h','B','"',0237,'9',0322,0340,'Q',0322,0262,'E',0336,0317,')',0261,'K','T',0311,0316,',',0350,0252,'k',0211,'j','S',036,031,0344,0301,'w','C','P','@',0244,'q',0314,'q',0366,0316,0205,0373,0277,0356,'|',017,'<',0370,0273,'$',0265,0356,'G',0241,'A','l','[',0207,'1','J','q',0314,0225,022,0250,0263,'0',0240,'A','*',0203,0247,0262,033,'~',0274,0251,'b',0262,06,0274,0364,'N',0344,'l',0213,'-',0234,0242,0372,0276,016,0335,037,'l',0207,0244,0203,0316,047,0353,0236,'|',017,'<',0346,0356,'q','/',0343,'~',0225,013,'w',0347,'Q',',',023,012,'(','S','3',']','7','n',0311,'X',0326,0320,0224,0271,'I',0203,'C',']',02,'r',0267,0354,0134,0254,0341,0242,0252,0350,0250,'+',047,0340,0231,0355,'$',014,0,0327,'s',0317,'-',0,03,'C',0274,0327,0315,'J',0200,'_',0354,034,0342,0302,'T','$','/',0336,0375,'q',0,0224,023,'(',03,0231,0352,'V',0326,'b','G','x',0353,0221,'6',0362,'#',0303,':','7',0314,0246,0324,0375,035,014,0316,'H','4',016,'[','Z',036,0335,0206,':',0226,026,047,'C','R',']',0374,0331,0244,0363,017,'r',027,'U',0274,047,0311,'g',0311,'e','Y',0216,0372,0313,0206,'^','a',0220,'d',0211,' ','h',016,0206,'$','f','6',0334,0365,0230,0202,'P',026,'X',0345,0201,0202,030,03,0251,0257,'+',0254,0331,0351,0313,'a','x',0336,0312,0256,0303,0241,'[','[',0211,'=',0243,0256,0,0323,0310,0352,'(','6','0',0331,'W','6',0350,0203,02,0307,'f','1','+','|','*',01,0337,0330,0243,'>','k','>','k','>','c','<',021,032,0211,0244,0310,016,0210,0274,'L',0301,0332,'2',0245,0325,'s',',','p',07,02,0312,0307,'P',0324,0320,'.','=',0211,0310,016,'V','0',0321,'u',0364,'I','[','R','#','0',0,0224,'v','y','Y',0255,'=',0267,'D',0270,'-',0341,'F','d','|','j',')','!','x',0254,0376,'&',0177,023,'?',0211,0233,0250,0206,06,04,01,0337,'q',0321,0270,'%',020,'B',0311,0357,0324,'a',022,0262,0345,'2',0202,0365,'h','E',0200,0273,0304,'N',0325,0257,'w',0224,01,014,0201,0322,0261,0252,'o',0276,'[','"',07,0354,0336,'#',0242,036,017,';',0355,'N',0326,'h',021,0177,0376,0263,0325,'l',0354,'D','t','/','c',0307,'Z','8',0206,0240,036,0232,'S',0364,'6',04,'o',',',0177,035,033,020,'f','/','t',0311,0323,0252,011,'V','Y','b',036,03,'H',' ','4',':',0245,0336,0214,'n',0257,')',0337,0203,'t',0355,'4',0325,'=','"',0217,',',0373,'1',0304,'P','i','9','s',0335,0277,'u',0347,'>',011,0370,'I',0256,023,'P',0360,0323,'W','w',0224,023,'k',0206,'#',0271,014,0217,0210,0372,'!',0213,0367,0321,036,0360,'K','l',0214,0247,'I',011,030,0230,0300,'u',0205,'%','c',0252,'G',02,0325,0367,0216,')','n','[',034,0272,0210,04,0367,0351,0213,0266,024,0220,'u',0205,0312,0327,0270,0320,'E',026,010,047,'7',0272,0376,0245,02,0272,0267,0234,0206,0327,',',0370,036,'z',')','d',0300,0330,'u',0230,0231,0321,'R',022,0252,0345,'2',024,0365,0274,'7',023,0355,0313,0252,0342,010,'w',0224,0273,0345,0352,'k','_','e',0271,0315,0266,0212,0321,0324,0346,0367,'_',0324,0366,0237,0247,'?',0273,0376,0347,0300,0363,0322,032,0254,0324,'|','L','"',':',0347,026,0251,'L',031,0225,030,06,0220,0310,'4',0352,0326,0255,0372,0303,024,'U','N','[','r',0265,0277,0367,0346,01,014,07,'P',0276,0312,011,0203,05,'/',0216,'U','_',0262,0356,'O','a',0250,'<',0246,'M',0223,0367,0227,0243,'A',0227,0244,02,015,0216,'G',0230,'A','R',0351,0221,0225,0200,0305,'|',0206,0261,'l','9',0212,0322,'K','v',0225,0302,0335,0242,0264,0220,0211,'@',0326,'-','`','q','T','u','A',0226,02,'j',0315,0362,0367,0345,02,'v',0250,';',0263,0,016,0356,0266,0265,'v','[',0221,')',0247,0227,'#','v',0262,0303,0216,015,'+',023,0220,0332,0324,0345,0346,0134,'X',0322,'L',0351,030,0256,0220,037,0303,'O',0341,0245,'Q',0221,'t',',',0327,0267,0322,05,'<',0242,'8',0272,'o',010,'n',0346,'m',0251,0252,'3',0355,03,0273,0361,';',0247,0342,'y',0337,0211,0331,'o',0332,'.',0134,'z',0221,0225,'!','P','_','~',0260,0204,0247,'C',0223,0336,';','V','-',']',0371,'o','G',0232,0272,',',02,030,016,0261,'X',025,'#','-',0,0300,0230,0276,0334,0242,0241,032,'M',0310,'D',']',033,0354,0345,0303,0177,0230,'b','Z',0336,0365,0211,0244,0267,0274,0267,0274,0270,01,';',0262,015,'k',0355,035,'^',0206,'3','P',021,'}',0261,'m',0260,'-',0263,'G','3','N','H',025,0326,030,'l','U','w','c',0361,'^',035,0216,'T',0226,0252,022,0224,0313,0205,'f',0376,0201,021,0207,0341,'2',0340,'L',0216,0134,0357,'X',0230,'c',0254,032,0362,0217,'D','|',0221,'R',0252,0276,'3',0370,'y',0374,'<',0376,'^','S',010,0360,0257,0360,0214,025,0202,0343,0320,0375,0361,0363,0315,0265,'F',0227,0367,0364,'Y',0356,0350,'M',0271,0235,0262,'^',021,'4','2',0206,0352,0377,0,'(','(',032,0325,0227,0205,'6','<',0321,'s',0260,07,'h','(',0200,025,0364,'D','|','T',0251,'Q',0226,'P','m',0343,0231,017,0243,0341,'H','l',',','y','W',0371,01,'@',0326,0254,0240,'I',0211,013,'m',0274,0265,02,'`','L','^',0,031,0213,0350,0365,'6',0206,'j',0215,0210,037,0,0346,'C',0350,0370,'R','h',036,0326,0324,0377,0,030,0340,016,0325,023,'M',037,'v','-',0266,0362,0233,'V','(',015,0346,'4','N',07,011,'G',0351,'4','o',0243,0215,0353,034,0313,0221,032,0270,0213,0373,0250,0257,0370,0204,0264,'/',',',0263,026,'n',0317,'?',011,0315,0345,'y',0200,'C',01,0364,0347,'$','v',0204,0241,07,0327,'0',0335,0251,0315,'J',0,024,'G',022,' ','_','"',0177,0202,0315,'@',0306,0345,0211,'x','s',0241,0371,0256,0301,047,06,'1',0203,'o',0251,07,0343,0264,'%',010,'>',0271,0200,0323,'d',0260,0371,0312,'X',0213,'I','[',0263,'?',0134,031,0357,'j','C','u',0302,0326,0311,0273,'S',0233,0347,'M',')',0260,'d','z',016,0201,0365,'m',0200,'u',0355,'/','M','t',0217,'9',022,0316,0355,'`',015,0354,'4','#',0343,0375,0337,'T',0334,0245,0252,0247,'c',05,0232,0245,0247,'-',022,0216,'p',0266,0211,0266,0334,'=','`',0340,016,0201,0365,0272,'B',0270,'Y','c','#',0263,'G',03,0240,'a',0271,0335,0227,0332,'v','{',0266,0247,0242,02,'9',0372,'e','c','z',0252,0246,02,016,'$',0306,'[','G',0250,0252,'U',0265,0335,0347,'|',0230,0300,0336,031,'.','V',0364,0373,0300,0240,'j',03,0353,0332,0341,024,'S','$',0333,01,'I',0247,'D',0361,013,'y','a',0205,014,0360,0210,02,0312,047,0347,0366,']',0364,06,'X',017,'2',0244,'!','v',0333,'-','}',0275,'m','F',0205,0233,'v',0321,0321,0334,0350,0320,0301,07,'3','1',0134,'@',02,0202,0217,0360,'X','2',0324,'.','w',0314,'l','=',021,05,0245,'=',' ',05,'w','l',0210,',','0',0325,0303,'/','{',036,021,0215,0363,'z',0255,'p',0305,0265,0325,0266,011,0377,0,0267,'6',0202,036,0254,0254,0317,0351,0202,012,'V',0325,0313,0232,0301,'Q','+',0370,'M','M',0331,0302,0251,0346,0304,'+','z',' ',0240,013,']',0210,0217,0260,'=',0306,'x','=','.','_',0361,'H',0231,0267,023,'s','{','Y',0272,021,024,0211,'I',0263,0324,'m','V',0363,0211,'l',0363,'e',0360,03,0325,0235,'t',0346,'|','D',0134,0257,0322,'(','k',0275,0210,'Q',0355,'6',0251,0342,0316,0267,'/',03,0334,'z',0235,0343,0301,0242,'a',01,0344,'6','C',0204,'<',01,0376,';',0302,'>',021,0207,']','r','c','%',0241,0334,'E','"','R','l',0375,'i',0206,0357,'f',037,'y',0335,0356,0332,0204,0203,0373,'?',0313,'n','V',0324,020,0353,0356,'W','?',0221,021,0300,0356,'(',0327,0324,02,0200,'-','v',0235,0227,'A',0242,'h',',',016,0304,'<',0177,0263,0374,0364,012,'K','<',0307,'b',0274,0325,'L',0275,0245,0227,'P',0361,025,0324,032,0327,0216,07,'q','F',0276,0210,024,01,'k',0264,0331,0366,0326,0204,0322,0250,'.',0267,0231,0367,0323,034,026,']',0240,'+',0375,'M',024,'>',0261,'{','c',0274,'P','(',0227,'}',0323,0332,'%',010,012,0243,0321,0315,'_',0252,0355,0236,'p','c',0320,'7','L',0373,013,0215,'L',0213,0245,0232,0302,'u',0205,'E',0363,0313,']','R','S',030,0263,0204,'H','&',026,031,'X',024,0263,'@','W',0373,0210,035,0365,030,'f','}','b',0263,0371,0221,0231,'c',0273,'G','*',0224,0245,0134,0246,0225,'i',021,'6',024,0352,0214,0222,0267,022,'h',0240,'z',0177,0362,'u',0377,0,0323,0177,0377,0332,0,014,03,01,0,02,0,03,0,0,0,020,0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'H','$',0220,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'H','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',022,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0202,'I','$',0222,'I','$',0222,011,'$',0222,'I','$',0222,'I','$',0222,'I','$',0222,014,'I','$','I','$',0222,'I','$',0222,'A','$',0222,'I','$',0222,'I',04,0222,'I','$',0222,'I','$',0222,'I','$',037,0316,'?','m',0270,'C',0325,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0202,'I','$',0222,'I','$',0222,'I','$',0272,'/','m',0266,0333,'m',0266,0332,0245,0322,'I','$',0222,'I','$',0222,'I','$',0222,'A','$',0222,'I','$',0222,'I','$',0212,0247,'m',0266,0333,'m',0266,0333,'m',0264,0277,'D',0222,'I','$',0222,'I','$',0222,'I',' ',0222,'I','$',0222,'I','$',0221,07,'m',0266,0333,'m',0266,0333,'m',0266,0333,'m',0,011,'$',0222,'I','$',0222,'I','$',0220,'I','$',0222,'I','$',0222,025,0311,'2',0333,'m',0266,0321,'%',0266,0333,'m',0266,0332,030,0222,'I','$',0222,'I','$',0222,'H','$',0222,'I','$',0222,'I',017,0322,015,025,0241,'>',0266,0352,0265,'t','6',0333,'m',0267,05,'$',0222,'I','$',0222,'I','$',022,'I','$',0222,'I',027,0335,0257,0303,'t',024,'d',0222,'I','$',0223,0301,0315,0266,0333,'B',0322,'I','$',0222,010,0,0,'I','$',0222,'I','$','T',0254,'-',0266,0345,'$',0222,'I','$',0222,'I','$','!',0273,'m',0266,0325,'$',0222,'H','&',0212,'5',0204,0222,'I','$',0222,0233,'_',0266,0333,0134,0222,'I','$',0222,'I','$',0222,'H','X',026,0333,'j','B','I',' ',0243,'8','^',0362,'I','$',0222,'O','-',0257,0333,'m',0256,'I','$',0222,'I','$',0222,'I','$',0222,0345,'m',0266,0252,'$',0203,'Q',0377,0,0377,0,0221,'$',0222,'I','<',0266,0327,0355,0266,0327,'$',0222,'I','$',0222,'I','$',0222,'I',047,'N',0333,'j',0240,'J',0177,0377,0,0371,'d',0222,'I','$','6',0333,'k',0366,0333,'k',0222,'I','$',0222,'I','$',0222,'I','$',0222,',','-',0266,0257,035,0377,0,0377,0,0323,0320,'I','$',0220,'k','m',0252,0373,'d','9',0311,'$',0222,'I','$',0222,'I','$',0222,'I','$',0206,0333,'g','O',0377,0,0377,0,'2',0210,'$',0222,'C',0355,0266,0252,'=',0331,0320,0344,0222,'I','$',0222,'I','$',0222,'I','$',0222,'O','-',0277,'/',0377,0,0377,0,0305,'$',022,'I','%','v',0333,'x',0236,0232,'M','r','I','$',0200,'I','$',0222,'I','$',0222,'I','$',0356,0324,037,0377,0,0377,0,'D',022,011,'$',0237,0333,'m',026,'O','l','.',0271,'$',0222,'@','$',0222,'I','$',0222,'I','$',0222,'E','0',0177,0377,0,0377,0,0322,'I',04,0222,017,'m',0265,011,047,0256,'y',0134,0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','%',037,0377,0,0377,0,0361,01,'$',0202,'I',0,0266,0327,0244,0223,0332,'`',0256,'I',' ',0222,'I','$',0222,'A','$',0222,'I','$',0222,0237,0377,0,0377,0,0225,'D',0222,'A','$','2',0333,'@',0222,'I',0350,0331,0327,'$',0220,'I','$',0222,'I','$',0222,'I','$',0222,'@','7',0367,0377,0,0323,022,'I',' ',0222,'7','m',0276,0311,'$',0361,0223,'k',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$','U','d',0177,0354,011,'$',0222,'H',035,0266,0201,'$',0222,0177,0210,'u',0311,' ',0222,'I','$',0222,'I','$',0222,'I','$',0203,0376,':',0221,'E',0244,0222,'H','$',0302,0333,'@',0222,'I','=','e',':',0340,0200,'I','$',0222,'A','$',0222,'I','$',0220,016,'?',031,'Z',0234,'0','I','$',022,'K','m','n','I','$',0236,0354,'=','p','@',04,0222,'I','$',0222,'I','$',0222,'I',036,0256,'t',0250,02,0255,'D',0222,011,']',0266,0363,'$',0222,'O','j','V',0271,' ',0222,'I','$',0222,'I','$',0222,'I','$',0206,033,0312,0205,0221,'C',0302,'I',04,026,0333,'T',0222,'I',047,0265,'[','T',0222,'I','$',0222,'I','$',0222,'I','$',0202,'s',0365,0370,0377,0,0265,0266,0211,'$',0202,'k','m',022,'I','$',0223,0336,015,0242,'I','$',0222,'I','$',0222,'I','$',0203,'A',0267,'"','I','l','v',0332,'D',0222,'A','-',0266,0211,'$',0222,'I',0352,'o','[','$',0222,'I','$',0222,'I','$',0222,'A',0340,'y',0232,0214,0206,'K','m',0356,'I',' ',0262,0333,'l',0222,'I','$',0365,'=','/',0222,'I','$',0222,'I','$',0222,'I','$',020,'>','2','&',0300,'<',0266,0223,'$',0220,'9','m','&','I','$',0222,'{',037,0365,0311,'$',0222,'I',04,0222,'I','$',0222,'C','{',0256,035,0244,0226,'[','j',0222,'H','!',0266,0303,'$',0222,'I','=','e','z',0344,0222,'I','$',0201,'I','$',0222,'I',07,015,0315,'g',02,'H','-',0264,0311,'$',02,0333,'z',0222,'I','$',0236,0271,035,'r','I','$',0222,'H',0244,0222,'I','$',04,'}',027,035,'I','$','v',0332,'$',0222,01,'m',0252,'I','$',0222,'O','R','*',0271,'$',0222,'I',0,0222,011,'$',0220,025,0350,'Q',01,'$',0222,'+','m',022,'I',03,0266,0325,'$',0220,011,047,0266,'C',0134,0222,'I',' ',0222,0320,'@',0222,'H',0,0321,'&','X',022,'I',025,0266,0361,'$',0201,0333,'n',0222,'H','$',0223,0327,'0',0256,'I','$',0222,'4',013,0240,'I','$',0340,0353,0231,036,'I','$',0232,0333,'x',0222,'@',0355,0267,'I','$',0202,'I',0351,'Y','W','$',0222,'H',0201,0377,0,0377,0,'D',021,'#',0327,0323,0300,'$',0222,'M','m',0274,'I',' ','v',0332,0344,0222,01,'$',0365,013,'k',0222,'I','$',0263,0377,0,0371,'"','@',0314,'D',0360,0340,0222,'I','#',0266,0336,'$',0220,033,'m','r','I',04,0222,'{','m',0265,0311,'$',0201,'O',0377,0,0377,0,0301,03,0201,'E',0340,0202,'I','$',0221,0333,'i',022,'H','5',0266,0341,'$',0202,'I','=',0266,0332,0344,0222,'A',07,0377,0,0377,0,0366,06,0377,0,';',0377,0,011,'$',0222,'H',0255,0267,'I','$',02,0333,'N',0222,'I','$',0236,0333,'m','r','I',' ',0227,0377,0,0377,0,0360,0305,'?',0365,025,04,0222,'I','$',0366,0332,'$',0222,04,'-',0261,0311,' ',0222,'O','m',0266,0271,'$',0222,'A',0177,0377,0,0376,'8',0377,0,0377,0,'0',0222,'I','$',0222,'+','m','r','I',03,0366,0333,'$',0222,011,'#',0226,0333,'|',0222,'I',02,0337,0377,0,0377,0,0253,0377,0,0377,0,'p','I','$',0222,'H',0325,0266,0311,'$',0202,'{','m',0262,'I','$',0223,'K','m',0330,'I','$',0202,0277,0377,0,0377,0,0377,0,0377,0,0376,0270,'$',0222,'I','$','&',0333,',',0222,'A','%',0266,0363,'$',0222,'@','/','o',0251,'$',0222,'H','=',0377,0,0377,0,0377,0,0377,0,0364,0300,0222,'I','$',0222,013,'m','J','H',' ',0204,0333,'M',0222,'I',' ',022,'I',010,0222,'I','$',033,0377,0,0377,0,0377,0,0377,0,0370,'"','I','$',0222,'I',0225,0266,0331,'$',0220,'M','m',0264,'I','$',0222,011,'$',0220,'I','$',0222,013,0277,0377,0,0377,0,0377,0,0366,01,'$',0222,'I','$','2',0333,'D',0222,'H','$',026,0333,04,0222,'I','!',0251,'+',04,0222,'I',' ','W',0377,0,0377,0,0377,0,0215,'$',0222,'I','$',0222,'[','m',0202,'I','$',022,'c','m',0242,'I','$',05,0333,'i',0202,'I','$',0222,'c',0377,0,0377,0,0377,0,0362,0222,'I','$',0222,'H',0275,0266,0271,'$',0222,011,'5',0266,0326,0244,0222,05,'m',0266,0311,'$',0222,'H','4',0377,0,0377,0,0377,0,012,'I','$',0222,'I','$','6',0332,'<',0222,'I',04,0220,'[','m',0362,'I',03,0266,0333,0134,0222,'I','$',0200,'O',0377,0,0376,'A','$',0222,'I','$',0221,0273,'m','J','I','$',0202,'I',0275,0266,0345,'$',0202,0373,'m',0204,'I','$',0222,'@',0372,0352,'4','p',0222,'I','$',0222,'E',0255,0266,'Y','$',0222,'A','$',0202,0333,'n','R','I',047,'$','4','$',0222,'I','$',027,06,0232,0342,'I','$',0222,'I',' ',026,0333,0224,0222,'I',' ',0222,'C','-',0264,0231,'$',0201,'$',0244,0222,'I','$',0222,'@',0,0222,'I','$',0222,'I','$',0206,'[','n',0362,'I','$',0220,'I','$','2',0333,'x',0222,'A','$',0202,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,')','m',0264,0211,'$',0222,'H','$',0222,01,'m',0265,0311,'$',0202,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'H','-',0266,0334,0244,0222,'I','$',022,'I','%','V',0333,'g',0222,'@','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','"',0226,0333,'e','2','I','$',0222,011,'$',0222,'O','m',0266,'y','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0226,'[','m',034,'y','$',0222,'I',04,0222,'I','&',0366,0333,0177,022,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0202,']','m',0266,0225,'$',0222,'I','$',0202,'I','$',0222,05,'m',0266,'s','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'A',0205,0266,0333,'d',0222,'I','$',0222,011,'$',0222,'I',05,'V',0333,'l',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','(',0226,0333,'i','"','I','$',0222,'I',' ',0222,'I','$',0220,'u','m',0266,0276,'d',0222,'I','$',0222,'I','$',0222,'I','&',0272,0333,'m',0266,0311,'$',0222,'I','$',0222,'I','$',0222,'I',' ',0366,0333,'m',06,0311,'$',0222,'I','$',0222,'I','$',0331,0233,'m',0264,05,'$',0222,'I','$',0222,'I','$',0222,'I','$',0222,031,'m',0266,0332,0320,022,'I','$',0222,'I','$',0314,0373,'m',0266,0324,0254,0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','#','.',0333,'m',0266,0307,0332,0302,'H',';',';',0261,'m',0266,0332,'r',0222,'I','$',0222,'I','$',0222,011,'$',0222,'I','$',0222,'I',015,0266,0333,'m',0264,036,0263,'&',033,'m',0266,0333,'m',0312,'I','$',0222,'I','$',0222,'I',04,0222,'I','$',0222,'I','$','A',0233,'m',0266,0333,'m',0266,0333,'m',0266,0333,'m',06,'I','$',0222,'I','$',0222,'I','$',0202,'I','$',0222,'I','$',0222,'I',0201,'v',0333,'m',0266,0333,'m',0266,0333,'m','.',031,'$',0222,'I','$',0222,'I','$',0222,'A','$',0222,'I','$',0222,'I','$',0222,0300,0235,0266,0333,'m',0266,0333,'m',' ','m','$',0222,'I','$',0222,'I','$',0222,'I',' ',0222,'I','$',0222,'I','$',0222,'I','$','W','x','E',0266,0333,'l',0372,'?','$',0222,'I','$',0222,'I','$',0222,'I','$',0220,'I','$',0222,'I','$',0222,'I','$',0222,'I',' ',0333,015,0266,0,0251,'$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0200,011,'$',0220,'H','$',0222,'I','$',0222,'I','$',0222,011,'$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'O',0377,0304,0,'-',021,01,0,02,01,02,05,01,011,01,01,0,03,0,0,0,0,01,0,021,'!','1','A',' ','0','Q','a','q',020,'@','P',0201,0221,0241,0261,0301,0321,0360,0341,0361,'`','p',0200,0377,0332,0,010,01,03,01,01,'?',020,0377,0,0344,0300,'V',014,0357,047,'y',';',0311,0336,'D','L','?',0370,02,0324,0236,03,'(',0315,'5',0256,04,017,0271,06,'_',0251,017,0250,'j','`',0277,0214,'(',023,'[','-',0237,'i',0374,0357,0356,01,'+',0242,0217,0336,0347,0362,0237,0211,0374,0247,0342,0177,')',0370,0237,0310,'~','#',0207,0327,0263,0373,0207,0211,'N',0252,0375,0303,'H',0235,'n',0377,0,'Q','W',0247,0260,0337,0322,012,'b','i','N','_',0204,'L','%',027,0244,'p',022,0275,0354,012,0242,'=','N',0235,'k',036,'a',07,0206,0311,0371,0351,'+',0215,0235,0177,'M',0240,'4','~',0,'}',0275,0200,025,026,'t',0332,'X',0344,'%','X','S',0360,0254,'}','%',0221,'7','A',0310,'|','u',0372,'K',0252,0206,0371,'c',0274,'@',0344,'u','=',0342,'Y',0351,0322,'P',0247,'k',0364,0215,025,'}',0362,0372,'i',0367,0200,'@',0240,0333,0331,0324,02,0333,0325,'?','8',0240,0266,'4','>',0177,0270,0326,'`',0353,0267,0273,'K','=',':','J',032,016,0215,'~','=','!',0301,0367,'n',0371,'}',0261,0330,0223,'g','2',0305,0273,0177,0314,'G','"',0367,031,'>','~',0351,032,'V',0262,0227,0307,'6','r','<','m',016,'w','U',0226,0371,'g',0375,02,0177,0320,047,0375,02,035,'I','v',0263,0226,013,0244,0267,'I','n',0234,0367,'b','M',0234,0300,0232,':',';','>',':','K',020,'_','G',0334,0302,';',0362,0320,0230,0272,0246,0134,0236,07,'X',0235,'i',0272,0313,'}','K',0202,0212,0211,'.',0204,0360,'-','O','0',0303,0330,0372,0211,'H','w','C',0357,027,05,'M','C',',',0245,0207,0256,017,0246,'a',0200,035,025,'_',0231,0244,'v','Q','{',0200,'O','H',0277,03,0346,0177,0336,'`',015,0375,'f',04,014,021,0205,0313,0350,0272,0217,'=',0267,025,0366,0200,'n',';',034,'}','e','3','e','l',0243,0347,014,010,'t',0247,0361,0257,0323,0226,0364,')',0363,'<',';','E','w','k','[',0216,0361,025,012,'}',0304,'H',0355,'f','E',0335,'7','?',0250,'A','@',0240,'1',0235,017,',','t',0255,'-',0235,0221,'4','b','S',0244,0247,'H',0266,022,025,'G','H','_',0216,0245,0332,'Y',0273,0332,0301,0363,0336,'Z',010,'{','k',0363,0212,'X',0236,0361,'W','^','h',0245,0211,0321,0225,'A','K',025,0221,'(',0230,0372,0344,'|',0240,0345,013,0246,0217,0313,0365,0311,0327,020,06,0276,0246,0317,0303,'h',0344,0320,0327,'g',0307,0270,0,0255,':','M',0327,'O','o',0365,0350,0366,'c',047,0302,013,04,0240,016,'+',0350,0220,'8','}',0222,0331,027,'h','z',0217,0267,0340,0300,0215,0364,0317,0306,02,'%',0217,'#','/','f',0316,0347,0210,015,'e',0310,'4',0256,0374,0312,021,'=',0342,';',0316,0354,0321,036,'v','l',':',0273,036,'X',0,0357,0376,'A',0333,0327,0372,0273,'M','?','n',0205,'2','D',0216,0341,'d',0224,'+','v',0316,0217,0207,0221,0246,01,'#',07,'L',0377,0,023,0317,' ','u','M','.','i','S','t',0227,'o','-',0226,0304,'0',0313,'_','1',0216,0266,0356,0301,016,0206,'w','w','_',0327,07,0365,'v',0232,'~',0340,0205,'V','N',0267,04,'z','v','`',0355,032,0325,0251,0306,0340,0354,'p',0216,0362,0360,0317,'S',0371,0247,021,0263,'~',0343,'P','!',0220,'r',0361,0326,0335,0330,'!',0320,0316,0356,0353,0372,0341,0376,0256,0323,'O',0334,'P','5',0207,'I',0224,03,'M',0217,0237,0334,02,'%',0217,023,0203,0261,0302,';',0313,0303,'=','O',0346,0234,02,'h',0326,';',0267,0215,025,023,' ',0362,0354,0340,0241,0310,'c',0221,0273,0372,0342,0376,0256,0323,'O',0334,'p',0214,0221,0246,0317,0361,'<','A',0347,'M',0317,0354,'q','8',';',034,'#',0274,0311,0213,0305,'m',0331,0364,'9','%',0217,030,0134,0247,0226,'Y',0231,'(','&',0230,0272,0257,'N',0327,0367,0342,'p',0364,031,'W','i',0221,'Q',0333,0335,0320,0355,'4',0375,021,'F',0307,0252,0205,0255,'A',0241,0355,0360,0264,0263,0344,'L',0275,0206,0246,0347,0236,025,0254,0260,0343,'H',0341,'%',0346,0310,0327,'r','-',0300,01,'G','(','/',04,0250,0315,'i','v',':',0376,0270,0234,'=',06,'U',0332,'V',030,0352,0377,0,'4',0364,0323,0364,0270,'D',0350,0266,0342,0345,'R',036,'(',0262,'=',0301,'c',0255,0271,0262,'C',0251,0235,0315,0307,0365,0300,0234,0271,0217,036,'"','P','i',0310,'{',0235,'!',0232,'9','l',03,0270,0367,0355,0,0201,'A',0305,'R',0315,'A',0273,0365,0353,0247,0351,'m',0325,'4','Z','0','*','a',0223,'"','*','+',0334,'6',':',0333,0233,'$',0321,'|','d',0350,0364,0364,'x',0363,'-',010,'^',0344,013,'Q',06,0203,0226,'G','Z','T',0332,';','^',0356,0377,0,0347,025,'h',0333,'Y',0267,'h',0252,0267,0327,'O',0324,'Y','/',0353,014,0243,0355,025,0245,0356,':',0214,0263,'P','t',0256,0260,0204,0353,',','c',0325,024,0250,0326,'*',0266,0361,0273,0242,037,'2',0254,0275,0341,0241,0327,0343,0304,0261,'p','b',0266,':',0304,0253,'k',0301,0247,0350,0325,021,04,0331,0350,036,0303,0356,'8','.','X',0352,0362,'v',0224,0325,0326,';',0267,0214,'-',0242,'R',0346,016,'w','q','v','!',0305,'A','G',0203,0205,'y',0300,0301,0325,0330,0217,'1','V',0373,035,'8','t',0375,'(','=',0221,'F','0',0316,0313,'+',03,0334,'p',0324,'+',030,0334,0212,0324,02,0212,0345,0205,0264,'J','b',0335,'|','l','q','%',0207,0344,'w','x',0264,0375,',',':',0247,'r','w',' ',0320,'}',0307,'(',026,0303,'g','b','t',0322,035,'V','x',0226,0355,0241,0232,'9',0210,'#','{',0300,0200,'A','A',0302,'+',0262,0373,0206,0354,'U','[',0305,0247,035,'5','#','7','R',0376,0323,0307,'<','q',0277,'C',0334,'u','*','C','}',0351,'b',0262,0334,0260,'A','G',0314,06,0317,'U',0250,0205,'P','s','@',')','g',0370,'m',0363,0341,'|',0250,'-',0360,'G',0223,0252,0217,06,0234,'z','p',016,0304,'H',014,'3',0266,0235,0242,'3','c',0334,'Q',0311,0335,0262,0202,0313,0337,0211,0324,0264,0256,'4',0202,'s',0312,0316,0270,0212,0210,020,'b',036,0370,0177,0325,0223,0226,'/',']',020,0362,0240,0243,0301,0303,'D',0275,0177,0347,'^','F',0237,0245,0350,0231,0372,0276,0211,0372,'+',036,0341,0206,0306,047,0244,',',010,0365,'u',0207,023,'3','Z',0353,035,' ','d',022,0231,025,'X',0207,0316,'a',020,0352,'y','?',0316,']','a',0301,0247,0227,0374,0341,05,'J',015,'b',0371,0201,0243,0301,0247,'#','O',0320,024,'a',0202,0235,0224,'N',0225,0373,0202,'Z','-',0226,'=',013,'1',01,0232,'Y',0234,0306,'`','a',0300,0265,0,'!',0225,0264,0203,0204,0313,0301,0350,0206,',',026,'y','3',0366,0210,0313,'i','W',0310,05,'Q',015,0222,0262,'<',0277,0347,012,025,'V',047,0215,0342,0333,'o','#','O',0321,024,'n','z',0253,'~',0207,0270,'*','4',0213,'m',0301,'U',0264,0275,'8',0201,0233,',','X',0270,'#',020,0134,0315,'I',0252,0225,'Q','K',0,'@',0242,0217,'L',':',0304,0254,0311,0263,0303,'.',0271,04,'u',0330,0326,01,'4',015,'<','p',0351,0255,0325,'w',0337,0223,0247,0351,'k','V',0261,012,0355,0356,020,0356,0214,0213,0350,0304,0235,'T',0345,'h','h',0226,0217,'5',022,0311,0335,020,0344,024,'b',034,'=','8',032,0305,0206,0323,0275,0232,'}','9','6',026,0237,'{',037,'N',027,0317,05,'~','Q','K',0312,'^','N',0237,0242,'(',0330,0212,'F',0263,0307,033,0364,'=',016,0277,'l',01,'l',0306,032,'z','Y','X','2','b',0322,0304,'p',014,0301,'-',0252,0,014,0312,036,0203,0373,'[',0343,'O',0353,0363,0303,'Q','5',0257,0311,0267,0327,0222,'v',0227,0364,'+',0376,0360,0340,01,'(',0356,'n','}',0271,'Z','~',0210,'%','>',0251,'/','b',0275,036,07,0266,'T','z',0,0357,'i','j',0261,014,0264,'5',0306,' ','p',0326,017,'P',0263,'h',0,'Q',0351,0241,0200,'J',0352,0355,0365,0341,06,0266,047,0314,0251,0236,'.',0311,0365,0343,05,01,',','+','2','N',0357,0371,0134,'4',0255,0353,'=','3',0236,'V',0237,0245,0327,'D','U',0263,'-',0227,0232,0370,0304,0332,0335,'W',0266,0300,0321,0356,'}',030,0232,0270,'f',014,032,'F',')','s',01,'0',0365,'B',0240,0365,'b',0224,0241,0304,'0','t',03,0363,'/',0205,0312,0260,0263,0303,0307,0236,0252,0301,0365,0227,0376,0300,0371,025,0302,0345,'X','Q',0340,0345,'i',0372,0341,'q','=',0315,'"','[','g',0266,'@','-',0213,'V',0336,0205,0331,'Q',013,0275,'.','.',030,0333,',','P',0265,0252,'P','(','|',020,0250,'I','a','Y',0202,0367,'?',0312,0341,'{','}',',','x',0307,030,0273,'b','#',0234,0360,033,':',05,0371,027,'4',04,'G',0353,0312,0323,0364,0272,0352,0217,'a','l','f',020,0262,'8',']','O',0266,'U','c',0350,0312,016,0364,0345,'B',0263,',',0247,0202,' ',035,'F','e','e',0370,0235,06,'R',0376,'?',0363,0205,'O','0','S',0341,0257,032,0211,0320,0247,0220,0341,0325,0250,0177,0134,'~','b',0261,'y','Z','~',0232,0266,'c',0264,'T',0323,'_','l',0212,'L',0355,0364,0352,0216,034,'Y',0210,0307,'O','@','w','G',023,'0',0300,024,'q','m','I','_',025,0327,013,0203,0311,'W',0307,'_',0267,031,0241,0221,'o',0234,034,'=',015,'K',0303,0377,0,'9','z','~',0232,0224,'e','0',0363,011,'P',0334,'e',0227,0212,0366,0250,0205,0261,0275,047,'J',017,'N','g','M','`',0224,'L',0312,'w',0210,0,030,024,034,'a',035,'U',0374,'2','p',0211,013,0244,'_',05,0361,0375,'3',0356,'p',0257,0324,0205,'w',0253,0276,'^',0237,0246,0357,020,0261,0372,0302,'j',0232,023,0235,'(','k','5',06,'%','4',047,0223,'V','>',0210,0366,0240,'K',0301,'+','V','5',0203,0257,0242,016,03,'0',0254,'y',07,0207,'V',07,'u','8',0177,0237,0271,0307,0364,0317,0271,0303,0365,0337,0261,0313,0323,0364,'W','D',0352,0211,'b',0225,'#',' ',0346,'K','Z',0372,'6',0237,024,0353,0350,0356,0310,'<','p',0337,01,036,'*',0206,0251,032,0262,'>','h',0300,'k',0351,'r',0360,'+',0233,037,0317,0334,0343,0372,'g',0334,0341,0372,0357,0330,0345,0351,0372,'/','E','G',016,0331,0227,0257,0234,'[',017,'.',01,'l','f','*','V',0360,0340,0340,0331,'|','4','-',0236,'2',0134,'6','g',0304,',','-',026,0201,0254,'m',014,'J',013,'t','D','@',0303,'4','s',0243,0371,0373,0234,0177,'L',0373,0234,'/','s','_','R',0377,0,0347,'/','O',0323,'^',0314,0300,012,0346,0310,'c',0306,0254,'2',0235,0341,0255,'I','u',0272,015,0326,'.',014,'|',0324,0333,0254,0261,0273,'D','%','s',0257,010,0134,0251,'B','k','?','*',0341,0376,'~',0347,032,0204,0317,0332,0317,011,'Z',0177,0213,0345,0351,0363,0355,021,'*',0276,'F',033,'p',035,0214,'{',030,0342,'i',017,021,0274,0255,0274,'1',0255,0243,0221,'e',0225,'e',0313,'.',0177,0325,'p',0233,'7','C',0346,0377,0,0234,0177,0315,0333,0204,0257,0227,0376,0216,'^',0237,'>',0310,0227,034,0212,0210,0251,'~',0213,'E',0314,'3',0326,0234,0334,'%',014,0306,'f','H',047,'N',020,01,0202,'P',0243,0226,'*',015,0237,'~',037,0340,'w',0343,047,'U','m','>','8','i','b',0324,'+',0275,'5',0365,0202,'m','"',0362,0264,0375,0202,0256,0374,0201,0254,0314,'?',0246,030,0213,'y',0341,03,014,0303,'(',0207,'W','2',0277,'F',07,0255,031,0341,'a','(','U',0367,0301,0306,0243,0340,031,0370,0300,';',016,'O',034,03,'M',0306,'}',0272,0134,0255,'?','`',0263,'<',0235,'{','H',0134,'#',0334,0361,05,0312,0226,0346,0272,015,0345,0363,0177,0316,023,0320,01,'S',0313,0207,0345,0306,0350,'X','$',0320,'?','B',0277,034,'.','f',0202,'o',0253,0277,0327,0224,0250,'0','l',0276,'+',0271,'+',0262,033,047,0260,'g',035,' ',0245,034,0303,'~',0325,010,'C',0225,'A',']',032,0317,0327,0204,0330,0322,'/',0207,'#','1',012,0236,015,0217,0277,010,0352,0205,'O',0207,'O',0255,0362,0317,'x',0341,0275,'1',031,'r','R',0317,'`',0247,0240,0207,'G','4','D','n','<',031,0216,'s',0300,0205,0320,'2',0370,0231,'_',026,'y',024,0240,'f',0207,'U','?',0316,026,021,0225,047,0223,'$','&','2','5',0312,032,0322,'n',036,0240,0270,0231,0362,0330,'9',0300,0271,'R',0334,0352,027,0256,037,0224,0373,'p',0344,0200,'l',027,0325,0305,'|',0256,'+',027,'~','@',0250,0334,'x','q',034,'c',0203,'I','F',0253,03,0303,0236,'}',0362,0355,0340,0246,0271,0271,'D',012,'(',0346,0241,015,0341,'U',0323,'V',0371,'s',0366,0256,021,0325,012,0337,06,0237,'[',0344,0240,'t',0241,0233,01,037,'=',0370,'H',016,'Z','^',0316,0236,0324,0226,'T',0266,0346,'0',04,':',016,'q',0225,0274,0217,04,0323,'N',033,'5',0207,0346,0336,0274,0362,0256,0306,'U',0236,037,0366,0270,'@',0274,032,0204,0336,'R','r',']',0241,027,0253,'-',0326,'[',0254,026,0214,'R',0207,'2',0303,0226,0326,'T',0267,'=',010,0346,0243,0301,0373,0341,'a',0350,'g',0365,0365,0232,0203,'%',0345,';',0332,'?',0314,0232,'p',0321,014,'a',0345,0327,0343,0310,013,'j',015,'[',0360,0235,0210,0254,0271,'v',0344,0270,036,'S',0330,0351,05,'+',0234,'n','j',0347,0306,0360,'M',0240,03,0206,0260,011,'o',0203,0375,0345,0203,'Z','L',0317,013,0217,0223,0177,0217,011,0235,0243,035,0235,0230,'/',0322,'W',031,0260,'<','`','=',0224,0205,0240,0203,'W','>',0200,'l',0370,'n',0374,'x','p','k','7','3',0320,0354,'s',',',0323,0245,0333,'g',021,' ',0365,0300,0357,0327,0343,0307,0243,0354,'v','6','"',0247,0221,'Q','~','~',024,0215,'W',0240,'k',04,0312,012,'8','E',0257,'a',0371,'b','%','j',0363,027,0212,'U',0307,0323,')','^','F',0274,'&','u',0215,'0','O',0301,0247,'s','n','*',0206,' ','Y',0305,'l',0234,0307,'>',0214,0225,0207,0205,'4',021,0226,0,'(',0347,'"',06,0254,032,0274,031,0354,'m',0363,0341,0323,'Y','f',0266,037,0227,0235,'f',0335,'.',0333,'8',0234,016,0361,'F','S',0244,'V','t',0234,'O',0320,'M',021,0235,0351,0336,0211,'o','+','s',0224,035,'f','B',0243,013,0375,020,0315,034,0201,0134,0366,0355,'j',0314,'.',0223,'M','8','A',0257,0370,026,'"','V',0257,'8',0212,0322,'K','1',0212,0206,0366,'o',0361,0342,0275,0317,0351,'6','z',0370,0367,'u',0311,015,0335,011,'J',0317,0255,0337,0343,0302,0341,0350,'2',0254,'P','6','<',017,'`',0134,0266,0307,'h',0361,'X','Y',0341,0341,0262,0261,'R','v',0202,0222,0363,0235,0273,'>',0354,011,0255,'*','k',0247,0376,01,0333,0213,0374,05,'z','~',0375,0210,0265,0301,0257,'w',0267,0353,0212,0312,0313,0346,'w',' ',036,0365,0272,0236,0352,012,'6',0262,0300,0307,'y',0372,016,0374,'H',0361,0260,';',0277,0344,'M',0255,'/',0330,0252,0340,0226,'0',026,',','C',0277,'_',0217,023,0320,0316,0316,0343,0372,0230,0353,'l',0354,0236,0350,012,026,0262,0316,013,' ',0375,'O','~',047,0212,0202,0337,04,'e','t',0302,'?','?',030,0373,032,'>',0364,0272,0222,0302,0337,'0',0354,0361,'=',014,0354,0356,'?',0250,'.',0303,0320,'h',0373,0233,'e','0','#','T',0233,06,0247,0372,0342,0323,'X','v',0224,'r','4','{','x',0364,0277,'d',0326,0242,0344,0351,0334,0226,'V','+',036,0334,'V','F','>',0217,'R',024,0265,0264,'z','v','}',0311,'k',010,'o',0214,'e',0351,0326,0277,'|','n',0215,0225,'.',0207,'o','1',022,0265,'}',0235,'R',0265,0243,0374,0322,030,'{',034,0211,0277,023,0221,'&',0316,'H',0325,013,'m',0275,'>',':',0307,0245,'I',0356,033,010,0263,'+',0361,017,'g','n',0265,0177,0272,'q',0253,'^',0213,0356,'{',0304,')','k','W',0332,'o',014,0364,0177,0232,'C',017,'c',0221,'7',0343,'C',0134,'x',037,0222,'(',0355,0303,'G',0333,0227,0202,'h',020,'|',0207,'n',0227,0277,0352,031,020,'l','`',0343,'@',0373,03,0356,'{',0315,'T',0301,'}',0256,0201,'W','c',0253,0376,'!',0202,0312,023,0215,0240,'S','f','"',0245,0267,'_',0206,047,'r',':',0236,0324,030,0255,'g',0317,'M',0253,0340,0206,'B','=','Y','~','{','r','6',']','=',0277,0324,0337,'L',037,'m','d',027,0354,0355,0334,0226,'V','_','3',0263,0310,'Z','.',0367,0342,'X','Q',0356,0374,'2',0334,'>',017,'f','.',0204,0364,0206,0217,0317,0253,0340,0201,'<',0313,'/',0371,0311,0316,0245,')',0351,0361,022,0245,0257,0267,0341,'D','k',0261,0204,'A',0271,0372,0362,'Z',025,'6','K',0207,05,0375,'2','|',0277,0354,0263,027,0316,'y',0255,0245,0201,0256,0347,0260,'#','B',0342,'e',0207,'S',0202,',',0177,021,0277,0352,016,'P',0272,0352,0374,0377,0,0134,0227,'C','-',0331,'y',0213,'u','5',0362,0212,0253,'}',0302,'}',01,0271,037,0340,0203,0247,0317,'X',014,0223,'D',0323,0224,0240,'k',0324,0374,0353,'(','Z',0373,'d',0371,':','E',0306,'l','8',0371,0272,'O',0212,0357,'"','4',015,0273,014,'O','*',0342,0321,027,'/','m',0247,'A',0201,032,036,0323,'D','k','[',0277,0241,',',0211,0232,0206,07,0343,0257,0322,'S',0240,'L','Z','[',0361,0274,'}','&',0205,'r',0234,'6',0251,'F',0203,0336,'4',0276,'t',0330,0370,'{',0226,0331,'s',0325,'d',0251,'O','b',0335,0370,'y',0240,0326,'N',0216,0221,0312,'C',0256,037,0202,0177,';',0373,0237,01,0322,0243,0363,'?',0201,0372,0201,'7',0375,'>',020,'`',0373,0337,0270,0232,0333,'k',0213,0373,0334,0317,0237,0200,037,'i','w',0257,'2',0272,0206,0335,'_',04,037,'"',0302,0356,0376,0243,'g',0265,0367,'9','g',0244,0224,0230,'n',0272,0236,030,023,0314,0260,0377,0,0276,0334,0264,']',0357,0304,0260,0263,0335,0370,'#',0264,'.',0376,0353,014,0324,0222,0223,035,0327,'S',0303,'>',034,'_',0333,'H',04,'K',035,0375,0245,'@',')',0265,0333,0362,0226,0252,'/',0361,'D','N',0344,0365,'}',0336,'*',0262,'X',0315,'t','r','|',0245,025,'n',0377,0,'r','~',0240,0242,'7',0321,0303,0365,0307,0326,01,022,0307,0177,'c','p',' ',0326,0322,0376,'Z',0313,047,'|',0242,0377,0,'$','L',0305,0333,'W',0317,'X',0201,0311,0352,0373,0321,'2','U',07,0240,036,'f',0,'_',0227,0333,'Y',0243,0223,0256,0277,'H','d','6',0332,0303,0371,0225,'B',0227,'J',027,0362,0271,'I',0267,' ',027,'H','m',0220,0356,0207,0336,'U',']',0363,0177,'k',0224,'@',0266,012,0277,014,'e',0320,0306,0252,0321,0373,'K','!',0260,'R','`','E','K','W',0253,0357,0302,0215,023,0274,035,'x','w','}',' ',0203,'=',0310,',',0376,'b',0177,'1','?',0230,0237,0314,'D',0341,0227,0261,0244,'*',0360,'w',0224,02,'w','X',0344,'/',0376,0271,0377,0304,0,'-',021,01,0,02,01,02,05,04,02,02,03,01,0,03,0,0,0,01,0,021,'!',' ','1','0','A','Q',0241,0261,020,'@','a','q','P',0221,0201,0361,0301,0321,0360,0341,'`','p',0200,0377,0332,0,010,01,02,01,01,'?',020,0377,0,0362,'e',0204,0247,'Y','N',0262,0235,'e',':',0377,0,0360,036,'Z','@',0271,0276,0241,032,'t',0207,' ',020,0272,0217,0231,033,0261,0177,'s',0346,'~',0347,0314,0375,0317,0231,0373,0237,'3',0367,01,'(','Q','K','/',0367,026,0272,'1','k',0237,0361,023,05,'P',0253,'8','#',0267,0345,0266,0364,030,014,'Y',0367,024,0344,0372,0233,0252,0375,0200,0323,'d',0337,'9','L','Y','1','K','O',0314,026,0325,0376,'D','{','h',0230,0341,'n',0323,014,0320,0370,0213,'m',0276,0335,'[',0242,015,01,'g','S',0361,0334,017,'m',023,0,0337,0314,'Z',0354,0367,0210,0255,'S','9','2',0357,012,0347,'t',0374,'H',013,'f',05,'K',0257,'(',0205,0205,0237,'.','|',0271,0362,0340,026,0257,'x',0212,0325,'3',0232,0244,0267,027,0370,'k','s',0317,'I',0260,0276,'C',0254,0247,'4','C','l',0217,0271,0375,0241,'?',0264,'#',024,'o',0322,'D',022,0230,'J','y',0351,021,032,'}','S',01,'q','P','$',013,'A',017,'L',0324,0274,0317,0207,04,0241,0237,026,'|','X',0276,'k','6',':','}','M',0302,'!',036,0134,032,0362,0372,0213,0323,034,';','U','L',0242,0305,0345,06,0366,0374,022,0202,0330,'I',0312,0365,0203,0226,0356,030,0330,'!',0246,'^','g',0227,0376,0242,'K',07,0313,'?',0270,'g',0367,014,0260,'C',0355,0233,'6',0221,0314,'?','?',020,0303,'X',0305,021,0214,0376,011,0236,0201,0324,'G',030,'Z',027,':',')',0370,0231,0313,0276,0347,' ','8',0134,0375,0202,015,0177,0200,0267,'4','F',0334,'^','^',0204,0263,0310,0214,'u',010,0366,0212,025,0251,'t',0215,'d',0231,'^',0312,0376,'"',013,'o',0264,026,0205,0303,'T','[',0342,021,'L','>','"','&',036,05,0262,0202,0363,']',034,';',0250,0226,0354,'C',0234,'C','v',015,0316,'l',014,033,0342,0334,'i','e','~',0207,0257,'a',';',0337,0215,'}',0377,0,0307,0270,0311,0245,'=','g','"',034,03,017,'I',06,'0','p',04,0332,0316,'c',022,0352,'w',0327,0242,024,0200,0240,0215,'0',0177,021,'t',0372,'E',0366,023,0275,0370,0327,0337,0374,'{',0235,0341,0212,'*','*',0303,035,'u',0212,'6','C',02,0177,0266,0245,'J',0307,'U',0304,'v',0326,0242,'h',0304,'#',034,'7','O',0250,'_','a',';',0337,0215,'}',0377,0,0307,0273,0247,'6','D','.','S',0244,'D','i',0324,'(',0331,014,011,0376,0332,032,0346,'3','W',032,0315,'@',01,0134,'7',0257,0210,0215,0355,0313,'W','a',';',0337,0215,'}',0377,0,0307,0274,0250,'s',0202,'(','M',':',0205,033,' ','5',0303,0321,02,0261,0326,'q',0254,0314,0204,0206,'a',0216,023,0206,0301,033,0240,'j',05,'h',0216,0274,0356,'S',0275,0370,0364,0336,0336,'U',0275,0,0327,0360,0317,0203,0376,'~',0247,0301,0377,0,'?','Q',')',0311,'M',036,0316,'_',0262,':','k',013,',',037,'G','$',0331,'9','|',0210,0316,0377,0,0343,0337,'S','v',0344,'e','"',0324,'+','d',013,'H',0355,0261,0255,025,022,0220,031,0200,'(',0341,0334,0277,034,0365,02,0264,'C',0,0177,0247,0247,'{',0361,0350,0305,')','e',0261,0244,0334,0247,0223,'F',030,'f','0',0344,07,'#','n','u','P','h',0201,032,'R','4',0354,0213,'H',0363,'9',0303,0134,'V',0355,0363,0214,'r','R',0212,'S',0201,'3',03,0230,0257,'q',0376,023,0277,0370,0367,0356,0217,'H',0262,'3',';',0216,0326,0213,'y','u',0241,014,012,034,'>',0276,'"',0333,'n',0243,07,0227,'o',0217,'^',0367,0343,0320,'+',0264,')',0324,0346,0177,'%',0220,0336,'y',0227,0271,0314,'i',023,0251,'e',0374,0225,02,0260,0326,'-',047,0255,'!',0332,012,0326,0305,0330,'-',025,'{','a','j',0330,0272,0344,0312,'o','(',0332,0353,0,'_',0335,0263,0277,0370,0374,03,0243,0212,0271,036,0202,0304,0272,0302,'e',0314,'f','Z',0314,06,'!',0261,0303,'E',0262,'D',':',0203,'U',0343,0224,0333,0327,0275,0370,0365,0263,0267,0222,0224,0364,'K',0241,0363,'U','>','/',0360,0247,0351,0223,'(','`','l',0312,'-','P',0256,0303,0351,0337,0374,'~',05,0266,'0',0332,031,0303,'0','9',0204,'p',016,'c',0275,'p',0200,'b',022,03,0210,0374,0254,0337,'R','r','$',0,0243,'G','{',0361,0350,'S',0213,'*','9',032,024,023,0345,02,'Y',0200,'(',014,0325,']',0252,0,'Y',0327,'}',0243,0311,0377,0,0263,0342,'<',0,'n',0243,0260,'V','=',035,0377,0,0307,0340,0213,'W','1',0302,0306,'J',0343,'Y','9',022,'S','<','K',0221,0273,0264,'D',0255,0335,'"',0271,0220,'@',0355,0247,0275,0370,0364,0257,0216,'S',0260,0200,'/',0300,0357,'2',0255,0371,'d',0241,0203,'K',0204,'U',0233,'o','?',0277,0230,0372,0315,'r','Z','.','c',031,'z','w',0377,0,037,0202,'P',0243,'3','c','Z','*','%','p','s',0,024,'q','0','-',0265,'0',0216,'Z',0273,0337,0217,'B','V',0323,'G','e','C','O',0352,0177,'u',0376,0323,0373,0257,0366,0204,'%',033,0246,0354,05,'g',0321,0337,0374,'~',04,05,0262,0321,0345,0333,06,0337,':',0230,0216,010,016,'%',0232,'n',0355,026,0333,'t',0255,024,'6',0325,0336,0374,'K',0370,0335,0323,'N',011,0234,0363,010,0246,0363,'R','9','M','5',0214,030,0303,0237,'_','>','T',0264,0216,0302,'R',0274,0206,'l','z','w',0377,0,037,0200,0332,022,0207,'1',032,0363,0313,024,0214,'Y','-',0210,0233,'=','X',024,0304,'6','8',0270,013,0215,'$','@',0335,0203,0315,'u',0367,0277,021,'Q',0134,0244,0334,0261,035,0376,024,0210,016,0206,0305,0226,'E','P','l',0253,'7','q',0374,0317,0354,'_',0365,011,0237,0266,033,'.',0222,0376,0351,'}',';',0377,0,0217,0300,':','V',')','[','E',0242,0375,'$',0300,0267,0215,'S',022,0251,'M',04,' ','@',011,0213,0335,014,'N','|','2','s',0221,021,'+','w','O','I',0274,0270,035,0357,0307,0246,0324,'C',015,0351,0232,'|',0320,0305,0340,034,033,07,0375,0277,'k',020,0377,0,0327,0375,'B',0326,'X',027,0260,'w',0223,0252,'~',0375,';',0377,0,0217,'~',',',0243,0206,0307,0241,0264,'B',0265,0302,'@','-','E',0233,0305,'d',0214,0303,'2',0303,031,0254,0264,'N','Y',0341,0322,'N','y',0322,013,0202,021,0274,016,0367,0343,0321,0330,0324,0206,0352,0352,0237,'6',0257,0346,01,0257,' ','T',0252,'/','q','V',036,0216,'9',0317,0371,'o',0361,01,0273,0264,'(',027,'K','V',0233,0320,0376,0275,';',0377,0,0217,'|',0240,'[','*','+',025,'[','a',027,015,0265,012,0347,'E',0251,'%','0',0225,'q',0311,0245,0312,'+',07,036,0213,'$','l',0276,016,0323,0246,0206,'4',0321,0336,0306,'x','=',0357,0307,0246,0366,0362,0255,0350,026,0277,0202,'|',0237,0363,0367,'>','O',0371,0373,0214,'5','"',0300,'P','=','^','c',0323,0277,0370,0367,0310,0201,0314,'g','*',013,')','D','A','l',0270,0344,'%','0',0233,'Z',012,',','J','Y','.',0321,'u',0362,'W',0,'W',0351,025,'r',0351,'+','f','x','.',0367,0343,0321,0206,'7',0210,0332,0354,'R',0376,0256,035,'+',0320,'K','O',0315,033,0206,'t','w',0377,0,036,0365,032,0212,'s',0217,'K','^',0241,0334,0313,0217,0236,'H',0360,0,033,'C','@','B','a',0274,'k',032,'7','_',0276,017,0316,016,0225,023,0316,01,'<',0270,'=',0357,0307,0245,0247,'^',0353,'z',01,0257,0232,'q',031,'a',012,'E','j',0300,0274,'(',032,'1',0267,0247,0225,'-','#',0260,0224,0257,'!',0233,036,0235,0377,0,0307,0271,0277,'R','6',0314,0367,0350,'M',0262,0376,'n','F',0321,0313,'=','0',0305,026,'X',0,0214,0372,02,0344,'t',0375,0263,034,032,'.',0223,'M',022,0230,'8',']',0357,0307,0245,0262,0230,0211,0270,0215,0211,0364,0317,0354,0247,0366,'P','@','K','`',0254,0272,'K',0375,'/',0357,0323,0270,0373,0220,0257,'Q','@',0346,'*','@',0231,'J',0201,07,'<','F',0312,030,0206,0322,'a',0204,0345,'p',05,036,0230,031,0223,'M','W','D',0371,0240,0327,0264,0311,0327,0235,'4',0365,0217,013,0275,0370,0364,'_',')',0270,0351,'m','_',0361,0274,0260,0361,0301,'T',0341,0234,0344,0271,0265,0203,0220,022,0241,0367,013,031,035,0216,'k','l',0254,'f','X',0305,'H',07,'2','C','9','W',0305,0327,0247,0177,0361,0356,0334,'&','9',']',0275,032,':',0365,0341,0227,'|',0276,0350,0244,'X',0206,'#',0327,0347,'R','X','W','-','6',013,0264,0327,0361,'A',',',':',0264,0320,'*',0227,0205,0336,0374,'z','3',0310,'[',0367,0203,0275,'K',0371,0330,'O',0264,0277,0304,0246,'6',0360,'-',01,'E','6','L',0263,0313,'2',0230,024,0373,0325,0372,03,0374,'-',0353,0351,0337,0374,'{',0241,0271,0211,0177,0350,'5',0250,0223,'9','`',0333,'(',0336,0357,'D',0350,'!',' ','4','o','0','u','g','O',0300,'.',0267,0350,'M','6',016,0250,0214,'r','8',']',0357,0307,0243,0355,03,'6',0272,'m','/',0344,0304,'P',0252,'j',0206,0315,0353,'f',0311,'g',0333,'+','6',05,024,027,0232,0203,0207,'z','i','6',0316,0361,0234,027,0242,0341,'N','u',0310,015,0271,0345,'^',0204,0357,0376,'=',0321,0273,'f',':','@',0254,'L','^','Z',0304,0207,'+',0312,0,'f',01,0243,':',0232,0327,'3','K',0364,'z',0325,0203,0236,0223,'K',0254,012,0307,013,0275,0370,0364,0273,0376,030,0257,0344,0354,025,',','B',0364,013,0372,0344,0375,0244,0265,'B','9',017,' ','w',0210,0230,'}',';',0377,0,0217,'r',0201,'1',012,'q',0350,0346,020,036,013,0212,',',0233,0367,'h',0352,0270,02,0215,'E','|',0375,0264,0255,016,'f',0263,02,0335,'t',0220,0236,'Y',0341,0367,0277,036,0230,0321,'6',0252,0232,0375,0253,0370,0251,'n',047,0300,'}',0234,037,0274,07,0324,'#',0325,'f',0337,0305,0227,0364,'_',020,0257,0230,' ','X',0272,0226,0256,0254,'1','|',0217,'N',0377,0,0343,0334,013,',','u','L','z',034,0330,'1',0231,'Q',04,'m','K',0330,0312,':','0','C','c','Y',047,0234,'t',0215,0355,'}',0366,0226,'J','r','8','}',0357,0307,0245,'@',0313,0235,0373,'_',01,'*','%',0344,'5','9',0356,0352,0354,'*','s','%',0330,0236,0255,'_','`','P',0376,'n',02,' ',0233,'(',01,0244,0272,0,0272,0134,0357,0237,'N',0377,0,0343,0210,0203,'y',0271,'1',026,0256,'n',0216,011,0260,'s',035,0224,033,022,0335,' ',0331,0201,'C','6','h',0251,033,0300,'X',0340,'6',016,0272,'{',0315,'}',0366,0236,0307,0207,0336,0374,'z','U',0216,0372,0357,0177,0221,'+',0231,'?',035,0343,'0',0270,06,0333,'2',0365,0311,'}',')',036,024,027,'C',0275,0201,03,0341,'M',0375,0372,'w',0377,0,034,'%',015,0341,'y','c','l',020,0270,'|',0320,0333,'x',0202,0223,'[',0205,'c','#','`',0226,0224,'j','#','6',0216,'S',0264,':',0306,'"',0242,'a',034,0300,0242,0270,035,0236,0236,0363,'_','}',0247,0261,0341,0367,0277,036,0231,'V',0376,'(',0376,0262,0206,' ',0206,0373,'/',0345,0265,0332,'W','#',0302,0216,0304,0251,'*',0203,'n',026,0320,05,012,'9',0267,'z','w',0377,0,034,020,'6',0313,'G',0314,0270,'=',')','i',0203,'e',0351,'#','l',0270,'I','s','.',0205,'N',0203,0313,0335,'F','l',0332,'(','T','S',034,'B',01,0301,0354,0364,0367,0232,0373,0355,'6','}','G',017,0275,0370,0364,0266,'H',0345,0233,0270,0277,0252,0237,'$',0301,037,'.',0216,0377,0,0343,0203,'o',0275,0257,03,024,031,0272,'I','A',0314,01,'[',0261,0313,0320,'#','-',';','"','0',0303,'@','p',0250,0335,'q',0247,0274,0326,0235,025,0323,0331,'<','>',0367,0343,'_',0177,0361,0300,'t','D',0255,0326,'4',0334,0252,'N','t',013,'(',0311,0234,'J',023,026,0370,0231,'1',0,0250,0350,0304,'"',0324,03,'F','x','~','C','K',04,0345,0235,'~','C','K',0257,0263,0207,0336,0374,'k',0357,0376,'8',033,'&','k',0201,0220,0363,0225,011,0350,0251,'l',0271,0277,0324,'M','>',0232,0245,'1',0347,'}',024,'2',016,033,0307,0363,0247,0266,'u',0221,0277,'O',0330,0220,0201,'8',']',0357,0306,0276,0377,0,0343,0203,'a',0300,0261,010,'e',0237,'B','|',0306,':',0256,0242,03,020,0200,0342,030,0216,0306,0220,',',0364,0327,0212,'/',022,0223,016,0237,0243,0270,']',0357,0306,0276,0377,0,0343,0203,'{',05,0223,0201,0230,0361,'2',04,0304,034,'j',047,'"',05,'3',0305,0261,0364,0306,0232,0336,'k','K','*',':','|',0235,';',0323,'#',0302,0336,']','|','E','W',0270,0326,0256,'M',031,'8','"',0247,06,'A','F',0310,05,'.',0273,01,0300,024,'q',011,024,0317,033,0272,'X',0227,0236,'x',026,0210,'a',0323,'t',0371,0360,0220,024,0307,'~',0275,0343,'4',0363,0275,'?','&',07,' ','@',0177,015,'p','E',0225,'1',0276,'|','s','8',0304,04,'8',0266,0343,'w',032,'i','p','@','0',0254,'p','0',0237,0326,0226,0371,0330,0200,047,011,011,0223,'q',0210,'!',0247,0252,0247,0367,0363,0373,0371,'_',0376,025,0225,'}',0361,'p',0357,'!',')',0256,'+',034,01,')',0236,'5',0250,'}',0351,0373,0316,05,'c',0201,'N','7','3',0252,0334,0356,'c',0335,033,0221,0310,0361,'l','k',0200,'(',0342,0255,027,0250,0233,0247,0313,0202,'L',0271,0305,'S',0223,0246,0335,0354,0373,0241,'e','q','@','g',0241,04,0207,032,0334,'n',0343,'U','z',0337,'.',025,'1',0347,0244,'G','"',01,036,'|',024,'D',017,0333,0365,'>',0340,'W',023,0347,'G',0316,0212,0273,0374,015,0314,0234,'<',0307,0177,0327,022,0352,0221,')',0247,0204,0307,03,05,'3',0307,'z',';',032,'P',0216,'l',021,034,0270,'A','P',0311,0223,'U',0207,'+','n',06,0307,0201,'_',0342,'"',0352,0264,'9',06,0226,024,017,0354,0351,'9','+','3',0360,0363,0341,0223,'(',0356,'<','%',0253,0204,'(',0343,033,021,0323,'y',0322,'J',0314,034,'4',05,'2',0251,0333,'K',011,0312,021,0355,':',0331,'Z','p',0375,0353,'`',0360,0201,0303,'K',0227,0340,0340,0221,'9','@',03,0217,'W',0264,'o',0250,'H',0346,0347,0211,0324,03,0306,0253,0273,032,0373,'s',0316,0276,0377,0,0343,0210,016,'1',0335,'k','P',020,01,031,0343,0232,'(',0351,0274,0351,0253,0275,0214,0260,'+',034,'B','e',0316,'#',032,'L',0356,0220,0316,0240,0250,0356,0312,0373,0345,014,0312,'J','M','(',0215,0306,'W','K','3',0256,'z',0361,022,0345,0241,0346,'[',021,0360,0305,034,0245,'0',']',0210,0225,'B',0351,0271,014,0321,0306,'Z',0313,':','|',0325,'R',0273,0271,0343,'P','n',036,'5','=',';',0220,'A','f',0244,0324,'s',0216,'_',0372,0226,'@',0372,',',0237,0330,0247,0366,')',0226,0247,0311,'R',0240,0221,0266,0363,0355,0200,012,'8',0250,024,0305,'-','"',0374,0243,0310,'L',0227,'S',0225,0315,0230,0200,033,'q',0362,'.',']',0265,'U',0336,0306,'X',025,0216,'0',012,'b',0257,'#',0266,0252,'7',0347,0226,0272,031,'G','I','G','I','A',0370,'D',0355,0365,023,0256,']',' ',0252,' ',010,0356,0357,0354,011,0303,'f',0321,022,0267,'4',0230,'L',0220,'h',0330,0374,'c','=',0242,'[',035,0215,0215,'X',0217,06,0336,0312,0345,'?','z',0202,0246,'a',0263,0277,'?',0305,' ','[',035,0246,035,0365,011,033,'s',0204,0366,0217,'d',0240,0260,0313,013,'g','m','b',0315,'?',0342,024,026,0307,'k',0377,0,0266,0244,'@',0335,0201,0327,'=',0243,0267,0277,'(',0224,'2','k',027,'d','y',0351,0370,'d',013,'A',033,0224,0362,0326,0370,0377,0,'^',0330,'9','B','8',014,0232,0202,0245,'0','=',0217,0302,036,0276,010,0247,'&',0262,'3','a',0267,0314,012,0301,0355,0304,'?',0372,0242,'#','N',0244,'V',0251,0227,'W',0354,0200,',',0374,013,0342,0357,0242,0134,'O',0343,'_','%','N',0360,'*','m',0356,'L','!',0376,0321,021,0247,'Z','u','w',0321,'-',0317,'=','=',0363,05,0242,'-',0277,'t','E','j',0335,'{','h',035,0340,02,0240,0367,'a',0377,0,'c',034,035,'&',0267,'-',0246,047,'5',0363,'2',0241,0356,0224,026,0313,0214,0210,0215,0377,0,07,01,0263,027,0224,04,'t',036,0364,0252,'b',022,0206,'x',027,02,0230,0206,017,0346,01,'v','{','a','m','T','>',0262,'F',0262,0243,0247,05,0301,0267,0322,0,'(',0367,0351,0307,0371,0211,0254,0276,0256,013,0226,0323,025,014,'S',036,0255,0363,012,0265,'~',0301,'C','y',0327,0311,0204,0253,0345,0234,0300,0340,0327,'+','g',';','|','?',04,'-',026,'M',0337,0321,034,05,'<','$','m',010,'=','A',010,'Q',07,0266,0237,'r',0226,0214,0312,'u',0324,0241,0274,0331,'B','9','F',015,0212,0262,0330,0242,'o',0237,015,0353,'b','+',0207,0341,'M',0303,'=','f','Y',',',0353,0305,'0',0331,022,0261,'~',0347,0314,0203,'Q','o',0271,0360,0277,'S',0341,'~',0242,0334,0323,'z',0331,0270,'/',0213,'T','.','V',0347,'`',02,0217,0303,0240,')',0226,'9',021,0254,',',0353,0357,0252,05,0261,034,0237,0304,0245,025,0370,0264,05,'2',0307,'"','g','R',0317,0210,0224,0323,0356,'U',0253,'&',0177,0322,'&',024,'?',037,0274,032,0244,0306,0177,'g',0240,0311,'M','>',0315,'z','f','g',0225,'"',06,'G',0314,026,0205,'~','Q',07,'y',0266,'s','|',010,05,0261,0216,0323,'y',0271,034,'$',0330,0270,'e',0254,'T',0262,0210,'[',0376,0271,0327,'O',0314,026,0205,'~','p','J','H','}',0214,0370,'q',0233,012,0237,'g',0356,'}',0237,0271,0366,'~',0347,0331,0373,0201,0225,')',0330,0304,'l',010,01,0267,0377,0,0134,0377,0,0377,0304,0,'+',020,01,0,02,01,02,04,06,03,0,03,01,01,0,0,0,0,01,0,021,'!','1','A',' ','0','Q','a',020,'q',0201,0221,0241,0360,'@',0261,0361,'P',0301,0321,0341,'`',0377,0332,0,010,01,01,0,01,'?',020,0377,0,0341,'B',0270,']','?',0370,0267,'O',0376,'-',0323,0377,0,0213,'t',0377,0,01,'q',0340,0206,0310,031,0374,0134,0376,'.',0177,027,'?',0213,0200,010,0210,0350,0217,0341,0326,'o',0360,0235,'?',')','C',0134,'B',0246,0332,0253,0265,'k',031,'%',',','[',017,0322,011,010,0302,'P',0265,036,'/','k',024,0275,0314,0355,')',0255,0225,0273,'6',0360,'$','k',0244,'q','1',0332,0177,'s','?',0271,0237,0334,0312,0377,0,0353,0225,0263,0253,0265,0242,032,'S',0252,0201,0334,0212,0221,0327,'D','z',0304,'L','F',016,0341,0251,0354,0240,'j',0322,016,'.',0220,'"',0275,01,0200,'Y',0275,'U',0376,'{',0247,0343,0324,0227,'Q','Q',06,014,0244,06,0306,0247,'h','_',0351,'Y','R',0263,023,0247,0220,'6',0333,'A','b',0362,022,'.',0241,'u',0301,0370,026,'M',0324,0224,0352,0304,'B',0302,0367,0247,'c',')',0224,0215,0254,0256,0330,0377,0,0311,'P',0217,0257,'n',0207,0366,'g',0344,'C',0207,0333,0363,035,'?',024,'1','-',0247,'&','-',0335,035,015,'5','}','a',0213,'U',022,0263,'`',0270,0274,0301,'M','V',0353,0257,0343,'9','d','X',032,'F','=',0246,';','n',']',0326,'`',012,'m',0330,0273,0325,'<',0273,'@',0223,014,'0','Y','p','l',0263,0362,']','?',014,'1','-',0247,'&',0240,'b',0374,'6','M','|',0317,0374,0213,'4',0232,'@',031,0305,'o',0257,0376,'~','b',0235,0233,026,0275,'z',0372,0305,0352,0216,0310,0316,0247,0267,'X',034,'s','%',013,0322,0240,0331,'g',0343,0272,'~',011,'2',0325,':',0,0207,',',0323,0346,0321,0322,' ',0302,'i',0,'g',025,0276,0277,0371,'?',0276,0237,0337,'O',0357,0241,030,';','E','D','J','y','J',0316,032,'R',07,0322,'}',0223,0375,'O',0262,0177,0256,'z',0235,0233,026,0275,'z',0372,0300,0201,'t','6','s',0327,'w',0363,'h',03,030,0260,0256,0305,0323,0370,0316,0237,0200,'f',0273,030,0273,0241,015,0266,'j',0207,0322,0321,0247,0326,'!','b',0212,02,0243,'4',0236,0264,0314,0376,0377,0,0376,'O',0357,0377,0,0344,0330,0216,'Z',0204,'h',04,0302,'&',0262,0353,'M',0214,0364,'v','c',0257,'J',05,'#',0342,0265,'x','d',0342,0256,0265,0322,'P',020,'5',0240,'*',0363,'0',0,0320,0254,0356,0274,0313,'j',0240,0360,0246,0260,014,0303,'7',0225,'u','<','4',0251,'s',0,0313,0326,'}','c',0375,'D','J',0244,0247,0370,'F',0267,'e','=','P','7',')',0247,'#','}','`','K','7',0275,0220,0222,'4','k',0326,0250,0300,0216,034,0341,0326,0263,011,'F','E',0320,037,'(',0345,0221,'J',')',036,'R',0226,'I',03,0346,0301,0362,0250,0250,0320,'_',0303,0336,036,'+',0344,'K',037,0304,'t',0347,02,0321,0263,0240,';',0262,0246,'@',026,036,0244,'N','`','4',0267,0255,03,'h','v','D',0320,0,0213,'C','X','Z',0363,0275,0240,0303,0342,0200,'I','G','B','Q',0320,0235,0254,' ','$',026,'a','f',036,0325,'l',0304,0270,0265,033,'3',016,0234,0307,0331,0251,0365,0226,'K',0225,0362,'[',0323,0333,0264,'P',0230,0272,0271,025,'U',')',0270,0342,'W',']','.',01,0,01,0240,'s','t','T',0271,0207,0316,017,0333,010,0301,0205,0333,'r',0266,0364,0277,'.',0361,0200,0322,034,0324,0353,0363,034,0262,')','E','#',0310,'y',0237,'f','G','Q','4',0200,033,0344,0267,'n',0353,015,0245,0230,0233,':',0234,0264,'j','j','|',0373,'3',0370,0351,0374,'T',0376,'*',0177,025,'?',0212,0207,'T','6',' ','$',0253,036,'i',0312,0205,'D',']',027,0210,0215,'l',0351,0336,0264,0307,',',0253,'R',0325,0353,'-',0310,'1','t',0267,'?',0250,0340,0227,'x',035,'/','0','.',06,0,'h',034,'N',0253,0206,032,'l','a',',',0360,01,'D',032,';','~','&',0272,'h','8','}',0342,023,'b',035,0255,0374,0204,0350,'h',024,014,0373,0355,0322,' ','^',0245,'C',0321,0344,021,'n',0213,024,0273,033,'2',0252,020,04,032,0346,0216,'C',04,0,0353,013,04,'l','c','X',';','B','h',0307,0243,'R',0315,014,0352,0263,0352,023,0210,0304,0251,0203,'Y',0260,'0',0274,0303,025,04,036,'f',026,'A','n','^',0304,'J',0230,015,'i',0352,'<','~',0357,0253,'>','?',0221,0347,0312,'~',0237,0214,014,01,035,'F',06,0274,'r','K','9',0263,0316,'4',0246,0326,0213,'1',0365,0333,0376,0304,013,024,0264,0256,0347,030,0363,'y','X',')',0263,016,'`',0327,011,'M',0307,'{',0203,'e',0232,'p',0227,'R','Z',0263,',',')',0204,'1','V',047,'9','e',0310,027,'h',0272,':','"',0266,0207,0223,'5',0324,0365,0216,0273,0334,0201,'i',0356,'F',0313,'W',0245,0313,'<',0276,0362,0307,0360,'U',0362,0354,0277,'Y',0273,0,'#',0326,0260,'o','d',0247,'^',017,0273,0352,0317,0217,0344,'y',0362,0237,0247,0344,';','2',024,0216,0214,'F',0325,'U',0303,0272,0274,0240,0363,',',0311,'M','s',0377,0,0177,0134,'g','B',0315,'R','$','1',0372,'N',0203,0324,0357,06,0313,'4',0361,'"',0200,0273,'e',012,'M',0200,0313,0324,'}',0346,'|','t','F','3','i','^',0374,'B','$',0333,'1',0276,0252,0203,'S',023,025,'_','*',0313,0365,0233,0260,02,'=','k',06,0366,'J','u',0341,0373,0276,0254,0370,0376,'G',0237,')',0372,'~','P',0202,0304,'L',0325,0233,'E','Z',0334,033,'N',0275,0221,0267,035,0270,037,047,0210,0350,'Y',0252,'D',0206,'?','I',0320,'z',0235,0340,0331,'f',0221,0232,'P','L',0210,0370,'K','H',012,'r',';',0304,'@',0333,0247,033,0212,'U',0332,02,033,0325,0264,0303,032,0310,'H',06,012,016,'J','f',0272,']','=',0204,0134,'^',0220,0301,0325,0134,'_','w',0325,0237,037,0310,0363,0345,'?','O',0312,027,0,0240,'#',0250,0302,0245,'{',0200,0246,0311,0336,'2',0362,0205,0224,0265,'c',0277,021,0320,0263,'T',0211,07,'q',06,0256,0233,0235,'a',0302,'J',0260,0351,037,'Q',0267,',','J','I','x',0343,020,'V',0260,0363,0213,'A','%',02,'V',0301,'5',0207,030,025,'D','t',0344,'6',0222,0263,'@',022,0352,'Q',' ',0252,0355,'k',0247,021,0320,0261,'V',0253,037,'z',033,'e','k','>','?',0302,0225,0233,',','2',0267,'(',0262,0177,'O','?',0323,0310,0223,'a','}',0352,'2',0216,0364,0371,'3',031,0302,0207,0301,0242,0205,0342,'4',026,'j',010,0204,0363,0345,'?','O',0315,'n',0362,'L','6',031,0226,0355,'u',0206,0317,0317,'g',0205,0,013,']',0243,0261,'p','*','Y',0322,'i',014,'n',0314,0272,0253,0203,0215,020,031,'b','u',034,0134,'D',03,026,0254,'@','r',0201,'i',0340,0351,0306,014,0232,02,0326,'0',013,'$',0367,0356,0342,':',026,'*',0325,'a',0217,0320,'v',036,0207,'x','h','O',0270,0317,0217,0360,0247,'5',010,'2',0207,0305,0202,0307,011,0210,016,'u','^','^','J','(',0227,0254,'a',010,012,0266,0242,0270,'B',0354,015,0211,06,'G',0305,0331,'h',011,'h',0225,0206,04,'F',0240,013,'D',03,'X',033,0357,0376,0357,017,'>','S',0364,0374,0353,'/',0326,'.',0301,030,0365,0254,0231,0331,013,0327,0201,'<',0326,0265,0255,'e','p',014,0231,0205,035,0230,0267,0307,0204,'Q',0336,0134,'7',' ',0321,013,'k','@',0255,'g',0305,0323,0217,'<',0304,'j',0325,'j',0305,'N','D','2',0246,0336,023,0214,0201,0355,'N',0304,'&',0247,0351,0257,'@','`','Q','F',0223,0352,'z','3',0343,0374,'(',02,0275,'n',0363,'O','z',027,0244,0260,0360,06,035,0302,'l',0233,024,'j',0335,033,'"',020,0363,0304,0352,0252,0311,0204,0274,0230,'c',047,'(',0220,010,'5',0341,'4','[',0226,0211,017,0350,0361,'r','d','Z',0326,'o','3',0303,0317,0224,0375,'9',015,' ',0337,0343,'Y','~',0261,'v',010,0304,0274,'M',0200,'x','-',0305,0276,'D','K',0250,'d',0313,'.','6',0215,'F','e','m','x',0317,'K','<',0263,'0',0324,030,'!','"','H','r','i',0,012,'4',0361,'t',0342,016,'*','2','<',0345,0242,0300,027,015,'w',0341,'@',05,'V',0200,0336,'Q',0315,030,025,'u',020,033,02,0200,0320,0360,0372,0236,0214,0370,0377,0,032,0235,'8','p','i',01,0204,0244,0273,'+','3','f',0262,030,0357,0241,'`','D','3','8',0236,0204,0341,0325,0336,'{','x',0371,0362,0237,0247,'!','.',05,'~','=',0341,0,020,' ',0301,'q',0272,021,024,'Y','z',0220,0325,0341,'j',0223,'4',011,'X',0215,0215,0253,'|','l','9',0266,05,'g',0332,0213,0216,0214,0204,0200,0235,02,0200,0340,'t',0341,'Z','-',0215,025,0236,0205,'_',0177,025,'B','U',0321,0177,0302,0,0,017,'@',036,'?','S',0321,0237,037,0340,0263,'w','9','Q',0231,033,'a',0222,0240,0371,0276,0220,'@',0244,'M',034,031,'Z','D',012,';',0201,'H',0270,0257,'a',0307,'T',0224,010,0256,0347,0217,0237,')',0372,'~',020,'U',0321,0277,'9',0253,0,'f',0345,0337,'k',' ','=',0342,0320,024,0240,'"',0252,'m',0343,020,0355,'Y','j','w',0234,0306,0310,'8',0344,0303,05,' ','+',034,'.',0234,'4',0333,'M','%',0256,014,'O','2',012,0243,0267,0246,0234,'4',0220,0365,0204,0356,0327,'_',0373,035,023,05,024,031,'x','>',0247,0243,'>','?',0300,0355,0236,0343,'{',0222,026,010,'Z','f',0262,'J',0321,0222,'Q',0257,0,0273,0265,0,0241,034,'J',0252,0236,0215,0205,0302,0346,'I',0245,'P',0347,0307,0317,0224,0375,'8','o',0233,'K',0272,0347,012,0212,0350,06,'Q','o','E',0313,'2','[','G',07,032,' ',013,'X',0230,0134,0310,'1',0250,'U','-','P','O',0201,0226,0270,0225,07,0223,0367,0300,014,0232,02,0326,'1','^',0264,'~',' ',0355,0377,0,'8','@',0322,0265,0344,0350,'G','T',030,0233,0245,0220,0341,0372,0236,0214,0370,0377,0,0,0322,037,015,0324,030,0260,'I','}','<','Y','2',']','y','R',0274,'(','7','`',0365,'|','|',0371,'O',0323,0206,0263,0177,0224,'~',0312,0264,0134,'|',0324,'Z',026,011,0314,'4',0324,034,014,'W','r',0336,'}','X',0210,0323,0207,0205,0361,'Q',0270,0207,0322,0206,010,'%','(',0265,'L',0361,0233,'<',0307,0357,0200,026,0245,'v',0224,0301,'Q',015,'8',0255,0332,'m',0341,'T','Z',0225,'h',0267,'6',0323,0336,033,'`','(',015,016,037,0251,0350,0317,0217,0207,0216,013,'}',024,010,0272,014,0353,'Q',0336,035,'>',0265,047,']','u','%',0230,0306,0276,'!',0303,0244,0337,0204,0210,012,'-',0212,0265,0254,0370,0371,0362,0237,0247,'&',0325,0355,0370,0246,0300,027,0230,0247,0207,'C','0',0363,0203,015,'@','V',0262,0334,0273,'*',025,'q',0331,'B','o',024,05,0232,0321,030,'1',035,'|','L','i',0266,'a',0226,'*','=','`','l','J',0304,'@',0,024,034,'b',0353,0305,'h',0266,023,033,'g','j',0335,'v',0353,0361,0302,0302,0310,'a','y',']','}',013,'}',' ',0212,'`','n',0263,'7',0305,0365,'=',031,0361,0363,'*','9',0264,0315,0232,'G',016,0327,'h',0216,0201,'h',0313,'D',0255,0240,0350,'@',0204,0327,'F',0303,0335,0237,'a',0377,0,'p',0237,'}',0222,020,0251,0334,0374,0303,0307,0317,0224,0375,'8','/',0362,0134,0232,05,0333,'1',030,0341,014,'T','p',':',0304,0134,0333,020,0315,'V',0240,0264,'Y','w',011,036,06,0256,016,'B',0306,'H','c',06,']',0223,'!',0335,0252,02,0252,0254,'$',0327,'/','0','%',0236,0320,'{','G',0373,')',0321,'u',0236,'C',0247,0213,0205,0273,'B',0322,0230,0304,0326,'p',0364,0345,0307,0261,'G',017,'K','Q','1',0272,0353,0366,0273,0300,0242,0216,'/',0251,0350,0317,0217,0360,' ','L','y',030,'Q','e',0231,0305,0344,0211,0217,0227,'$',0324,0325,0226,0270,'0','4','*',014,'J','^',0240,'g',015,0331,'j',04,0322,0312,0277,'W',0217,0237,')',0372,'E',0250,0274,0315,'9',0313,'E',0303,'M',']','P',0314,0362,0273,'r',0312,'G',03,0203,0300,0320,0320,'I','~',0254,0302,036,'(','s','D',0244,0203,0263,'6','g','T','H',0352,'!',0347,'w','%',0300,'P',030,021,0232,0241,0302,0345,'`','f',0202,0207,0260,0373,036,0236,05,0224,0342,'t',0361,0255,030,020,0354,'W',0321,'=','x','M','j',0335,0313,'Z',0322,'$','Q','l','R','F','o',0217,0352,'z','3',0343,0374,07,0214,0316,011,0330,'p',014,';',0203,0212,01,'9',015,'P','j','L',05,'4','|',01,'>',0317,0223,'Z',032,'(',05,'}','G','_',037,'>','C',0364,0341,014,0237,0216,'Q',0313,0250,0213,'g','6',0205,0215,0215,'X','j','*',0266,0301,'m','{',0312,0333,'y','!',0324,'g','D',020,0,0365,0217,':',0206,0204,'r',0362,'^','&','4',0327,'x',0341,0250,'H',0312,0352,'i',0327,0300,'R','(','(',0321,0322,0376,'}',0343,05,'b',036,0260,'_',0234,033,'8',']','<',05,0225,05,0260,0362,'T',06,0232,0327,'_','#','N',035,0345,0273,'J','6',0341,0373,0230,04,012,02,0203,0217,0352,'z','3',0343,0374,01,'d','-',0250,0312,0330,0242,0331,0375,0354,0377,0,'{',')',036,013,03,013,')',0215,'_',0227,0217,0237,')',0372,'x',0327,0344,022,' ',']',0261,02,'[','X','f','R',022,0242,0227,'j',0334,']','x',0265,06,0260,0245,0253,07,0134,'a','r',0212,013,0325,0301,0373,0202,0256,'_',0214,0351,'s','#',022,0345,0201,021,'C','3','l',010,0323,021,0225,0265,0317,0202,037,'M','=',0306,0317,0324,0307,'`','Q',0300,'4',0314,0310,'0','(',0341,'t',0360,'1',025,035,02,0224,'$','C','p',0322,0325,0253,0236,020,0222,0273,'T',0256,0314,0236,0336,0234,0217,0251,0350,0317,0217,0360,0253,'h',0251,0357,'h','#','W','S',0206,'#','A','0',07,0253,'`',02,0226,0263,0246,016,037,'>','S',0364,0374,0240,012,0350,'B',0330,0302,0214,0271,'K',0267,'1',0134,'%',0343,0300,0255,0224,021,'j',0272,'A','G','G',0314,024,0221,'.',0322,0225,'K',025,014,0225,0266,'o','Y',033,'N',0231,0326,'9','k','8',0362,0340,0275,0213,0257,'#',0247,0277,0304,0323,0211,0323,0302,0331,'F',0304,0323,'{','7','3',0302,'h',0223,0267,'K','q',0345,')','4',':',0202,0206,0265,0310,0372,0236,0214,0370,0377,0,05,0230,0322,020,0224,010,0264,',',023,033,0220,0256,'O',0225,0355,'@',03,'u',0270,0326,'|',03,0244,0337,0204,0210,012,'-',0212,0265,0254,0370,';','9','>','S',0364,0374,'u','K',0206,'q',0340,')','D',0250,0227,0244,025,'K','f','B',07,0200,0366,'W','X','S','l','W',014,0350,012,'F',012,02,016,0230,'&',0204,'_','V',0246,0202,016,'A',0351,'d',0310,'k',013,'j',033,0306,0221,01,'7','~',0353,0303,'k',0266,0336,0220,0354,0275,'u','`',0331,'|','N',0236,02,'+',0255,0240,0235,'1',0352,0360,0322,'&','x',0245,0332,'!',0355,0311,0372,0236,0214,0370,0377,0,02,'0',' ','[','a',0267,024,0217,0234,0376,'7',0376,0317,0343,0177,0354,0276,0226,01,0324,'7',0242,0277,'G',0202,012,0301,0362,0237,0247,0343,0245,022,'2','7',05,0240,'.',0330,0251,',','L','0',0362,0263,'Z','b','*','Z',0314,0357,'*',0240,'`',014,0213,'(',0210,'4',0306,'6',0222,0361,'.','A','Z',',',0311,0346,0366,0230,011,'r',0134,'i','M',0240,0321,'*',0326,0301,'m','J','<',011,0324,0251,0255,'|','p',0240,'C','8',011,'t',0347,0326,'w','j','%',0325,0207,023,0244,024,'T',012,0260,0356,'(',0211,0240,'l','_',015,0314,0323,'O',020,0331,'}',0271,'?','S',0321,0237,037,0340,0267,0317,'%','-',0301,0356,0200,0365,0217,0347,012,0366,0203,'v','B',07,'%',0253,'W','1',0242,0343,'*','t','b',0340,0201,'o',024,0336,0221,0324,'J','~','9',015,0253,'D','n',0272,'/',0300,'N','|',0247,0351,0371,010,012,0350,'C',0332,'A',0246,'X',0276,0310,0276,0255,0270,0360,0356,'V',031,0275,'#','h',0322,0250,032,0260,04,'0',0305,0304,'`','^',0365,0,0354,0227,',','+',0275,0321,'A',012,'b','6','Y',0244,0,06,0204,0257,'X',0341,0273,'V',026,'9','/',0261,01,'U','z','p','9','d','X',032,'F',015,0261,0330,']','6',0317,023,0244,027,0252,0311,'j',0351,'C',032,0221,'[','u',0227,03,0226,'E',0,0265,'`',0333,035,0205,0333,'|',0362,'~',0247,0243,'>','?',0300,03,'v',0362,037,0341,0205,0266,0300,014,0257,0236,0303,'s',0326,014,'K',0212,0212,'J',0311,0211,'y',022,'d',0233,04,0250,015,015,0224,0337,'P','t',0361,0363,0345,'?','N','[',07,0230,0,0355,'T',0277,0330,0252,'[','1',0364,'>',027,'0',0245,0302,0232,0261,010,' ',012,0200,0305,0267,',','m',0310,'Q',0357,035,0325,016,021,0346,0226,'&','k','U',0204,0203,0360,025,'G',0200,0202,0260,'D',0207,'Q','D',015,07,'r',0370,'@',026,0231,'f',0313,0371,0304,0351,01,0251,0341,'K',0327,014,'I',0254,0223,0252,0345,0340,'$',0210,'t',014,'!','6','L','h','P','r','~',0247,0243,'>','?',0302,0262,0306,0274,0341,0222,'.',0310,0325,0227,'P',0202,0202,024,021,'A','A',024,0212,0366,'"',020,'&',0263,'o',01,0227,'Y',06,0257,027,'E',0220,0364,0277,0324,'5',0312,0262,'1',0200,0272,'W',0303,0317,0224,0375,'9','n',0220,0327,0226,0267,0320,']',0263,'8',01,032,'e',0306,'k',0206,0230,0350,0265,'a','o','j',0304,0262,'W','+',032,0264,033,0264,024,0273,0224,'F',0313,03,027,'/',0322,'6',0200,0205,0231,031,',',0206,'W','a','j',0304,'-','4',014,034,025,'2',0261,0321,'_',0307,0205,0342,022,05,'N',0215,0370,0235,'!',0346,'H',0250,'9','p',0247,'u','y',0351,0263,'H',03,0350,01,0311,0372,0236,0214,0370,0377,0,05,0304,'e','H',';',0354,'7',0337,'S',0256,0221,'z',0134,026,0272,015,'~','D','3',0277,0305,0306,'5',02,'_','A',0353,0,0220,'B',0304,'l','|','|',0371,'O',0323,0361,0317,05,025,'C','2','!','{',0254,'Y',0356,0337,012,0261,'F',0221,037,0241,'t','G',0234,0220,0251,0244,0222,'o',034,01,'o','H',0200,'j',0336,'e',0217,0325,014,021,0205,'4',012,0246,'x','Y',0266,0360,017,0323,'^',021,0264,0255,023,')','{',0361,':','B',0244,'J',0265,0267,0327,0204,027,'[','F',0217,'7',0267,036,0257,0217,0324,0364,'g',0307,0370,030,0215,'R',022,0273,'U',0215,'N',0204,0270,0211,036,0,013,0206,'^',0321,'j',0363,0217,'<','R',0205,0323,026,05,0275,0256,'5',0205,'8',0211,0200,0315,'h',0,0260,0272,']','_',037,'>','S',0364,0345,'(','-','j',':',0243,'u','b',0304,023,0274,';',0203,03,'T',',',024,0261,0321,0343,'*','V','*',0345,0262,035,0325,'0',0354,0265,0340,',',0340,0335,0205,0344,0255,0242,'"',0275,0267,011,0350,07,'H',0235,'p','(',0206,'r','.',0251,0235,'~',0214,'A',0335,0240,04,02,030,016,032,0204,0224,0254,'[',0327,0333,0204,0247,022,'Q',0362,'>','x',0235,047,0312,0177,0256,024,'+',0320,'L',0215,0326,'x',0234,'M','^','?','S',0321,0237,037,0341,0217,'N',0305,036,0201,0224,0363,'~',0322,0254,0236,0275,'v',0331,'G',0350,'@','q','K',037,036,024,0336,032,0321,0207,0251,'j',0351,0254,'u',0254,'U',0304,';',01,'T',0242,0306,'1',0343,0347,0312,'~',0234,0215,'%','8',03,'E',0212,'e',023,024,0203,0224,'v',0207,0327,0317,'"',0241,'O','x',0231,0213,0265,0214,0211,0300,'[',035,0373,05,0360,0233,',',04,0323,0324,0330,0315,'E',0332,0323,035,023,'k',02,'{','U','@',032,0351,'s','g',0210,0215,0342,0363,010,'8',0326,'X',0325,0332,0242,'f',014,0355,031,025,0366,'Y',0,020,'+',034,'@','k',0211,'6',0203,'W',0207,0353,'y','C','N',027,'I',0362,0237,0353,0207,0354,';','q','*','!',0361,0372,0236,0214,0370,0377,0,013,'r',031,'Q',0354,0134,0377,0,' ','K',0344,0303,0246,'l','b',0355,'P',0216,0335,'*','U',',',0313,'h',033,'o',0250,0307,05,0350,0234,'/',05,'l',0321,'4',012,0337,0307,0317,0224,0375,'8',0315,0222,0202,032,0272,0350,06,012,0202,'w','b',030,07,0315,0213,'C','j',0245,0231,017,'9',0336,'b',0305,')',0322,0345,0177,'W',035,'c','6',010,'<','-',0302,'%','D',0261,0323,0251,'l','I','V',0260,'A',0315,'h','Z','C','@',0203,'P','9',0270,0322,027,'U',0325,017,0237,0272,0240,'V',0232,0306,0306,032,0227,035,0375,022,0320,0332,034,0360,026,0234,0177,'9',0303,0365,0274,0241,0247,013,0244,0371,'O',0365,0303,0366,035,0270,0306,0236,'?','S',0321,0237,037,0341,'t',0340,'E',0,0354,0226,017,'W',036,'T','2','^',016,'-',015,'l',0214,0276,0220,034,'j',0272,0332,'K',0254,0200,'p',0363,'C','E',032,0240,':',0327,0333,0307,0317,0224,0375,'8',0316,0355,'4','S',023,0372,'s',0224,0362,0234,0262,',','j','z',0360,0261,0262,' ',035,0344,'A',0262,0374,010,0373,0320,'[','q','r',0336,0350,0304,0250,0226,0250,0204,'Y',0232,0227,'5',0333,0206,0210,0321,'4',0364,'G',0345,0202,0311,04,0224,0220,0356,'L','@',0372,013,'E','[',0325,'F',010,014,'+',0273,'I',0247,037,0316,'p',0375,'o','(','i',0302,0351,'>','s',0375,'p',0202,0315,0324,0351,';','z','q','j',0206,0236,'?','S',0321,0237,037,0340,0235,02,')',0313,0245,'n','v',021,'n','$',0335,06,0325,011,'j',0356,0360,0371,0362,0237,0247,027,'~',0206,0134,012,0250,021,07,'3','z',0210,0222,0327,0211,0304,0331,0216,'n',0264,'2',0217,'-',01,0277,022,0204,05,'a',0210,0347,025,0266,0343,012,'X','m',0270,'u',0224,0224,'!',026,0251,'`',0301,0242,024,0240,0214,'h',0273,'e',0207,'R','Q','l','<',015,0324,04,0324,010,0370,0211,0220,' ',0205,'@',0243,0220,017,'<','x','R',0227,'~',037,0255,0345,015,'8',']','!','"',0266,0251,'n',0234,'"',0354,027,0250,0371,0361,0356,0361,0372,0236,0214,0370,0376,'G',0237,')',0372,'q','8',0332,0256,023,':',0332,0313,'U',0242,0361,0215,'7',021,0134,0311,'y',0360,0254,0276,01,010,'#',0230,0366,0252,'!',0270,0326,022,0361,010,'6','7','!','B',0350,'1',05,'+',';',0220,0203,0275,010,05,0224,0353,01,0335,'k',015,'J',0254,0207,'%',0300,0325,'^','[','X',0206,0240,01,0203,0222,047,' ',0264,027,0257,011,0276,0313,0277,'C','c',0347,0211,0322,016,030,'q',020,0264,032,0263,030,0206,'C',0207,'|',036,'/',0251,0350,0317,0217,0344,'y',0362,0237,0247,025,0337,0271,'4','A','d','p','U','[',0310,'|',0232,024,0322,0315,'c',0230,047,'W','k',0270,0303,'W',0254,'2',0341,'a',0314,0327,0300,'s',021,0322,'=',0237,'k','@',0344,'Y','K',0240,0312,010,0324,0234,0254,0305,0216,0252,030,'%',0246,'D',025,0256,'R','.',06,05,0327,'M','N',037,0251,0347,0304,0351,04,0316,0341,'u','=','x','X','#',0340,0274,026,0266,0363,0235,0335,'d',0302,'q',07,0213,0352,'z','3',0343,0371,036,'|',0247,0351,0304,0226,'M',0377,0,034,0265,',',01,'W',0310,'P','p',0214,0305,'e','J',0326,0225,'i','o',0316,'m',0270,0245,'Z',0274,02,0253,032,0224,'!',0364,'Y','P','V',0372,'K',0226,0252,'4',0207,0274,'U',020,010,'0',034,0247,024,0346,'e','t','=','7',0341,020,'8','7',0260,'}','^',047,'H','k',020,0226,0326,0265,'b','!','%','6','V',013,025,0301,0334,0266,'U','t',0335,'F','Z','L','m',0325,025,0302,0351,07,0217,0324,0364,'g',0307,0362,'<',0371,'O',0323,0214,0311,0261,0331,031,0325,0251,'p','R',0235,'9',010,'L',0215,'e',0134,0254,0353,026,0206,'X','8',0214,025,0254,034,0311,0316,'f','d','M',0344,0326,033,'`','V','9','`','c',0246,0326,0326,0353,0361,0303,0200,0325,'P',0244,0357,0276,0207,023,0244,'a',0365,'@',0366,0205,06,'m','Z','x','R','.','/','T',0354,'g',0210,'+',0307,0352,'z','2',0330,0263,'U',0337,034,'v',0315,0373,0246,0347,0310,'~',0234,'d','E',027,023,'z',025,';','l',0274,0201,0246,0310,035,')','8',0214,0370,'l','+',0361,'X',032,' ',0323,'*',0266,0271,'}',0216,0252,0240,'k',020,0266,0225,0271,06,0215,'|',0270,'Y','$',035,0250,';','q',':','x',017,'z','6','4','5',0375,0277,'N',025,0207,0255,0205,016,0366,0372,'>',0374,0234,'q',0366,013,'W','*','!',0325,0257,0331,'!',0306,'&',0203,0310,0341,02,06,0356,'>',0204,0244,0362,0360,033,033,036,0321,'E','Q','T',0327,'Q','=','x',0315,015,022,'-','S',012,010,0241,'s',0331,0302,'^','`','X','t',030,'&',015,'M',0226,'i',02,0212,'9','v',0341,0212,03,0221,022,'k','$',0352,0271,'x',012,'j',0353,'n',0300,0254,'F','`',015,'{',0253,'?','<','N',0236,030,0277,04,0333,':',0373,0374,'p',0211,03,0310,0245,'M',0375,0177,'p','W',026,0305,0210,0234,0204,020,'K',023,'$',0334,0330,0200,'N',0365,0322,'>',0231,0261,')',030,'B',0224,'U',0320,0373,0223,0373,037,0365,'?',0261,0377,0,'S',';','3','H',0376,0323,013,0366,0213,'`',0362,'"',0220,01,'s',01,0320,0352,0300,0330,0262,'+',0327,0220,'/',0210,'Z',0204,0275,0214,014,'J','y',0242,025,0253,017,'y','l',0314,0253,033,',',0201,'0',012,0307,'3',026,'X',013,0303,'g',0331,0357,0302,0200,04,'a',0240,0335,0373,0264,01,0264,0,0342,'t',0360,0247,014,'@',014,0210,023,'Y',07,'D',0303,0301,'D','B',0350,023,'$','#',0246,0244,015,0207,'C',0220,'A',0210,'j','B',0306,'Z','R',0326,0266,0236,0277,0370,0205,'w','|',021,'N',0344,0376,'T',0177,'*','?',0227,017,0300,')','l',']','E',0326,034,'4',0241,'W','%',0336,']',0251,'p',0344,0324,'F','d',0252,'y',0270,'R','s',0231,0217,'A','-',010,'`','P','.',0216,'c',05,'J','^',0220,';','G',' ','P','y','z','{','p',0214,'=','l',')','w',0263,0325,0366,0343,'t',0360,'8',0217,0206,0342,'T',015,0215,026,0253,027,'~',0367,0302,0247,0366,0260,'Q',0337,'>',0376,0374,0235,'r',0342,0325,0351,025,0322,'P',0232,0372,' ','a',0326,0301,'A',0300,'q',0307,'c','a','L','#',0275,'h',0312,0230,0203,'6','~',0211,0267,'$',034,0321,'*','a',0226,0333,'j','%',025,013,0230,':',')','y',0231,']',0240,0301,05,'%','w','i',0231,0247,'2',0355,0212,0244,'[',021,0226,0246,0375,'Q',0265,0367,0341,0324,'B',0251,'m',0365,0277,'^','7','O',024,0322,0244,0212,0275,0261,0350,'{',0360,0357,'l','r',0205,0245,027,0347,0322,'k',0324,')',02,0231,'/',0214,0200,0252,0355,0203,'X',030,'&','@',0300,0235,']',0330,'m','*',0362,0340,'z',0254,0266,033,06,0246,0237,'6',0177,033,0377,0,'g',0361,0277,0366,'k','+','n',0325,'z','0',0327,0232,023,0355,'q','7',0205,0323,0275,0300,0314,0342,'^',0272,0267,'=',0371,'9',0203,0314,0261,0211,0262,'D',0245,034,'r',0224,025,0253,013,'}','l',0314,0306,0253,0261,04,0232,012,0243,0232,0205,04,023,'z',02,'y',0376,0270,'Q','N',06,03,'F',0335,'~',0346,0,010,'"',0365,0301,0134,'n',0236,' ',0211,'|','a',014,0335,0276,0362,0310,0205,0320,'&',036,035,0307,06,0273,'c','V',0377,0,0217,032,0272,0333,'[','E',031,'a',0207,'a','8',0275,'X','.',0304,010,0231,0336,'^',023,':','T',010,0277,'[',0302,0253,'Z','K',0216,014,'{',0232,'q','O',0216,'J',0220,0273,'#','i',0202,0326,'!','!','T',0362,0260,0206,0273,0304,'A','a','`',0200,0316,0202,0361,0315,'`','"',']','W','L','L',0247,034,'/','&',017,'n',022,0216,034,022,033,0261,0257,0363,0220,0351,0342,'<',036,'a','b','0','0',04,020,'P','[',0223,'N',0347,0273,0302,0302,0317,0271,'B',0234,0336,'v',0377,0,0260,032,'"',013,0324,0343,0250,06,0201,037,'0',0307,030,'E','U','W','Z','q','>','S',0364,0344,0202,016,0214,0264,0352,0345,'E','|','g','^','H','m','v',0346,'5',0233,'I',0202,010,'j',0355,'S',0235,0223,0317,0365,0222,0247,0355,'<',' ','}',0224,0367,'Z','?','r',0213,0256,0305,' ',0274,0343,0220,0351,0301,0325,0134,'S',';',0247,023,'=',0353,'n',']',0263,'<','u','}','W','W','#',0317,0224,0375,'9','K',0353,024,0222,0245,'*',0246,'a',015,'T',0361,0250,0202,0326,'k','B',0322,0343,0357,'}','b',02,'T',012,0346,0273,0211,0203,';',030,'#',0317,'P','%',0245,0340,0366,0341,'e',0215,021,'1',0262,'k',0366,0310,'n',0202,0,'6','9',016,0234,05,'M','E','n','%','E',0222,0331,0201,'M',0247,0245,0374,0360,0322,'@','K',0356,0217,0275,' ','`','$',020,0254,'9','8','R',0202,0320,'o',0206,'?','P',0220,07,035,0326,'X','`',02,'0',015,023,0212,0233,06,'Q',0262,0367,0257,'X',04,'@',0257,0252,0255,0377,0,0256,'Q',022,0261,'*','^','g','-',010,0206,015,0250,'F',0251,023,0264,0356,'%',0215,0245,'0','M',023,010,0240,0371,'J',0205,06,0314,'o',0302,0226,0250,'g',0250,030,0346,0273,0240,0225,']',0210,0217,0245,0330,0252,0266,'|',0234,0373,'p',0262,0320,0317,0252,'4',036,0360,011,05,0253,0233,'s',0376,0371,'.',0234,'>','H','a',0235,0362,'"',02,'#','H',0355,0303,'Q',0263,0233,'F',0271,'z','g',0356,0260,'=',0200,0265,0210,0350,0360,'b',0333,'$',0330,0344,0371,0217,011,0267,'u','%',0342,0300,016,'Q',0321,0352,'J',0347,'*',0352,0202,'y',0214,0372,0367,0373,0237,'^',0377,0,'s','&','v',0255,020,'-','G','T','P','7',0363,'w',0200,'A',0202,06,'U','a',0315,'K','G','K','0','{','r',0303,0350,025,0230,0310,0310,'P',010,0354,0267,033,0261,0205,0352,'P','#',0204,0213,022,030,'i','N',0260,032,0267,'(',0207,0204,014,0323,0234,0375,014,0245,'l',':','#',0223,'Q',0353,'R',0332,0274,'(',0355,' ',0235,0211,0367,0257,'h','n',0202,0,'6','9','.',0234,'*',0321,'@','X',0214,'B','d',02,0201,'e',0321,0353,0357,0302,0345,0225,'b','R','=','a',06,0200,0330,0246,'c',0206,0337,0235,0212,0340,'t',0377,0,0250,0263,05,0213,0227,0334,'L','K','w','$',0357,'=',0347,'y',0357,024,0352,0254,'c',021,'@',0251,0352,0303,0314,0300,0353,047,'W',0253,'4',0346,011,'^','r','G',0327,0333,0315,'S',0331,0232,0257,0267,022,0277,0216,'4','8',0275,0241,0341,'M',0320,0202,'(',0,0347,05,0356,'5',0325,0340,0250,0230,0,'.',0227,0340,0256,022,0254,'p','-','V',016,0200,032,04,0262,0336,'9','N',0234,'F','E','z',0332,0213,'/','e',0215,'i',0324,0354,0360,0277,'p',0200,0374,'0',0375,014,',','Z',0255,'C',0207,0260,0374,'$',0214,0324,'-',0134,0317,0,0303,'L','x',0260,012,037,0323,0374,023,0225,'R',' ',0272,'4',0216,0331,0300,0334,'l',0267,'^','-','c',0275,'.',0221,':',';',0377,0,0310,024,'Q',0247,')',0323,0211,',',0214,0244,'*',016,0253,0276,'7',0373,0277,021,0211,0345,'O','t','$','p',0222,'g',0236,0202,'q','_',0370,'D',0,05,0350,02,'[',026,0321,'g',0347,0323,0211,0256,'J','l',035,'9',0373,0357,01,0243,0200,013,0243,'^','[',0247,032,022,0211,0266,'#','*',0310,'t',0342,0355,0333,0247,021,0326,0260,0357,'t',0207,'Y','E',0372,0315,0334,011,0303,0254,'?',0301,' ',0,'/','@',021,0363,'%','b',0225,0270,'1',021,025,'[','W','~',026,'^','K',032,'[',0253,0330,0203,'s',0265,'A','s','W',',','+',0226,0351,0310,'q',0226,'g',0236,0240,0307,0224,0253,0314,'-',0317,0216,'#',0255,'a',0336,0351,016,0260,01,0321,'4','m','Y',0307,0370,'d','m',0347,'h',02,'8',0347,'c',0303,0332,'D','D','U','m',']',0370,0134,0230,0207,'Z',0226,0200,0227,'r','+','t',0215,0237,'9','X',0255,0245,0203,0267,'-',0323,0222,0273,'$',0245,0277,'f','?','t',0220,'|',0234,'F',024,016,01,0364,'b','=',0261,'e',0367,' ',0337,0370,033,0315,'x','#',0326,0252,05,0354,'K','d',011,'H',015,0342,0373,'q',0243,0341,0313,0302,0366,'P',0335,04,0,'l','s','n',0316,'S',0332,'1',024,036,0217,'x',0364,',','U','"','q','.',0360,0261,'6',0354,0365,' ',0340,'k','<',016,0275,0341,'2',0321,';',021,0377,0,0,0343,'0',0355,'{',0330,'#',0244,'K',0251,'K','P','4',0263,'~','2',0313,0252,022,0377,0,0344,0330,0352,'5',03,0232,0312,0243,0224,0226,'S',0244,'q',0372,016,0203,0321,0357,036,0205,0212,0244,'N','$',04,'D','l','M',0243,'h',0247,030,'4',0302,0375,0363,0216,'+',0225,0213,0272,'?',0234,0225,021,'X',0,0213,012,0311,0346,0265,'X',0357,0177,0250,0273,0302,0324,0323,0261,0320,0343,'3',';',06,0226,0177,0250,'x','~','m',0,'@',02,0202,0216,'s',0247,'-',',',0247,'I',0243,'D',0301,'A',0336,'/',0271,0,0323,0334,'w','8',0336,0376,015,0270,'Q',0334,0336,';','B',0340,032,'+','~',0232,'o',0,034,0336,025,'<',0317,0312,'/',035,'t',0200,'C',0342,02,'&',0213,'5','o',0231,',','/',033,012,':','5',0256,0234,'h',0,0252,0320,033,0304,'k','a',0267,'z','Y',01,0267,0315,0240,017,0315,0257,03,0224,0302,'V',06,'%',0311,'O','%',021,0344,'&',0370,0272,0247,0314,0365,'m','=','O',0342,0365,0313,0322,'R',0264,'j',':','z','~','0',0314,0273,030,020,0212,0272,0326,03,0235,'}','H',0305,'a',020,'I',0235,'_',047,'o','x',0345,0225,'j','Z',0275,'x',0320,',','R',0332,0273,020,036,'*','#','F',0314,0,0,017,'@',037,0237,'D','{',0223,'~',0310,0302,0317,0233,0276,'K',0263,0311,07,'L',0313,'4','5','4','t',0233,'*',')','5','k',0337,035,0346,'G',020,0340,0254,0325,'@',0250,0230,0241,0372,'~',02,0221,015,0325,'D',03,0302,'T',0315,'b',04,'*','c',035,07,'P',0367,0204,026,0260,0343,027,'O',0216,'K','!',0216,0304,0365,'X',012,'G','R','W','p',0206,0314,05,01,0241,0376,04,0320,015,04,037,'H',025,'Z','W',0312,0335,0320,0332,' ','?',03,'W',0334,'w',0345,'_',0244,0251,'u',0211,'Z','1',01,0234,'3',015,0352,0372,'w',0231,036,0275,'W','C','H',0,03,'?','V',0362,0214,'L','"',031,015,'1','q','@',031,'t',03,'/',0202,0345,026,'O','J',010,'K',023,'k',0264,0,0375,'h',0277,0311,034,031,0365,'A',0324,'z','K',0345,'!','k','#',0276,'?',0362,'c','[','$',0247,'d','J',0363,0253,0375,0307,0222,0345,0225,'@','Z',0275,'&',0214,0336,0204,0271,0263,0266,' ','`',0305,'(','z',0317,0342,'?',0203,0227,0276,'K','M','(','T',0314,'S','6',0251,0372,0343,0177,0272,0307,',',0252,'B',0221,0351,0314,'J',0204,'(',030,'i','Y',0204,0134,'K',0224,0263,0267,0201,'#','q','y',0177,'$',0356,0375,0374,0243,'k',013,'*',0317,0374,0314,0357,0205,0243,07,0226,'b','B',026,'z',0224,'8',011,'j','V','.',' ','X',0325,'m','}','y',0207,0,020,025,015,0326,0273,0377,0,0344,'r',0317,010,'/',016,0225,01,'h',0321,0320,035,0217,0305,0177,010,026,0215,035,0211,0334,0205,0350,0312,025,013,0235,'O','X',0305,'a','P','A',0235,'O','#','o','h',0345,0225,'H','R','=','?','5','7',0305,'U',036,'g',0243,'i',0350,0177,023,0256,036,0260,0276,'e','c',027,0315,0374,'w',0361,013,0307,'U',0240,0220,0277,031,'B',0241,'s',0251,0353,'0','y',0354,0343,0246,0333,0357,0322,'/','0','S','U',0232,0353,0371,016,'Y',024,02,0325,0207,'k',0236,0371,0227,'W',0231,'o',011,'A',0253,0326,'G',0257,0314,0,'s','X','E',0363,0177,'%',0374,'j',0222,0350,013,'"',0267,036,06,0333,0326,0372,0305,'T',025,0372,'@',0372,'C',0321,0300,032,'*','E',0346,012,'j',0263,']',0177,011,0313,'"',0200,'Z',0260,'L','e',']','@',0363,0210,0251,0265,0213,035,027,0374,0214,'J',0332,0336,'7','w','L',0307,0310,0207,07,0267,0345,0277,0222,05,013,0320,0134,0,06,0256,'!',0267,'|','K',0272,'d',025,0226,0320,0256,0221,0266,023,05,'^','e',0306,'t',0217,'T',0303,'m',0254,'E','$',0324,0351,'2',0255,0242,01,'m','T',07,0255,'r',034,0226,0227,'a','W',0244,0267,0224,030,'#',0332,'!',0245,'n',0213,0362,0231,0315,0275,'Y',0255,'=',0373,'O','e',0375,0222,'L','E','2',0350,'f',0212,0227,'0','y','~','s',0371,0250,':',0223,'I','n',0262,'0','|','u',0,'_',0251,0341,0246,0355,'V',0364,'A',0303,0255,'k','B',03,0231,0310,'j','Z',0361,'B',',','z',0301,'l',024,0250,'O','h',05,033,0320,'W',0377,0,015,'}',0245,0362,'+',0202,0237,0375,'7',0377,0331,};
diff --git a/src/embedded_images/nwipe_exclamation.jpg.h b/src/embedded_images/nwipe_exclamation.jpg.h
new file mode 100644
index 0000000..124fcb6
--- /dev/null
+++ b/src/embedded_images/nwipe_exclamation.jpg.h
@@ -0,0 +1,16 @@
+/* Autogenerated by hxtools bin2c */
+#ifndef NWIPE_EXCLAMATION_JPG_H
+#define NWIPE_EXCLAMATION_JPG_H 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern const unsigned char bin2c_nwipe_exclamation_jpg[63304];
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+
+#endif /* NWIPE_EXCLAMATION_JPG_H */
diff --git a/src/embedded_images/nwipe_exclamation.xcf b/src/embedded_images/nwipe_exclamation.xcf
new file mode 100644
index 0000000..dcbbe78
--- /dev/null
+++ b/src/embedded_images/nwipe_exclamation.xcf
Binary files differ
diff --git a/src/embedded_images/redcross.c b/src/embedded_images/redcross.c
new file mode 100644
index 0000000..fa22606
--- /dev/null
+++ b/src/embedded_images/redcross.c
@@ -0,0 +1,4 @@
+/* Autogenerated by hxtools bin2c */
+#include "redcross.h"
+/* Autogenerated from redcross.jpg */
+const unsigned char bin2c_redcross_jpg[60331] = {0377,0330,0377,0340,0,020,'J','F','I','F',0,01,01,02,0,'v',0,'v',0,0,0377,0341,'(',0246,'E','x','i','f',0,0,'I','I','*',0,010,0,0,0,07,0,022,01,03,0,01,0,0,0,01,0,0,0,032,01,05,0,01,0,0,0,'b',0,0,0,033,01,05,0,01,0,0,0,'j',0,0,0,'(',01,03,0,01,0,0,0,03,0,0,0,'1',01,02,0,015,0,0,0,'r',0,0,0,'2',01,02,0,024,0,0,0,0200,0,0,0,'i',0207,04,0,01,0,0,0,0224,0,0,0,0246,0,0,0,'v',0,0,0,01,0,0,0,'v',0,0,0,01,0,0,0,'G','I','M','P',' ','2','.','1','0','.','3','0',0,0,'2','0','2','3',':','0','2',':','2','1',' ','2','0',':','3','9',':','5','5',0,01,0,01,0240,03,0,01,0,0,0,01,0,0,0,0,0,0,0,011,0,0376,0,04,0,01,0,0,0,01,0,0,0,0,01,04,0,01,0,0,0,0,01,0,0,01,01,04,0,01,0,0,0,0,01,0,0,02,01,03,0,03,0,0,0,030,01,0,0,03,01,03,0,01,0,0,0,06,0,0,0,06,01,03,0,01,0,0,0,06,0,0,0,025,01,03,0,01,0,0,0,03,0,0,0,01,02,04,0,01,0,0,0,036,01,0,0,02,02,04,0,01,0,0,0,0200,047,0,0,0,0,0,0,010,0,010,0,010,0,0377,0330,0377,0340,0,020,'J','F','I','F',0,01,01,0,0,01,0,01,0,0,0377,0333,0,'C',0,010,06,06,07,06,05,010,07,07,07,011,011,010,012,014,024,015,014,013,013,014,031,022,023,017,024,035,032,037,036,035,032,034,034,' ','$','.',047,' ','"',',','#',034,034,'(','7',')',',','0','1','4','4','4',037,047,'9','=','8','2','<','.','3','4','2',0377,0333,0,'C',01,011,011,011,014,013,014,030,015,015,030,'2','!',034,'!','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','2','2','2','2','2','2','2','2','2','2','2','2','2','2','2','2','2','2',0377,0300,0,021,010,01,0,01,0,03,01,'"',0,02,021,01,03,021,01,0377,0304,0,037,0,0,01,05,01,01,01,01,01,01,0,0,0,0,0,0,0,0,01,02,03,04,05,06,07,010,011,012,013,0377,0304,0,0265,020,0,02,01,03,03,02,04,03,05,05,04,04,0,0,01,'}',01,02,03,0,04,021,05,022,'!','1','A',06,023,'Q','a',07,'"','q',024,'2',0201,0221,0241,010,'#','B',0261,0301,025,'R',0321,0360,'$','3','b','r',0202,011,012,026,027,030,031,032,'%','&',047,'(',')','*','4','5','6','7','8','9',':','C','D','E','F','G','H','I','J','S','T','U','V','W','X','Y','Z','c','d','e','f','g','h','i','j','s','t','u','v','w','x','y','z',0203,0204,0205,0206,0207,0210,0211,0212,0222,0223,0224,0225,0226,0227,0230,0231,0232,0242,0243,0244,0245,0246,0247,0250,0251,0252,0262,0263,0264,0265,0266,0267,0270,0271,0272,0302,0303,0304,0305,0306,0307,0310,0311,0312,0322,0323,0324,0325,0326,0327,0330,0331,0332,0341,0342,0343,0344,0345,0346,0347,0350,0351,0352,0361,0362,0363,0364,0365,0366,0367,0370,0371,0372,0377,0304,0,037,01,0,03,01,01,01,01,01,01,01,01,01,0,0,0,0,0,0,01,02,03,04,05,06,07,010,011,012,013,0377,0304,0,0265,021,0,02,01,02,04,04,03,04,07,05,04,04,0,01,02,'w',0,01,02,03,021,04,05,'!','1',06,022,'A','Q',07,'a','q',023,'"','2',0201,010,024,'B',0221,0241,0261,0301,011,'#','3','R',0360,025,'b','r',0321,012,026,'$','4',0341,'%',0361,027,030,031,032,'&',047,'(',')','*','5','6','7','8','9',':','C','D','E','F','G','H','I','J','S','T','U','V','W','X','Y','Z','c','d','e','f','g','h','i','j','s','t','u','v','w','x','y','z',0202,0203,0204,0205,0206,0207,0210,0211,0212,0222,0223,0224,0225,0226,0227,0230,0231,0232,0242,0243,0244,0245,0246,0247,0250,0251,0252,0262,0263,0264,0265,0266,0267,0270,0271,0272,0302,0303,0304,0305,0306,0307,0310,0311,0312,0322,0323,0324,0325,0326,0327,0330,0331,0332,0342,0343,0344,0345,0346,0347,0350,0351,0352,0362,0363,0364,0365,0366,0367,0370,0371,0372,0377,0332,0,014,03,01,0,02,021,03,021,0,'?',0,0367,0372,'(',0242,0200,012,'(',0242,0200,012,'(',0242,0200,012,'(',0242,0200,012,'*',0275,0345,0365,0256,0237,'n',0323,0335,0334,'E',04,'K',0311,'y',030,01,'^','W',0342,0217,0217,0336,036,0321,0314,0220,'i',021,'I',0252,0134,0216,03,')',0331,020,'?',0357,'w',0374,05,0,'z',0345,'V',0273,0324,'l',0254,'c','2',']',0335,0301,02,016,0255,'$',0201,'@',0374,0353,0345,015,'s',0343,0177,0214,0265,0215,0313,025,0324,'V',021,036,0211,'l',0230,' ','}','N','M','p','w',0332,0235,0376,0247,')',0226,0372,0362,'{',0227,'=',0345,0220,0267,0363,0240,017,0257,'u','/',0213,0276,06,0322,0311,'Y','u',0350,'%','q',0374,'6',0352,0322,0237,0374,'t',021,0134,0345,0317,0355,015,0340,0370,0230,0210,'a',0324,'g',0307,'q',010,'P',0177,'3','_','-','Q','@',037,'J',0311,0373,'H','h','*','~','M',036,0375,0276,0254,0242,0226,'?',0332,'?','@','b','<',0315,'"',0375,'>',0214,0246,0276,'i',0242,0200,'>',0247,0266,0375,0241,'<',033,'3',01,'4','z',0204,031,0356,0320,0206,03,0362,'5',0322,0351,0237,025,0374,021,0252,0220,' ',0327,0355,0243,'s',0374,'7',031,0210,0377,0,0343,0300,'W',0306,'t','P',07,0337,026,0367,0226,0267,'q',0207,0266,0270,0212,'e','=',014,'n',030,037,0312,0247,0257,0203,0264,0375,'g','S',0322,'e',022,'i',0367,0367,026,0314,'9',0375,0324,0205,0177,'J',0364,015,013,0343,0267,0214,'4',0202,0211,'s',',',032,0214,'#',0252,0334,'.',030,0377,0,0300,0205,0,'}','c','E','y','g',0205,0276,';','x','g',']',')',06,0241,0277,'J',0272,'n','1','1',0314,'d',0373,'7',0370,0342,0275,':',0336,0352,013,0270,'V','k','y',0222,'X',0233,0220,0350,0331,06,0200,'%',0242,0212,'(',0,0242,0212,0316,0324,'5',0313,015,'1',0202,0134,0316,03,0221,0235,0252,'2','h',03,'F',0212,0347,'O',0215,'4',0254,0377,0,0313,'o',0256,0312,'_',0370,'L',0364,0237,0357,'K',0377,0,'|','P',07,'C','E','b','E',0342,0315,'"','S',0217,0264,024,0377,0,'}','H',0255,013,'m','N',0306,0360,0342,0336,0352,047,'>',0201,0271,0240,013,'t','Q','E',0,024,'Q','E',0,024,'Q','E',0,024,'Q','X','~','(',0361,'f',0223,0341,035,'*','M','C','U',0271,021,0242,0217,0221,07,'/','!',0354,024,'w',0240,015,0231,'e',0216,030,0232,'I',']','Q',024,'e',0231,0216,0,025,0343,0376,'6',0370,0363,0245,'h',0376,'m',0237,0207,0325,'u',013,0301,0225,0363,0217,0372,0245,'?','_',0342,0374,'+',0311,'<','}',0361,'c','Y',0361,0244,0217,'m',033,'=',0226,0227,0236,'-',0221,0271,'q',0376,0331,0357,0364,0351,'^','}','@',033,'~','!',0361,'v',0273,0342,0233,0266,0270,0325,0365,031,0247,'$',0361,036,0354,'"',0373,05,034,012,0304,0242,0247,0264,0263,0271,0276,0270,'[','{','H','$',0236,'f',0340,'$','j','X',0237,0312,0200,' ',0242,0275,'C',0303,0177,02,'<','W',0255,0354,0226,0370,'E',0245,0333,'7',';',0247,0371,0237,0360,'A',0375,'H',0257,'Y',0320,0376,01,'x','O','L',010,0367,0306,0343,'Q',0230,'u','2',0266,0324,0377,0,0276,'G',0365,0240,017,0225,0325,'Y',0216,024,022,'}',0,0253,0326,0332,036,0257,'z','3','k',0245,0336,0316,'?',0351,0225,0273,0267,0362,025,0366,0276,0235,0341,'/',017,0351,'H',0253,'e',0243,0331,0302,027,0246,'"',031,0255,0205,'E','A',0205,'P',0243,0320,014,'P',07,0304,'P',0370,017,0305,0327,037,0352,0374,'3',0253,'}','Z',0315,0327,0371,0212,'I',0274,013,0342,0333,'s',0373,0317,014,0352,0343,0334,'Y',0310,0303,0363,02,0276,0337,0242,0200,'>',015,0271,0321,0265,'K','#',0376,0225,0246,0336,'A',0377,0,']','`','e',0376,'b',0251,0220,'T',0340,0202,017,0241,0257,0277,036,'4',0221,'J',0272,'+',03,0331,0206,'k',023,'R',0360,'g',0206,0365,'d','+','{',0243,'Y',0313,0236,0247,0312,0,0376,0224,01,0360,0355,025,0365,026,0275,0373,'>',0370,'g','Q','W','}','2','{',0215,'6','c',0310,0332,'|',0304,0317,0373,0247,0267,0320,0212,0362,'o',022,'|',020,0361,'v',0202,'^','H',' ',0217,'R',0266,'^','D',0226,0307,0346,0307,0272,0236,'G',0353,'@',036,'m',']',027,0206,'|','s',0342,037,011,']',',',0332,'V',0243,'*','F','>',0364,016,'w','D',0343,0320,0251,0343,0362,0346,0260,'g',0202,'k','i',0232,031,0342,'x',0245,'C',0206,'G','R',010,0374,'*',':',0,0372,0243,0301,037,034,'t','O',020,0230,0254,0365,'m',0272,'m',0373,'a','A','s',0373,0247,'>',0315,0333,0361,0257,'V','V',016,0241,0224,0202,017,' ',0216,0365,0360,015,'{',0227,0302,'_',024,'x',0256,0317,'J',0222,')',0346,0363,'t',0260,0270,0267,0373,'F','Y',0224,0377,0,0262,'}','(',03,0333,0274,'E',0342,'$',0322,0342,'0','@','C',']',0260,0343,0321,07,0251,0257,'0',0325,'5','T',0265,015,'=',0324,0214,0362,0271,0310,04,0345,0230,0322,'j',0272,0250,0265,'G',0271,0270,'s','$',0256,'x',04,0362,0306,0270,0213,0273,0271,'o','n',032,'i','N','I',0350,'=',07,0245,'c','R',0247,'.',0213,'s',0351,'2','<',0212,'X',0331,'{','j',0332,'S','_',0217,0374,017,'3','Z','_',024,0134,0226,0375,0324,'1','*',0377,0,0265,0222,'j','h',0274,'R','z','M','l','>',0250,0325,0316,'Q','X','{','I','w','>',0316,'Y',026,'_','(',0362,0373,'%',0375,'y',0235,'z','x',0222,0305,0207,0315,0346,'!',0364,'+',0232,0320,0263,0324,'m',0356,0216,0353,'i',0301,'a',0316,01,0303,012,0340,'+','G','C',022,0235,'Z',037,'+','#',0256,0357,0367,'{',0325,0306,0254,0257,0251,0344,'c',0370,'g',07,012,023,0251,'I',0270,0264,0233,0337,'M',017,'{',0360,0256,0266,'u','+','S','o',';','f',0346,021,0311,'?',0304,0276,0265,0320,0327,0231,0370,'V','G','O',020,0333,0354,04,0356,0312,0266,'=','1','^',0231,']','G',0347,0341,'E',025,0300,'k',036,',',0277,0373,'t',0261,'Z','H','!',0212,'6','*',010,'P','I',0307,'~','h','l',016,0376,0212,0363,0315,'7',0306,'7',0326,0322,0377,0,0246,'7',0332,'b',047,0234,0200,030,'}','1','Z','~','(',0370,0213,0243,'x','s',0303,017,0253,0274,0302,'G','o',0222,030,07,0337,'g',0364,0307,'o',0255,'+',0201,047,0216,0374,'{',0246,'x',033,'H','k',0233,0247,'Y','.',0334,037,'"',0330,037,0231,0317,0370,'{',0327,0310,0376,047,0361,'f',0257,0342,0355,'Q',0357,0365,'[',0223,'#',023,0362,'F','8','H',0307,0242,0212,0217,0304,0276,'#',0324,'<','U',0256,'O',0252,0352,'2',0227,0232,'S',0302,0347,0204,'^',0312,'=',0205,'d','S',0,0245,012,'Y',0202,0250,'$',0236,0,035,0352,0366,0217,0242,0352,032,0376,0247,016,0237,0246,'[','=',0305,0314,0247,012,0252,':','{',0223,0330,'{',0327,0323,0377,0,016,0376,015,0351,'~',024,0216,'+',0355,'M','#',0276,0325,0207,0315,0275,0227,')',011,0377,0,'d',036,0376,0364,01,0345,'~',07,0370,027,0253,0370,0205,'"',0275,0326,']',0364,0333,026,0303,04,'+',0373,0327,037,'C',0323,0361,0257,0240,0374,'3',0340,0217,017,0370,'J',0324,'C',0244,0351,0361,0306,0330,0371,0246,'a',0272,'G',0367,',','k',0242,0242,0200,012,'(',0242,0200,012,'(',0242,0200,012,'(',0242,0200,012,'(',0242,0200,012,'(',0242,0200,'9','_',025,0374,'<',0360,0347,0214,'-',0331,'5','+',024,023,0343,011,'s',020,0333,'"','~','=',0376,0206,0276,'x',0361,0317,0301,']','o',0302,0251,'%',0345,0201,'m','K','O','^','K',0306,0237,0274,'A',0356,0243,0372,'W',0326,'5',0207,0342,035,'z','-','&',03,032,'m',0222,0345,0307,0312,0207,0240,036,0246,0200,'>','P',0360,'W',0202,0344,0326,0246,'[',0353,0324,')','`',0207,' ',036,0262,0237,'O',0245,'z',0305,0335,0325,0276,0223,'d','0',0252,0252,0243,'l','q',0257,031,0366,0247,0352,032,0204,'6',020,0264,0262,'m',05,0211,'*',0212,'1',0223,0354,'+',0211,0274,0273,0226,0366,0341,0246,0224,0344,0236,0203,0260,036,0202,0261,0251,'S',0227,'E',0271,0364,'y',036,'G',',','l',0275,0255,']',')',0257,0307,0313,0323,0273,013,0273,0311,0257,'g','2',0314,0331,047,0240,0354,'>',0225,05,024,'W',')',0372,'D','!',030,'E','F','*',0311,05,024,'T',0366,0266,0262,0336,0134,',','1','.','X',0367,0354,07,0255,01,'9',0306,021,'r',0223,0262,'B','Z',0332,0313,'y',':',0303,012,0345,0217,0344,'+',0264,0323,'t',0330,0264,0370,02,'/',0315,'!',0373,0317,0216,0246,0227,'N',0323,'b',0323,0341,0331,030,0334,0355,0367,0233,0271,'5',0350,'^',032,0360,0270,'@',0227,0267,0361,0374,0377,0,'z','8',0233,0267,0271,0256,0252,'t',0355,0253,0334,0374,0347,'<',0317,'e',0214,'n',0215,027,'j','k',0361,0377,0,0200,'?',0302,':',024,0266,0214,'u',013,0221,0265,0235,'6',0306,0207,0250,07,0271,0256,0266,0212,'+','c',0346,0204,'=',015,'x',0217,0211,0311,'[','}','C',07,037,';',0177,0350,'U',0355,0307,0241,0257,022,0361,' ',0315,0276,0241,0376,0363,037,0326,0263,0253,0360,0263,0267,'-',0377,0,'}',0245,0376,'%',0371,0234,0265,0216,0277,'s','k',0204,0227,0367,0321,0217,'S',0310,0374,'k',0242,0315,0206,0267,'d',0321,0272,0244,0321,'0',0303,'#',0216,'E','p',0365,'$','3',0313,'o',' ',0222,027,'(',0303,0270,0256,'h',0324,'q','?','@',0315,'8','{',017,0214,'N','t',0375,0311,0367,'[','?','U',0372,0231,'~',047,0370,'o','5',0263,'=',0336,0217,0231,'a',0352,'`','?','y','~',0236,0242,0271,'=',013,0303,0232,0237,0210,0265,0270,0264,0235,'>',0331,0336,0351,0333,05,'H',0306,0301,0334,0267,0240,025,0354,0372,'o',0210,'#',0270,02,';',0254,'G',047,0367,0277,0205,0277,0302,0272,0257,016,'_',0332,0350,':',0224,0267,0221,0331,'B',0315,'p',02,0313,'"',0250,016,'@',0351,0315,'u','F','j','G',0347,0330,0334,06,'#',05,'S',0222,0274,'m',0347,0321,0372,'3',0250,0360,017,0303,0355,'3',0300,0332,'J',0303,02,',',0267,0322,'(','7',027,'$','|',0314,'}',07,0240,0256,0302,0252,'i',0372,0225,0256,0247,07,0233,'m','(','a',0335,'{',0257,0324,'U',0272,0263,0214,'(',0242,0212,0,'(',0242,0212,0,'(',0242,0212,0,'(',0242,0212,0,'(',0242,0212,0,'(',0242,0261,0365,0355,'z',035,036,0337,'h',0303,0334,0270,0371,023,0323,0334,0373,'P',03,'u',0375,'z','-','"',0337,'j',020,0367,'N','>','D',0364,0367,'5',0345,0272,0266,0255,0344,0254,0227,'w','R',031,'%','s',0300,047,0226,'4',0355,'[','V',0362,0374,0313,0273,0271,013,0310,0347,'<',0236,'X',0372,012,0341,'/','o','f',0276,0234,0313,'+','}',027,0262,0217,'J',0306,0245,'N',']',026,0347,0321,'d','y','$',0261,0322,0366,0265,'t',0246,0277,037,'%',0372,0260,0275,0275,0226,0372,0340,0313,')',0347,0260,035,0,0252,0364,'Q',0134,0247,0351,'0',0204,'i',0305,'B',012,0311,05,024,'U',0213,';','9','o',0256,04,'1',016,'{',0236,0300,'P',023,0234,'i',0305,0316,'n',0311,011,'i','i','5',0354,0342,'(','W','$',0365,'=',0207,0326,0273,'m',';','N',0216,0306,025,0212,'%',0335,'#','}',0346,0307,',','i',0266,'6','0','i',0266,0333,'T',0217,'W','v',0357,'W','~',036,'x',0307,'@',0325,'<','i','>',0223,0303,0334,',','{',0255,0245,047,0344,'v',037,'x',017,'|','c',037,0215,'u','S',0247,0313,0253,0334,0374,0343,'<',0317,'%',0215,0223,0243,'G','J','k',0361,0377,0,0201,0331,035,0327,0206,0374,'0','-',0202,0336,0337,'(','3',036,'R','3',0374,036,0347,0336,0272,0272,'(',0255,0217,0233,012,'(',0242,0200,012,0361,'_',022,017,0223,'R',036,0357,0374,0315,'{','U','y',016,0272,0326,'r',0353,0332,0235,0224,023,',',0206,'9','6',0310,0243,0252,0226,031,0307,0353,'Q','5','x',0330,0337,013,'Q','R',0257,012,0217,'d',0323,0374,'O','0',0242,0255,0352,032,'|',0332,'|',0345,'$',031,'S',0367,'X','t','"',0252,'W',021,0373,035,'*',0260,0253,05,':','n',0351,0205,'i',0351,0332,0325,0305,0216,020,0376,0362,037,0356,0223,0323,0351,'Y',0224,'P',0235,0210,0257,0207,0245,0210,0203,0247,'V','7','L',0364,'-','#',0134,06,'E',0270,0261,0270,'1',0312,0275,'W','8','#',0352,';',0212,0364,'=',027,0305,0360,']',0225,0202,0373,020,0314,'x',017,0374,'-',0376,025,0363,0354,'R',0311,014,0202,'H',0334,0253,016,0204,'W','E',0246,0370,0210,'1',021,'^',0340,036,0202,'A',0323,0361,0255,0341,'[',0243,'>',027,'4',0341,'z',0224,'o','S',011,0357,'G',0267,'U',0351,0337,0363,0365,'>',0210,0316,'F','E',025,0347,032,037,0213,'n',',','B','G','9',0373,'E',0257,'n','~','e',036,0306,0273,0333,035,'B',0333,'Q',0200,'K','m','(','u',0356,';',0217,0250,0256,0204,0356,'|',0233,'M',';','2',0325,024,'Q','L','A','E',024,'P',01,'E',024,'P',01,'E',025,0223,0256,'k',0220,0350,0366,0304,0222,032,0341,0207,0356,0343,0376,0247,0332,0200,023,']',0327,'b',0321,0355,0270,0303,0334,'8',0371,023,0372,0237,'j',0362,0335,'_','W',0330,'d',0274,0274,0220,0274,0216,'x',035,0311,0364,024,0272,0276,0256,'G',0231,'{','y','!','y',033,0267,'r','}',05,'p','w',0327,0323,'_',0316,'e',0224,0377,0,0272,0243,0240,025,0215,'J',0234,0272,'-',0317,0241,0311,'2','I','c',0247,0355,'*','i','M','~','>','K',0365,'a','}','{','-',0375,0311,0226,'C',0354,0253,0331,'E','V',0242,0212,0345,'?','J',0247,'N','4',0342,0241,05,'d',0202,0212,'*',0315,0225,0214,0327,0363,0210,0242,037,0357,'1',0350,05,01,'R',0244,'i',0305,0316,'n',0311,011,'g','e','5',0365,0300,0212,'%',0372,0267,'e',036,0265,0331,0332,'Y',0333,0351,0226,0244,'.',024,01,0227,0221,0273,0373,0232,0233,'K',0322,0304,013,035,0255,0254,'e',0344,'c',0216,07,',','k',0277,0267,0360,'E',0234,0332,035,0315,0246,0240,0242,'I','n',0242,'(',0347,0262,02,';','W','U',':','|',0272,0275,0317,0315,0263,0274,0362,'X',0331,'{','*','Z','S','_',0217,0233,0375,021,0363,'W',0215,0274,'t','u','/','7','M',0323,030,0255,0246,'J',0311,'/','O','3',0330,'{','W',037,0244,0352,'w',032,'6',0255,'k',0251,'Z','1','Y',0355,0344,022,'!',0372,'v',0251,0374,'G',0242,0134,'x','s',0304,'7',0332,'M',0322,025,0222,0332,'R',0234,0377,0,022,0366,'?',0210,0301,0254,0272,0330,0371,0323,0356,'o',012,'x',0202,0337,0305,036,032,0261,0325,0355,0230,024,0270,0214,026,037,0335,'a',0303,')',0367,07,'5',0263,'_','<','~',0316,0276,'(','1',']','_','x','n','y','>','I',0177,0322,' ',04,0364,'n',0214,07,0351,'_','C',0320,01,'E',024,'P',05,'-','_','Q',0213,'I',0321,0357,'5',011,0330,',','V',0320,0264,0254,'O',0240,031,0257,0213,0355,0374,'_',0177,'o',0342,0273,0235,'p','9','g',0271,0225,0236,'h',0311,0341,0224,0236,0237,0205,'}',027,0361,0357,'Z','m','3',0341,0343,0331,0306,0333,'d',0324,'&','X','z',0377,0,0,0371,0233,0371,01,'_',')','P',07,0277,0330,0337,0351,0336,'*',0322,'<',0310,034,'2',0237,0274,'?',0212,'6',0256,0177,'P',0323,047,0323,0345,0303,0215,0321,0237,0272,0343,0241,0257,'8',0360,0326,0251,0250,'i',0232,0315,0277,0366,'s',023,'$',0322,',','~','_','i','2','p',01,037,0215,'}','1',0253,'x','R',0362,0312,0325,'^','D','[',0210,'Y','F',0362,0243,0356,0236,0371,037,0326,0261,0235,'.','m','Q',0356,'e',031,0335,0134,0275,0362,'?','z',017,'u',0333,0315,036,'C','E','o','j','~',037,'x',0211,0232,0320,'n','N',0246,'>',0343,0351,0353,'X','$',020,'H','#',04,'W','3','M',';','3',0364,'|',036,'6',0206,'2',0237,0264,0243,'+',0257,0305,'z',0205,024,'Q','H',0352,'/',0351,0372,0255,0305,0203,0200,0254,'^','.',0350,0307,0217,0303,0322,0273,'=',027,0304,037,0275,'Y',0354,'n',014,'s','/','T',047,0237,0304,'w',025,0347,0264,0350,0344,'x',0234,'<','l','U',0207,' ',0203,'W',031,0270,0236,'&','i',0221,'a',0361,0311,0313,0341,0237,'u',0372,0367,'>',0207,0321,0274,'[','o','}',0266,033,0275,0260,'N','x',04,0237,0225,0217,0364,0256,0222,0276,'v',0323,0274,'F',016,0330,0257,'x','=',04,0200,'q',0370,0327,0240,0350,0236,',',0270,0260,'T','I','[',0355,026,0275,0271,0344,017,'c',']','0',0250,0244,'~','y',0217,0313,'q',030,031,0362,0326,0216,0235,037,'F','z','E',025,'V',0307,'Q',0265,0324,0240,022,0332,0312,034,'w',035,0307,0324,'U',0252,0320,0340,012,'(',0254,0315,'k','Y',0207,'G',0265,'.',0330,'i',0233,0375,0134,'~',0247,0374,'(',01,0272,0336,0267,06,0217,'m',0271,0260,0323,0260,0375,0334,'~',0276,0347,0332,0274,0267,'W',0325,0316,0351,'/','o','e',0334,0354,'x',036,0276,0302,0227,'Y',0326,'I','i','/',0257,'d',',',0307,0240,0365,0366,02,0270,'+',0373,0371,'u',013,0203,',',0234,01,0302,0257,'`','+','*',0225,'9','t','[',0237,'A',0222,'d',0223,0307,'O',0332,'T',0322,0232,0374,'|',0227,0352,0302,0372,0372,'k',0373,0203,',',0247,0217,0341,'^',0312,'*',0255,024,'W','!',0372,'U',':','p',0247,05,010,'+','$',024,'Q','V',0254,'l','&',0277,0234,'G',020,0371,0177,0211,0217,'E',024,05,'J',0220,0245,07,'9',0273,'$',026,'6','2',0337,0334,010,0243,034,'u','f',0354,05,'w',':','V',0222,'#',0331,'i','g',021,'y',034,0366,0352,0307,0324,0323,0264,0235,'$',0203,035,0235,0224,'E',0235,0277,'3',0356,'k',0324,'4','-',06,035,036,014,0234,'=',0313,017,0236,'O',0350,'=',0253,0256,0235,'>',']','^',0347,0346,0331,0336,'w','<','t',0275,0235,'=',')',0257,0307,0315,0377,0,0220,0315,03,0303,0361,'i',021,'y',0222,'a',0356,0230,'|',0317,0375,0337,'a','[','t','Q','Z',0237,'<','x',07,0355,025,0341,'e',0305,0227,0211,0240,'L','7',026,0367,04,016,0243,0370,'I',0376,'U',0363,0375,'}',0267,0343,0335,07,0376,022,'O',04,'j',0272,'j',0250,'2',0311,03,'4','Y',0354,0340,'e',0177,'Q','_',022,0262,0225,'b',0254,010,' ',0340,0203,0332,0200,'6','<',047,0256,'I',0341,0277,024,0351,0332,0264,'l','G',0331,0346,'V','p',';',0247,'F',037,0226,'k',0356,033,'i',0343,0272,0266,0212,0342,'&',015,034,0250,035,030,'w',04,'d','W',0300,0265,0366,037,0301,0335,'_',0373,'_',0341,0246,0226,'Y',0267,'I','l',0237,'g','l',0365,0371,'x',037,0245,0,'w',0224,'Q','E',0,'|',0347,0373,'H','j','f','M','[','I',0323,03,'|',0261,0306,0323,'0',0367,047,03,0372,0327,0206,'W',0244,0374,'t',0275,0373,'_',0304,0373,0310,0363,0221,'o',024,'q','~',0231,0376,0265,0346,0324,01,0336,'|',035,0321,0227,'Y',0370,0233,0245,0243,0250,'h',0255,'X',0335,'8','#','?','p','q',0377,0,0217,025,0257,0260,0210,04,'`',0214,0212,0371,0307,0366,'n',0260,0363,'5',0335,'^',0375,0227,0375,'T',013,032,0267,0324,0222,0177,0220,0257,0243,0350,03,0226,0326,0374,'!',015,0336,0353,0213,014,'E','7','S',037,0360,0267,0370,'W',0231,'k','~',033,'-','3',0254,0221,0233,'{',0245,0353,0221,0303,'}',0177,0306,0275,0332,0250,0352,'Z','M',0246,0253,017,0227,'s',030,'$','}',0327,037,'y',0177,032,0231,'A','I','j','t',0341,'1','u',0260,0225,025,'J','2',0263,0376,0267,'>','f',0272,0264,0232,0316,'c',024,0310,'U',0273,'z',037,0245,'C','^',0261,0342,'?',011,'I','j',0204,'O',027,0237,'j','O',0313,'(',034,0257,0327,0322,0274,0353,'S',0321,0246,0261,'b',0351,0231,'!','=',030,016,'G',0326,0271,047,'M',0304,0375,023,')',0342,012,'8',0333,'S',0251,0356,0324,0355,0321,0372,0177,0221,0231,'E',024,'T',037,'@',025,'z',0303,'U',0271,0260,'`',021,0267,'G',0335,033,0247,0377,0,'Z',0250,0321,'A',0235,'j','4',0353,'A',0323,0250,0256,0237,'F','z',016,0215,0257,0346,'A','5',0224,0355,024,0303,0252,0347,0237,0313,0270,0257,'F',0321,'|',']','o','z',026,033,0335,0260,'O',0323,'w',0360,0267,0370,'W',0317,'H',0355,033,0207,'F','*',0303,0220,'A',0344,'W','A',0247,'x',0217,01,'c',0274,0317,0247,0230,07,0363,0255,0341,'[',0244,0217,0205,0315,'8','^','t',0357,'S',07,0252,0376,'^',0253,0323,0277,0347,0352,'{',0316,0261,0254,0333,0351,026,0236,'c',0220,0322,'0',0375,0332,03,0313,0177,0365,0253,0313,'5',0215,'a',0235,0244,0276,0275,0227,',','z',017,0350,'*',013,0275,'J','8',0355,'E',0304,0323,0227,'E','P',027,0346,0317,0320,012,0342,'u',013,0371,'u',013,0203,'#',0222,024,'p',0211,0331,'E','i','R',0252,'K','M',0316,014,0233,'#',0251,0215,0251,0315,'U','Z',013,0177,'?','%',0372,0205,0376,0241,'6',0241,'9',0222,'C',0205,037,'u',07,'E',025,'R',0212,'+',0220,0375,'*',0235,'8','R',0202,0204,025,0222,012,'(',0253,0232,'v',0237,'.',0241,'p','#','@','B',017,0274,0370,0340,012,02,0255,'X','R',0203,0251,'Q',0331,'!','4',0375,'>',']','B',0177,'.','>',024,'}',0346,'=',05,'w',0232,'6',0214,0314,'c',0262,0262,0214,0263,036,0247,0371,0223,'O',0321,'4','F',0226,'D',0262,0261,0213,0223,0313,'7',0365,'&',0275,'G','G',0321,0255,0364,'{','o','.','1',0272,'F',0373,0362,021,0313,037,0360,0256,0272,'t',0371,'u','{',0237,0232,'g','Y',0334,0361,0363,0366,'t',0364,0246,0277,037,'7',0376,'C','4','M',022,015,036,0333,012,03,0316,0303,0347,0223,034,0237,'a',0355,'Z',0264,'Q','Z',0236,0,'Q','E',024,0,036,'F','+',0342,0237,0210,0332,'8',0320,0376,' ','k',026,'J',0273,'c',0373,'A',0222,'1',0376,0313,'|',0337,0326,0276,0326,0257,0226,0377,0,'h','[',01,'m',0343,0310,'.',0200,0307,0332,'m','T',0237,0252,0234,0177,'Z',0,0362,'J',0372,'#',0366,'n',0325,014,0232,'v',0257,0245,0263,'g',0312,0221,'f','Q',0350,030,'c',0371,0212,0371,0336,0275,'w',0366,'w',0275,'0','x',0372,0346,0323,'8',027,'6','m',0370,0225,' ',0377,0,0215,0,'}','E','E',024,'P',07,0306,037,024,0346,'3',0374,'N',0327,0334,0366,0271,'+',0371,0,'?',0245,'q',0365,0324,'|','H',05,'~','$','x',0204,'7','_',0267,'H',0177,'Z',0345,0350,03,0350,0337,0331,0262,' ','4','M','j','l','r','n','U',0177,0361,0321,'^',0345,'^',')',0373,'7',0262,0237,013,'j',0240,'u',027,'c','?',0367,0310,0257,'k',0240,02,0212,'(',0240,06,0274,'i','"',024,'u',014,0244,'`',0202,'8','5',0307,0353,0236,015,'W',015,'>',0234,07,'<',0264,07,0241,0372,0177,0205,'v','T','P',033,037,'?',0352,0376,031,'>','c',030,'S',0311,0234,037,0232,'6',030,07,0374,'+',0226,0236,0336,'[','i',014,'s','!','V',036,0265,0364,0266,0255,0241,0331,0352,0321,'b','T',0333,'(',037,',',0253,0324,0177,0215,'y',0277,0210,'|',')','%',0260,'+','w',017,0231,016,'~','I',0224,'t',0377,0,012,0302,'t','z',0304,0372,0234,0257,0211,0252,0341,0355,'O',023,0357,'G',0277,'U',0376,'g',0226,0321,'Z',0272,0226,0211,'=',0231,'2',047,0357,'!',0365,03,0221,0365,0254,0252,0346,'j',0332,'3',0357,'p',0330,0252,'8',0232,'j',0245,031,']',05,024,'Q','A',0270,'Q','E',024,0,'Q','E','Z',0323,0254,0215,0375,0342,0300,033,'h',0306,'I',0366,0240,0212,0225,'#','J',016,'s','v','K','V',';','N',0323,'f',0324,'&',0332,0203,010,'>',0363,0236,0202,0275,07,'C',0320,0236,0345,0322,0316,0312,'<','(',0373,0316,'z',017,'s','N',0320,'4',06,0272,0221,',',0254,0324,'*','/','.',0347,0260,0365,'5',0352,'Z','f',0231,'o',0245,0332,',',020,'/',0373,0314,'z',0261,0365,'5',0327,'N',0237,'.',0257,'s',0363,'<',0353,':',0236,'>','|',0220,0322,0232,0331,'w',0363,'c','4',0255,'&',0337,'I',0265,021,'B',0271,'c',0367,0334,0365,'c','W',0350,0242,0265,'<',020,0242,0212,'(',0,0242,0212,'(',0,0257,0236,'?','i','H',0,0277,0321,047,0307,'&','7',0134,0376,'"',0276,0207,0257,0237,0377,0,'i','R','?',0342,'D',';',0376,0363,0372,'P',07,0200,'W',0241,0374,021,0270,0362,'>','+','i','C',0376,'z',0254,0261,0377,0,0344,'6','?',0322,0274,0362,0273,0237,0203,0200,0237,0213,':',016,'?',0347,0244,0237,0372,')',0350,03,0354,'j','(',0242,0200,'>','4',0370,0265,01,0267,0370,0243,0256,0256,'0',032,'p',0343,0361,'P','k',0213,0257,'R',0370,0373,'`','m','~','$','=',0306,0334,'-',0315,0262,'>','}','H',0310,'?',0310,'W',0226,0320,07,0321,037,0263,'d',0343,0373,'3','Z',0267,0317,'"','d','|',0177,0300,'q',0375,'+',0335,0353,0346,'_',0331,0317,'Q','X','<','_',0250,'X','3','`',0334,0332,0357,'Q',0356,0207,0377,0,0262,0257,0246,0250,0,0242,0212,'(',0,0242,0212,'(',0,0246,'I',022,'M',033,'G','"',07,'F',030,'*','F','A',0247,0326,'v',0261,0254,'A',0244,'Z',0231,'$','!',0244,'o',0365,'q',0347,0226,'?',0341,'@',034,0177,0212,'t',033,'M','8',011,0340,0225,'T','H','q',0344,'7','?',0227,0265,'y',0247,0210,'t',0350,'`',0205,'.','a',01,'2',0333,'Y','G','N','{',0327,'U',0253,'j',0357,';',0311,'{','}','/',0377,0,'[',0330,012,0340,0265,'=','N','M','F','l',0237,0226,'%','?','"',0177,0217,0275,'a','Y',0306,0326,0352,'}','O',013,0341,'q','R',0304,'{','h',';','A','o',0331,0371,0177,'[',024,'h',0242,0212,0346,'?','D',012,'(',0242,0200,012,0232,0322,0352,'K',';',0205,0236,'#',0363,'/',0257,'C','P',0321,'A','3',0204,'g',027,031,'+',0246,'w',032,'/',0211,'w','N',0217,024,0246,0336,0344,'t',031,0353,0376,'5',0351,'Z','7',0214,'a',0271,'+',05,0376,'"',0220,0360,'$',037,'t',0375,'}','+',0347,0320,'H','9',07,04,'V',0346,0233,0342,011,'!','+',025,0327,0317,037,'M',0375,0307,0370,0326,0320,0254,0326,0222,'>','#','5',0341,'v',0257,'W',05,0377,0,0200,0377,0,0227,0371,037,'H','+',06,0,0251,04,036,0204,'R',0327,0227,'h','>','*',0236,0311,'S',0313,0220,0134,'Z',037,0340,047,0247,0320,0366,0257,'C',0323,0265,'k','M','R',021,'%',0274,0231,'?',0304,0207,0206,'_',0250,0256,0224,0356,0256,0217,0215,0224,'e',06,0343,'%','f',0213,0324,'Q','E','2','B',0212,'(',0240,02,0276,'r',0375,0244,0256,'C','k',':','5',0267,'u',0205,0330,0376,'$','W',0321,0265,0362,0217,0307,0275,'G',0355,0237,021,'^',0334,020,'V',0326,04,'O',0241,'<',0237,0351,'@',036,']','^',0221,0360,'*',0337,0317,0370,0251,'`',0330,0317,0225,024,0262,'}','>','B','?',0255,'y',0275,'{','7',0354,0345,'a',0347,'x',0303,'Q',0276,'+',0305,0275,0246,0300,'}',0335,0207,0377,0,023,'@',037,'L',0321,'E',024,01,0340,037,0264,0226,0227,0362,0350,0332,0252,0216,0357,03,037,0324,0177,'*',0371,0376,0276,0277,0370,0317,0240,0266,0271,0360,0333,'P',0362,0223,'t',0366,'{','n',0220,016,0277,')',0371,0277,0361,0334,0327,0310,024,01,0325,0374,'5',0326,0306,0201,0361,017,'F',0276,'v','+',027,0236,'"',0227,037,0335,0177,0224,0347,0363,07,0360,0257,0265,07,'"',0276,01,'F','(',0352,0312,'p','T',0344,032,0373,'o',0300,0272,0362,'x',0223,0301,0232,'f',0244,0255,0271,0236,020,0262,'s',0310,'q',0301,0240,016,0216,0212,'(',0240,02,0212,'+',';','X',0326,' ',0322,'-',014,0262,035,0322,036,022,'<',0362,0307,0374,'(',0,0326,'5',0213,'}','"',0327,0314,0220,0356,0221,0270,0216,'1',0325,0217,0370,'W',0226,0353,032,0303,'M','$',0227,0327,0322,0365,0350,'=','=',0,0243,'Y',0326,0232,'W',0222,0372,0372,'^','{',01,0374,0200,0256,013,'Q',0324,'e',0324,'g',0336,0374,' ',0373,0251,0351,'Y','T',0251,0313,0242,0334,0367,'r',0134,0226,'x',0371,0363,0317,'J','k','w',0337,0311,013,0251,'j','R',0352,'3',0226,'b','V','!',0367,023,0323,0377,0,0257,'T','h',0242,0271,033,0271,0372,'e',032,'0',0243,05,'N',0232,0262,'A','E',025,'=',0245,0254,0267,0227,013,014,'K',0222,'z',0236,0300,'z',0320,'T',0347,030,'E',0312,'N',0311,011,'o','k','=',0323,025,0202,'&',0220,0201,0223,0216,0325,033,0243,'F',0345,035,'J',0260,0352,010,0257,'G',0320,'4',011,'%','+','e','d',0200,0266,'2',0356,'z','}','I',0250,0265,0277,017,0260,'v',0202,0372,03,034,0243,0356,0276,'?',0221,0357,'[','{',031,'Z',0375,'O',0222,0134,'[','K',0353,016,016,037,0273,0350,0372,0372,0330,0363,0272,'*',0365,0376,0227,'q',0247,0267,0316,'7','G',0331,0307,'J',0243,'X',0237,'U','F',0265,':',0360,'U',')',';',0247,0324,'(',0242,0212,015,'K','6','w',0363,0330,0311,0276,026,0343,0272,0236,0206,0273,'-',037,'_',022,'H',0222,'A','1',0202,0345,'y',0300,'l',037,0376,0275,'p',0224,0252,0305,030,'2',0222,010,0344,021,0332,0252,'3','q',0330,0361,0363,'L',0227,017,0217,0215,0336,0223,0356,0277,'^',0347,0320,0232,047,0213,0241,0273,0333,05,0366,'"',0230,0360,037,0242,0267,0370,'W','Q',0326,0276,'t',0323,'<','D','A','X',0257,':','t',022,0177,0215,'z','&',0205,0342,0311,0254,'B','G',';',031,0355,'{',034,0344,0250,0366,0256,0270,'T','R','?','9',0307,0345,0270,0214,014,0371,'+','-',':','>',0214,0364,'z','*',013,'K',0310,'/','`','Y',0255,0344,'W','F',0356,'*','z',0263,0200,'l',0216,0261,0306,0316,0347,012,0240,0222,'O','`','+',0341,0317,027,'j',0377,0,0333,0336,'.',0325,'u','@','r',0267,027,014,0310,'s',0374,'9',0302,0376,0200,'W',0325,0277,026,0265,0377,0,0370,'G',0376,036,'j','r',0244,0233,047,0270,0214,0333,0304,'G',0134,0267,031,0374,0263,'_',034,'P',01,'_','K','~',0316,'z','W',0331,0374,'/',0250,'j','L','9',0272,0270,0332,0247,0331,'F','?',0236,'k',0346,0232,0373,'O',0341,0256,0212,0332,017,0303,0355,'"',0316,'T',0331,'9',0201,'e',0225,'}',031,0271,'#',0365,0240,016,0262,0212,'(',0240,010,0256,' ','K',0233,'i',' ',0220,02,0222,')','V',07,0270,'#',025,0361,017,0214,'4',031,'<','5',0342,0275,'G','J','p','@',0206,'S',0263,0335,017,'#',0364,0257,0270,0353,0347,0357,0332,'#',0302,'M',0276,0317,0305,026,0261,0344,'c',0354,0367,'X',035,'?',0272,0337,0314,'~','T',01,0340,'5',0357,037,0263,0277,0213,0204,'7','W','~',027,0272,0223,013,'(',0363,0355,'s',0375,0341,0367,0227,0371,037,0316,0274,036,0264,'4','=','^',0343,'A',0326,0355,'5','K','V',0304,0326,0322,07,036,0376,0243,0362,0240,017,0273,0350,0254,0217,014,'x',0202,0323,0304,0376,036,0263,0325,0354,0234,'4','S',0246,'H',037,0302,0335,012,0237,'p','j','m','c','X',0203,'H',0264,'2',0312,'w','H','x','H',0307,'V','?',0341,'@',06,0257,0254,'[',0351,026,0246,'I','H','2',037,0271,036,'y','c','^','Y',0255,'k','M','+',0311,'{','}','.','I',0350,0277,0320,012,'M','s',0134,'2','H',0367,0267,0262,'e',0217,012,0243,0371,01,0134,015,0375,0374,0267,0367,06,'I',011,0333,0374,'+',0331,'E','e','R',0247,'.',0213,'s',0350,'2',0134,0222,'x',0351,'{','J',0232,'S','_',0217,0222,0377,0,'1','u',015,'F','m','B','m',0362,034,'(',0373,0250,':',012,0251,'E',025,0310,'~',0225,'J',0224,')','A','S',0246,0254,0227,'@',0242,0212,'r','#','J',0352,0210,0245,0231,0216,0,035,0350,'-',0273,'j',0307,'A',04,0227,'3',',','Q',')','g','c',0300,0256,0367,0303,0372,013,0253,'G','i','l',0236,'e',0304,0237,'}',0261,0376,'x',025,'[','A',0320,0332,02,0221,0242,'y',0227,'r',0361,0307,'o','a','^',0273,0240,'h','q',0350,0366,0277,'6',032,0345,0307,0357,037,0372,017,'j',0351,0245,'N',0336,0363,'?',';',0342,014,0357,0353,'R','x','z',017,0334,'[',0276,0377,0,0360,013,032,'>',0223,016,0221,'d','!',0214,02,0347,0231,037,0273,032,0232,0373,'O',0266,0324,'m',0314,'7','1',07,'S',0320,0367,037,'C','V',0250,0255,0317,0226,'<',0317,']',0360,0264,0372,'z',0273,05,0373,'E',0241,0376,',','r',0243,0334,'W',0237,'j','~',037,')',0231,'l',0306,'W',0251,'N',0377,0,0205,'}',030,'@','`','A',0,0203,0324,032,0344,0265,0277,010,'$',0273,0256,'4',0340,021,0372,0230,0273,037,0247,0245,'g',':','j','g',0241,0227,0346,'x',0214,04,0371,0251,'=',':',0256,0214,0360,022,010,'$',021,0202,':',0212,'J',0355,0265,'m',02,'9',0335,0301,0214,0301,'r','8','9',030,0347,0334,'W',037,'s','i','5',0234,0246,'9',0220,0251,0354,'{',037,0245,'r',0312,016,'.',0314,0375,'#',',',0316,'0',0371,0204,'}',0307,'i','u','O',0177,0370,'(',0206,0212,'(',0251,'=','P',0255,015,';','W',0236,0301,0202,0347,'|',']',0320,0237,0345,'Y',0364,'P','c','_',017,'K',021,'M',0323,0253,033,0246,'z','f',0201,0342,'I',' ','e',0270,0261,0233,0217,0343,0211,0272,037,'b','+',0323,'4','}','v',0327,'W',0213,0344,';','&',03,0346,0210,0365,037,'O','Q','_','6','A','q','-',0264,0242,'H',0134,0253,017,'N',0365,0327,0350,0336,'"',022,0312,0230,0220,0301,'t',0277,'t',0203,0214,0375,'+',0246,0235,'k',0351,'#',0363,0374,0337,0207,'j','a','o','W',017,0357,'C',0361,'_',0346,0274,0316,'w',0366,0200,0361,'h',0325,'<','G',016,0201,'k','&',0350,'4',0361,0231,0260,'x','2',0236,0337,0200,0376,'u',0343,'u',0350,0236,'7',0360,'5',0314,'s',0334,'j',0366,06,'K',0205,0221,0332,'Y',0320,0374,0316,011,'9','$','z',0327,0235,0220,'A',0301,0353,'[',0237,'0','u',0277,015,'|','8',0336,047,0361,0326,0235,'d','T',0230,022,'A','4',0377,0,0356,')',0316,'?',036,05,'}',0242,0,'P',0,030,03,0201,'^',047,0373,'<',0370,'Q',0254,0264,';',0257,021,']','E',0266,'K',0326,0362,0355,0362,'9',0362,0327,0251,0374,'N',0177,'*',0366,0332,0,'(',0242,0212,0,'+','7',0304,032,'5',0277,0210,'4','+',0275,'.',0351,'C','E','q',031,'C',0236,0307,0261,0255,'*','(',03,0341,'-','{','F',0272,0360,0366,0273,'y',0245,'^','!','I',0255,0244,'(','s',0334,'v','?','B','9',0254,0332,0372,'c',0343,0277,0200,0206,0257,0245,0177,0302,'K','a',016,'o',',',0323,0375,' ','(',0345,0342,035,0377,0,012,0360,'o',013,0370,'^',0353,0304,'w',0301,'U','Y','-',020,0376,0366,'l','p','=',0207,0275,0,'z',047,0300,0337,025,'j','Z','4',0267,0326,'f',0335,0347,0322,0334,'o',0316,0354,010,0345,0366,0372,0367,0256,0337,']',0327,'Y',0331,0357,'o','d',0334,0307,0205,'A',0374,0205,'d',0306,0232,0177,0206,0264,0250,0355,0340,'E',0216,'4',030,'T',035,'X',0372,0377,0,0365,0353,0225,0275,0275,0232,0372,'s',',',0315,0364,'^',0312,'+',032,0225,'9','t','[',0237,'E',0222,'d','r',0307,'K',0332,0325,0322,0232,0374,'|',0227,0352,0307,'_','_','K',0177,'?',0233,')',0343,0370,'T','t','Q','U','h',0242,0271,'O',0322,')',0323,0215,'8',0250,'A','Y',' ',0242,0212,'(',',','P',013,020,0,0311,'<',0,'+',0257,0320,0264,'S','l',026,'I',023,'u',0313,0360,0252,'9',0333,0355,0365,0252,0372,026,0217,0345,'*',0335,0334,047,0357,017,'(',0247,0370,'}',0376,0265,0353,'^',026,0360,0350,0265,'U',0277,0273,'O',0337,'0',0314,'h',0177,0200,'z',0375,'k',0242,0225,'?',0264,0317,0205,0342,',',0363,0236,0370,'L',';',0323,0355,'?',0321,'~',0245,0257,015,'x','}','t',0310,05,0305,0300,06,0351,0307,0375,0360,'=','+',0241,0242,0212,0350,'>','0','(',0242,0212,0,'(',0242,0212,0,0312,0325,0364,033,'M','^','#',0346,015,0223,01,0362,0312,0243,0221,0365,0365,0257,'6',0327,'|',';','%',0251,'6',0367,0320,0206,'C',0367,'d',035,017,0320,0327,0257,'T','7','6',0260,'^','@',0320,0317,032,0311,033,'u',04,'R','i','5','f',']',':',0223,0247,'%','8',';','5',0324,0371,0257,'R',0321,0247,0261,'%',0327,0367,0220,0366,'a',0324,'}','k','2',0275,0247,'^',0360,0234,0266,'!',0346,0266,06,'k','n',0245,'q',0222,0243,0337,0324,'W',0235,'j',0236,036,030,'i',0254,0307,'=','L',0177,0341,0134,0263,0244,0343,0252,0330,0373,0274,0243,0211,'c','V',0324,'q','z','K',0371,0272,'?','^',0337,0221,0315,0321,'J',0312,'U',0212,0260,' ',0216,010,'4',0225,0221,0365,0341,'@','$',034,0216,015,024,'P',07,'E',0245,'k',0305,'U','a',0274,'`','W',0240,'r','y',037,'Z',0243,0254,0374,'<',0261,0327,'u','[','[',0253,'Y','R',0324,'I','*',0213,0217,0356,0262,023,0311,036,0207,025,0227,'Z','z','f',0261,'5',0213,0204,'r','^',03,0325,'O','o',0245,'k','N',0253,0216,0217,'c',0344,'s',0236,033,0215,'k',0326,0302,0253,'K',0252,0350,0375,';','?',0300,0372,'?','L',0263,0266,0323,0364,0313,'k','K','@',0242,0336,030,0325,'#',013,0323,0,'U',0272,0363,'/',014,'x',0270,0332,0242,0251,0220,0315,'f',0335,0263,0222,0237,'O',0360,0257,'H',0266,0271,0206,0356,05,0232,011,03,0306,0303,' ',0212,0352,'M','5','t','|',024,0341,'*','r','p',0232,0263,'D',0264,'Q','E','2','B',0212,')',031,0225,020,0263,034,'(',031,'$',0366,0240,012,'Z',0275,0364,032,'~',0233,'4',0327,0,'2',0355,'*',020,0377,0,031,'=',0253,0305,0256,'&',0260,0320,0355,010,0202,04,0205,013,022,0220,0306,'1',0311,'9',0256,0213,0305,0276,'$','[',0231,'d',0231,0230,0213,'X','x',0211,'?',0274,'}','~',0246,0274,0272,0356,0356,'[',0331,0314,0262,0266,'I',0350,';',01,0351,'X',0325,0251,0313,0242,0334,0372,',',0213,'%','x',0351,0373,'Z',0272,'S','_',0217,0227,0371,0213,'y','y','-',0365,0301,0232,'S',0311,0350,';',01,0351,'U',0350,0242,0271,'O',0322,'a',010,0302,'*',020,'V','H','(',0242,0212,012,012,0350,0264,'-',034,0261,027,'w',')',0362,0365,0215,'O',0177,'z',0203,'D',0321,0315,0313,0255,0314,0343,020,0251,0312,0251,0376,'#',0376,025,0352,'^',027,0360,0351,0275,0221,'o','.','S',026,0250,'~','U','?',0306,0177,0302,0266,0245,'N',0372,0275,0217,0220,0342,034,0363,0331,047,0205,0303,0277,'{',0253,0355,0345,0353,0371,026,0374,'+',0341,0335,0373,'5',013,0304,0371,'G','1','#',016,0277,0355,032,0355,'i',0,0,0,06,0,0350,')','k',0250,0370,020,0242,0212,'(',0,0242,0212,'(',0,0242,0212,'(',0,0242,0212,'(',0,'#','5',0313,'k',0276,022,0216,0350,'5',0305,0200,'X',0346,0352,'c',0350,0255,0376,06,0272,0232,0243,0252,0353,032,'~',0207,'c','%',0356,0245,'w',025,0265,0272,014,0227,0221,0261,'@',036,'/',0254,'h',013,';',0272,0311,031,0202,0351,'x',0344,'c',0363,0256,':',0346,0326,'k','I','L','s','!','V',0355,0350,'~',0225,0241,0343,'O',0215,0221,'j','z',0374,'C','K',0323,0320,0351,0320,022,036,'W',030,0222,'o','q',0350,'=','+','b',0312,0377,0,'K',0361,'N',0235,0346,'@',0341,0324,0216,'W',0243,'!',0254,'*','Q',0276,0261,'>',0223,047,0342,032,0270,';','R',0255,0357,'C',0361,'^',0236,'^','G','#','E','h',0352,'Z','D',0332,'y',0334,'~','x',0211,0341,0300,0376,'u',0235,0134,0307,0350,'t','1',024,0261,024,0325,'J','R',0272,'a','E',024,'P','l','[',0261,0324,047,0261,0233,'|','m',0220,'x','e','=',0305,'z',047,0205,0374,'X',0366,0347,0314,0267,'m',0321,0237,0365,0260,'1',0257,'0',0251,'m',0356,'%',0265,0231,'e',0211,0212,0260,0375,'j',0341,'7',07,0241,0341,0346,0371,'%',034,'|','y',0227,0273,'5',0263,0357,0344,0317,0247,'4',0355,'J',0337,'S',0265,'Y',0355,0337,' ',0375,0345,'=','T',0372,032,0271,'^','5',0341,0237,021,'I',023,'%',0324,015,0206,030,022,0307,0236,015,'z',0355,0215,0354,'Z',0205,0234,'w','0',0234,0243,0214,0375,017,0245,'v','F','J','J',0350,0374,0316,0275,011,0320,0250,0351,'T','V','h',0261,0134,0337,0214,'u','/',0262,'i',0242,0325,011,022,0134,'q',0307,'e',035,'k',0244,0256,'S',0306,0366,017,'5',0244,027,0210,011,0362,'I','W',0307,0241,0357,0371,0377,0,':','f','G',0214,0370,0232,0345,0236,0365,'m',0301,';','#','P','H',0367,'5',0207,']','n',0271,0244,'5',0356,047,0200,017,'9','F',010,'?',0304,'+',0226,0232,011,'m',0337,'l',0261,0262,'7',0241,025,0305,'Q','5',047,'s',0365,',',0207,023,0207,0251,0202,0204,')','=','b',0265,']','o',0327,0357,'#',0242,0212,'*',017,'l','+','W','F',0322,'M',0374,0236,'d',0240,0210,024,0363,0376,0321,0364,0250,0364,0255,'*','M','B','`',0314,012,0300,0247,0346,'o','_','a','W',0374,'Q',0342,'{','O',012,0351,0313,014,'*',0255,'t',0313,0210,0241,035,0275,0317,0265,'k','N',0237,'6',0257,'c',0346,'s',0354,0361,'a','"',0350,'P',0177,0274,0177,0207,0374,021,0336,'(',0361,'m',0257,0205,0355,'R','8',0321,'%',0273,' ','y','p','g',0200,'=','O',0265,'u',0336,017,0370,0355,0341,0315,'N',010,0255,'u','e',032,'L',0352,02,0345,0271,0213,0360,'n',0337,0215,'|',0313,'{','{','q',0250,0336,'I','u','u',')',0222,'i',016,'Y',0215,'W',0256,0263,0363,0226,0333,'w','g',0337,'6',0267,'v',0327,0266,0351,'=',0254,0361,0317,013,0214,0254,0221,0260,'e','#',0352,'*','j',0370,'g','B',0361,'f',0273,0341,0251,'|',0315,'#','S',0270,0266,031,0311,'E','l',0241,0372,0251,0342,0275,'G','D',0375,0242,0265,0253,'U','H',0365,'m','6',013,0305,035,'^','6',0330,0307,0372,'P','#',0351,'Z','+',0311,'t',0277,0332,027,0302,'7',0213,0213,0350,0257,0254,033,0276,0370,0274,0305,0374,0324,0223,0372,'W','Y','i',0361,'C',0301,'7',0201,'|',0257,022,'X',02,0335,04,0222,'l','?',0223,'b',0200,':',0352,'+','!','<','U',0341,0371,'@',')',0254,0330,0260,'>',0223,0257,0370,0322,'I',0342,0317,017,'B',0244,0311,0255,'X','(',036,0263,0257,0370,0320,06,0305,025,0307,0336,'|','T',0360,'E',0222,0261,0223,0304,'v','O',0267,0250,0205,0374,0303,0377,0,0216,0346,0271,015,'W',0366,0210,0360,0255,0247,0313,0247,0332,0337,0337,0267,'f',010,'#','_',0374,'x',0347,0364,0240,017,'_',0252,0327,0332,0215,0226,0231,'j',0367,'W',0327,'P',0333,'@',0203,'-','$',0256,024,017,0304,0327,0315,0272,0347,0355,017,0257,0336,0253,'G',0245,'Y','[',0330,0251,0350,0354,'w',0260,0376,0225,0345,0372,0327,0210,0365,0217,021,0134,'y',0372,0266,0243,'=',0333,0216,'G',0230,0334,'/',0320,'t',024,01,0364,'?',0213,0276,'?',0350,0332,'b','=',0276,0201,037,0366,0215,0317,'A',')',0342,' ','}','}',0353,0300,'<','I',0342,0355,'s',0305,0227,0315,'u',0253,0337,0311,'9',047,0345,0217,'8',0215,07,0242,0257,'A','X','t','P',01,'W','4',0315,'V',0367,'H',0274,'K',0253,')',0332,')',024,0366,0350,0336,0304,'w',024,0232,'v',0233,'{',0253,0336,0245,0235,0205,0264,0227,027,016,'p',0261,0306,0271,'&',0276,0201,0370,'s',0360,'*','+',037,'+','T',0361,'Z',0244,0267,' ',0206,0216,0310,034,0252,'z','n','=',0317,0265,0,'3',0303,'m',0251,'x',0213,0302,0303,'U',0271,0322,0245,0206,03,0362,0261,'e',0371,'_',0375,0240,':',0343,0336,0261,0265,']',05,0241,015,'=',0240,'-',037,'t',0356,'>',0225,0364,'b','C',034,'p',0210,0221,025,'c','Q',0264,'(',030,0,'z','b',0271,'-','{',0302,'+',' ','k',0235,'9','@','~',0255,017,'c',0364,0377,0,012,0316,'t',0324,0317,'K','.',0315,'+',0340,'*','s','S','z','u',']',037,0365,0334,0360,'*','+',0256,0326,'<','>','%','g','x','S',0313,0270,'S',0363,'!',030,0315,'r',0222,0305,'$','2',024,0221,012,0260,0352,015,'r','J','.','.',0314,0375,'+','.',0315,'(','c',0351,0363,'R','z',0365,']','W',0365,0334,'e',024,'Q','H',0364,'M',015,032,0363,0354,'z',0202,022,'p',0217,0362,0265,'{',027,0202,'u',023,035,0324,0226,',',0337,'$',0243,'r',017,'q',0377,0,0326,0257,015,07,07,'"',0275,037,0303,'Z',0201,'V',0323,0357,013,'`',0253,'.',0363,0370,0340,0326,0364,'%',0253,'G',0304,'q','v',021,'.','L','J','[',0350,0377,0,'5',0372,0236,0315,'H',0350,0262,'!','G','P',0312,'F',010,'#',0255,'-',025,0322,'|','I',0310,'j',0336,013,'Y','d','i',0264,0347,'X',0311,0344,0304,0335,'?',03,0332,0271,0351,0374,'#',0252,'t','{','!',' ',036,0204,032,0365,012,'(','j',0373,0225,031,'J','.',0361,'v','g',0224,0177,0302,'/',0250,016,'?',0263,033,037,0356,0212,'r',0370,':',0376,'C',0377,0,' ',0305,037,0357,05,0257,'U',0242,0247,0226,'=',0215,026,'&',0262,0332,'o',0357,'g',017,0244,'x','%',0326,'E','{',0375,0211,022,0377,0,0313,'$',0357,'V',0374,'W',0360,0343,0303,0236,'.',0261,020,'^',0331,0254,'S','"',0355,0212,0346,020,026,'H',0377,0,036,0343,0330,0327,'[','E','Q',0223,'m',0273,0263,0345,'O',027,'|',013,0361,026,0203,0276,'}',',',0177,'j','Z',016,0177,'v','1',' ',036,0353,0337,0360,0257,'.',0232,031,'m',0345,'h',0246,0215,0343,0221,'N',031,035,'H',' ',0373,0203,'_','~','W',';',0342,037,02,0370,'o',0305,0,0235,'W','I',0267,0232,'l','`','N',020,',',0203,0376,04,'9',0240,'G',0304,'T','W',0321,032,0377,0,0354,0343,'k','.',0351,'4','-','U',0340,'n',0321,0134,0215,0313,0371,0216,'k',0315,'5',0257,0203,'~','6',0321,0213,'1',0322,'Z',0362,'!',0377,0,'-',',',0330,'I',0307,0323,0257,0351,'@',034,025,025,'v',0343,'F',0325,',',0311,027,':','m',0344,'$','u',022,0300,0313,0217,0314,'U','2',012,0234,020,'A',0364,'4',0,0224,'Q','E',0,024,'R',0252,0263,034,'(','$',0372,012,0277,'k',0241,'j',0367,0254,026,0327,'K',0275,0234,0236,0202,'8',031,0277,0220,0240,014,0372,'+',0320,0364,'_',0202,0236,'6',0326,012,0263,0351,0253,'a',021,0376,';',0267,010,'q',0376,0350,0311,0375,'+',0323,'4',017,0331,0323,'M',0267,0333,'&',0271,0251,'I','v',0343,0254,'p',015,0211,0371,0365,0240,017,0235,0255,'m','.','/','n',022,0336,0326,011,047,0231,0316,026,'8',0324,0263,037,0300,'W',0254,'x','K',0340,036,0271,0254,04,0270,0326,0344,0376,0314,0266,'<',0354,0306,0351,'H',0372,'t',025,0364,'.',0201,0340,0375,03,0303,'1',0355,0322,'t',0273,'{','f','?','z','E','A',0275,0276,0255,0326,0267,'(',03,0235,0360,0267,0202,'4',037,07,0331,0210,'4',0233,'$','G',0307,0317,';',0215,0322,'9',0365,'-',']',025,024,'P',01,'E',024,'P',06,'&',0267,0341,0333,'}','Y',013,0256,'"',0271,03,0211,0,0353,0354,'k',0313,0274,'A',0341,0307,022,030,'.',0243,'1','N',0277,'u',0307,'C',0376,'"',0275,0262,0251,0352,':','e',0266,0251,'l','a',0270,0214,'7',0367,'[',0272,0237,'j',0231,'E','I','Y',0233,0341,0361,025,'p',0365,025,'Z','N',0315,037,'3','^','Y',0315,'e','9',0212,'U',0301,0354,'{',021,'U',0353,0325,'|','I',0341,'W',0264,0314,'w',021,0371,0266,0354,'~','I','@',0351,0365,0364,'5',0347,'Z',0226,0225,'6',0237,'&','N','Z','#',0321,0307,0365,0256,'I',0323,'p',0364,'?','F',0311,0363,0352,'X',0344,0251,0324,0367,'j','~',017,0323,0374,0214,0372,0353,0374,'6',0373,0264,0275,0277,0335,'r','+',0220,0256,0263,0303,037,0361,0341,047,0373,0377,0,0322,0235,'/',0215,023,0305,021,'O','/','o',0263,'G',0320,'4','Q','E','v',037,0232,05,024,'Q','@',05,024,'Q','@',05,024,'Q','@',05,024,'Q','@',05,024,'Q','@',021,0313,'o',014,0343,023,'C',034,0203,0321,0324,032,0313,0271,0360,0256,0201,'w',0237,'?','G',0262,0223,'>',0260,0212,0330,0242,0200,'9','w',0370,'s',0340,0371,'>',0367,0207,0254,'?',0357,0320,0245,'O',0207,'^',020,0214,0202,0276,037,0260,0343,0376,0231,012,0351,0350,0240,014,0233,0177,014,'h','V',0230,0362,'4',0213,'8',0361,0351,010,0255,'8',0341,0212,025,0333,024,'H',0203,0321,'T',012,'}',024,0,'Q','E',024,0,'Q','E',024,0,'Q','E',024,0,'Q','E',024,0,'Q','E',024,0,0311,0241,0216,'x',0232,')','Q',']',030,'`',0253,016,015,'p','^','"',0360,'y',0205,'^','[','H',0314,0326,0307,0357,'D','F','J',0377,0,0210,0257,'@',0242,0206,0256,'4',0334,']',0326,0347,0316,'z',0207,0206,0335,'d','/','i',0202,0247,0252,'1',0344,'}','+','[','F',0262,'{','+',021,024,0237,0353,030,0356,' ','W',0256,'j','>',027,0323,0265,027,'2',024,'h','e','=','^','>','3',0365,035,'*',013,037,07,0330,'Y',']',',',0345,0344,0230,0241,0312,0253,0343,0,0372,0326,'Q',0245,030,0312,0350,0365,'q','9',0316,'+',023,0206,'X','z',0256,0351,'u',0352,0375,'O',0377,0331,0377,0341,015,'7','h','t','t','p',':','/','/','n','s','.','a','d','o','b','e','.','c','o','m','/','x','a','p','/','1','.','0','/',0,'<','?','x','p','a','c','k','e','t',' ','b','e','g','i','n','=','"',0357,0273,0277,'"',' ','i','d','=','"','W','5','M','0','M','p','C','e','h','i','H','z','r','e','S','z','N','T','c','z','k','c','9','d','"','?','>',' ','<','x',':','x','m','p','m','e','t','a',' ','x','m','l','n','s',':','x','=','"','a','d','o','b','e',':','n','s',':','m','e','t','a','/','"',' ','x',':','x','m','p','t','k','=','"','X','M','P',' ','C','o','r','e',' ','4','.','4','.','0','-','E','x','i','v','2','"','>',' ','<','r','d','f',':','R','D','F',' ','x','m','l','n','s',':','r','d','f','=','"','h','t','t','p',':','/','/','w','w','w','.','w','3','.','o','r','g','/','1','9','9','9','/','0','2','/','2','2','-','r','d','f','-','s','y','n','t','a','x','-','n','s','#','"','>',' ','<','r','d','f',':','D','e','s','c','r','i','p','t','i','o','n',' ','r','d','f',':','a','b','o','u','t','=','"','"',' ','x','m','l','n','s',':','x','m','p','M','M','=','"','h','t','t','p',':','/','/','n','s','.','a','d','o','b','e','.','c','o','m','/','x','a','p','/','1','.','0','/','m','m','/','"',' ','x','m','l','n','s',':','s','t','E','v','t','=','"','h','t','t','p',':','/','/','n','s','.','a','d','o','b','e','.','c','o','m','/','x','a','p','/','1','.','0','/','s','T','y','p','e','/','R','e','s','o','u','r','c','e','E','v','e','n','t','#','"',' ','x','m','l','n','s',':','d','c','=','"','h','t','t','p',':','/','/','p','u','r','l','.','o','r','g','/','d','c','/','e','l','e','m','e','n','t','s','/','1','.','1','/','"',' ','x','m','l','n','s',':','G','I','M','P','=','"','h','t','t','p',':','/','/','w','w','w','.','g','i','m','p','.','o','r','g','/','x','m','p','/','"',' ','x','m','l','n','s',':','x','m','p','=','"','h','t','t','p',':','/','/','n','s','.','a','d','o','b','e','.','c','o','m','/','x','a','p','/','1','.','0','/','"',' ','x','m','p','M','M',':','D','o','c','u','m','e','n','t','I','D','=','"','g','i','m','p',':','d','o','c','i','d',':','g','i','m','p',':','e','5','9','4','4','0','5','e','-','8','d','f','0','-','4','a','b','a','-','a','2','d','a','-','c','d','f','c','8','0','0','2','8','5','9','e','"',' ','x','m','p','M','M',':','I','n','s','t','a','n','c','e','I','D','=','"','x','m','p','.','i','i','d',':','f','c','4','3','7','6','9','5','-','f','e','5','5','-','4','b','c','f','-','9','4','2','3','-','8','4','f','9','d','e','b','2','c','9','1','7','"',' ','x','m','p','M','M',':','O','r','i','g','i','n','a','l','D','o','c','u','m','e','n','t','I','D','=','"','x','m','p','.','d','i','d',':','8','0','6','1','1','f','0','9','-','1','5','8','1','-','4','b','9','c','-','b','4','5','8','-','9','8','4','f','e','e','c','e','1','2','6','0','"',' ','d','c',':','F','o','r','m','a','t','=','"','i','m','a','g','e','/','j','p','e','g','"',' ','G','I','M','P',':','A','P','I','=','"','2','.','0','"',' ','G','I','M','P',':','P','l','a','t','f','o','r','m','=','"','L','i','n','u','x','"',' ','G','I','M','P',':','T','i','m','e','S','t','a','m','p','=','"','1','6','7','7','0','1','1','9','9','7','6','6','7','0','8','1','"',' ','G','I','M','P',':','V','e','r','s','i','o','n','=','"','2','.','1','0','.','3','0','"',' ','x','m','p',':','C','r','e','a','t','o','r','T','o','o','l','=','"','G','I','M','P',' ','2','.','1','0','"','>',' ','<','x','m','p','M','M',':','H','i','s','t','o','r','y','>',' ','<','r','d','f',':','S','e','q','>',' ','<','r','d','f',':','l','i',' ','s','t','E','v','t',':','a','c','t','i','o','n','=','"','s','a','v','e','d','"',' ','s','t','E','v','t',':','c','h','a','n','g','e','d','=','"','/','"',' ','s','t','E','v','t',':','i','n','s','t','a','n','c','e','I','D','=','"','x','m','p','.','i','i','d',':','9','2','f','7','c','d','9','7','-','d','a','8','4','-','4','9','3','e','-','b','e','0','8','-','b','c','6','3','2','3','4','5','b','d','7','2','"',' ','s','t','E','v','t',':','s','o','f','t','w','a','r','e','A','g','e','n','t','=','"','G','i','m','p',' ','2','.','1','0',' ','(','L','i','n','u','x',')','"',' ','s','t','E','v','t',':','w','h','e','n','=','"','2','0','2','3','-','0','2','-','1','2','T','2','0',':','5','9',':','4','9','+','0','0',':','0','0','"','/','>',' ','<','r','d','f',':','l','i',' ','s','t','E','v','t',':','a','c','t','i','o','n','=','"','s','a','v','e','d','"',' ','s','t','E','v','t',':','c','h','a','n','g','e','d','=','"','/','"',' ','s','t','E','v','t',':','i','n','s','t','a','n','c','e','I','D','=','"','x','m','p','.','i','i','d',':','9','5','5','4','6','6','0','d','-','7','8','b','4','-','4','e','b','7','-','9','1','c','4','-','9','e','d','0','4','3','9','7','0','0','e','8','"',' ','s','t','E','v','t',':','s','o','f','t','w','a','r','e','A','g','e','n','t','=','"','G','i','m','p',' ','2','.','1','0',' ','(','L','i','n','u','x',')','"',' ','s','t','E','v','t',':','w','h','e','n','=','"','2','0','2','3','-','0','2','-','2','1','T','2','0',':','3','9',':','5','7','+','0','0',':','0','0','"','/','>',' ','<','/','r','d','f',':','S','e','q','>',' ','<','/','x','m','p','M','M',':','H','i','s','t','o','r','y','>',' ','<','/','r','d','f',':','D','e','s','c','r','i','p','t','i','o','n','>',' ','<','/','r','d','f',':','R','D','F','>',' ','<','/','x',':','x','m','p','m','e','t','a','>',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','<','?','x','p','a','c','k','e','t',' ','e','n','d','=','"','w','"','?','>',0377,0342,02,0260,'I','C','C','_','P','R','O','F','I','L','E',0,01,01,0,0,02,0240,'l','c','m','s',04,'0',0,0,'m','n','t','r','R','G','B',' ','X','Y','Z',' ',07,0347,0,02,0,025,0,024,0,022,0,030,'a','c','s','p','A','P','P','L',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0366,0326,0,01,0,0,0,0,0323,'-','l','c','m','s',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,015,'d','e','s','c',0,0,01,' ',0,0,0,'@','c','p','r','t',0,0,01,'`',0,0,0,'6','w','t','p','t',0,0,01,0230,0,0,0,024,'c','h','a','d',0,0,01,0254,0,0,0,',','r','X','Y','Z',0,0,01,0330,0,0,0,024,'b','X','Y','Z',0,0,01,0354,0,0,0,024,'g','X','Y','Z',0,0,02,0,0,0,0,024,'r','T','R','C',0,0,02,024,0,0,0,' ','g','T','R','C',0,0,02,024,0,0,0,' ','b','T','R','C',0,0,02,024,0,0,0,' ','c','h','r','m',0,0,02,'4',0,0,0,'$','d','m','n','d',0,0,02,'X',0,0,0,'$','d','m','d','d',0,0,02,'|',0,0,0,'$','m','l','u','c',0,0,0,0,0,0,0,01,0,0,0,014,'e','n','U','S',0,0,0,'$',0,0,0,034,0,'G',0,'I',0,'M',0,'P',0,' ',0,'b',0,'u',0,'i',0,'l',0,'t',0,'-',0,'i',0,'n',0,' ',0,'s',0,'R',0,'G',0,'B','m','l','u','c',0,0,0,0,0,0,0,01,0,0,0,014,'e','n','U','S',0,0,0,032,0,0,0,034,0,'P',0,'u',0,'b',0,'l',0,'i',0,'c',0,' ',0,'D',0,'o',0,'m',0,'a',0,'i',0,'n',0,0,'X','Y','Z',' ',0,0,0,0,0,0,0366,0326,0,01,0,0,0,0,0323,'-','s','f','3','2',0,0,0,0,0,01,014,'B',0,0,05,0336,0377,0377,0363,'%',0,0,07,0223,0,0,0375,0220,0377,0377,0373,0241,0377,0377,0375,0242,0,0,03,0334,0,0,0300,'n','X','Y','Z',' ',0,0,0,0,0,0,'o',0240,0,0,'8',0365,0,0,03,0220,'X','Y','Z',' ',0,0,0,0,0,0,'$',0237,0,0,017,0204,0,0,0266,0304,'X','Y','Z',' ',0,0,0,0,0,0,'b',0227,0,0,0267,0207,0,0,030,0331,'p','a','r','a',0,0,0,0,0,03,0,0,0,02,'f','f',0,0,0362,0247,0,0,015,'Y',0,0,023,0320,0,0,012,'[','c','h','r','m',0,0,0,0,0,03,0,0,0,0,0243,0327,0,0,'T','|',0,0,'L',0315,0,0,0231,0232,0,0,'&','g',0,0,017,0134,'m','l','u','c',0,0,0,0,0,0,0,01,0,0,0,014,'e','n','U','S',0,0,0,010,0,0,0,034,0,'G',0,'I',0,'M',0,'P','m','l','u','c',0,0,0,0,0,0,0,01,0,0,0,014,'e','n','U','S',0,0,0,010,0,0,0,034,0,'s',0,'R',0,'G',0,'B',0377,0333,0,'C',0,03,02,02,03,02,02,03,03,03,03,04,03,03,04,05,010,05,05,04,04,05,012,07,07,06,010,014,012,014,014,013,012,013,013,015,016,022,020,015,016,021,016,013,013,020,026,020,021,023,024,025,025,025,014,017,027,030,026,024,030,022,024,025,024,0377,0333,0,'C',01,03,04,04,05,04,05,011,05,05,011,024,015,013,015,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,0377,0302,0,021,010,02,'X',02,'X',03,01,021,0,02,021,01,03,021,01,0377,0304,0,035,0,01,0,02,02,03,01,01,0,0,0,0,0,0,0,0,0,0,06,07,05,010,01,03,04,02,011,0377,0304,0,034,01,01,0,01,05,01,01,0,0,0,0,0,0,0,0,0,0,0,05,01,02,03,04,07,06,010,0377,0332,0,014,03,01,0,02,020,03,020,0,0,01,0332,0220,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,07,0311,0211,'!',04,'0',0213,030,03,030,'x',0216,017,'a',0222,'3',0304,0240,0230,023,0223,'8','}',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0305,025,'1','Q',025,'y',014,':','@',0,0,0,07,'a',',',',',0362,0334,'-',0303,'8','r',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'y',0212,'|',0240,'J',0204,0360,0,0,0,0,0,0,0,017,'I','j',0227,0361,'t',0236,0360,0,0,0,016,0263,0306,'|',035,0247,047,0321,0334,'v',0200,0,0,0,0,0,0,0,0,0,'b',0215,'w','5',0304,0213,0,0,0,0,0,0,0,0,0,03,':','l','A',0261,0346,'|',0344,0,01,0360,'V',0204,'$',0353,0,034,0237,'f','D',0367,0222,'B','n','z','@',0,0,0,0,0,0,0,0361,032,0332,'k',')',0204,0,0,0,0,0,0,0,0,0,0,0,'d','M',0217,'6','x',0313,0,016,0262,0242,'#',07,']',0233,'X','=','o','m',0215,0305,'?',0350,0277,'C',047,0223,0317,0344,'r',0300,0367,'d',0216,'V',0200,'f',0213,0244,0355,0,0,0,0,0,0,07,05,'2','i',0371,022,0,0,0,0,034,0231,'b','F','d',0214,0231,0311,0216,'1',0244,'p',0303,03,0200,0,0,0,014,0361,0266,0345,0366,'v',037,05,'F','E',0217,'.',')','z',0346,'7',0275,'x',')','&',0,0346,0224,0366,'4',0275,'7',0307,0344,0362,'y',0371,'V',0347,'4',0366,'f',0204,0237,0226,'H',0,0,0,0,0,016,014,'1',0247,0245,022,'|',0200,0,0,0344,0365,0226,'q','k',026,'I','<','3','g','`',0,03,0340,0304,0220,'B',0267,'*',0302,0254,'1',0307,0,0,0,'9','-',0343,'r',0310,'a',024,'<',0270,0245,0253,0230,0336,0365,0340,0244,0240,0,0,06,'V',0350,'+','*','W',0347,0256,0372,0333,'~',0235,0200,0,0,0,0,016,012,0320,0322,022,'*',0,0,034,0235,0245,0266,'_',0245,0304,'e',0,0,0,0,0,0,'x',0312,0210,0241,'J','`',0362,0200,0,07,0240,0331,02,'h','y',0261,'J',0327,021,0275,0353,0301,'I','P',0,0,01,0355,'G',0332,'R',0377,0,'6',0253,'m',0344,'e',0200,0,0,0,0,0340,0327,0343,'N',017,' ',0,0,'{',0215,0203,'6','P',0227,0200,0,0,0,0,0,0,0,'#',0246,0271,032,0340,'b','@',07,'y',0261,'d',0350,0363,'b',0224,0256,'c','{',0326,'>',0222,0300,0,0,03,0352,0224,0235,0356,'r',011,026,0357,';',0366,027,0311,0366,0,0,0,0,'>','M','X','5',0134,0370,0,0,'v',0233,02,'m','I','%',0,0,0,0,0,0,0,0,0,06,034,0325,0363,'[',0317,031,0334,'l','I','=','<',0370,0344,0353,0230,0316,0365,0216,0244,0270,0,0,0,0372,0245,0263,0275,0316,'C','"',0335,0347,'b',0303,',','P',0,0,0,01,0362,'j','a',0254,047,0,0,'r','O','M',0314,',',0240,0,0,0,0,0,0,0,0,0,0,04,' ',0323,0242,'f','X',047,0237,034,0225,'s',031,0336,0261,0324,0230,0,0,0,037,'T',0266,'{',0271,0307,0244,033,0274,0370,'O',0313,' ',0344,0,017,'5',025,0305,0264,0302,')',0350,',',0213,0253,0233,0255,'x','5','`',0325,'C',0200,0,'>',0315,0224,'6',0304,0366,0,0,0,0,0,0,0,0,0,0,0,07,05,'Z','B',0216,0214,'r','5',0314,'o','z',0306,0333,'2',0,0,0,'>',0351,'l',0367,'w',0216,'g',0367,'<',0,0261,'K',014,0,0,' ',0226,0326,0261,0267,037,026,0345,0346,0354,'_','E',0201,'u','`','w',']',0246,047,0300,0,036,0363,'t',0213,0310,0344,0,0,0,0,0,0,0,0,0,0,0,'8','+',022,014,'t','c',0220,0256,'c','{',0326,'6',0331,0240,0,0,01,0366,0262,'}',0271,0306,0263,0333,0236,017,0222,0312,047,0240,0,0,'+','{','m',0257,0255,'V','1','_','J','H','6','<','$',0253,'o',0227,0366,']',0253,0204,'(','K',0356,0205,'V',0240,'r','f',0315,0354,',',0300,0,0,0,0,0,0,0,0,0,0,0,034,025,0231,04,':','1',0310,'W','Q',0275,0347,031,'l',0330,0,0,0,0373,'Y','?',0335,0343,031,0335,0277,013,0364,'Y',0344,0334,0,0,0,0256,'-',0266,0275,0262,0352,0242,'/',0351,0356,0212,0346,0366,'V',':','q',0265,0310,'s','[','>',037,0344,0253,'k','Z','N',0373,0274,0246,'h',0337,0202,0301,0,0,0,0,0,0,0,0,0,0,0,0,'+','R',02,'t',0331,0277,']','F','w',0234,']',0263,0200,0,0,03,0261,0216,0300,0335,0342,0371,0275,0277,017,0366,'Z',0204,0304,0,0,0,025,0275,0212,0372,0226,0325,'Q','_','N',0371,0253,0262,'9','R','I',0223,0304,'L',0367,'x',0377,0,'u',0372,030,'B',0220,0276,0355,0307,0255,'l',0300,0,0,0,0,0,0,0,0,0,0,0,02,0267,'+',0363,0246,0315,0352,0352,'3',0274,0342,0355,0235,0,0,0,035,0214,'v',016,0367,024,0315,'m','x',0256,0302,0330,'%',0200,0,0,0,025,0255,0227,'@',')',0212,0257,0212,0372,'c',0303,']',0360,07,0255,0241,'8',0333,0343,0371,0275,0237,017,0364,'X',027,'V',0303,0272,0356,0300,0,0,0,0,0,0,0,0,0,0,0,0256,'J',0364,0352,0263,'v',0272,0215,0357,'8',0253,047,0200,0,0,07,'k',025,0203,0275,0304,0363,';','^','3',0254,0366,0227,0370,0,0,0,0,0255,0354,0255,'}','L','u',0224,'W',0322,'x',0352,0312,0,07,'*','H',0362,'x',0231,0236,0347,' ',0357,0311,035,0230,0255,'m','[',0253,0234,0255,'@',0,0,0,0,0,0,0,0,0,02,0274,'+',0243,0252,0315,0312,0356,'7',0274,'b','l',0364,0,0,0,03,0265,0206,0303,0336,0342,'9','}',0257,037,0320,'k',0311,025,'?','G',0211,010,0,0,0,05,'w','m','+',0253,'m',0255,'#','>',0216,0306,'R','d',0,0,0364,0264,0346,0273,'<',0233,'?',0265,0317,0376,0353,'l',0376,0267,0330,'w',0273,'@',0,0,0,0,0,0,0,0,01,'_',025,0301,0327,'f',0335,'w',033,0336,'1',026,'z',020,0,0,01,0334,0303,'a',0357,0360,0374,0266,0317,0221,0363,032,0350,'A',0301,0261,'f',0344,034,0200,0,0,02,0275,0266,0225,0305,0266,0326,0361,0237,'D','b','i','>',0,0,016,'L',0335,0336,'Z','m',0273,0307,'=',0231,'a','2',0253,0355,'K',0256,0220,']','h',0,0,0,0,0,0,0,0,' ','%','j','u',0331,0267,'^','F',0367,'l','=',0236,0214,0,0,0,'w','0','X',0233,0374,';','+',0263,0344,0374,'f',0270,020,0340,017,'I',0372,':','K',0200,0,0,01,06,0265,'W',0333,'d',032,'?',0267,'F',0261,0373,0220,0,0,01,0333,'L','3',015,0216,'c',047,0334,0346,'=',0265,0305,'e',']','t',0352,0352,0375,0,0,0,0,0,0,0,01,03,'+','C',0252,0315,0252,0366,'7',0273,'a',0254,0364,0200,0,0,03,0275,0257,'b','H','p',0334,0246,0307,0225,0367,0220,0323,'U',0200,0,0330,0303,'r',0,0,0,01,0211,0245,0324,036,';','z',0361,0315,0327,032,035,0363,0312,0335,0,0,0,03,')','X','9',0316,0347,031,0310,0347,0363,'R',012,0326,0324,0272,0354,0255,'@',0,0,0,0,0,0,'A','J',0310,0353,0263,'f',0275,0215,0356,0330,'[','=','(',0,0,0,0357,'k','X',0262,034,'3',047,0261,0345,0362,'E',0310,'}',0237,0233,0304,'l',0,'d',017,0322,0223,'6',0,0,0,'R',0246,0256,'Z',0330,'+','m',0361,0341,0236,0256,'t','{',0337,0225,0272,0,0,0,07,'e','1',0313,0266,'9',0234,0253,'o',0226,0367,0335,0212,0305,0272,0351,0355,0325,0372,0,0,0,0,0,01,010,'*',0363,0256,0335,0212,0372,'3',0272,0341,',',0364,0340,0,0,03,0320,0325,0261,'d','8','^','K','c',0314,0345,0213,0214,0367,0203,'Z',015,'B',0,03,'p',015,0224,0,0,01,0301,0243,05,'&','Z',0366,'_','y',0323,026,'7',07,0247,0257,0264,0273,0227,0225,0272,0,0,0,0,0312,'V',012,'u',0271,0306,'r',031,0374,0336,'z',0265,0265,'.',0256,'^',0265,0,0,0,0,01,012,'*',0323,0256,0335,0212,0376,'3',0272,'`',0354,0365,0,0,0,03,0322,0324,0261,'d','x','V','G','?',0233,0315,027,021,0354,0,0305,0237,0232,0306,' ',0,'X',0207,0350,'q',0364,0,0,030,0243,0363,'8',0360,0202,0324,0307,0236,0364,0246,0277,'M',0222,0220,'=',036,0331,0206,0267,0322,0200,0,0,0,'>',0351,'d',0273,'?','5',0225,0356,'r',0276,0353,0260,0330,0227,']','?',0272,0277,'@',0,0,0,020,0302,0254,':',0355,0317,'_',0306,'w','<',025,0236,0250,0,0,0,'z',0232,'v',',',0217,011,0310,'g',0363,0271,0342,0340,'=','@',0,'i',0261,0256,0200,03,0350,0375,037,'&','`',0,01,'H',0232,'4','p',01,'`',0343,0330,0330,'j','k',0375,0253,025,0323,0352,'P',0375,'~',0235,0361,'[',0300,0,0,0,031,':',0302,'N',0267,'8',0316,'G','?',0231,0316,'V',0266,0255,0325,0314,'V',0240,0,0,020,0362,0251,':',0355,0317,0,0214,0356,'X',033,'=','X',0,0,0,0365,'4',0254,'i',036,021,0357,0317,0347,0344,'E',0276,'w',0200,0,'+','C',0363,0334,0340,0,'m',0341,0263,0,0,01,0246,'f',0273,0200,017,'a',0372,'S','D',012,0333,'b','T',0246,'3',07,0251,0201,0351,0366,0257,05,'%',0,0,0,0,037,'t',0262,'Y',0237,0233,0313,'w','9','W','u',0330,',','+',0253,'`',']','w',0330,0,02,'"','T',0347,']',0271,0240,021,0275,0313,03,0217,0326,0,0,0,017,'[','F',0306,0221,0340,0376,0354,0360,'2','r',0334,';','@',0,03,0254,0374,0330,'"',0300,02,0340,'7',0320,0,01,0301,0371,0312,'A',0200,05,0266,'o',0301,0311,016,0266,0225,0205,0266,0370,'l',0334,0205,0351,'v','8',0336,'?','q',0300,0,0,0,0,0311,'V',032,'u',0271,0306,'r','Y',0374,0276,'n',0265,0265,'.',0256,'j',0265,0,'D',0312,0230,0352,0267,'4',06,'7',0270,'`','1',0372,0340,0,0,01,0354,0254,'}',0215,'#',0301,0375,0271,0240,0345,0205,0260,'v',0,0,0,032,'b','k',0310,0,0313,037,0246,0307,0240,0,014,'9',0371,0222,'t',0200,015,0260,'6',0220,03,0301,'E','W','m',0261,0232,'0',032,0276,0376,017,0253,0327,0272,033,' ',0,0,0,017,0252,'[',',',0317,0316,'%',0273,0234,0247,0272,0355,'{',06,0352,0330,'W',']',0366,'E','J',0220,0352,0267,',',012,'7',0270,'G',0361,0372,0360,0,0,01,0355,0254,'u',0215,'#',0301,0375,0231,0241,'&','%',0252,'}',0,0,0,02,0205,'4',0224,0,017,0321,0222,'v',0,05,'b','~','}',034,0,'r','o',0311,'m',0,016,010,'E',0264,0255,'-',0267,035,0212,'f',011,0245,0332,0360,0366,0372,'0',0,0,0,0,0310,0326,036,'s',0267,0305,0362,0233,036,'g','3','T',0322,0352,0326,0365,0257,'U',0271,'`','Q',0335,0272,'=',0213,0330,0200,0,0,017,'u','c','l','i',036,015,0353,0315,015,'7','-',03,0220,0,0,0,010,'y',0371,0270,'p',0,'7','l',0276,'@',0,0327,0343,'K','N',0,'9','?','M',014,0370,0,03,027,'E','U','m',0261,0353,'o',0211,0351,'u','X',0206,016,0231,0363,'[',0200,0,0,0,037,'T',0262,'M',0177,'7',0230,0310,0362,0376,0354,0272,0334,'S','$',016,'7',0267,'G','q','{',' ',0,0,01,0357,0254,']',0215,'%',0301,0275,'Y','b',047,0245,0224,'r',0,0,0,0,':',017,0314,'c',026,0,'6',0250,0332,0240,0,'5','P',0325,'`',01,0226,'?','N',0216,0300,0,0,0371,'+',0353,'i',']',0333,'n',033,'[',0326,0301,'4',0373,'7',0216,0262,0200,0,0,0,01,0355,'G','L',0367,'y','N',013,'_',0330,0307,'1','{','`',0,0,01,0220,0254,'U',0215,'%',0301,0275,'9','b','l','2',0305,0,0,0,0,0,017,0316,'r',06,0,'6',04,0335,'0',0,'4',0324,0327,'P',01,'9','?','F',0300,0,0,01,035,0242,0254,'S',015,0217,'b',033,037,0325,0243,'X',0372,'/',0315,'n',0,0,0,0,0,0,0,0,'2','5',0210,0261,'d',0270,'7',0253,',',']',0220,'O',0300,0,0,0,0,0,'h','y','M',0200,013,0220,0337,0,0,'4',0214,0241,0300,05,0232,'~',0203,0200,0,0,0,'t',025,0271,07,'0',0332,'~',0232,017,0245,0330,0374,025,0233,0,0,0,0,0,0,0,014,0215,0320,0366,'4',0227,06,0364,0344,0214,0263,0211,0310,0,0,0,0,0,01,0244,0305,010,0,',',0323,0364,034,0,015,027,')','0',01,'o',033,0352,'r',0,0,0,0,'#','e','X','b',0261,0344,0210,'G',0365,'(',0276,036,0225,0327,'v','@',0,0,0,0,0,01,0222,0272,026,0305,0222,0340,0376,0254,0221,0266,0231,'3',0,0,0,0,0,0,06,0233,032,0350,0,',',023,0364,'L',0,015,026,')','@',01,'o',0233,0352,0,0,0,0,0,0363,0225,0241,013,'1','z',0223,0320,']',036,0321,0215,0254,0360,0,0,0,0,0,'2','W','C','X',0322,0134,023,0325,0222,'>',0327,'%',0300,0,0,0,0,0,0,032,'l','k',0240,0,0260,'O',0321,'0',0,'4',0210,0242,0,05,0234,'~',0203,0,0,0,0,0,0,0214,025,'i',0215,0262,0261,'(',0356,0243,024,0303,0323,0272,0356,0310,0,0,0,0,02,'S',0237,0237,0315,0244,'x',0334,0212,0332,'[',0367,'W',0274,0,0,0,0,0,0,03,'J',012,010,0,'Y','g',0350,'H',0,032,'f','k',0270,0,0235,0237,0243,07,' ',0,0,0,0,0,'<',0245,'b','C',0314,'^',0267,0247,0256,0264,';',0267,'K','8',0,0,0,0,'v','1','L','3',0362,0311,'F',0347,'4',0312,'W',035,0243,'}',0322,'j',0324,0,0,0,0,0,0,'h',0211,'L',0200,013,0210,0337,' ',0,'5','H',0325,0200,01,0231,'?','N',017,0260,0,0,0,0,0,02,',','T','G',0315,'+','[',0305,'}',011,0210,0266,'|',0,0,0,0,0,0312,0323,0316,'N',0267,0270,0257,0277,'<','4',0316,0345,0231,'u',0336,0223,0220,0,0,0,0,0,0374,0353,'+',0360,01,0177,033,0254,0,06,0275,0232,'^',0,'9','?','N',014,0330,0,0,0,0,0,010,0271,'Q',0235,'d','J','?',0254,'C',0265,0372,'g',0,0,0,0,0,0,';','-',0301,'0',0317,0312,'%',';',0374,0347,0336,0266,0321,0276,0262,0232,0327,0220,0,0,0,0,':',0217,0314,'s',020,0,'6',0230,0332,0340,0,'*',0303,0363,0374,0340,03,0223,0177,0213,'T',0,0,0,0,0,'"',0345,'F','u',0322,0261,030,0376,0263,017,0301,0322,0270,0,0,0,0,0,0,0,0313,0323,0314,'N','d','8',0317,0277,'<','<',0322,0352,0331,'G',0250,0,0,0,0,'E',017,0315,'`',0,'7',0134,0277,'@',0,0302,037,0231,'g','P',07,'&',0332,0233,'<',0,0,0,0,0,0214,025,031,0325,'J',0303,0343,0372,0354,'G',07,'F',0340,0,0,0,0,0,0,0,016,0333,'u',0346,0233,034,0202,'K','#',0340,0262,025,'Z','D',0230,0344,0,0,0,024,'Y',0243,0340,0,'~',0212,026,0,0,034,037,0233,0344,'0',0,0134,'f',0370,0200,0,0,0,01,030,'*','3',0252,0227,'C','#',0373,04,'O',07,'B',0,0,0,0,0,0,0,0,0,0317,'<','d',0342,'G',0216,'z',0263,0350,0316,0313,034,0355,0,0,0,'4',0344,0327,020,01,0222,'?','N',017,'P',0,034,032,0134,'k',0361,0300,06,'@',0375,'6','=',0300,0,0,0,021,0222,0242,':',0251,'t','*',';',0261,0305,'p',0373,0360,0,0,01,0350,'j',0330,';',0374,'K',037,0216,'v',031,0247,0323,0372,'k',0266,0,0,0,0,0346,0224,0364,0322,'>','o',0267,0306,'s',0333,0336,'K','3','U',0254,'f',016,'@',0,037,07,0346,0361,017,0,026,0311,0277,'`',0,01,'D',0232,'D','p',01,0311,0274,0345,0330,0,0,0,010,0311,'Q',035,'4',0276,023,035,0331,0242,0370,'}',0330,0,0,0,0355,'b',0261,'w',0370,'f','[','g',0311,0360,'y','5',0367,0240,0261,0375,0237,012,0365,0340,0,0,0,016,'i','B',0222,'k',0271,0324,0316,'O',0224,0372,0257,0301,'d',0325,'8','>',0200,0,0257,017,0316,0363,0200,01,0266,0246,0317,0200,0,06,024,0374,0320,'<',0200,02,0353,'7',0240,0344,0,0,04,'d',0250,0216,0232,'d',0203,'F',0366,0210,0326,'/','p',0,0,0,'>',0251,'m',0203,0275,0305,0363,0273,'~',026,0317,'=','e','^','x',0355,'E','#','z','4','?',027,'Z',0370,0256,'@',0,0,0,0,0310,'R',016,'{',0277,0304,'2','[','P',0322,0302,0323,'=',' ',03,'P','M','i',0,037,'G',0350,0271,';',0,0,016,015,017,')',0300,01,0351,'?','I',0211,'(',0,0,'F',0212,0204,0351,0267,'$',022,';',0266,'G','1','{','@',0,0,01,0312,0223,0255,0256,'I','$',0336,0346,0366,021,'c',03,0300,'U','D','l',0306,'i',0316,'A','t',';','^','6',0371,0320,0,0,0,0,0355,0267,'^','g',0237,0217,0311,0244,0274,036,'R',0253,'h',0315,034,0236,03,0363,0134,0301,0,011,0341,0372,',','}',0,0,0,0242,0315,' ','8',0,033,'@','m',0240,0,02,'4','T',047,'E',0271,'`','q',0335,0272,'=',0213,0330,0200,0,0,01,'2',0330,0346,'r',0331,016,'O','4','-','3',0220,017,0222,02,'W','G',0237,036,'H',0134,'g','[',0215,0333,0321,'>','k','P',0,0,0,0,0316,0335,0343,0247,'R',034,'c',0333,0233,'N',0314,'&',0306,0272,0232,'n','p',0,'6',0330,0331,0340,0,0,03,0306,'~','k',0221,0360,01,0224,'?','I','L',0330,0,0215,025,011,0321,'n','x',014,'o','p',0300,'c',0365,0240,0,0,0,'J','s','x',031,0264,0217,032,0223,0326,0226,0361,0366,0,0,0301,025,'9',0214,'R','?',037,0354,0340,0372,0335,0233,0242,0273,0200,0,0,0,01,0354,0266,'&','y',0277,0304,0362,0373,'~','v','h','i',0231,013,0,036,0303,0364,0230,0221,0,0,0,034,032,0252,'j',0271,0300,0,0331,'S','o',0216,'A',033,'*',03,0317,'n',0304,02,'7',0271,'`',0261,0372,0240,0,0,0,'$','9','<','|',0366,'O',0207,'g','k','e',0312,'w',0200,0,0,0363,0225,0201,016,'<',0232,0373,0320,'8',0376,0333,0207,'z',0220,0,0,0,0,0373,0246,031,0216,'~','Q','*',0221,0347,021,0212,0265,0324,0303,034,02,0377,0,'7','T',0,0,0,03,06,'~','m',0230,0260,01,0351,'?','C','I',0361,033,'*',023,0315,'f',0315,'}',033,0335,'0',0226,'z','p',0,0,0,'3','7',0371,0273,016,'O',0203,0345,0256,0327,0272,'O','`',0,0,0,'8','!',0305,'b','y','m',0272,'!',031,0323,0342,0230,0272,0177,0305,0331,0,0,0,0,03,'9','w',0216,0236,'H',0361,'o','.',']',']','y','!','g','i',0372,'*','N','@',0,0,0,'8','5','H',0325,0243,0200,01,'f',033,0274,'T',0347,0226,0315,0272,0366,'7',0272,0341,0254,0364,0200,0,0,0,'d',0356,0205,0261,0344,0370,026,'B',0375,'+',0244,0312,0,0,0,0,01,0213,'*','s',07,'F',027,'O',0326,'A','4',0373,'O',0226,0273,0340,0,0,0,01,0355,0244,'4',0377,0,0177,0212,'{',0266,0274,0375,024,'J',0315,0326,'9',0,0,0,0,030,0263,0363,0210,0216,0,013,04,0330,'s',0213,'7','+',0310,0336,0357,0210,0263,0320,0200,0,0,01,0355,0254,'u',0223,'%',0300,'}',0371,'b',0256,'C','<',0,0,0,0,0,035,'E','n','A','O','.',0276,0334,016,';',0265,0341,'k',0353,0200,0,0,0,037,'T',0267,0272,0315,'i',0266,0347,032,0220,'H','x',0271,0211,'k',037,'@',0,0,0,0,'Q',06,0221,0234,02,0300,'6',034,0342,0315,0352,0356,'3',0273,0342,0255,0237,0,0,0,03,0320,0324,0262,'d',0270,'&','C','4',015,0272,'J','@',0,0,0,0,0,016,010,0251,'V',036,';','Q','(',0316,0221,022,0307,0326,'>','+',0220,0,0,0,01,0315,')','*',0277,0230,0314,0245,'9','l',0272,0353,'-','S',0274,0,0,0,0,'|',032,'&','S','D',0374,0330,0203,0347,034,0205,'w',033,0335,0361,'v',0316,0200,0,0,01,0332,0303,'c','H','p',0274,0256,0307,0225,0264,'I',0260,0,0,0,0,0,0,0,0307,025,'9',0200,'0',0332,'~',0252,011,0245,0332,'<',0225,0222,0,0,0,0,014,0315,'|',0224,0372,'K',0212,'I','2','i',0333,0325,'{',0200,0,0,0,04,'p',0322,0362,0347,'>','1',0310,0327,'Q',0275,0347,031,'l',0320,0,0,0,037,'k','l','-',0336,047,0232,0333,0361,'6','!','b',0,0,0,0,0,0,0,0,':',0312,0344,0201,036,'L',033,0260,'(',0356,0331,0207,'z',0260,0,0,0,0,0310,'Y',011,'?',0221,0341,0262,015,0250,0213,'x',0315,0,0,0,0,'a','J','T',0361,0342,0223,0256,'c',0273,0316,':',0331,0200,0,0,0,'9','R','y',0267,0310,'$','[',0274,0356,'j','Z','G',' ',0,0,0,0,0,0,0,01,0301,030,'*',0223,0307,'m',0360,0350,0316,0241,024,0307,0324,'8',0255,'@',0,0,0,035,0366,0351,'O','w','8','|',0217,'{',0314,'[',04,0234,0344,0,0,07,'I','J',0221,0334,'R',0365,0314,'o','y',0360,'R','P',0,0,0,016,'I',0236,0317,'.',0225,0357,0362,0311,'A','o',037,'@',0,0,0,0,0,0,0,0,0,0307,025,'1',0201,'0','Z',036,0276,011,0253,0332,':','+',0272,0,0,0,0,0373,0245,0223,'L',0374,0206,'U','#',0317,'-',012,0246,0247,' ',0,01,'U',0220,'{','6','k','H',0277,0241,0261,0364,0225,0,0,0,0,'K','3',0363,0331,0234,0207,036,0316,0326,0227,')',0334,0,0,0,0,0,0,0,0,0,01,0362,'k',0311,'W',0226,0205,036,034,022,0320,030,0376,0333,0214,'z',020,0,0,0,07,'*','L','r','r',0211,'t',0247,'3',0264,0311,0240,0,02,'*','T','$',012,'/',0271,'G','q',0373,' ',0,0,0,04,0217,'/',0213,0235,0311,'q',034,0255,'m',0272,017,'`',0,0,0,0,0,0,0,0,0,'8','#','F',0236,024,0250,',',0242,0370,';',0254,0276,025,025,0326,'#',016,0222,0,0,0,0,0372,0245,0266,026,0317,011,0317,0310,'y',';',0254,0314,0200,017,0222,0213,'"','Z',0236,0336,0274,0322,0355,0274,0,0,0,0,'g','/',0362,0366,014,0237,011,0363,0335,0206,0352,'%',0,0,0,0,0,0,0,0,0,07,07,0214,0327,03,'W','L','Q',0300,04,0214,0330,0202,'C','D','f',';',0240,'B',0265,0372,0367,']',0331,0200,0,0,0,0366,'[',031,'g','K','|',0351,'(',0311,0206,0344,'9',0,0210,025,'1',05,0214,0355,0221,0254,'^',0340,0,0,0,014,0255,0320,'6','4',0237,04,0363,0337,0251,0255,0246,'0',0331,'#','b',0314,0331,0310,0,0,0,0,0,0,0,030,0303,'_',0315,'e','#',047,0,0,017,'i',0267,'G','i',0212,0324,0364,'p',015,036,0335,0344,0254,0230,0,0,0,023,'|',0374,'z','S','#',0317,'.',0243,':',01,'W',020,0202,017,033,0332,'c',030,'}',0330,0,0,0,0367,0326,'*',0311,0223,0340,']','y','#','u',0270,0207,0200,'d',013,0264,0277,013,'L',0357,'9',0,0,0,0,0,016,0242,0267,'(','B',0213,'1',07,0,0,01,0357,'7',' ',0276,0310,'Q','Y',0236,034,';',0260,030,0316,0331,0207,0257,0253,0,0,0,031,'[','<',0325,0225,'5',0300,047,0325,'Y','@',020,0342,0250,'<',0270,0245,0253,0210,0336,0371,0341,0244,0220,0,0,'=','m',033,'"','K',0201,0375,'f',0205,0326,0362,032,0,0,0344,0316,026,0311,'k',0226,'A','3','=',0200,0,0,'<',0304,'H',0255,0212,0260,0250,0210,0330,'8',0,0,016,'K',030,0335,'b','l','r','p','`',0312,0224,0306,'Y','|','2','3',0251,'E',0254,0351,0334,'V',0240,0,0,0372,0245,0266,0224,0217,0315,'r','}',0215,033,0260,03,0340,0246,'L',01,0361,'n',0314,'k','O',0242,0306,0265,0275,0367,0236,0335,0336,0372,0352,0374,0262,'t',0323,'?',013,0275,0267,'E',0317,0244,'x',0216,'W','4','.',0275,025,0201,0300,0,0,01,0310,'=','D',0220,0222,0231,0223,0274,0344,0350,'1','$','l',0214,0236,' ','p',0,0,0,'r','d','M',0250,'6','H',0364,0,01,0344,'*',0222,'1','D','r','?',0334,0302,0265,'{',037,'E',0373,0,0,0,0260,0266,0270,0134,0207,'{',0307,0354,' ',0,0370,'+',0242,06,'u',0200,024,012,0134,0134,0255,0231,0262,0334,':',015,0134,'5',0320,0360,0200,0,0,0,0,0,0,0,0,0,01,0311,0336,'_',0246,0325,0222,'3',0220,0,0,0370,'+',0322,0276,'1',':',0236,0216,0277,0321,0356,036,'J',0310,0200,0,023,'<',0374,0232,'Y','#',0315,'6',' ',0,01,0341,'#',0307,0200,0362,035,'G',047,0240,0366,031,'B','F','}',03,0202,'8','k','Q',0257,'f','$',0340,0,0,0,0,0,0,0,0,07,047,0270,0276,0215,0232,'&',07,' ',0,0,0,0340,0213,025,'Q',0214,0301,0277,0,0214,0355,0270,0233,0275,'@',0,011,'N','^','q','6',0223,0344,'[',020,0,0,0,0,0,0,0,'8','1',0245,034,'P','%','d','u',0234,0,0,0,0,0,0,03,0223,0350,0260,'K',0360,0276,014,0331,0310,0,0,0,0,03,024,'T',0206,023,036,'H','<','_',']',0215,0272,030,0,'I','/',0347,0223,0271,'^','=',0261,0,0,0,0,0,0,0,0,01,0301,032,')',0302,0241,'+','2','>',016,0,0,0,016,'A',0301,0232,',',0222,0334,'.','B','`',016,'@',0,0,0,0,0,017,'9','V',0221,012,'"',0261,0235,012,033,0213,0257,0374,0326,0360,'$','w','s',0311,0344,0267,036,0330,0200,0,0,0,0,0,0,0,0,0,'p','|',021,'R',012,'B',0310,0251,0200,'1','g',0234,0340,0364,031,'#','<','J','I',0221,':','%',0307,'q',0310,0,0,0,0,0,0,0,03,0202,04,'V',0346,07,'G',0324,0300,0365,';',0177,'E','w',04,0243,'/',':',0233,0311,0362,015,0210,0,0,0,0,0,0,0,0,0,0,0,016,01,0310,0,0340,0344,0,0,0,0,0,0,0,0,0,0,01,033,'*','c',021,0257,'#',010,0216,0354,030,'G',0262,0237,'l',0360,0311,04,0207,0217,0330,0200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'1',0305,'V','G',')','O','.',0276,0367,0253,'c','K',0325,'U',0374,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0340,0213,021,'`','M','L',0360,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,07,0377,0304,0,'0',020,0,01,04,02,01,02,06,02,0,05,05,01,0,0,0,0,04,01,02,03,05,0,06,07,' ','0',020,021,022,023,024,'@',025,'1','"','2','4','P','`',026,'!','#','%','3',027,0377,0332,0,010,01,01,0,01,05,02,0377,0,047,'&',0334,' ',0360,0316,'H',0240,017,010,0346,'j',0226,'$',0334,0335,0347,0222,'s','U',0233,0261,0374,0305,'t',0354,0177,'-',0337,0273,031,0313,'{',03,'q',0274,0303,'v',0231,037,'4',0332,'7',' ',0346,0347,0340,0334,0321,'Y','"',07,0311,0332,0371,'x','-',0355,'y',0270,0213,0347,0375,0344,0333,'A','+',0231,'i',0313,'t',0240,0345,0237,'3','X',0317,0226,033,0255,0325,0226,'I','3',0345,0356,0262,'G','F',0265,0373,'m',0275,'f','V',0363,025,0260,0313,'W',0314,025,'&','e','u',0330,026,0254,0376,0345,'9',021,0215,035,0337,'*',0324,'U','e',0317,',',0134,'Y','a','g',020,'s',0376,0244,04,'J','3',0351,0371,'B',0356,0257,')','9','v',0256,0303,04,'6',03,0243,0355,0272,'F',0307,0213,'`','2','g',0344,0305,0134,0371,0303,0256,'|',0321,0361,013,0201,'q',0262,0261,0377,0,'r',0302,0320,'Z',0250,'6',016,'d',0212,'%',0270,0331,'l',0257,'d',0373,'U','w',0207,'S','K','A',0314,0256,'n','T',0336,0203,'w',017,'[',0336,0330,0333,'m',0260,0254,0330,0347,0271,0335,'H',0367,'5','[','j','[','r','=',0200,0330,0360,']',0251,0253,0203,0221,031,'Q','}','R',0214,0204,030,'v',0216,'`','d','Y','i','r','m',0314,0377,0,'x',033,02,'k','g',0326,'y',0201,0361,0255,'u',0240,0266,0320,'t','I','#','b','e',0315,0303,0254,037,0217,'{','c','I',0256,0241,0215,']','~',0357,'8',0357,0230,0270,0313,'Q',0237,0215,0231,0217,0351,0254,0262,'}','t',0361,'J',0331,0243,0372,'J',0276,'Y',0266,'r','`',024,011,'}',0264,'X',0354,'S',0367,'a',0250,'8',0224,0207,'G',0275,'!',031,0306,0333,'#',0361,0274,'U',0260,0273,035,0305,';',013,'q',0374,'i',0262,'3','%',0321,'/',0341,0311,'i','l',07,'N',0355,'E',0351,0264,'D','j',0134,0252,'-',0256,'5',0310,0364,0307,0275,0261,0262,0346,0345,0307,0277,010,'!',0203,'F','Y','o','.','N',0204,'_',',','a',0223,'G',0215,0264,'%',0270,'=',0343,0333,0203,027,031,'I',0232,0315,0217,0245,0337,'F',0322,0334,'Z','a','w',016,'P','*',0351,'U','|',0327,0262,'8',0223,0230,0372,0276,'0',0276,0262,0300,'8','J','4',0300,0270,0273,'_',017,03,0241,0256,0257,'j','F',0326,0365,0253,032,0354,'.',0230,023,0230,'o',031,0353,0346,0345,0207,011,0214,0374,0263,0342,0213,0332,0374,'2',0270,0252,0367,'v',0265,036,'E',';',0134,'}','.',0312,05,0360,'w','W','*','s',0360,0202,030,'4','e',0226,0362,0344,0354,0326,0223,0361,0211,0310,'d','X','e',0215,0336,0344,'}',0375,0273,'y',013,'U',0203,'`',0330,0315,0330,0314,0354,'G',023,0346,0222,0223,0213,0256,'m',0362,0233,0210,0252,0,0300,0253,'E',0257,0217,0350,022,034,05,0262,0343,0212,')','l',0262,0357,0211,'m',0353,'p',0221,0246,022,'N',0270,' ',0220,0231,'u',0232,04,0244,0203,010,'!',0203,'F','a',0217,'2','N',0320,'q','{',0304,0370,'U','9','_',']',0336,0337,'9','.',':','|','(',0231,'L',0237,0254,'@',0247,'>','}','s',0207,'I',')',')','u','Z',0312,010,0376,0265,0265,0,027,'q','l','<','4',0255,0313,032,0242,0352,'g',0350,0206,027,0221,'.',0265,0255,0262,0242,',',0236,'v',015,031,0206,'<',0311,';','H',0236,'k','W',']',0361,0274,04,0203,0344,0222,0306,'$','l',0356,'*',0371,047,'!','r','_',0245,0134,0345,'r',0365,'G',033,0246,'~',0257,0304,0205,0330,0345,'6',0273,'_','A',07,0332,0263,0250,016,0336,015,0243,0207,0344,0207,012,022,'P',0246,0310,0242,'|',0362,'k','Z',0323,'*','b',0311,0347,'`',0361,0230,'c',0314,0223,0264,0210,0256,'Z',0332,0304,035,'<','5',0312,0247,0265,0375,0305,'_','$',0344,'n','F','Y',']',0327,0254,'i','v',033,'D',0332,0276,0207,'[',0254,0263,0357,'l','z',0205,'v',0315,06,0321,0307,'V',':',0354,0272,0316,0262,0332,0250,0362,'y',0330,'<','f',0232,0343,'$',0355,'5',0252,0345,0255,0255,'A',0223,0302,0222,0213,0327,0237,0256,0242,010,'`',0261,033,0264,'J',0367,':',0364,0347,'`',0273,01,'p','I','_','o',05,0202,'b',0347,'%',0362,022,0267,0255,0255,'W',0273,'J',0342,0247,026,0202,0213,020,'p',0375,0365,0377,0,'d',0275,0272,0371,'^',023,'L',0321,0343,'4',0327,031,047,'i',0255,'W',0255,'m','j',014,0236,024,0224,']',0215,0256,'u','h',0371,0347,0340,0327,'+',035,'Y',0262,0371,'g','$',0357,0251,'T','+',0225,0134,0275,'!',03,'5',0201,':','?',033,017,'B',0317,0354,037,0254,0274,0273,0367,0327,'&',0231,0260,'F','i',0256,'2','N',0323,'Z',0257,'u','u','j',012,0231,0373,0312,'J','/','o',0263,0266,0377,0,'>','N','C',0320,0261,'.',0361,0217,'l',0215,0313,':','q',0255,0242,0273,0325,0311,0251,'^',0212,0212,0202,'n',0216,0323,'t',0221,'5','a',0177,0260,'~',0262,0362,0363,0335,0360,0232,'f',0301,031,0307,'8',0311,';','M','j',0275,0325,0325,0310,'+','q',021,0134,0264,0264,'H','?','k','m',0376,'l',0233,0377,0,'l',034,0271,05,'p','v',0221,0225,0340,0346,0243,0222,0367,'K',0214,0234,'$','Y','C',0227,'*','j','I',0272,';','O',0324,06,0325,'A',0376,0303,'{','y',0356,'x','K','+','`',0214,0343,0234,'d',0235,0246,'1','d','u','u','r',010,0334,'k','U',0356,0245,0243,'A',023,0265,0266,0377,0,'>',020,0236,'S',0370,0205,'p',0350,0262,')','[','3','2',0316,0234,'k','h',0217,0323,015,034,0275,047,'M',0203,'V',03,0373,015,0355,0347,0253,0302,'Y','[',03,016,'9',0306,'I',0332,'c',035,'#',0253,0353,0232,'#','q',0214,'t',0257,0246,0245,'h','-',0355,0355,0237,0372,0341,0277,0354,'W','@',0344,0310,'3',0203,0264,0214,0257,06,0271,'X',0352,0315,0227,0313,031,'#','e','o',0337,0275,0274,0363,0360,0226,'V',0302,0303,0316,'q',0222,'v',0243,0215,0322,0276,0276,0275,0242,'7','$',0221,0260,0263,'A',0331,0300,0265,0263,0356,'m',0251,0374,'y','a',0375,'g','P','W',016,0213,'"',0231,0223,0267,0,0264,0236,0275,0325,0326,0360,'X','7',0356,0336,0336,'x','I','#','a','a',0347,0270,0307,0366,0243,0215,0322,0274,012,0366,0206,0314,0232,'f',017,026,0313,0263,0276,0331,0365,'v','3','T',0237,'E','q',025,0355,'_','o','m',0375,'e',0217,0365,0335,'p',020,0361,0334,035,0263,047,0360,'k',0225,0216,0254,0331,'U',0271,034,0215,0225,0237,'j',0366,0363,0323,0341,'$',0215,0211,0207,0236,0343,037,0332,0212,047,'L',0360,0,'h','l',0302,010,0214,'H','v','=',0222,'K',0211,'|','8',0213,'f',0370,'V',035,0275,0267,0371,'r',0321,'<',0216,0354,0207,'k',' ',0330,'9','Q',0222,0334,06,0316,'z',0367,0326,0334,0303,'b',0237,'b',0366,0363,0321,0341,'$',0215,0211,0226,026,016,'1',0375,0250,0242,'t',0317,0,06,0206,0314,',',0270,0201,0203,'`',0330,0245,0271,0233,0304,'r',036,',',0372,0265,0343,'6',032,'N',0326,0327,012,0270,'l',0274,0207,0323,'?','j','9',035,023,0203,0271,'G',0342,'/',0232,'1',0356,0215,0365,0233,'#','d',0304,'T','r','}','[',0313,0317,'k',0301,0357,'l','m',0260,0260,'S',037,0332,0206,027,'O',' ',' ',0264,'6','`','a','H','t',0334,0247,0253,'H','=','g','O',017,'l',037,016,0317,0265,'j',0330,0335,'^','9',021,0225,011,'b',0264,0270,0210,035,0343,'I',0333,022,0302,'Q',024,'C','c','-',0271,']','s','5','z',0203,'g',01,0354,0372,'w',0227,'~',0316,'~',0361,0357,'H',0333,'a','`',0245,0273,0265,014,'.',0236,'@','A','h','q',0340,' ','I','a','0',0,'G','_',011,0301,'G','`','&',0303,'O','%',015,0277,'@',06,0311,0134,'m','5',0223,'.','*',0373,'<',0253,'y',0370,0275,'s','U',0331,'?',031,'#',0134,0217,'i','b','0',0270,0310,035,0303,'K',0333,'c',0326,'7',05,'r',0213,0210,0250,0344,0216,'W','B',0372,0315,0221,037,0215,'r','9','>',0205,0345,0337,0307,'E','_','5','{',0322,'6',0330,0330,')','n',0355,'A',03,0210,0220,' ',0230,034,'y',']',']','%',0214,0301,0205,030,'0',0370,'s',026,0273,0357,0207,0323,0303,'7','~',0375,0177,'g',0226,'.',0177,'#',0262,0346,0245,0263,'|','e',0375,0341,0241,'4',0310,0310,031,0343,'?',0270,'%',0204,0202,'(',0246,0306,'[','r',0272,0342,'j',0365,02,0316,033,06,'w',0257,'.',0376,'2','*',0371,0253,0234,0214,'m',0215,0212,0226,0356,0324,020,'<',0231,03,011,0201,0307,0225,0265,0222,'X',0312,'(',0221,0207,017,0215,0230,021,0331,0201,'k',0134,0372,0253,'.',0215,016,0335,'i','v',0217,'?','>',0305,0241,0255,0256,0257,'8',0247,034,'g',0206,0265,0266,0250,0230,0307,0243,0333,'8',0354,'%',0206,0326,'H',047,'u',0256,'V','8','+',0237,'<','E','G','$','r',':',047,0326,'l',0250,0354,'k',0221,0355,0355,0335,0335,0374,'D','U','W','+',0234,0214,'m',0215,0212,0224,0356,0320,0343,0274,0231,03,015,0201,0307,0225,'u','R','X',0310,'8',0321,0213,027,'O','2','R','|','[','N',0204,'r',0265,'u',013,'d',0272,0327,'z',0371,'j',0323,0341,'k',035,032,0346,0323,'%','R',0304,0364,0236,017,'/','<',':',0235,035,0216,'j',0261,0335,0301,017,0220,'E',020,0350,0313,'L',0256,0267,0236,0275,0300,'Z','A','`',0336,0315,0335,0322,06,0327,'9',0134,0256,'r','1',0266,'6','*','S',0273,'C',0216,0362,0244,020,'F',011,036,'T',0324,'>',0306,'H',' ','`',0361,0365,'r',025,'?',0346,'u','~',0236,026,0264,0367,01,0353,0346,'k','?','~',0353,0240,'1',0234,'a','c','T',017,035,']',0236,0277,'(','~',06,01,031,'m','(','9',04,'w','q',0256,'V','8','+',0234,'k',0221,0310,0311,035,023,0253,'6','_','<','k',0321,0355,0352,0272,0272,'@',0232,0347,'+',0334,0347,'#','R',0312,0305,'J','w','h','a',0236,'T',0202,010,0301,'#',0312,'z','g','X','>','(',0233,014,'}','r','1','$','f',0317,'[',0370,0213,0376,0216,',',0263,0370,033,'g','R',0346,0355,'a',0371,'=',0237,0243,0213,0253,'?','%',0267,'x','Z','k',0321,0225,0204,015,' ',0262,'I',033,'e','i',0325,016,0213,0274,'!',0362,010,0242,037,031,'i',0225,0366,0323,0327,0272,0276,0326,013,06,0364,']',0134,0240,'-','s',0225,0356,'s',0221,0251,'e','d',0245,'/','h','a',0236,'T',0202,0212,0301,'#',0312,'j','W',034,0346,'1',0261,'3',0263,0314,0265,0177,036,0357,0242,0244,0265,02,0316,011,0222,'x',':','n',011,0370,'u','s','?',0335,0227,0243,0204,0253,0374,0242,0361,',','(',0216,0216,0316,0212,'`','|',016,0252,'a',031,'4','/',0201,0375,0306,0271,'Z',0241,0134,0343,0134,0217,'F','=',0321,0272,0263,'e',0306,'=',0262,'7','.','n','P',06,'=',0353,'#',0225,'Q',0251,'e','d',0244,0257,'h','Q','^',0134,0202,0212,0301,'#',0312,'Z','5','-','Z',0324,'c','{',0134,0305,'_',0362,'u',0256,0235,' ',0325,'?','V',0351,0344,0203,'>',036,0241,0323,0305,0300,0374,'=','?',0241,'S',0317,'-','5',0306,'O',0223,'B',0370,036,'@',0321,0222,0303,'k','d',021,'{',0242,0235,' ',0212,'%',0204,'E','&','W',0332,0317,'^',0342,'6','x',0376,'#',0336,0351,'^',0252,0215,'K','+','%','%','{','B',010,0362,0344,030,'f',013,036,'R',0321,')',030,0210,0215,'N',0336,0334,027,0344,'u',0276,0236,035,'1',047,0326,'z','y',0230,0217,'F',0273,0323,'@',032,'W',0322,0365,035,']',015,0203,',',0251,0246,0257,'U','O','4',':',0237,0317,034,0325,'j',0367,021,'U',0252,035,0302,0263,'"',0225,0223,'7',025,'|',0222,0312,0313,0344,'/','h','A',036,0134,0203,0214,0301,'c',0312,'J','/','w','?',']',0331,0243,0367,'a',0263,0201,05,0261,0350,0341,022,0272,0271,0272,'n',0232,0230,020,0233,'X',0323,0322,0316,0267,'5',034,0226,0272,0346,'9',0252,0325,':',0275,0245,0264,0201,'d',031,0335,0321,0212,'x',0262,'G','t',';',0222,0316,0317,0344,'v',0303,015,0346,'H','8',0354,032,'<',0244,0242,0372,033,0324,037,033,'m',0350,0341,0222,'}',0275,0213,0247,0232,0244,0365,'^','t','h',0360,0244,0373,'g','j',0312,0226,023,0320,0332,0351,0200,'|',0221,'6','f',0231,'N',0350,0361,'S',0311,'~',0270,'a',0274,0311,' ',035,0203,'G',0373,0312,'J','/','G',0321,0345,0210,0275,0275,0303,0243,0211,0344,0366,0367,036,0236,'b','w',0236,0317,0321,0306,0314,0365,0356,0335,0271,'a','d',0354,0263,0327,035,026,'*','y',')','u',0261,0223,0204,0207,' ',0253,0365,'B',011,0346,'I',04,014,036,'4','O','5',0244,0242,0366,'>',0227,'2',0303,0351,0331,0272,'8',0342,'_','o','r',0351,0345,0327,'y',0355,0275,034,'T',0317,'V',0345,0335,0262,0244,0204,0364,'0',011,0201,0221,0361,0266,'D','6',0231,'S',025,0276,0237,0246,020,'O','2','H',' ','`',0361,0265,0252,0345,0245,0243,'A','S',0351,'s','R',0177,0335,'t','h',013,0345,0270,0364,0362,0343,'|',0266,0356,0216,')','w',0247,'r',0357,'O',04,'d','G','g',0256,0274,'|','T',0302,0353,0343,')',012,06,'A',']',0364,01,015,0306,'K',014,'-',0202,'6','1','d','u','5','"',04,0237,'O',0232,0227,0376,0353,0243,'@','O','=',0307,0247,0230,0233,0345,0263,0364,'q',0253,0375,033,0267,0320,0263,0242,0204,0354,',',')','B',0221,0354,'I',032,'m','?',0226,'+','<',0273,0364,'2',0243,'e',0312,'S',0341,04,0210,'f','d',0354,0372,'|',0313,'7',0253,'f',0350,0343,0230,0375,0315,0313,0247,0232,0243,0364,0336,0364,'h',0262,0373,';','w',0321,' ','h',0312,0216,0317,'_',0220,'_',03,0,0214,0266,'K',033,0241,0223,0272,0307,0254,'O',016,0353,0325,0215,'r','=',02,0260,0230,07,0326,0336,0302,'w',0323,0345,0231,'=',0315,0303,0243,0212,'#',0367,'7',016,0236,'n',0207,0370,0272,')','f',0370,0367,014,'_','S','>',0215,0315,0303,'@','c',0234,0257,'v','Z','=',0257,'7',0276,'%',0204,0202,0250,0306,'F','S',0177,'Y','Y',0261,'H','>','@','L','e','G',0364,'7',0331,0376,'F',0337,0321,0303,'C','{',0233,047,'O','3',0214,0217,0240,0351,0244,'-',017,0250,0372,027,'7','-',01,0222,'H',0351,'_',0226,0226,'~','I',0364,'X',0367,'F',0340,0256,'=','X',0212,0216,'A',015,0230,')','+','o',0241,'7',0277,'+',0375,0270,0255,0210,0371,'v',0235,034,'"','/',0361,0364,0362,'`',0177,'3','P',0351,0343,023,'~','n',0235,0337,0271,0270,'h',014,0222,'G','J',0374,0263,0264,0362,0372,0241,0330,0310,'.',014,'d','e','7',0365,0225,0233,023,0307,0310,010,0214,0230,0373,0233,'I',0277,0216,0327,0272,'x','l','?','g','[',0351,0273,033,0346,'T','J',0317,'j','N',0216,022,0260,0365,013,0336,0270,0270,'m','{','$',0221,0322,0277,',',0355,'>',0274,'r',':',047,03,'n',0222,'b','/',0236,010,'l',0301,'>',0262,0356,'#',0273,0234,0275,'c',0361,'5',0216,0235,014,'%',07,'T',0351,0134,0334,'+',0377,0,027,0262,'t','q','U',0247,0343,0366,0316,0355,0305,0303,'k',0331,'$',0216,0225,0371,'g','i',0352,0373,'@',0331,0274,'u',0204,0206,020,0324,'_','%',0253,0330,0326,'<',0216,'V',0312,0336,0317,'3',0332,'{',0326,0335,025,0242,0374,0333,01,0241,0370,0343,0365,'s','%','g',0307,0276,0350,0257,'-',0300,034,011,'-','4','>',0335,0305,0303,'k',0331,'$',0216,0225,0371,'g','i',0353,0355,0301,03,0210,0226,012,0310,'!',0214,0272,'V','H',0262,0304,0370,']',0335,0204,0227,0300,0360,0255,030,'G',0205,'}',0244,0325,0357,0257,0264,0206,0301,0235,'n','r','1',0273,'m',0237,0345,0366,'.',0216,'1',0255,0374,0206,0333,0327,0313,0325,0177,'3','[',0351,0342,'k',0217,0310,0353,'=',0253,0213,0206,0327,0262,'I',035,'3',0377,0,'Y','g','i',0356,0366,0343,0215,0322,0274,0,'Z',034,'h',0236,'k',0204,014,0302,'Z','m','d',0202,0257,'q',027,0313,021,'|',0260,033,'w','G',0221,0310,0331,'[',034,0216,0205,0365,'[',023,'f',0317,'?','>',0255,0362,0343,0360,0272,0307,'O',012,0326,'z','F',0353,0271,0257,'m',0245,'Y','C',0270,'B','z','8',0242,0363,0361,'{',047,'f',0342,0341,0265,0354,0222,'G','L',0365,'_','$',0262,0263,0367,0273,'h',0212,0345,0255,0257,'A','Y',0224,'4',0336,0303,'m','(','c','3',010,026,'A','$','T','G','!',0325,010,0270,0346,0253,027,0272,')',0262,010,0341,016,0214,0246,0345,'U',0363,0302,0310,010,'a','1',0364,'s','5',0337,0276,'o','G',0227,0236,'i','U',037,0205,0326,0273,034,0247,'O',0370,0315,0243,0240,'y',0336,')',032,0345,0274,'w',0264,0275,'w',027,015,0257,'d',0262,0272,'g',0252,0372,'R',0312,0317,0344,'/','n',0256,0273,0330,'L',0240,0246,0363,0361,',','(',0215,0216,0316,0222,'P',027,013,01,0205,'!','!','8','^',0363,'$','t','n',02,0331,'$',0304,'_','<',06,0306,'Z',0371,'+',0255,'b',0261,'g',0201,0306,'0',01,'.',0354,0337,'s','k',0321,0244,0324,'-',0326,0312,0215,0364,0247,'c',0226,0350,0377,0,'#',0257,0364,0360,0326,0305,0355,0313,0325,'q','n',0332,0370,0345,0225,0323,'=','U',032,0226,'V','j','J',0366,0352,0253,'|',0274,'(',0251,0276,'S',0277,']',012,0210,0251,'i',0256,'#',0361,0354,'X',0335,'$','m',0225,0247,'T',0272,'.',0370,'V',0217,033,'!',0235,0204,'2','9',035,013,0352,'v',06,0221,0341,0314,033,017,0303,0254,0351,0341,0212,'O','l','n',0311,0203,'0',0301,'o',0352,0237,'I','o',0321,'X','|',0225,'v',024,0266,'q',0134,0326,'t',0134,'[',0266,0276,'9','e','t',0322,'9',0310,0306,0330,0331,')','K',0333,0252,0255,0367,'<',')','j',024,0371,'X',0304,'c','z',0254,'j','!',0260,'i',0325,0323,01,'&',035,'V',0322,'2','h','_',03,0373,0243,024,0361,'^',025,0213,012,'L',0253,0330,0134,'+','v',0373,'9',0355,0366,016,0201,05,'y',0244,0320,'U','2',0226,0243,0265,0314,0272,0377,0,'W',020,'m','?',030,0257,033,'{','v',0327,0307,',',0256,0232,'G',0275,030,0333,033,025,'-',0335,0272,0312,0357,0222,0344,'O','$',0252,0254,'}',0214,0360,0302,0301,0342,0354,'O',03,010,0216,0323,'_','x',0276,04,014,0302,032,'m','s',0305,'^',0353,0134,0254,'p',026,0376,0254,'E',0363,0313,0255,'|','{',0230,0254,0352,'H',0251,0237,0307,0210,'h','?','!','s',0333,0272,0253,0216,0346,0262,0314,011,'*',0317,0350,030,0211,04,0237,'L',0331,0243,0331,0351,0262,0336,0335,0265,0321,0313,'+',0247,0221,0357,'H',0333,'a','b',0342,0335,0333,0256,01,'K','{','Z',0214,'h',0,0276,0302,'q',04,0214,'(','{','v',0232,0373,012,0311,0240,0220,'i',034,0324,'r',037,'Q',0216,'j',0265,0335,0320,0255,036,'.','C','3',047,'a',0365,0360,'Y','A','}',0255,'M','N',0374,0212,047,'M','&',0235,'@',0335,'r',0207,0271,0314,':',0317,0223,0272,'t','=',0255,0332,0305,0315,0205,0344,'C',015,',',0256,0236,'I','$','l','L',0260,0260,'q',0216,0355,0202,023,0214,0226,'(',0233,013,05,025,0346,'M','^',03,'+',0340,0356,0235,']',011,0361,0330,0324,0315,0134,0354,'6',0265,0204,0244,0343,0274,'w',0367,'E',')',0342,0274,'3',0230,'c','%',0215,0263,'3','c',0324,0134,036,'q','6',0260,0266,'v',0335,0333,'*',0370,0255,01,0330,')',0245,0241,0266,0350,0326,'u',0227,'Z',0311,033,033,023,'$',0221,0260,0260,0373,07,030,0376,0330,0242,0270,0271,'G',035,0243,'E',024,'N',0236,'J',0232,0266,0327,'C',0337,0222,'6',0310,0313,']','u',0321,'g',0353,047,035,0204,'4',0332,0327,0212,0275,0304,0377,0,'e','l',0276,0205,02,0321,0263,0347,0355,'5',0243,0307,0254,0215,035,0347,0336,0345,'-','K',0363,'5',0276,':',0316,0262,0353,'Y','"',0211,0260,0307,',',0255,0205,0207,0236,0343,037,0333,0202,07,021,' ',0202,'4','H',0232,0325,'{',0251,'j',020,010,0376,0225,0245,024,'f',0341,03,'H',',',0212,0210,0344,'>',0243,034,0325,'k',0273,0265,0366,0336,0234,'k',0221,0351,'S','x',0360,0226,')',0231,'<','}',0307,'"','9','9',047,'P',']','~',0327,'5',0235,'e',0366,0322,'E',023,'`',0216,'i',0233,04,'g',034,0343,037,0333,0216,'7','J',0360,01,'h','q',0345,015,'7',0307,'o',0324,'0',010,0216,0216,0312,0242,'Z',0367,'a',0325,0254,')',047,031,0343,';',0272,015,0213,0204,'X','g','a',014,0255,0265,0226,0272,'A',014,0210,0330,0273,0227,0364,0220,'_',0325,0201,0241,0317,05,0254,'q',0266,'&','M','3','G',0214,0323,0234,'d',0235,0266,0265,0134,0265,0265,0350,'#','2',0202,0227,0353,0275,0210,0366,0332,0353,0252,0334,'T',0362,'Y',0307,'a',',','6',0271,0342,'/','t','s',034,'<',0302,0230,0302,0232,021,0322,0203,'-','m',0244,'V','1',0366,0355,'l',022,0274,'g',0275,'e','|',0363,0260,'x',0315,'5',0346,'I',0333,0375,0345,']','w',0307,'L',0241,0246,0371,'.',0375,'}',0233,'J','8',0316,'B','F',0220,'I',034,0324,'r',037,'Q',0351,0305,'O','.',0354,'3',':',07,0203,'d',0322,'R',031,0336,'<',0225,'7','L','=',';','2',0312,0330,'#',0264,0260,'u',0201,'$',020,0321,0242,',',0307,0226,0376,0345,'U','o',0243,0302,0226,0241,'O',0221,0255,'F','7',0355,032,04,'G','E','e','Q','-','{',0260,0352,0306,021,0222,0302,0370,035,0334,'k',0225,0253,'_','j',0222,'c',0134,0255,'Z','[',0304,047,'<',0373,033,015,0257,0311,0225,0357,'H',0330,'i','n','.','n',0345,'U','o',0253,0302,0252,0261,0326,'3','C',013,'`',0217,0355,0253,0221,0251,0273,0362,'p',0325,0361,0353,0373,0213,014,0317,0336,024,034,'e',0264,0300,'^','#',0273,0265,'v','^','x',0213,0345,0224,'6',0377,0,')',0275,'[',05,0217,0303,033,'-','l',026,'w',0367,'*',0353,0276,'B',0376,0262,0274,07,0330,'N','(',0254,016,037,0267,'w',0261,01,0257,0215,0267,'r','i',0267,0376,':',0376,0337,' ',030,'1','1',0227,013,0330,0222,'4',0372,0245,0213,0272,0213,0344,0265,0246,0241,014,0216,'G','D',0372,0263,0322,0300,'^',0207,'*','5',',',013,'q',0205,'Z',0224,0243,017,0334,0255,0257,'R',0336,0326,0243,'P',0222,0242,016,'-','~','P','d',03,0355,024,'d','!','C',0264,'r',0374,'p','e',0225,0241,'V',0344,0364,'S',0336,0223,'M','-','=',0340,0367,'0',0341,0365,')','.','H',0305,0215,0375,0261,'g','Q',0246,0215,0351,'#','(',0316,0370,'f',0364,'l',0207,'|','q','r',0362,'_','Q',035,0260,01,'q',0222,'G',033,'b','a','e',0304,024,033,06,0301,'-',0321,025,'w','F',0323,'O',0256,0363,'+','|',0253,'.',0301,0271,0213,0353,035,'d','-','l',';',017,'1',012,'2',0134,0354,0226,027,0362,0365,0214,'L',0242,'O','J',015,0241,'t',0330,'`',014,'-',0244,014,0361,0237,0333,0244,'#',0325,036,'S',0227,0363,0,0361,0332,0232,0250,'^','^','A',0351,0233,0264,030,0216,'2','X','!','h',0361,0224,'T','a',0301,0260,0354,022,']','O',0342,031,0344,'W',0315,'G',0313,0366,'`',0345,'?',047,0321,0332,0344,'S',0307,';','~',0203,0344,'l','M',0270,0344,'Z','J','|',0273,0346,'C','J',0313,033,'s','-',0245,0354,04,04,0366,'$',0351,'<','c',05,'.','y','e',0266,0276,0322,'2','H',0335,023,0347,035,0204,'0',0352,0347,0212,0356,0325,'t',0276,0311,'9',0252,0223,0345,047,0216,0314,047,0274,036,021,03,'I',0210,0221,'$',025,0375,0201,0206,'y','R',012,'3','E',0210,0222,'c',022,035,0213,'a',0222,0346,'~',0272,0353,0303,0352,']','W',0313,0367,01,0345,0177,'3',0326,'M',0200,0357,0224,'V',031,021,0220,'N',0235,0211,011,0212,'$','7','t',0244,03,017,0346,'J',0201,0362,0317,0231,'m',011,0313,'-',0226,0322,0337,0267,0252,0350,0326,033,'L',0272,0326,0240,06,0260,'?',0215,0225,'L','V',',','4',011,'@',0225,0315,'G',0241,0365,'>',0216,0322,'/',0222,0212,0377,0,'p','z','Y','}',0233,'?',027,0265,036,0333,'J',0251,'+',0345,0307,0261,'$','i',024,'l','v','M','_','<',012,0202,0314,0271,0370,0362,'1','A',0235,0271,0361,0245,0317,0217,'.',017,'[','9',016,030,'V',013,030,0343,0310,'T',0233,0274,0366,'(','w','r',02,0246,025,'E',0333,0256,'C',0310,'9',';','b',0203,031,0313,0327,0355,0317,0376,0311,'}',0213,0314,0227,0330,0356,'_',0277,'v','O',0312,033,014,0330,'V',0347,'v','b',020,'q',05,0367,'@',0256,'&',0314,0215,'K',0210,0343,037,'!',0201,0203,0307,0322,'H',0261,0227,025,0255,'$',0200,'.',037,'V',0223,'#',0343,'X',0327,0261,'O',047,0254,'H',0336,0261,0277,0241,0354,'l',0215,0264,0326,0325,0270,0366,':','7','v','+',0252,'f',0260,'x','`',0304,014,'V','u',02,0134,017,0263,0360,0374,0260,'a',0200,0317,'_','7',0336,0200,'y',011,0223,'Y',0342,'C',',',026,0227,'^',02,0200,'~',0302,0265,034,0226,0332,0366,'*','+','T',0300,030,'S','H',031,0343,'I',0327,'B',0357,0340,0353,' ','(',013,'G',0353,02,'9','_',0251,0263,035,0251,0277,'?',0322,'s','b','j','r',0347,0372,'O',033,0252,'B',0231,06,0276,034,017,'F',0243,'S',0306,0343,'^',02,0366,035,0203,0206,0346,0213,',','j','L',0251,0227,0355,06,011,026,023,'k',0374,'>','i',0231,'E',0251,0326,0353,0261,'w','-','h',0343,'9',010,036,'A','d',0234,'v',0220,0303,0200,'x',0216,0352,0242,'w',0374,0277,'X',0352,0301,'l',0341,0274,0341,0352,0363,'2',0347,0216,'.',0352,'2','H',0237,013,0376,0243,'X',0257,'u','F',0203,'u','s',0224,'|','4',',',031,'[','H',025,'<',']',0363,0353,0242,0260,0212,0302,0262,'Z',0351,036,0304,0221,0266,025,'k',017,'U',037,0365,037,'f',0317,'Z',0255,0270,'m',0247,014,0327,'O',0226,0134,'I','v',026,033,0257,0331,'W',0273,0274,035,'1',0307,0272,0267,0212,0257,016,'Z',0276,026,022,',',0252,0324,'j','i',0223,0313,0351,0313,03,047,0216,0332,0211,0341,0347,0357,',','*',0275,'x',0250,0255,'_',032,'O',0352,'~',0343,0343,'l',0210,'n',0247,'Q','`',0206,'q','-',021,'K','?',011,016,0271,'?',012,0330,'5','g',0342,013,0330,0361,0334,'[',0260,'7',035,0306,'{',03,'q',0274,'i',0260,';',023,0213,0266,07,'d','<','E','}','"',0305,0302,0266,'N',0301,0370,'F',',',017,0210,'h',0307,'P',0264,0312,'j',0374,0216,06,'B',0337,0257,0345,0226,0332,0367,0257,034,0325,'b',0235,'Z',0322,'R','h','_',03,0274,'(',0223,0376,'o',0360,'[','J','X',0354,032,'H',0262,011,')',02,0306,'K','L',0253,0220,'u',0362,0312,0201,0226,030,0177,0301,0214,06,'#',0243,0261,0244,0230,025,0305,026,'%','v',014,';',0312,0233,0374,033,0313,0317,015,0327,0206,')',']',0252,023,0346,0232,0251,'^','u',0265,021,'W','7',0374,'/',0377,0304,0,'A',021,0,01,03,02,02,07,05,03,012,05,04,03,01,0,0,0,01,02,03,05,0,04,021,'!',06,022,023,'0','1','A','Q','"','@','P','q',0241,020,0321,0341,024,' ','#','2','3','`','a',0201,0261,0301,'B','R',0221,0222,0360,'4','C','b','r','p',0200,0262,0361,0377,0332,0,010,01,03,01,01,'?',01,0377,0,0333,';','{','W',0356,0225,0252,0312,'q',0253,'M',016,0272,'t','k','>',0240,0232,'o','C','-',0200,0355,0270,'M','?',0241,'G',0375,0207,0177,0255,'=',0242,0262,'M','p','N',0267,0225,';',033,'x',0307,0332,'4','h',0202,'2','?','q','#','#','^',0223,'{','d',0327,016,'g',0245,'G',0307,0263,034,0310,'e',0221,0363,'H',0307,0215,'9','c','l',0367,0332,'6',015,'.',012,'5','|','Y',025,'}',0242,026,0257,014,'m',0216,0241,0364,0251,'(','{',0250,0263,0364,0303,'#',0317,0356,04,'l','k',0322,'o','l',0232,0374,0317,'J',0217,0217,'f','5',0220,0313,'C',0343,0272,0232,0260,022,026,'k','k',0237,021,'J',05,047,'T',0370,0364,'t','s',0322,'O',06,0232,036,'g',0245,'G','G','3',032,0310,'i',0241,0361,0335,0337,0334,013,'[','W',036,'<',0205,')','E','j','*','<',0374,'v',':','9',0351,047,0203,'-',017,0205,'F',0306,0263,030,0310,'i',0241,0346,'z',0356,0311,0,'b','k','I',047,0305,0326,'6','v',0377,0,'W',0231,0353,0343,0261,0361,0357,'I','<',031,'d','|','*','6','5',0230,0306,'v','M','~','g',0256,0354,0220,0221,0211,0255,'!',0322,'#','p','M',0255,0251,0354,0363,'=','|','v',0302,0301,0351,027,0203,',',0212,0214,0214,'f','1',0235,0223,0134,'y',0236,0273,0265,'(','$','k','*',0264,0207,'H',0215,0321,'6',0266,0247,0261,0314,0365,0335,0237,022,0260,0260,'z','E',0340,0313,'"',0242,0343,031,0213,'g','f',0337,036,'g',0256,0355,'J',010,032,0312,0341,'Z','A',0244,'&',0354,0233,'k','c',0330,0353,0327,'w',0226,036,0315,0222,0365,'u',0360,0313,0333,0217,0206,0330,0330,0275,' ',0360,'e',0221,'Q','q','l',0305,0263,0263,'o',0217,'3',0273,'R',0222,0204,0225,'(',0345,'Z','A',0244,'*',0275,'&',0332,0330,0366,'?',']',0341,0366,'1','j',0317,0311,020,0302,0223,0212,'p',0251,'M',020,'J',0361,'r',0304,0341,0370,'U',0305,0263,0326,0253,0331,0274,0234,017,0206,0330,0330,0275,' ',0360,'e',0221,0235,'E','E','3',026,0316,0315,0276,'<',0316,0355,'k','K','i','*','Q',0300,012,0322,015,' ','U',0362,0215,0275,0271,0301,0277,0327,'z','i',037,'X','S','?','d',0237,'!',0354,0275,0217,0266,0277,'F',0245,0302,'q',0251,'}',030,'~',0307,027,'X',0355,'#',0324,'x',']',0225,0223,0327,0357,06,'Y',031,0232,0212,0212,'f',')',0235,'D','}','n','g','v',0265,0245,0264,0225,0254,0340,05,'O',0351,02,0257,0324,'X','c','&',0307,0256,0370,0360,0366,'[',0234,'Y','A',0374,07,0314,0227,0321,0206,'/',0261,'v',0337,0262,0277,'C','W',0226,'/',0330,0271,0263,'}','8',037,010,0263,0263,'z',0371,0340,0313,'#',022,'j','&','%',0230,0246,'u',021,0232,0217,023,0273,'q',0304,0264,0222,0265,0234,0,0251,0371,0365,'H','+','b',0306,'M',0217,']',0371,0372,0236,0313,023,0215,0253,'g',0360,0371,0267,0226,'L','_','7',0263,'}','8',0212,0227,0321,0207,0354,'q','v',0337,0264,0217,'Q',0340,0326,'v','n',0337,'<',031,'d','b','M','D','D',0265,024,0316,0252,'s','Q',0342,'w','n',0272,0206,'P',0134,'p',0340,05,'O','O',0256,'E','[',026,'r','l','z',0367,03,0365,'}',0221,'g',033,'&',0217,0341,0363,0345,0364,'a',0213,0354,']','c',0262,0277,'C','W',0266,027,026,016,'l',0337,'N',036,07,'i','h',0355,0353,0241,0226,'F','$',0324,'D','C','Q','L',0352,0247,'5',036,047,'v',0353,0250,'a',05,0307,016,0,'T',0354,0362,0344,0227,0262,'k','&',0307,0257,'q','>',0310,'c',0214,'{',047,0376,'#','q','s','h',0315,0342,'6','o',0247,021,'R',0332,'(',0355,0276,'.',0331,0366,0223,0323,0235,020,'R','p','>',01,'k','j',0355,0353,0241,0226,'F','$',0324,'<',';','Q','M','`','3','Y',0342,'w','o','<',0206,020,0134,'p',0340,05,'N',0316,0256,'M','{','6',0362,'l','z',0367,'#',0354,0201,'8',0306,0261,0345,0272,0225,0321,0373,'Y','!',0255,0206,0252,0372,0324,0214,'E',0324,'b',0260,'y','9','u',0345,0354,012,0353,'D','s',035,0356,0326,0325,0333,0307,'C',',',0214,'I',0250,'h','v',0242,0232,0300,'f',0263,0304,0356,0336,'y',0273,'v',0313,0256,0234,0,0251,0311,0327,'%',027,0250,0214,0233,035,0327,'D','n',0366,0366,';','#',0305,033,0267,'Z','C',0311,'(','p','b','*','[','D',0270,0273,'a',0375,0276,0352,'q',0245,0262,0242,0207,06,07,0275,0333,'[',';','v',0350,'e',0221,0211,'5',015,014,0324,'S',']','V','x',0235,0333,0357,0267,'l',0331,'u',0323,0200,025,'7','8',0344,0243,0232,0251,0311,0261,0300,'w','D',0220,016,'t',0264,0352,0234,'*','"','U',0310,0247,0366,0211,0372,0247,0210,0253,'K',0306,'o',0232,017,'2','q',07,'y','%',017,'k','&',0234,035,031,0365,0347,'R',0320,'W',021,'j',0304,0346,0216,0275,0346,0332,0335,0313,0267,'C','-',014,'I',0250,'X','V',0242,0232,0352,0263,0304,0356,0356,'.',033,0265,'l',0272,0351,0300,012,0233,0233,'r','Q',0314,06,'M',0216,03,0273,0360,0250,0211,'w',0242,0235,0326,'F','i',0346,'*',0322,0355,0253,0326,'C',0354,0234,0216,0361,'h','K',0211,0325,'X',0304,'T',0276,0211,05,'b',0365,0207,0366,0373,0251,0326,0226,0312,0312,034,030,037,'f','=',0322,0336,0335,0313,0247,03,'M',014,'I',0250,'X','V',0342,0332,0304,0346,0263,0304,0356,0356,'n','Z',0264,'h',0274,0361,0300,012,0232,0232,'v','U',0314,'8',' ','p',035,0343,0353,'e',0316,0270,'T','$',0333,0221,'.','`','s','A',0342,'*',0322,0361,0233,0346,0203,0254,'+',021,0275,0222,0207,0265,0223,'F',016,0216,0327,'Z',0225,0202,0272,0214,'V','*',030,0243,0257,'t','a',0207,'.',0134,015,'4','1','&',0241,'!',033,0213,'o',023,0232,0317,023,0273,0272,0272,'j',0315,0242,0363,0307,0,'*','f','e',0331,'W','z',' ','p',035,0353,020,0340,0301,0134,'i','I',')','8',032,0217,0223,0270,0215,'s','h',0311,0374,0252,'&','v',0332,'Q','8',014,0227,0323,'z',0264,'%',0304,0352,0250,'b','*','_','D',0222,0274,'^',0261,0310,0364,0367,'S',0254,0270,0302,0313,'n',0214,017,'q','a',0207,'.',0134,015,'4','1','&',0241,'!',033,0213,'o','Y','Y',0270,'x',0235,0335,0335,0333,'V','M',027,0236,'8',01,'S',023,016,0312,0273,0211,0311,03,0200,0357,0211,'R','V','5',027,'J','N',0251,0244,'-','M',0253,']',07,03,'P',0332,'W',0255,0203,027,0377,0,0335,0357,0244,0251,'+',032,0311,'8',0215,0354,0224,'E',0254,0232,'0','x','g',0327,0235,'J',0300,0335,'F',034,'H',0326,'G',']',0373,',',0271,'p',0340,'i',0241,0211,'5',07,06,0334,'[','z',0313,0315,0303,0273,0274,0274,'f',0305,0242,0363,0307,0,'*','^','a',0351,'W','q','V','I',034,07,0200,0304,'O',0334,'F',035,'S',0332,'G','J',0260,0222,0267,0222,'o','h',0302,0277,'-',0352,0222,0225,0215,'U',014,0252,'_','D',0322,0356,'/','X',0344,'z','S',0314,'9','n',0262,0333,0243,03,0274,'e',0225,0334,'8',033,'l','b','M','A','A',0242,'-',0275,'u',0346,0341,0335,0336,'^','3','`',0311,'y',0343,0225,'K','K',0275,'*',0356,0262,0262,'O','!',0340,'v',0267,'o','Y',0270,035,'a','X',032,0206,0322,'f','o',0260,'j',0343,0262,0277,'C',0276,0221,0211,0266,0223,'F',0253,0303,'>',0274,0352,'V',02,0346,'0',0353,'a',0254,0216,0273,0246,0232,'[',0353,015,0266,'1','&',0240,0240,0221,030,0215,0243,0231,0270,'}','7','w',0267,0254,0330,'2','^','x',0345,'R',0322,0317,'J',0275,0256,0277,0253,0310,'x','/',012,0206,0322,0227,'-','0','f',0357,0264,0216,0274,0305,'1','p',0325,0323,'a',0326,'U',0210,';',0325,'$',',','j',0253,0205,'K',0350,0232,036,0305,0353,034,0217,'J','}',0207,'m',0226,'[','u','8',037,'f','?','5',0246,0226,0372,0303,'m',0214,'I',0250,'(','$','F','#','h',0346,'n',037,'M',0335,0365,0363,'1',0354,0227,0236,'5','+','*',0364,0243,0332,0356,'p',0344,'<','"','6','Z',0346,'1','z',0314,0234,0272,'T','T',0345,0264,0242,'{',047,05,0364,0337,'H','E','[','I',0243,'U',0344,0347,0327,0235,'K',0300,0334,0305,0250,0253,015,'f',0372,0373,0376,'k','M','-',0345,0206,0333,030,0223,'P','0','(',0215,'F',0325,0334,0334,'>',0233,0273,0373,0366,'c',0231,'/','<','j','R','Q',0351,'G',0266,0216,'p',0344,'<',')',0267,026,0322,0202,0320,'p','5',015,0245,'a','x','1','}',0307,0371,0275,0364,0225,05,0215,'d',0357,'T',0224,0254,'j',0250,'b','*','_','D',0222,0351,'/','X','d',0177,0224,0360,0374,0272,'U',0315,0243,0326,'n','l',0337,'N',0251,0366,'6',0332,0335,'X','B',06,'$',0324,014,012,'#','Q',0265,'w','7',017,0246,0356,'B','A',0230,0326,'K',0257,032,0223,0223,'z','M',0355,0243,0274,'9',017,015,0207,0322,07,0343,016,0252,0373,'H',0351,0323,0312,0254,'d',0355,'d','S',0255,'n',0254,0177,']',0365,0355,0203,022,015,0226,0337,'O',0276,0256,'4','>',0365,012,';',022,024,'+','G',0340,04,'j','v',0317,0375,0241,0364,0335,0310,0310,0263,032,0311,'u',0323,0344,':',0324,0224,0223,0322,'o','m',']','>','C',0247,0207,0262,0373,0266,0313,0332,'4',0254,015,'D','i','c','o',0340,0325,0377,0,'e',']','y','|','(',020,0241,0210,0357,022,'R','L',0306,'3',0265,'t',0371,016,0265,'#','"',0364,0223,0305,0327,0217,0303,0304,0242,0347,0356,0343,'p','@','8',0243,',',0216,'y','~',025,033,'/','k','(',0234,'Y','9',0363,034,0373,0264,0234,0233,'1',0214,0355,035,0343,0310,'u',0251,011,07,0244,'^','/','<','~',036,'(',0333,0212,'i','A','m',0234,010,0250,0215,',','I',01,0231,014,0277,0345,0327,0317,0245,'%','Z',0331,0247,0207,'s',0224,0224,'f','-',0235,0243,0234,'y',012,0277,0277,'z','E',0342,0363,0307,0305,0343,'&',0256,0342,0325,0364,'G',024,0364,'<','>',025,033,'3','i','&',0234,'Z','V',012,0350,'x',0367,031,'Y','6',0242,0330,0332,0271,0307,0220,0253,0353,0347,0244,036,'/','<','|','d',022,016,'"',0241,0264,0253,022,031,0221,'8','t','W',0277,0337,'A','a','X','j',0346,016,0377,0,'L','m',0234,'v',0325,016,0243,0202,'N',0177,0237,0263,037,032,0205,0233,'z','-',0300,0222,'q','o',0230,0367,'S','/','"',0341,0260,0353,'|',016,0371,0306,0320,0362,013,'k',030,0203,'S',':','(',0244,'b',0375,0207,016,'i',0367,'R',0320,0244,035,'U',014,017,0215,0,'I',0300,'T',023,'N',0261,034,0322,036,0343,0334,'$',0340,0255,'$',0301,'R',0206,013,0353,0357,0353,'R','1','7','Q',0253,0325,'x','e',0324,'p',0361,0216,'5',0243,0232,'=',0263,0302,0362,0350,'g',0310,'w',047,0230,'n',0341,05,0247,'S',0210,'5','3',0242,0212,'g',0351,0254,'3',034,0323,0323,0312,0224,0222,0203,0202,0206,07,0305,'@',0307,'!','Z','=',0243,0233,',','.',0356,0306,'|',0207,'u',0225,0200,0266,0224,0355,036,0312,0372,0217,0336,0244,0242,'n',0243,027,0252,0370,0313,0257,'/',023,0,0250,0340,'+','G',0264,'t','1',0205,0325,0330,0355,'r',035,';',0273,0354,'7','r',0331,'i',0321,0210,'5','3',0242,0357,'Z',0257,'^',0305,':',0310,0351,0314,'V','c',0304,022,0222,0243,0202,'k','G',0264,'t','Z',0201,'u','t',';','|',0207,'N',0365,'1',0243,'v',0327,0340,0272,0337,'a',0177,0257,0235,'^','Y','=','b',0346,0311,0361,0237,0241,0362,'>',034,0224,0225,0235,'T',0361,0255,037,0321,0341,'f',05,0315,0310,0355,0364,0351,0273,0271,0271,'n',0321,0242,0363,0247,0,'*',0373,'I','/',0256,'^','*','e','e',011,0344,05,'F',0351,'z',0333,01,0273,0321,0255,0370,0325,0265,0323,'7',0215,0355,'X','V',0262,'w',0274,'j',0362,0302,0336,0371,0222,0313,0311,0313,0364,0362,0251,'}',034,0270,0214,0305,0304,'v',0333,0353,0317,0363,0360,0304,'!','N','(','%','#',022,'k','G',0364,'y','6',' ',0134,0134,014,0134,0375,'7','n','8',0206,'P',0134,'Y',0300,012,0236,0233,0134,0233,0272,0210,0373,'1',0353,0355,0264,0276,0271,0261,'^',0322,0335,'x','T','>',0221,'[',0311,0375,032,0373,016,'t',0353,0345,0275,'#',032,' ','(','`','j','o','E',0222,0377,0,0323,'X',014,025,0314,'r','4',0373,016,0333,'/','d',0362,'u','U',0341,'-',0266,0247,'T',020,0201,0211,'5',01,0243,0351,0260,'H','}',0374,0334,0375,'7','j','P','H',0304,0326,0221,'O',033,0345,0374,0235,0203,0364,'c',0327,0346,0343,0206,'b',0241,0264,0251,0326,0224,030,0276,0355,047,0371,0271,0217,'?',0302,0220,0264,0270,0235,'d',034,'F',0372,'N',036,0332,'Q',030,':',';',0134,0217,'1','R',0221,'7',021,'.',06,0336,0314,036,07,0221,0360,'v',0333,'[',0313,015,0266,'1','&',0240,'`',021,034,0235,0263,0331,0270,'}','7',0232,'K','?',0266,'&',0316,0324,0366,'y',0235,0304,'l',0315,0324,'b',0206,0310,0342,0237,0345,0345,'Q',0322,0314,0311,0241,'*','g',0363,0352,'7',0317,0333,0265,'r',0202,0333,0311,0304,'T',0316,0213,0273,'f','v',0226,'@',0251,035,'9',0217,0205,'p',0343,0340,0215,'4',0267,0326,033,'l','b','M','A','@',0242,'5',033,'W','s','p',0372,'o','4',0226,0177,'g',0215,0235,0251,0317,0231,0335,'6',0342,0331,'P','[','G',03,'P',0372,'T',0335,0306,014,'^',0366,'U',0327,0221,0367,'W',0226,0372,'_','F',0255,0244,'q','u',0256,0303,0235,'z',0371,0325,0325,0233,0366,'N','l',0237,'N',07,0300,0231,'e',0313,0207,03,'M',014,'I',0250,'8','&',0343,021,0264,'^','n',037,'M',0346,0221,0317,013,'4',0233,'[','s',0333,'>',0224,'I','Q',0304,0357,'!',0264,0221,0370,0342,032,'{',0264,0337,0250,0253,'[',0266,'o','[',0332,0260,0254,'F',0372,'F','1',0211,'6',0366,'o',0216,034,'*','Z',016,0346,')','X',0253,'6',0371,'+',0337,0323,0300,030,'a',0313,0227,03,'M',014,'I',0250,'H','6',0342,0333,0326,'V','n',036,'{',0311,0371,0304,0306,'7',0263,'o',0355,017,0245,'-','j','u','E','k','8',0223,0276,0265,0276,0271,0261,'^',0275,0272,0365,'j',036,'y',0211,'4',04,0250,0352,0271,0323,0335,0276,'u',0244,'>',0202,0333,0203,020,'j','k','E',0224,0306,'7',026,'9',0247,0371,'z','y','Q',0313,'#',0337,'m',0255,0235,0273,'t','4',0310,0304,0232,0205,0204,'j',')',0274,'N','n',036,047,'y','5','0',0334,'S','=','V','x',012,'}',0367,'.',0134,'.',0272,'q',047,0270,'%','J','A',0326,'I',0300,0324,'.',0225,0341,0205,0275,0377,0,'/',0342,0367,0322,026,0227,022,026,0236,07,'}','/',0243,'v',0362,'8',0272,0337,'a',0316,0275,'|',0375,0365,'u','j',0365,0233,0245,0227,0323,0202,0207,'{',0265,0265,'v',0361,0320,0313,'#',022,'j',032,025,0250,0246,0272,0254,0361,';',0311,'Y','6',0242,0330,'.',0257,0217,'!','W',0227,0216,0337,'<','^','x',0346,'{',0234,'6',0220,'?',032,0240,0332,0316,0263,']',':','y','U',0235,0363,027,0355,07,'X','V','#','}',')',026,0314,0253,';',047,'r',0350,'j','V',026,0342,')','d','(','b',0216,'J',0345,0360,'=',0346,0322,0321,0333,0327,'C',',',0214,'I',0250,'x','v',0242,0232,0300,'f',0263,0304,0357,'/',0357,0232,0217,'`',0274,0351,0251,')',027,'d',0237,'/',';',0371,'~',035,0326,0332,0355,0373,'7','6',0254,'+',03,0376,'q',0250,'m','!','b','O',0350,0227,0331,'s',0365,0362,0336,0221,0210,0302,0235,0267,'K',0311,'-',0273,0232,'O',020,'j','k','G',035,0215,0305,0346,'{','M','z',0217,0363,0257,'w',0263,0263,'z',0371,0340,0313,'#',023,'Q',021,014,0305,'5',0252,0234,0324,'x',0235,0345,0315,0313,'V',0215,027,0236,'8',01,'S',022,0316,0312,0277,0256,0257,0252,'8',016,0357,0303,0205,'C','i','Z',0231,0372,031,03,0210,0344,0256,0236,'t',0332,0322,0352,02,0321,0300,0357,0247,0264,'c',0134,0233,0253,021,0237,'4',0376,0343,0335,'K','B',0233,'Q','B',0306,07,0272,'X',0330,0275,' ',0360,'e',0221,0235,'E','D',0263,024,0316,0242,'>',0267,'3',0274,'u',0324,'0',0202,0343,0207,0,'*','v','i','r',0216,0352,0247,'&',0307,017,0177,'z',0205,0236,'z','-','z',0253,0355,'7',0323,0247,0225,'Y','_',0261,' ',0330,'u',0205,'b','=','F',0372,'o','G',0332,0225,0372,'D',0235,'W',':',0365,0363,0253,0253,'7',0354,0234,'-','>',0234,017,0371,0303,0271,'X','X','=','"',0360,'e',0221,'Q','q','l',0305,0263,0263,'o',0217,'3',0274,'R',0202,06,0262,0270,'V',0220,0316,0231,05,0354,031,'?','F','=','{',0345,0245,0355,0305,0213,0233,'[','u','`','j',036,'}',0211,'4',0206,0325,0331,'w',0247,0273,'}',0177,026,0324,0215,0266,0301,0374,0317,'#',0314,'T',0204,']',0314,'b',0365,'_',031,'r','<',0217,'p',0217,0217,'z','I',0340,0323,'#',0341,'Q',0221,0214,0306,'3',0263,'k',0217,'3',0327,'y',0302,0264,0222,0177,0345,04,0332,'[',036,0317,'3',0327,0277,'!','j','m','A','h','8',021,'P','z','S',0216,0255,0255,0367,0367,'{',0375,0364,010,'9',0215,0355,0345,0233,'7',0314,0226,037,030,0203,'S','0','/','D',0235,'l','u',0220,'y',0364,0363,0337,'F','F',0273,'&',0370,'i',0277,0314,0364,0250,0370,0346,'#','Z',0331,'2','>',';',0335,'%',0237,0343,'e','j','|',0317,0355,0340,'0',0332,'D',0374,'f',014,0271,0332,'o',0324,'y','U',0245,0343,027,0315,0355,'m',0325,0210,0336,0251,')','X',0325,'P',0304,'T',0346,0214,'*',0337,'Z',0346,0313,'4',0377,0,'/','O','-',0345,0265,0273,0227,'n',0245,0226,0206,'f',0243,'c',0332,0216,'a','-','6','3',0347,0275,0322,'I',0377,0,0222,0244,0332,'[',036,0331,0342,'z','Q','8',0346,'|',012,'>','R',0346,'1',0315,'{','u','q',0342,'9',032,0211,0233,0267,0226,'O',0321,0344,0241,0304,'o',0264,0227,'G',0303,'x',0337,'Z',0214,0277,0210,'~',0373,0256,'5',0243,0320,0250,0217,'h','<',0277,0264,'W',0246,0367,'H','g','D','r','6',',',0375,0241,0364,0245,')','K','Q','R',0270,0370,'"',026,0246,0324,026,0203,0201,025,01,0244,0333,'|','-','o',0217,'o',0221,0353,0361,0336,0251,'!','C',05,'T',0374,':',0243,']',0332,0244,'v',024,'r',0374,'?',015,0316,0213,'F',0246,0366,0344,0272,0347,04,'~',0273,0331,0311,0244,'E',0265,0202,'s','p',0360,0247,0236,'[',0353,'.','8','q',047,0301,0341,'4',0235,'v',0201,'6',0327,'y',0243,0257,'1',0360,0246,']','K',0315,0207,023,0300,0357,'$',0254,'[',0221,0266,'U',0273,0234,0351,0346,0226,0303,0212,'i',0301,0230,0313,'q',0242,'6',0333,033,015,0257,'5',035,0344,0274,0253,'Q','L','k',0253,0353,036,02,0256,0256,0235,0274,'t',0274,0361,0304,0237,011,0212,0234,0270,0212,'W','g',0264,0223,0313,0335,'V',022,014,'H',0262,035,'d',0376,0134,0306,0363,'L','l','C','7',011,0272,'@',0311,'y',037,'?',0363,0364,0334,'h',0225,0350,'~',0317,'`','x',0243,0364,0335,0310,0310,0263,032,0301,'y',0337,0377,0,'j',0376,0375,0331,027,0313,0316,0237,013,0263,0275,'~',0301,0335,0265,0272,0260,'?',0347,032,0206,0237,'b','M',01,013,':',0256,0364,0367,'n',0364,0206,0325,'W','q',0356,'!',03,023,0307,0372,'n','#','$',034,0215,0270,017,'#',0363,0253,')',013,'{',0366,0303,0214,0253,'s','y','x',0325,0213,'%',0347,0216,'B',0245,'e',035,0224,0177,'h',0276,034,0207,0206,0241,'j','m','A','h','8',021,'P','z','R',010,026,0362,07,'>','J',0367,0327,035,0311,032,0303,03,'W',0326,0377,0,'$',0271,'q',0214,'0',0300,0237,0351,0313,0323,'q','o','r',0355,0252,0303,0214,0253,03,'V',':','d','R','5','o',021,0217,0342,'*',0326,'Z',0312,0361,':',0315,'8','?','J','7','v',0351,0314,0270,'?',0255,031,'K','!',0305,0321,'I',0221,0264,'W',07,'G',0365,0257,0225,'0',0177,0334,037,0324,'W',0312,031,0376,'q',0375,'j',0366,'f',0312,0305,032,0313,'X',047,0240,0251,'I','W',0345,035,0327,'s',0207,'!',0342,020,'z','F',0355,0222,0303,'7','G','Y',0277,0376,'~',024,0323,0351,'}','!','m','f',0223,0271,0322,0266,'R',0324,0232,0210,0376,' ',017,0355,0373,'n',0370,0373,'q','#',0205,'k',0253,0257,0211,0303,0315,'=',022,0274,0263,'A',0342,'=',0325,'c','~',0304,0203,'!',0366,'N','_',0246,0343,'M',0200,0332,0260,0177,03,0373,'}',0303,0263,0276,0270,0260,'s','h',0302,0260,'>',0237,0230,0250,'I',0266,0345,0233,0300,0344,0340,0342,'?','q',0363,0364,0325,0264,0374,0231,0247,'0',0317,034,'=','>',0342,'!',0305,0264,0255,'f',0316,06,0240,0264,0231,'7','d','[','^','d',0347,'^','G',0343,0363,0264,0323,0375,033,0177,0366,0375,0217,0334,'h','M','(','U',0250,026,0367,0270,0251,'<',0217,'1',0347,'H','Z',0134,'H','R','N',' ',0374,0315,'4',0377,0,'F',0337,0375,0277,'c',0367,036,032,'}',0370,0265,0245,012,':',0315,'t',0351,0345,'V',0267,'l','^',0267,0265,0267,'V',0260,0366,0351,0242,0323,0362,'V',0221,0216,'z',0337,0267,0334,0213,'9',013,0250,0362,'U','l',0275,0134,'j','#','I','m',0357,0322,033,'x',0352,'9',0350,'|',0250,020,'s',025,0244,0362,')',0277,0274,0324,'o',0352,0267,0227,0347,0317,0356,'R','/',0256,0233,'F',0315,016,0250,017,'3',0377,0,0206,0177,0377,0304,0,'1',021,0,02,01,03,03,03,02,05,04,02,02,03,0,0,0,0,01,02,03,0,021,'!',022,'0','1',020,'@','P',04,'"',' ','A','B','a','q',023,'2','Q','`','3',0221,024,'R','p',0200,0201,0377,0332,0,010,01,02,01,01,'?',01,0377,0,0333,'6','`',0274,0321,0365,037,0365,025,0372,0357,'C',0324,037,0230,0241,0352,027,0347,'B','D','?','?',0350,0262,'H',020,'Q','%',0215,0317,0307,0251,0277,0232,'Y',0235,'~',0364,0222,07,0376,0200,0356,020,'Q','%',0215,0316,0322,0266,0223,0253,0317,0273,0204,024,'I','c','s',0267,'k',0342,0206,'<',0353,0270,'A','D',0226,'7',';',0220,0305,0365,037,':',0356,020,0134,0323,'1','c','s',0271,024,'_','S','y',0327,'p',0202,0346,0231,0213,033,0235,0310,0242,0372,0233,'n',0365,'z',0277,0220,'w',010,'.','i',0230,0261,0271,0334,0212,'/',0251,0266,0317,'K',0216,':',0337,0306,0273,0204,027,'4',0314,'X',0334,0356,'E',027,0324,0333,0207,0247,0336,0226,'R','9',0240,0301,0270,0353,0177,024,0356,020,0134,0323,'1','c','s',0271,024,'V',0367,'6',0351,0243,'C',0240,'6',0310,0244,0226,0370,'n',0267,0361,014,0301,05,0315,'3',027,'7',';',0221,'E','o','s','o',036,0203,0340,'Y',012,'R',0260,'n','<','C','0','A','s','L',0305,0315,0316,0344,'Q','[',0334,0333,0347,0236,0203,0341,0343,'"',0222,'o',0223,'u',0277,0203,'f',010,'.','i',0230,0271,0271,0334,0212,'-','>',0346,0354,016,0312,0310,'R',0225,0303,0361,0340,0331,0202,013,0232,'f','.','n','w','"',0213,'N','O','=',0211,0350,'y',';',037,'z','Y',0276,'M',0326,0375,0363,'0','A','s','L',0305,0315,0316,0344,'Q','i',0311,0347,0262,'=',033,0367,035,0245,'b',0234,'R','H',037,0250,'=',0333,'0','Q','s','N',0345,0315,0316,0344,'Q','i',0311,0347,0263,'=','$',026,'}',0305,0230,0217,0335,'@',0203,0221,0322,0375,0313,'0','Q','s','N',0345,0315,0316,0344,'Q','i',0311,0347,0264,'=','$','M','b',0270,0301,0334,04,0256,'E','$',0201,0261,0322,0375,0273,'0','Q','s','N',0345,0315,0316,0344,'Q','h',0311,0347,0266,'=',036,'=','u',0371,0335,'I','H',0303,'P',' ',0344,'t',0277,'h',0314,024,0134,0323,0271,'s','s',0271,034,'Z','2','y',0356,017,'I','#',0325,0221,0134,'`',0356,0202,'W','"',0222,'P',0330,0351,'~',0311,0230,'(',0271,0247,'r',0346,0373,'|',0340,'T','q',0350,0311,0347,0272,'"',0334,'t','t',017,'L',0245,'9',0336,'I','J',0340,0320,' ',0344,'t',0276,0373,'0','Q','s','N',0346,'C',0267,0316,05,'G',036,0217,0317,'x','G',0314,'t','"',0374,0323,0304,'F','W','x',022,0271,024,0222,0206,0301,0351,'}',0322,'B',0213,0232,'w','2',035,0276,'p','*','8',0364,'~','{',0373,'t','x',0203,'d','Q',04,'`',0357,'$',0245,'y',0240,'C','d','t',0276,0321,'!','E',0315,';',0227,';','`','_',02,0243,0217,'G',0347,0300,0333,0243,'(','l',032,'x',0312,'o',02,'W','"',0222,'P',0330,';','D',0205,027,'4',0356,0134,0355,0200,'N',05,'G',030,'A',0341,'-',0321,0341,0371,0256,0372,'J','W',0232,04,'6','G','K',0374,'$',0200,'.','j','G','.','v',0300,'$',0330,'T','q',0204,0360,0326,0350,0361,0207,0246,'R',0234,0357,02,'W','"',0226,'P','y',0370,'I',0,0134,0324,0222,'k',';','`',026,'6',025,034,'a',07,0212,'"',0370,'4',0361,025,0310,0336,'"',0374,0322,'H',0361,0375,0305,'+','+',0345,'z',023,'l',0232,0222,'M',0177,0215,0260,013,033,012,0216,'0',0203,0305,0333,0244,0220,0352,0367,'/','5',0301,0261,0347,'x','`',0334,'P',0365,03,0346,'*','I','5',0237,0266,0330,05,0215,0205,'"',04,036,'=',0343,'Y',07,0270,'S',0306,0321,0375,0307,'r',01,'c','a','H',0201,07,0222,0223,0323,0352,0312,'`',0326,'A',0263,'v',0312,0245,0215,0205,'"',04,026,036,'Q',0321,'d',026,'j','x','Z','>','2','*',0367,0354,0325,'K',033,012,'D',010,',','<',0274,0236,0234,'7',0271,'p','h',0335,'M',0233,0261,'U',',','l',')',020,' ',0260,0363,'.',0212,0342,0315,'O',013,'G',0221,0221,'W',0337,0204,0331,0272,'_',0315,'I',010,'l',0216,'{',01,0352,012,0376,0356,'(',020,0331,'_','6',0306,0354,'H',0354,05,0320,0335,'j','9',0226,'L',034,036,0227,0362,0362,0313,0253,0332,'8',0354,0210,0275,'$',0345,'?','~','E',013,'0',0272,0364,0277,0224,0226,']','x',034,'v',0240,0224,'7','Z',0216,'Q','&','>','t',07,0223,0222,']','x',034,'v',0344,'^',0223,0324,024,0366,0277,037,0315,03,'|',0371,031,'%',0327,0201,0307,'t',0254,'b',0312,0324,'r',0254,0203,036,';',0212,0222,'M','x',034,'m',0376,')','a','P','=',0324,0360,'|',0326,0257,0275,'o',0342,0243,0365,037,'L',0237,0357,0306,023,'n','j','I','5',0376,'7','"',0217,'N','O','=','e',0205,'e',0373,032,'`',0321,0233,'>',0367,'4',0222,'4','<','d','R',':',0270,0270,0361,'$',0333,'&',0244,0223,'_',0343,'r',030,0355,0356,'o',0204,0200,0302,0306,0244,0201,0243,0312,'d','P','7',0336,0310,'7','^','j',')',0303,0373,'[',0237,016,'H',02,0346,0244,0220,0277,0343,'r','(',0257,0356,'m',0211,'}','>',0277,'r',0340,0327,0271,'N',0226,033,0305,'C','s','I',0352,012,'{','d',0343,0371,0240,'o',0221,0341,011,012,'.','j','I',013,0235,0310,0243,0325,0356,'<','m','<','k','(',0263,'S',0243,'E',0316,'F',0372,'1',0214,0373,'j','9',026,'A',0340,0211,012,'.','i',0334,0271,0334,0216,'=','y','<','n',0311,0351,0355,0356,0217,0375,'U',0367,0263,'{',0251,0261,0250,0246,0325,0207,0347,0300,'3',05,027,'4',0356,0134,0356,'F',0237,0250,'h',013,'`','o','K',012,0313,0371,0242,032,'3',0245,0367,0210,0276,015,'G','1','L','?',024,015,0305,0307,'z',0314,024,0134,0323,0271,'s',0270,0210,0134,0330,'R',0250,'Q','a',0330,'2',0207,026,'j','x',0232,',',0214,0212,06,0342,0343,'y',030,0306,'q','I','"',0270,0270,0356,0331,0202,0213,0232,'w','.','n','w',025,'K',033,012,'E',010,',',';','9','=','=',0316,0244,0257,0261,0336,0317,'*','j','9',0265,0373,'O','=',0313,'0','A','s','N',0345,0315,0316,0340,04,0233,012,'D',010,';','Y','a','Y','G',0336,0230,'4','f',0317,0272,'k','I',0376,'j','9',0364,0373,'d',0377,0,'}',0273,'8','A','s','L',0305,0315,0316,0347,'5',034,'z',07,'n',0300,'0',0261,0251,'!','h',0262,0271,024,015,0305,0367,0222,'S',026,'9',024,0244,'0',0270,0355,035,0302,013,0232,'f','.','n','w','b',0217,'F','O','=',0324,0220,0134,0352,'J',0315,0354,'w',0225,0231,015,0326,0243,0220,'H','.',';',047,'p',0202,0346,0231,0213,033,0235,0330,0242,0267,0270,0367,0222,'D','$',037,'z','!',0220,0331,0267,0205,0327,' ',0346,0241,0233,0365,'0',0334,0366,016,0341,05,'3',026,'7',';',0260,0305,0365,'7','|',0312,034,'Y',0251,0342,'h',0270,0343,'x',0213,0324,'S',0375,022,'s',0275,'#',0204,024,'I','c','s',0273,024,'z',0275,0307,0300,0313,07,0325,035,'_','t',0213,0363,'Q','L','S',0332,0374,'n','3',05,027,'4','I','c','s',0273,024,'z',0362,'x',0360,'r','D','$',0374,0321,05,'N',0226,0336,0212,'_',0322,0366,0236,'6',0344,0177,0324,';',0261,0307,0254,0375,0250,013,'x','G','E',0220,'i','j','t','h','y',0310,0335,'"',0365,0351,0244,'?',0343,'}',0231,0336,0303,'H',0335,'D','.','m','@',05,026,036,036,'H','t',0346,'>',')','N',0241,'}',0303,0374,0212,0215,0304,0213,0250,'l','L','n',0373,0212,0245,0315,0205,'*',0205,026,036,'&','X','5','{',0223,0232,0317,015,0316,0347,0247,'m',',','S','b','e',0322,0373,'`',026,'6',024,0210,020,'X','x',0271,'"',022,012,' ',0307,0207,0333,'$',0241,014,'>','[',022,'&',0261,'j',' ',0251,0261,0331,02,0370,025,034,'z',07,0215,'e',016,',',0325,'$','m',016,'y',']',0243,0232,0200,0352,0214,'_','a',0224,'6',015,'4',07,0351,0242,012,0363,0323,'?',0,'R',0334,'T','q',0204,0362,022,'A',0247,0335,037,0372,0240,0327,0343,'g',0322,0236,'W','~',0336,'N','X','5',0373,0227,0232,0374,0354,'z','s',0357,'#',0372,034,0260,0254,0243,'<',0325,0231,016,0227,0370,0341,0377,0,047,0364,'G','@',0342,0315,'N',0215,017,'<','|','Q',0177,0224,0177,'E','"',0370,'5','$','&',',',0247,024,015,0362,'>',010,0377,0,0312,0277,0321,0345,0203,':',0222,0257,0326,034,0311,0375,'"','X','D',0243,0357,'D','4','x','~',0236,0231,'y',0220,0377,0,0363,0372,'W',0374,'x',0257,'{',0177,0341,0237,0377,0304,0,'G',020,0,01,02,03,04,05,012,02,07,07,03,04,03,01,0,0,01,02,03,0,04,021,022,'!','A','Q',020,' ','"','0','1',05,023,'#','2','@','R','a','q',0201,0241,'B',0221,024,'3','S','b','r',0261,0301,'$','P','c',0202,0222,0242,0321,'4','`',0223,'C','s',0203,0262,'5','D',0341,0361,0377,0332,0,010,01,01,0,06,'?',02,0377,0,'s',0364,0363,'l',0265,0340,0245,0210,' ',0316,0207,024,'0','l','V',':','&',037,'s',0314,'R',':','.','M',0247,0343,0134,'l',0311,0313,0244,'z',0305,0311,'e','?',0313,027,':',0322,0177,0361,0210,0332,'u',0245,0177,0343,021,'x','e','_',0313,033,'R',0222,0353,0371,0307,'M',0311,0300,0376,05,'G','M','*',0373,'^','[','P','?','k',0346,'I',0301,0324,0322,':',031,0326,0134,0360,013,025,0375,0365,'n','f','a',0266,'G',0336,'T',020,0301,0134,0342,0376,0340,0273,0347,04,'I',0313,0267,',',0234,012,0266,0214,036,'z','}',0333,047,0341,'I',0240,0215,0265,0251,0177,0210,0327,'{','T',0250,0244,0370,030,037,'G',0237,'y',')',037,015,0252,0210,02,'i',0266,0246,0223,0211,0245,014,01,'2',0227,'%',027,0343,'x',0200,0251,'I',0246,0336,0257,'t',0337,0373,0314,0255,0327,022,0332,06,'*','4',0205,'!',0205,031,0327,'F',015,0360,0371,0302,0222,0301,'L',0223,'_',0303,0343,0363,0202,0271,0207,0226,0362,0216,'+','=',0226,0323,'N',')',0265,'f',0223,'H',01,'o',011,0266,0307,0302,0367,0371,0204,0242,'p','*','I',0314,0315,0351,0216,'r',']',0344,'<',0214,0320,'k',0274,0332,'P','O',0231,0217,0257,'o',0372,0243,0353,0321,0363,0217,0257,'o',0372,0204,'}','{',0177,0326,'"',0347,0233,0376,0250,0271,'@',0371,036,0330,']',0232,'}',014,' ','b',0243,012,'k',0222,0230,0347,'O',0333,'9',0302,012,0247,'&',0226,0347,0334,0340,0221,0332,0303,0222,'s','+','d',0214,0215,0320,0226,0371,'U',0213,'C',0355,0232,0377,0,020,035,0223,0230,'C',0303,' ','o',033,0202,0245,033,')',030,0230,'-','K',033,'(',0305,'y',0305,0352,047,'Z',0241,'D','E',0323,016,'|',0343,0353,'-','y',0210,0243,0355,0323,0357,'"',03,0215,0252,0322,'O','f','S',0263,016,0245,0246,0307,025,'(',0302,0230,0344,0204,'s',0212,0341,0364,0205,0360,0364,0202,0354,0344,0302,0336,'W',0336,'7',016,0336,035,0225,'y','l','8','1','A',0204,0261,0312,0355,0333,'O',016,'}',034,'}','D',07,0245,037,'K',0315,0234,'R','u','J',0224,'l',0244,'c',026,021,0262,0310,0303,'=',025,'Q',0,'x',0305,023,'W','<',0243,'e',0241,'O',023,033,'m',0221,0345,037,'Y','O','8',0331,'X','>',0272,0240,0203,0321,0236,0262,'a','+','A',0252,'U',0303,0262,')',0231,'r',047,047,';',0251,'7',047,0316,012,0347,037,'*','N',015,0216,0250,0337,02,0314,0234,0303,0240,0342,0206,0211,0200,'Q',0311,0217,0337,0336,026,0177,'8',0377,0,0343,025,0352,0342,'?',0314,0177,0245,'B','|',0334,021,0376,0231,0265,'y','9',037,0374,'b',0217,0210,'q',037,0346,'6',0271,'1',0357,'J',030,0351,'d','&','[',0374,'L',0250,'o',0203,0322,'O',0251,0225,'e',0201,0204,'K',0362,0215,'%','&','x','[',0370,025,0,0244,0324,034,'F',0202,0245,032,'$','c',034,0332,'.','`','{',0350,'+','Y',0213,'J',0341,0200,0326,0331,'u','_','8',0372,0312,0371,0307,'J',0233,'C','1',025,'m','U',0360,0321,0364,'U',0233,0215,0350,0354,'J',0230,0234,'y',',',0266,'3',0205,'K','H','Z',0224,0223,0341,'k',0343,'_',0370,0212,0233,0316,0352,0303,014,0270,0372,0262,'m','6',0214,02,'e',0223,'*',0216,0363,0352,0244,'V','w',0224,024,0257,06,'S','O',0316,0260,017,0321,'9',0365,'f',0352,0211,0366,0212,'K','I','0',0310,0373,0210,02,'.','H',036,0232,0367,0200,'`',0246,'b','Q',0227,0222,'p','Z',01,0212,0375,04,'4',0177,0204,'J',0177,'(','&','J','}',0306,0316,'N',0246,0320,0202,'Z','i',023,0210,0315,0245,'_',0357,026,'f','e',0335,'`',0377,0,021,04,'n',0322,0323,0244,0315,'I','}',0232,0215,0351,0362,0217,0244,0312,0276,0222,0232,'m',03,0305,'>','q',0315,0267,'s',03,0337,'A','Z',0314,'Z','W',014,06,0351,047,0341,'7',035,010,'Z','n',')','5',0204,0253,'1','^',0300,'B',0217,'=','6','z',0254,047,0365,0202,0374,0333,0244,0217,0205,0261,0325,'N',0344,'!',0264,027,026,'x','%','"',0244,0300,'S',0215,0211,'&','O',0304,0367,037,0224,05,'M',025,0317,'9',0367,0315,023,0362,0200,0334,0264,0273,'l',0240,'p',010,'M',';',011,'C',0315,'!',0324,0234,024,'+',05,'L',0266,0251,027,'3','g',0207,0312,024,0271,'[','3',0315,'}',0313,0225,0362,0202,0333,0355,'-',0227,07,0302,0261,'C',0270,'K','m',0244,0251,'j',0270,01,05,'J','5',0230,'p','m',0237,0323,'A','Z',0315,0321,'i',0134,'0',033,0266,0323,0343,0245,0202,'x',0331,0337,0256,'G',0223,0224,035,0235,0340,0247,'0','o',0377,0,0330,'[',0317,0270,0247,']','Y',0251,'R',0267,01,0231,'v','V',0373,0247,0341,'@',0254,'%',0356,'U','w',0350,0310,0373,026,0357,'W',0251,0213,'2',0222,0250,'A',0305,'f',0365,037,'^',0317,0315,0316,'K','!',0341,0231,027,0210,'S',0274,0220,0375,0177,0200,0367,0350,'`',0263,'9','.',0271,'w','2','X',0343,0252,0226,0333,'I','R',0325,0300,010,0347,034,0332,0231,'W',023,0335,0320,'V',0263,'t','Z','<','0',033,0272,016,'1',0316,'/',0353,016,031,'h','m',0261,0361,032,'@','H',0270,013,0267,0265,0341,016,'r','o','$',0271,0177,07,'f',07,0344,' ',0222,'j','N',047,0134,'!',011,'+','Y',0340,0224,0212,0223,010,0177,0224,0325,0364,'F','8',0363,'C',0256,'`','5','%','.',0206,0263,'V',047,0327,0265,0226,'g','%',0320,0372,017,'x','B',0237,0344,'u',0363,0210,0343,0364,'u',0233,0375,014,')',0227,0333,'S','N',0247,0212,'V','(','t','%',0264,'&',0322,0325,'p',02,03,0256,0213,'S','*',0376,0335,05,'k','4',021,'S','r','p',033,0272,013,0314,'s',0216,'^',0347,0345,0247,0351,'N',012,012,'l',0215,0355,'a',0316,'K',0344,0307,'(',0201,'s',0257,0247,037,01,0270,0375,0235,034,0333,037,023,0353,033,'"',02,0233,'o',0236,0232,0305,0367,'8',0366,0373,'3','L',0216,'s',0341,'y','=','a',025,'B',014,0334,0252,0216,0313,0250,034,'<',0340,'<',0360,012,0232,'W',0366,0350,'+','Y',0240,0212,0233,0223,0200,0335,0320,'^','b',0332,0357,'s',0362,0322,037,0230,033,'?',012,'5',0324,0343,0206,0211,021,'I','t',0330,'N','j',0343,037,'^','G',0220,0212,0255,'|',0352,'r','T','l',033,'+',0356,035,'.','r','O','&',0271,'C',0301,0367,0223,0371,015,'p',0,0251,'8',010,'D',0347,'+',0202,0206,0270,0246,'_',023,0347,011,'e',0206,0322,0323,'i',0340,0224,0217,0334,025,0202,0303,'?','U',0211,0317,'A','Z',0315,0,0212,0233,0222,'8',047,'v',0,025,'&','-',0256,0367,'?','-','!',0371,0201,0370,'Q',0270,'i',0241,0361,032,0235,'@',0244,0232,021,0210,0200,0334,0327,0374,0221,0364,031,07,01,0232,'x','m','8','>',04,0301,'$',0324,0234,'N',0262,'%',0345,0333,'.',0272,0273,0202,'S',010,0232,0235,01,0371,0357,036,010,0375,0304,'X','`',0364,0177,022,0263,0320,'V',0263,'@','"',0246,0344,016,011,0335,0200,05,'I',0213,'k',0275,0323,0355,0244,'?','0','6',0276,024,'e',0271,'c',0310,0350,'Z',0302,0210,'U','b',0313,0343,0371,0204,'U','&',0243,0303,'E',0207,0321,'S',0202,0261,020,'V',':','f',';',0343,'U',022,0262,0215,0227,034,'W',0264,03,'@',0354,0342,0206,0333,0247,0364,0375,0304,'e',0330,';',037,022,0263,0320,'V',0263,'@','"',0274,020,'8',047,'v',022,0221,'R','b',0322,0257,'t',0373,'h',0240,0274,0300,'y',0361,'W','0','N','[',0251,0177,']',013,0374,'G','E','P',0257,'L','"',0311,0330,'s','-',024,'"',0242,024,0364,0227,'F',0347,0331,0340,'`',0266,0362,012,026,'0',':',033,0225,0225,'E',0267,026,'~','P',020,0200,027,'2',0241,0322,';',0211,0375,0304,'e',0345,0316,0317,0304,0241,0240,0255,'f',0200,'F','H',034,023,0273,011,'H',0251,'1','i','W',0272,'}',0264,0,0221,'R','p',0200,0363,0302,0257,'`',';',0273,0266,'<',0216,0207,'?',021,0324,011,'w','m','9',0343,026,0220,'m',015,026,037,'E','N',012,0304,'B',032,'a','?','H','K',0212,0262,0222,'?','X',027,05,0316,',','t',0216,'~',0237,0270,0214,0274,0271,0273,0342,'X',0320,'V',0263,'D',0210,0311,03,0202,'w','a',')',025,'&','*','o','p',0343,0240,'%','"',0252,'8','@','q',0315,0247,0217,0366,0357,030,0362,0320,0357,0236,0255,0244,032,'x','E',0223,0260,0346,'Z',01,'I',0241,030,0300,'n','k',0376,'H',012,'I',012,'I',0304,'~',0340,'2',0362,0347,0361,',','h','+','Y',0240,021,0223,'c',0200,0335,0204,0244,'T',0230,0251,0275,0303,0304,0350,'+','Y',0262,0221,0304,0230,0232,0226,'J','h',0362,'o','m','J',0370,0206,'4',0336,0313,0237,'=',016,0371,0353,0204,0273,0266,0234,0361,0213,'H','U',0241,0243,'`',0325,030,0240,0360,0215,0223,'e',0314,'P','{','q',0227,0227,'?',0211,'c','A','R',0215,0,0214,0233,034,06,0354,'%','"',0244,0305,'x',0270,'x',0235,012,'q',0305,04,0241,'<','I',0202,0313,'$',0242,'T',0177,'t','1','6',0301,0243,0215,'*',0320,0211,'y',0326,'M','R',0342,'~','G','y','-',0353,0241,0337,'=',0305,0244,'*',0221,'e',0315,0205,0373,'h',05,'&',0204,'b',' ','7','5','x',0373,'H',012,'B',0202,0222,'q',035,0254,0313,0313,0233,0376,'%',0215,05,'J','4',02,'2','l','p',033,0260,0224,012,0223,031,0270,'x',0235,012,'u',0325,04,'!','<','I',0216,'m',025,'D',0260,0340,0234,0364,0253,0222,0336,'W','B',0376,0323,'u',0301,'[',0311,'o','3',0241,0315,0325,025,0266,0210,0252,025,0351,0242,0255,0253,'g',024,0236,021,'A',0260,0357,'p',0366,0223,'.',0301,0332,0370,0224,'4',025,'(',0320,010,0240,0271,0261,0300,'n',0302,020,'*','L','f',0341,0342,'t',')',0327,0225,'a',011,0212,015,0211,'t',0365,'Q',0250,0333,0315,033,'.','6',0253,'I','1','-','8',0216,'*','M',026,'2','V',';',0266,0234,0301,012,0320,0227,'0','P',0335,0332,'A',0262,'`','%',0355,0223,0336,0212,0213,0340,')','&',0212,030,0300,'n','k','e',']',0374,'"',0240,0324,'v','c','.',0301,0333,0370,0225,0226,0202,0245,032,01,024,027,'6','8',015,0330,'B',05,'I',0214,0326,'x',0235,01,0266,0307,0231,0312,'%',0346,0330,'R',0326,0333,'W',':',0237,0327,'Y',0336,'L','q',']',034,0306,0322,'?',026,0355,0376,'t',0331,'@','A','$',0302,']','i','V',0333,'U',0340,0305,0205,'z',030,'(','X',0336,0134,'m','#',0272,'c','f',0343,0335,0321,'J',0333,'k',0270,'b',0255,0253,'k',024,0236,'=',0220,0260,0301,0333,0370,0225,0226,0202,0245,032,01,024,027,'6','0',0335,0204,' ','T',0230,0315,'g',0212,0264,'X','l','y',0253,'(',0260,0330,0363,'9',0303,0262,0356,0246,0323,'n','&',0311,021,'1','&',0340,0352,'+','d',0346,'0',0325,'b','e',0243,'G',032,'P','P',0211,'i',0306,0372,0256,0240,'+','t','X','B',0250,0364,0321,0260,'<',0261,0217,0243,'>',0177,'f','Q',0343,0334,'0',010,'5',07,030,0262,0257,'C',05,012,0336,'U','&',0206,',',0277,'q',0357,'E','E',0342,02,0220,0242,0225,014,'D',06,0346,0266,'O','~','*',015,'G','a',',','0','z','L','U',0226,0202,0245,032,01,026,'S','s','c',0337,'v',020,0201,'S',024,027,0253,023,0242,0312,'.','N','*',0312,03,'m',0217,'3',0236,0226,0271,'U',0244,0355,0265,0260,0345,'2',0326,0230,0344,0327,016,0323,'&',0332,'?',011,0335,')',0204,0252,0255,0312,0213,036,0270,0350,'L',0234,0322,0272,'3',0324,'Y',0303,0303,'E',015,0312,034,014,'Y','X',0246,0366,0355,0244,'w','L','l',0233,0373,0247,'E',01,0266,0337,'p',0305,'[','V',0326,')','<','w',0345,0206,'O','K',0211,0356,0305,'O',030,'*','Q',0240,021,'e','7','6','=',0367,'a',010,025,'1','A','z',0261,':','(',0233,0220,':',0312,0200,0333,'b',0203,'Q',0371,'W','E',0244,':',0222,0223,'X',0231,0224,'s',0254,0312,0312,'u','d',0335,0255,033,'Z',0271,0245,0371,035,0314,0304,0312,0270,'4',0202,0250,'z','a','f',0252,'q','E','W',0351,'L',0264,0342,0255,'3',0301,'.','w','`',')','&',0240,0342,'"',0312,0305,'D','T','m',0267,0236,0366,0251,'4','0',022,0377,0,0365,0305,'E',0342,02,0220,0242,0225,014,'D',06,0346,0256,'?','i',025,'I',0250,0314,'o',013,',',0232,0272,'x',0236,0354,'T',0336,'`',0222,'h',04,'Y','M',0315,0217,'}',0330,'B',05,0361,'D',0361,0304,0350,0356,0264,'8',0252,03,'m',0246,0312,'F',0263,034,0242,0201,0261,'0',',',0253,0361,'j',0202,015,010,0306,'$',0246,0276,'"',0212,'+',0317,'p','X',07,'n','e','V',')',0341,0216,0250,'e',0372,0271,'-',0356,0230,'m',0344,'^',0333,0202,0251,'V','z',012,0330,0270,0367,' ',0202,'(','F',0366,0353,0323,0335,'1',0262,'h',0256,0351,0321,0262,'m','7',0334,'1',0260,'h',0274,'P','x',0356,0213,'-',032,0274,'q',0356,0301,'$',0324,0301,'$',0320,010,0260,0213,0233,036,0373,0260,0204,010,0262,0236,'8',0235,025,';',',',0216,'*',0200,0333,'i',0262,0221,0257,'4',0200,0232,0272,0330,0347,021,0346,'5',0247,'$',024,'o','i','V',0322,'<',016,0342,'^','L',035,0226,'Q','R','<','N',0253,'2',0350,0353,':',0260,0201,0352,'a',0211,'2',0330,0346,0233,'@','H',031,'A','[',']','+','^',0343,'E',0367,'+',0274,'"',0213,027,'`',0255,0355,'A',0241,0200,0207,0377,0,0256,'*',015,'D',05,'$',0224,0250,'b',' ','7','5',0377,0,'$',05,'$',0324,034,'F',0277,'4',0321,0253,0307,0373,'`',0222,'j','L',022,'M',04,'X','E',0315,017,'}',0335,0204,'|',0362,0213,')',0343,0211,0317,'E',0265,0354,0260,'1',0316,02,020,',',0244,'a',0270,'R',025,0300,0212,030,0236,0225,0340,020,0341,0247,0226,0253,010,047,'b','`',026,0310,0334,'O',0275,'Z',0247,0234,0262,0237,'-','Y','r','z',0222,0340,0272,0177,'/',0327,'I','q',0236,0215,0337,'c',05,016,0244,0245,'Q','e','B',0242,012,0331,0332,'F','X',0215,0366,0311,0252,'{',0246,'6','M',025,0335,':','6','M',0246,0373,0206,'6',015,027,0212,016,0257,'6',0335,0357,037,0355,0202,0245,032,0223,0214,'T',0334,'"',0302,'.','h','{',0356,0354,'#',0324,0345,026,'S',0352,'s',0321,0316,'9',0262,0310,0376,0350,011,'H',0262,0221,0206,0352,'^','q','#','e',0366,0350,'|',0306,0254,0254,0300,'6','y',0267,02,0253,0341,015,0270,'8','-',' ',0353,'M','?',0334,'i','G',0332,026,0276,0361,0256,0257,'(',0316,0234,'T',032,036,0227,0376,0272,0226,035,'M','|','r',0202,0244,0364,0215,'f','0',0320,'T',0215,0207,'?','8',0262,0261,'C',0275,0250,'4','0',020,0377,0,0365,0305,'A',0250,0200,0244,0233,'*',030,0210,015,0315,0177,0311,01,'I','6',0201,0304,'h',0346,0333,0275,0363,0355,05,'J','5','Q',0306,'*','n',021,'a',027,'5',0371,0356,0354,0247,0324,0345,026,'Q',0352,'s',0320,036,'x','Q',0234,07,'z',02,'R','(',06,033,0264,'L','c','.',0350,'7','x',0335,0255,0311,0356,0250,0325,'|',0330,012,0363,0326,0235,' ',0321,'K',026,06,0264,0245,'z',0316,0325,0303,0352,'n',0326,'.','K','l','/',0273,0201,0202,0207,022,'R',0241,0201,0213,'+',025,0212,0365,0233,0317,'}',0262,'j',0236,0351,0213,0215,0225,'w','N',0215,0203,'T','b',0203,025,'h',036,'x',0340,'p',0202,0245,033,'J','<','L','T',0334,'#',0233,'E',0315,0376,'{',0273,')',0365,'1','a',03,0327,'@','}',0361,'F',0360,'N','q','A','p',0336,'r',0203,030,0251,0243,'M','e','3','Z',0226,']','#',0347,0254,0303,']',0367,0253,0362,0326,0222,0226,034,033,'i',')',0366,0327,0262,0342,'o',0301,'C',0210,0212,0365,0332,0357,0215,05,'l',0177,'D','P',0212,035,0355,'E',0306,',',0275,'x',0316,'-','!','V',0206,0212,0236,021,0315,0267,0365,0177,0236,0356,0312,'x','b','`','!',03,'@','~','`','l',0374,'(',0317,'|',0264,'w',0222,'D','M','2',05,03,'n',0251,'>',0372,0274,0245,'-',0344,0347,0351,0255,0311,0255,'e','i','Z',0262,'l',0221,'P',0343,0310,'I',036,'f',022,'2',033,0212,021,'Q',05,0331,'^','8',0267,0376,' ',0202,'(','D','w','U',0234,'Q','c',0327,'}','i',07,0323,'8',0332,'%',07,0312,'9',0266,0217,'G',0211,0317,'w','D',0360,0304,0300,'B',06,0200,0374,0300,0374,'(','=',0203,0224,0321,0374,'O',0323,'U',0366,'q','u',0237,0313,'Z','M',025,0270,'3',0372,0352,0362,'b','H',0257,'K','_',0225,0373,0272,0365,035,0357,010,0243,0211,0273,05,016,06,'(',0241,'X',0264,0325,0351,0312,'(','{','E',05,0311,0304,0300,'B',05,06,0200,0374,0300,0332,0370,'Q',0330,']',0373,0315,0245,'_',0236,0253,'>','-',0250,'k',' ','d',0320,0325,0344,0317,0304,0257,0375,016,0360,0241,'i',012,'I',0300,0301,'r','[','m',035,0314,'D','P',0305,'i','e','Y',0306,0320,0273,'>',0315,'A','r','q','T',04,' ','P','E',07,030,017,0314,012,0271,0202,'r',0354,'M','9',0337,'`',017,0226,0257,047,0375,0345,'Y',0326,'X',0311,0264,0376,'Z',0262,0336,011,'Q',0366,0337,025,016,0215,0336,0360,0213,'.',0246,0231,034,014,'Q','B',0242,012,0231,0277,0302,'/',0271,'Y','v',':',013,0222,'8',0252,02,020,'(',' ',0,'*','L',07,0236,025,'w',01,0335,0354,'r','g','6',0216,0257,'%',0177,0335,0375,'5',0234,'9',0266,0237,0313,'V','[',0305,012,033,0362,0207,022,024,0223,0234,027,'%',0366,0333,0356,0342,'4','^','(',0254,0342,0361,'T',0346,';',015,05,0311,034,'L',04,' ','P',010,011,'H',0252,0216,020,035,'t','U',0357,0375,'{','$',0230,0376,021,0325,0344,0257,0373,0277,0246,0262,'N','m',015,'^','M',0361,'*',037,0330,'{',011,'Z','z','7','s',030,0305,0227,'S','L',0216,'q','B','*',' ',0251,0221,'_',010,'5',0270,0214,016,0375,0304,036,'*',0341,0242,0323,0255,0326,0274,027,0335,0200,0266,0324,024,0223,0210,0354,0215,'7',0334,'`',037,0236,0257,047,'}',0325,0327,'Z','M',']',0346,'?',']','^','L','W',0361,'i',0355,0330,0212,034,'H','R','`',0270,0315,0134,'k',0334,'h','7','Q','x','*',012,025,0326,033,0340,0244,0232,021,01,'/',012,034,0342,0240,0324,'E',0246,0225,0346,0234,014,04,0253,0243,'w',0272,'{',033,0277,'u',0244,0217,0317,'U',0217,04,'(',0353,'r','k',0271,0332,'N',0254,0213,0246,0340,0207,0320,0257,0356,020,016,'c',0261,'X','F',0323,0347,0332,011,'<','N',0205,0224,0366,014,0323,0224,']','q',0313,'@','C',0375,'#','y',0342,' ','-',0245,05,047,0260,0362,0232,0353,'^',0222,0236,0332,0256,0275,0366,'l',0237,'}','i','w',0251,'{','n',0323,0347,0255,047,'0',0236,016,0264,0225,'{','v',033,010,0332,'|',0373,'A','R',0215,0245,034,'t',026,'Z','7',0342,0256,0305,'T',0232,030,0262,0367,034,0342,0242,0361,026,0332,'U','<','3',0200,0225,0364,'N',0345,0201,0337,0255,'}',0321,'X',0234,'z',0266,0202,0335,'R',0201,0365,0325,0345,')',0217,0302,0215,'i',0313,0255,026,0350,0261,0255,'%','^',0263,'u','o',0344,'i',0330,',','#','i',0363,0355,05,'J','6',0224,'q',0320,'Z','d',0371,0253,0262,0323,0254,0234,0242,0251,'7',0345,0240,'!',0376,0221,0274,0361,021,'m',0265,05,047,'{','?','1',0334,'h',0353,'8',0377,0,0333,':','}',0256,0326,0234,'g',0276,0322,0200,0371,'B',0220,'x',0244,0323,'W',0224,'$',0217,0300,0260,0350,0365,0377,0,0371,0277,0260,0215,0247,0316,031,'A','Z',0315,0245,034,'t',026,0231,'>','j',0354,0366,0222,'h','`','%',0333,0225,0236,0213,'M','*',0236,020,022,0256,0215,0334,0263,0336,'s',0,0321,'S',016,04,0376,0272,0334,0236,0331,026,'U',0315,0332,'P',0361,0327,0237,'b',0226,'R',034,'%','>','Z',0255,'6','N',0304,0312,'K','~',0274,'w',0326,021,0264,0371,0303,'(','+','Y',0264,0243,0216,0202,0323,'&',0354,'U',0332,0202,'U',0266,0230,0252,'O',0244,'T','@','n','g','i','=',0374,0240,')',04,')',047,021,0272,0225,0222,'J',0256,'e',026,0324,'<','N',0254,0264,0275,'+',0316,'8',023,'w',0234,'4',0320,0340,0204,0204,0353,0263,'4',06,0313,0355,0336,'|','F',0254,0274,0312,'z',0315,'8',027,0362,'0',0313,0350,'6',0222,0342,'B',0201,0336,'X','F',0323,0347,0200,0312,012,0326,'m','(',0343,0240,0264,0321,0331,0305,'[',0260,0204,0361,'0',01,'m','+','9',0250,'A','S','G',0233,'=',0334,'"',0312,0305,0223,0276,0264,0223,026,'U',0262,0275,033,06,0250,0305,06,'6',015,027,0212,'N',0340,0250,0360,027,0304,0364,0316,05,0302,07,0220,0325,0226,'7',0331,'b',0256,035,0300,0230,02,0253,0226,']','}','5',0222,0302,0215,0134,0225,'W','7',0351,0206,0356,0302,'6',0237,'<',06,'P','V',0263,'i','G',035,05,0246,0216,0306,047,'=',0330,'J','E','I',0214,0326,'x',0230,0240,0274,0350,0242,0204,'T','m','#','=',0375,0227,'o','N','q','i','&',0242,02,0220,'J','T','1',020,033,0231,0331,'^',013,0300,0353,0316,'<',015,034,'R','l','#',0314,0353,'N',0317,0251,'=','u','s','i','>',033,0211,0231,'U',012,0207,'P','D',':',0312,0372,0310,'Q','N',0250,0227,'Q',0243,'3','b',0307,0363,'a',0372,0356,0254,0247,'i',0343,0300,'e',05,'k','6',0224,'q',0320,'[','h',0354,'b','s',0335,0320,'^','b',0322,0257,'p',0373,'h',023,017,016,0220,0365,'F','P','V',0337,'F',0357,0261,0213,016,0246,0312,0242,0206,012,0331,0270,0345,024,'P',0241,0361,0337,'l',0233,0262,0213,0256,'V','Z',03,'n',0325,0306,'}',0304,05,0266,0253,'I',':',0262,0334,0232,0205,'l',0264,'9',0305,0371,0353,'I','K',0322,0213,0263,'i','^','g','r',0343,0211,024,'n','d','s',0203,0317,035,'V',0336,'l',0321,'m',0250,'-','>','b','%','g','[',0377,0,0250,0213,0306,'G',035,0305,0224,0355,'<','x',014,0240,0255,'f',0322,0216,'1','S','p',0216,'m',0273,0233,0317,'=',0347,':',0340,0333,'<',06,'Z',04,0313,0343,0360,'$',0351,0260,0352,'k',0343,0224,'Z','O','H',0317,'{','G',012,'+','8','6',0275,'<','w',0325,'I',0241,0200,0207,'n','9',0350,0264,0331,0273,024,0340,'c','d',0331,'^','(',':','^',0230,'p',0321,015,0244,0250,0304,0314,0342,0370,0272,0262,'}','5','d',0230,0245,'R',025,0316,'/',0310,'E',07,015,0317,0322,0220,'*',0354,0251,0265,0374,0272,0317,0362,'K',0252,0271,']','#','U',0367,032,0366,'S',0264,0361,0340,'2',0202,0265,0233,'J','8',0305,'M',0302,',','7','s',0177,0236,0360,'<',0350,0277,0341,032,03,0357,016,0210,'p',031,0352,0320,0336,' ',0271,'+','q',0305,020,'R',0241,'E',014,014,'Q','B',012,0233,0275,'9','o',0354,0253,'m',0270,0264,0203,'Q',01,'h','U',0225,014,'D',06,0246,'6',034,0301,'X',035,015,0362,'k','j',0351,'f','/','_',0341,0326,0231,0345,'5',0213,0334,'<',0333,'~','C',0216,0351,0326,034,025,'C',0211,')','1','3','&',0261,'N','m','W','y','j',0261,'6',0311,0243,0215,'*',0320,0211,'y',0306,'M','P',0352,'k',0253,'e',';','O',036,03,'(','+','Y',0264,0243,0214,022,'M',0,0213,010,0271,0257,0317,'x',036,'p','l',0340,'4','[',']',0314,0247,0217,0214,04,0244,'P',014,'5',0357,026,0134,0301,'b',',',0270,'.',0301,'X',035,026,0221,0262,0270,0262,0261,'C',0276,0264,0230,0247,05,0345,0243,0233,0230,0253,0215,0216,07,030,0232,0230,0230,05,06,0325,022,0223,0202,'p',0325,'i',0206,0205,0134,'q','A','"','%','d',0321,0377,0,'I',0,'z',0356,0330,0345,'V',0323,0374,'7','i',0355,0254,0276,'H','}',']',033,0273,'L',0327,05,'b','5',',',0247,'i',0343,0300,'e',05,'k','6',0224,'q',0202,0245,032,01,026,'S','s','C',0337,'y',0316,'/',0352,0307,0276,0212,'p','l','u',0225,011,'m',02,0211,033,0222,0207,022,024,0223,0234,027,030,0351,032,0313,021,0242,0212,021,'^','(',0317,'}','P','h','`','6',0367,036,0366,0215,0241,'a',0341,0325,'p','G','6',0372,'|',0225,0201,0324,0134,0373,0211,0253,'R',0275,'_',0307,0274,0230,0223,'t','U','.',0246,0220,0364,0253,0242,0213,'i','V','N',0253,'o','4',0253,016,' ',0332,'J',0204,'7','0',010,017,0247,'e',0324,'d',0255,026,'S',0264,0361,0340,'2',0202,0265,0233,'J','8',0301,'R',0215,0,0213,')',0271,0241,0206,'{',0312,0233,0233,034,'L',04,0201,'@',' ','6',0216,030,0234,0240,'6',0330,0240,036,0373,0302,0343,';',016,0345,0201,0202,0207,022,'R',0241,024,'"',012,0332,0371,'A',07,0210,0337,'Y','V',0333,'y','e',01,'H','5',020,'Y','}',026,0222,'}',0240,0250,'t',0222,0307,0202,0362,0363,0320,0206,0320,'-','-','f',0310,031,0230,0227,0225,037,'Y','K','N',034,0325,0275,'o',0225,0331,'M',0335,'G',0251,0354,'u',0222,0245,023,0364,'G','v',']','O',0353,011,'S','J',016,'-',0301,'T',0322,012,0326,'m','(',0343,05,'J','4',02,'(','.','l','p',033,0312,'p','H',0342,'`','!','"',0200,'B','[','l','T',0230,010,'O',034,'U',0236,0372,0313,0211,0277,05,'b','"',0361,'i',0274,026,'4',022,'(',0225,0347,026,'V',')',0276,0264,0237,0224,']','r',0361,'L',024,'-','!','I','<','A',0205,'L','I',0202,0266,'q','F',')',0203,0312,017,047,0366,'y','^',0255,'q','^',0371,0351,'W',0205,0246,0335,'M',0223,017,0311,':','/','l',0334,'s',030,'j',0207,0236,026,'e','G',0367,'B','P',0221,'D',0244,'P',010,'*','Y',0242,'D','d',0330,0340,'7',0201,011,0365,'9','@','B','8','B','P',0201,'U',030,0315,0323,0326,'W','`',')','P',0264,0223,0201,0202,0344,0266,0322,'{',0231,'h',0242,0204,'T','m','#','}','i',';','*',034,010,0200,0205,0354,0257,'G',0321,'y',0264,0264,0331,'U','m',047,'3',027,'o',0276,0235,'.',0212,0315,0313,0213,0351,0361,047,'P','<',0360,0263,'*',0237,0356,0200,0204,'&',0312,'G',0,' ',0255,'f',0200,'F','M',0216,03,'x',020,0201,'|','Y','O',034,'L',04,0201,'R','b',0332,0357,'y','^',0335,0214,0255,0276,0215,0357,'c',05,016,0246,0312,0242,0206,'-',0265,0362,0202,017,021,0276,010,'w',0207,'z','*',015,'D',06,0335,0333,'g',0377,0,'X',013,'A',0264,0223,0210,0336,0320,0336,' ',0314,'2',0237,0330,0246,015,'G',0335,'9','h',017,'<',012,'e','S',0375,0320,0224,'!','!',')','M',0300,010,'+','Y',0240,021,0222,07,01,0274,011,'H',0251,'1',0232,0317,023,0240,'>',0360,0351,'O',01,0227,'e',0262,0350,0256,'G','(',0257,']',0254,024,'4','T','l',0257,'8',0262,0261,'M',0365,'8',0243,'(',0264,0223,'Q',027,'m','6','x',0242,03,0215,0232,0217,0313,'z',0364,0234,0300,0252,'V','.','9',034,0341,0326,0347,0307,'D',0312,0251,0370,0340,'!',0,'%','#',0200,020,'V',0263,'A',027,0334,0201,0300,'o',0,027,0223,026,0225,'{',0247,0333,'@',0231,'|','~',04,0237,0317,0263,0220,0241,'P','p','0',']',0225,0274,'b',0334,'_','q',0213,'+',021,'^','(',0317,'}','i',033,'#',0273,033,'&',0374,0240,'-',0263,0346,'3',0212,0246,0345,0216,')',0336,025,'|','f',0344,0210,'*','Q',0252,0214,025,0254,0320,'E','M',0311,0301,';',0336,'q',0301,0322,037,'m',01,0367,0207,'D','8',014,0373,'Q','Z',':','7',0263,0316,012,035,'M',0225,'E',010,0250,0202,0266,0277,0247,'}','i','&',0206,'(','n',0134,05,0266,0253,'*',021,'a','{',017,'e',0236,0351,'K','Y',0242,'D',025,0374,02,0344,0210,'+','T','U',0134,'0',033,0320,0363,0243,'k',0341,032,'-',0256,0346,023,0357,01,' ','P',016,0327,'a',0321,0134,0216,'"','+',0327,'k',05,0350,0264,0235,0225,0376,'q','e','b',0207,'{','P','h','`','6',0357,'[','8',012,'I',0241,0314,'@','e',0363,'G','p','W','{','s',0314,'6','z','4',0361,0361,'0','T',0243,'@',' ',0250,0235,0234,06,0364,'<',0350,0273,0341,032,'2','h','u',0225,011,'B',05,0224,0216,0330,'I',0270,'C',0262,0134,0235,'f','f','g',0202,0227,0305,011,0204,0261,'9','F',0336,0340,027,0202,0264,'Q','B',0374,0342,0373,0323,0236,0370,'4',0347,0241,0212,0307,'2',0351,0351,'G',03,0236,0275,0204,036,0221,0317,'a',0240,0264,0236,0242,'}',0367,0274,0343,0203,0243,036,0372,03,'h',0341,0212,0262,0204,0266,0330,0240,035,0260,0275,'8',0370,'l','`',0237,0210,0302,0230,0225,0254,0244,0236,'@',0355,'+','J','Y',0232,0253,0254,'w',0261,'L','%',0326,0226,026,0203,0210,0212,'(',']',026,0333,0352,0345,0275,0250,0343,026,'k',0264,0230,013,'I',0242,0204,'%',0177,020,0271,'C',0307,'T',0223,0300,'B',0334,047,033,0274,0243,'g',0254,0253,0267,0266,0225,0365,'c',0336,0,027,01,05,0327,0326,033,'l','q','&',020,0251,047,0220,0372,'M',0345,'i','<','{','Y','q',0367,'R',0322,06,'*','0',0246,'9','!',034,0352,0370,'s',0353,0340,'<',0241,'O',0315,0274,0247,0235,'8',0253,'V',0255,'*',0255,0374,'M',0236,06,'-','4','h',0277,0211,0263,0304,'h','+','j',0345,'e',05,047,0210,0336,05,0217,'X',012,030,0302,'j','z','5',0334,0255,'^','i','=','g','?','-',011,'G','t','o','2','@',0342,'`','%','"',0200,'B',0236,'y','V','P',0230,0356,0313,0247,0252,0210,016,0311,0314,'-',0205,'}',0323,'q',0204,0265,0312,0354,0320,0375,0273,'C',0364,0200,0344,0234,0312,037,'O',0335,'=',0234,0273,'4',0372,030,'@',0305,'f',024,0327,'%',0264,'f',034,0373,'U',0334,0230,'+',0235,0230,'S',0237,'s',0202,'G',0246,0341,'.','2',0242,0207,07,012,'B','&',0346,0345,'y',0245,037,0207,032,'g','M',031,'/','8',0262,0241,0353,0274,'-',0234,'8','h','m','G',0254,'6','N',0243,'g',02,0235,011,'w',05,']',0273,0262,'8','b','`','!',02,0200,'B',0235,'u','V',020,0234,'b',0203,'f',']','=','T','j',07,'e',0236,'[',016,'f',0203,'H','J',047,'P',0231,0326,0373,0334,025,011,012,0177,0350,0216,0237,0201,0373,0275,0342,0323,'k','J',0323,0232,'O','a',0264,0265,04,0214,0314,024,0256,'h','<',0350,0377,0,0246,0316,0321,0202,0216,'N','a','2',0250,0357,0271,'z',0243,0234,0234,0230,'r','a','_','x',0356,'Q','/',',',0322,0236,'y',0134,022,0230,'D',0337,'(',0201,'1',';',0304,'#',0341,'o','A','v','_','e',0314,'S',0201,0202,0225,0213,'*',030,030,0262,0261,025,027,0243,'=',0332,'M','t',':',0311,0307,'h','j',07,'@',0332,'l',0373,'h','(','V','0','B',0205,0335,0354,'7','!',010,036,0260,020,0237,0234,')',0327,'T',020,0204,0361,'&','(','6','e',0223,0325,'N','{',0200,'e','&',0335,'c',0301,'*',0273,0345,01,'3',011,'j','u','?','{','e','P',04,0324,0273,0322,0307,0275,0326,020,0236,'o',0224,'Y',05,0134,022,0263,'d',0374,0242,0255,0272,0205,0371,'+','s','U',0270,0224,0371,0230,'!',0356,'Q','`','(','|','!','W',0307,0354,0315,'=','5',0342,023,'d','{',0301,022,0214,'5','*','3',';','F','?','k',0236,'y',0341,0335,0265,'A',0362,033,0260,'[','O','3','+',0361,'>',0261,'w',0246,'q','b','Y',0272,0272,'z',0317,'+',0254,0255,'K',0366,0134,0301,'Q','e',0321,0344,'s',0212,021,'X',0266,0327,014,0267,'U',0204,'+',0302,030,'>','4',0324,')','U',0340,0301,'4',0253,'G',0202,0264,'Y','P',0250,0212,0264,0253,036,06,'(','[',047,0305,'7',0307,0325,'/',0345,037,'T','c',0352,0225,037,'T',0277,0351,0217,0253,'_',0364,0307,'P',0240,'f',0250,0262,0201,0353,01,015,0246,0322,0240,0313,0315,'0',0344,0253,')',';',')','<',025,0343,0275,0253,'/','-',0223,0233,'j',0244,'t',0134,0245,'1',0374,0313,0265,0371,0305,0363,0334,0357,0343,'@',0216,'2',0352,0363,'o',0377,0,0330,0352,0312,0177,0304,0177,0314,'u','%',07,0376,'3',0376,'c',0377,0,0254,'<',0233,'?',0346,'.',0234,015,'~',04,017,0326,'(',0357,')','?',0374,0252,0263,0371,'G','O','0',0353,0337,0367,026,'U',0275,'K','2',0254,0251,0367,016,011,020,0211,0236,'X','!',0327,'8',0211,'q',0325,036,'p',033,'m',01,010,034,022,0235,'b',0207,'S','i','0','V',0215,0266,'s',0313,'A','S','w','.',010,'U',0312,030,'n','@',0312,022,0241,0304,032,0352,0224,0250,'Z','I',0300,0301,'r','V',0361,0366,'q','e','I',')','9',035,0315,0302,0313,'x',0254,0305,0206,0323,0346,'s',0202,0304,0343,010,'}',0263,0336,020,0247,0271,'!','|',0352,'>',0301,'f',0377,0,'C',05,0251,0226,0226,0313,0203,0341,'X',0247,'o',015,0264,0205,'8',0263,0301,')',025,'0',0227,0271,'M','_','D','c',0217,'6',':',0346,03,'R','L','%',0241,0336,0304,0356,'h','o',020,']',0225,036,'m',0305,015,0306,'2','^',06,',',0250,'n',034,033,0216,0225,0260,0270,0270,0255,'>',0261,0262,0372,0275,'D','l',0276,'=','D','}','z','>','Q','{',0350,0371,'G',0327,0373,'F',0323,0253,'0',025,'d',0250,0216,0361,0212,01,'A',0341,0250,'[',0235,0227,'C',0277,'z',0227,0210,'S',0234,0224,0377,0,':',0237,0261,'w',0217,0316,'9',0271,0271,'u',0260,0257,0274,';','X','j','Y',0225,0274,0341,0301,02,022,0347,')','8','%',033,0373,'4',0336,0250,0263,')','.',0220,0254,0134,'U',0352,';',0322,0264,'l','=',0236,'p','P',0342,'l',0250,'E',0225,010,0373,0231,0353,0254,'x','v','r',0324,0323,010,'}',07,05,0210,'R',0344,035,'T',0233,0235,0336,')',0205,037,0243,'}','!',0241,0361,0263,'|','Y','q','%',012,0311,'B',0235,0224,'%',' ',0250,0234,04,02,0324,0242,0233,'l',0374,'n',0354,0210,'J',0371,'J','`',0314,'+',0354,0333,0271,'0',033,0224,0226,'m',0204,0375,0321,0330,',',0270,'/',0301,'Y','E',026,'*',0214,024,'"',0212,025,0202,0266,0357,'N','Z',0312,0362,0355,'D','M',0311,0264,0357,0215,'/',0202,0251,'9',0207,'%',0216,'G','h','A',',',0206,0346,0323,0367,015,0377,0,'(',0244,0304,0223,0315,0237,0303,0277,011,0227,0224,'u',0322,'r','L',02,0343,'I',0225,'A',0305,0303,'x',0364,0200,'g',0246,0326,0371,0305,'-',0334,'#',0366,'i','&',0322,'{',0304,'T',0366,'B',0207,022,024,0223,0234,027,032,0252,0331,0367,032,'-',0265,0307,021,024,'7',035,'C',0333,'h',0244,0205,014,0214,'t',0374,0236,0302,0277,0226,0221,'T','6',0343,07,0356,'*',0350,'<',0317,'(',0270,'<',024,0230,'<',0334,0353,'+',036,'F','6',03,'.',0217,0307,'H',0377,0,'L',0223,0344,0270,0377,0,'E','_','#',037,0350,0210,0363,'1',0376,0225,'#',0315,'Q',0266,0206,'[',037,0367,'#','n','q',0204,'z',023,035,'7','(',0257,0371,023,0,0270,035,'|',0216,0362,0256,0216,0207,0223,0331,07,'2','+',024,'m',011,'@',0311,'"',0235,0244,0273,'-','r',0261,'D',020,'E',014,'U','7','.',',',0254,'P',0351,'Y',0360,0377,0,'c','Z',033,017,'w',0263,0202,0333,0251,0262,'b',0212,036,0261,'T',0213,'I',0321,'i',0134,'O',0373,036,0303,0251,0256,'G',021,05,'@','s',0215,'w',0206,0212,0330,025,0320,0226,0221,0326,'W',0373,' ',0251,'=',012,0276,0357,010,0331,'q',0242,'<','I',0377,0,021,'{',0215,'S',0314,0377,0,0210,0273,'m',0303,0305,0177,0354,0317,0377,0304,0,'+',020,01,0,02,0,04,04,05,05,01,01,01,0,0,0,0,0,01,0,021,'!','1','A','Q',' ','0','a','q',020,0201,0221,0241,0261,'@',0301,0321,0360,0361,0341,'P','`',0377,0332,0,010,01,01,0,01,'?','!',0377,0,0323,'-','k','R',0376,0303,'`','c',0271,0312,0265,'E',027,0372,05,0177,0230,0337,'u','>','#','N',0326,0316,0374,0304,0274,0274,0264,0315,0243,0242,'O','p','R',0214,0372,0265,'e',027,0231,'S',0357,01,'^',0212,0374,0300,0253,':',0225,0374,' ',0266,'z',0300,0275,0256,'Q',']','9','{','2',016,'F',0373,0177,0330,'c','1',0262,0360,0213,0362,0224,033,014,'0',0237,0232,'&',0364,0331,'?','i',0226,0201,'O',0266,0342,'v',0347,'S',0232,0353,'u',0252,'e','o','Q',013,0314,0235,0265,014,']','*',']','~',0315,'5',0371,0301,'k',0213,017,0205,0237,0375,'&','g','f','C',0211,0225,05,'o',0342,0311,0305,0200,'$',0244,'j',0324,0277,'J','(',0373,033,'j','+',0246,'1','l',0134,013,0376,0254,034,0201,0241,0346,016,0263,0273,023,'3',0364,0220,'V',023,0231,'o',016,0363,'5',035,0214,'B',0237,0356,'}','[',0,'q','^',012,',',0201,0303,047,0312,'C','h','9',07,0260,0257,0253,'N',035,0226,'3',0312,'m','^','9',0235,0330,'V','d',0307,0324,07,' ',0331,0202,0334,0202,'4',0206,'^',0247,'d','V',0330,0352,0360,0335,'C',010,015,'F','Q',0340,0215,047,'F',0215,0211,0202,013,'}',0303,0322,032,'-','A',0364,0254,'*',0217,'t','D',0242,0354,0340,0341,0354,0326,'*','F',0337,0300,037,'_',0222,0224,'*',0177,'f',0334,0241,0340,0201,0234,0313,0304,'p',0200,0360,0332,0264,0226,0221,'^',037,'+',0341,0325,'%',')','N',023,'|',0236,020,05,'@',0236,0252,0262,'}',0265,0324,'<','l',0366,0341,'<','e',':',011,'(','I','-',037,'G','B',0334,010,'k',016,031,0313,0252,0134,0226,'c',0350,0373,'s',0211,0251,'X','-',0364,'%','U','B',0312,027,0242,'%',05,031,0270,047,':',0375,'C',')',0225,'v',04,'e','(',0235,'R','D',0134,'7',0355,0246,'<','C','3','~','`',0347,024,030,0332,037,0134,0230,0273,'8',026,'o','}','!',0325,'5',0211,'c',0340,'k',0211,'j',0322,'0','U',0270,037,'7',0302,0266,01,0221,0253,025,0245,'y',023,0205,025,0215,';',0223,'*',036,0250,0266,'~',0200,'E','(',0273,013,027,0340,031,0254,0317,04,']',0302,'h',0355,0364,',',037,07,0233,0305,0350,021,0365,0304,' ',0320,'w',0321,031,0221,'L','U',0327,0222,'N',0270,'g',0372,04,0266,03,'[',07,'b',0337,'Z',0225,035,015,0353,0347,0211,033,'F',030,'S','_','3','P',0366,0325,'D',0366,0252,0343,'{',0302,023,'3','(',0,0215,0330,0374,0223,'}',020,'A',',','U',0206,0354,'a','R',0241,'>','T',0351,0272,'S',0345,0230,0255,0272,013,0236,0327,0237,'.',0334,'{',033,'E',0325,0366,0224,'R',0354,0252,0350,0210,0261,0326,0377,0,'~',025,0260,014,0215,'X',0355,'(','v','N','U',0210,0306,033,0217,'U',' ','g','M',0356,'y',0206,'T',0370,0314,'{',0354,'%',0272,0226,'3',07,'`',0373,0362,010,0353,'&',0226,0354,02,'z','w','e',0350,'~',0365,')',0272,0343,'X',0207,'f','g','{',0203,0367,'h','@','}',010,'3','*','F',031,'g','G','R',0276,0337,0264,0302,'G',0307,0362,0330,'{',0314,0326,0200,0377,0,'I',0344,'1','f',0200,0244,'H',0241,0300,'<',024,020,032,'j',0304,'8',07,0311,0345,0355,0353,'g',0264,012,'+',0302,0367,0312,'L','y',0313,'D',035,0254,'R',0307,0363,'E',0261,0,0326,0257,'#',')','P',0275,'w',0355,0326,'Y',07,0246,0353,'}',0203,0134,0275,'a',0312,'g',05,0346,026,'>',025,0364,0253,'W','r',0363,0,0302,'(',031,0331,0372,0371,0334,'$',023,'`','v','9','>',0134,'.',0201,'+','U',03,0230,'o',0300,'<','+',' ','=',0342,034,023,0345,0362,0320,0202,0326,'A',')','i',0230,'l','x','6',0216,'`',07,0247,'C',0232,'L',0232,031,0254,0134,024,'^',0237,0277,'_','v','#','e','Z',0226,0257,033,0333,0332,0260,035,011,'h',0315,0270,036,0273,'B',02,'V',01,'o',0273,0231,'~',0257,'A',0305,033,'P','W',013,'b',0374,0236,'q','=',0355,'R',036,016,0311,0232,0223,011,'1',0261,'~',07,0205,'l',017,'y',0226,'s',0313,0345,0231,05,'0',02,020,01,'r','<','`',0365,'R',0315,0353,0315,'&','M',06,'+',027,0332,0227,0361,0334,0377,0,',','x',0210,010,0245,'~',020,0335,0204,0202,0254,'%',0371,'v','>',0275,021,0204,0300,0327,0237,'0',0256,06,'%',0260,':','w',0370,0205,'X','<',0216,0307,0205,010,037,'y',0221,'s',0313,0345,0203,'5','0',02,011,'1','|',']',0367,0223,'^',0254,0,'Q',0201,0305,0251,'W',0262,0206,0217,'B',0324,'B',0373,'`',0230,0252,'Y',0312,0217,0272,0346,0370,'*','.','n',02,'^',047,'_',0345,0343,'`',0316,0240,'-','X',0333,0335,'d',0377,0,017,0244,'>',07,'U',0201,0377,0,01,014,0232,015,'Y','a','X','^',0377,0,0361,0341,0363,0300,'S',0340,0342,0271,'h','[',0,04,06,02,0370,02,0331,'^',0177,0232,0374,0263,'.','6','S',07,0362,'?',0276,024,0272,0274,'v',0360,'|',030,0341,')','"','f','&','@',0373,0314,0327,0232,'l','m',0272,0305,',',0213,'R',0325,0342,'M','}','I','l','@',0274,0260,027,0323,':',0303,0376,02,0201,'V',0203,'X',0365,'h',0300,'=',0275,0274,'|',013,0343,0300,0271,'n',031,'T',04,'"',07,0200,02,0200,'-','t',0202,'o','s',0247,0271,0345,0260,'/','&','X',0365,0200,0325,017,0337,022,010,'+',0344,0257,04,01,0320,'~','H','4',0272,'#',0211,0334,0340,'"',0350,'=','2',033,0275,' ',0243,014,'6','=',0266,037,0360,'T',026,0340,'F','u','c',01,0366,036,'&',015,'f','{',013,0226,0370,035,'@','C',0325,0223,027,0340,'x',031,05,'0',02,022,'z',0342,0337,0274,'y','^',0313,0301,0373,0315,0374,'0',0242,'5','y',0245,'G','v',0344,0366,0360,'F','d',0301,035,'f',012,'3',0337,0327,'Q',0316,0345,'!',010,0357,'k','0',0310,'n',0301,0276,'%',0314,'M',0216,0237,0360,'V',0213,'e',0212,0221,0206,0273,0241,0341,0277,'#','K','3',0373,0253,0345,0245,07,'P',023,06,04,0305,0370,036,017,021,0324,015,'`','`','n',0307,0372,0345,0254,037,02,0247,0372,'_',0210,0323,'1','7',0222,'i','~','`',0342,0256,0247,0203,020,0350,'?','$','O','g','f',0366,0301,'2','D',0261,0215,0354,0351,'+',0376,06,'R',0316,0333,0326,'t','<',016,0306,'q',0227,0346,0377,0,0272,'y','n',0313,014,04,0302,'~','{','g','C',0301,0307,0262,0201,0254,020,0301,'y','s',017,013,0257,0300,'R',0353,0341,0356,0266,0320,0312,0216,0350,0311,0355,0340,0361,035,'`',0314,0215,0236,0231,03,0357,'1',0213,0200,0226,'?',0360,'4',033,0227,0304,'<',03,0206,'y',0232,0273,0373,'7',0226,0201,0262,0301,'0','Y',0345,0216,0207,0201,0331,035,0344,011,'[','F','~',0266,'_',0311,0315,0362,0,0360,025,0305,0306,0234,'&','&',0375,022,031,022,'m',0341,'{',0276,0352,'P',021,0346,'G',0313,0177,0256,0243,0266,0374,'C',0300,0313,'g',0226,'a',';',0376,0305,0345,0272,'l',0260,'K',0202,0274,0243,0320,0360,'<',011,'j',0300,0211,'@','|',0372,0330,0255,'O',0352,'n','y',0230,'C',0346,'2',0236,0240,0366,'y',0207,023,0257,0333,0341,0373,'}','9',026,'t',0366,0321,0225,0364,'{',0311,0234,'x',0230,0341,'1',047,'D',' ','3',0363,0230,0371,020,'K','>',0257,0260,0326,0223,0241,0340,'I',0261,0312,0312,0311,'~','q',0352,0362,0320,0246,'X','%',0241,0257,0344,'O',03,02,'W',02,0345,'O',0362,'_',027,0306,0367,025,0337,'<',0317,0274,0276,'_',0356,'z','x','w',013,'~',0334,0252,0216,0326,0346,'v',0225,0274,0357,0250,0360,0306,011,'n',0224,030,0333,0337,'c',0352,'{',033,'i',0272,021,'m',0200,033,034,0254,0304,07,0236,':',0274,0264,0341,0246,0237,0247,0216,0207,0200,0235,05,0253,031,'2',0256,0347,'W',0201,0276,0200,015,021,0270,0325,025,'6','6',03,0327,0226,016,0314,017,0237,0363,0302,0210,0320,'=',0316,'X',0364,0216,0244,0362,0346,0262,'`',0222,01,0324,0216,0271,0266,015,'&',032,'2',0203,0233,0276,0320,0310,0223,021,'>',0232,0245,0332,0300,'}',0204,'[','m',0305,0206,0330,0326,0254,0306,0217,0236,':',0274,0342,06,0376,0356,0206,0371,03,'v','t',0273,0227,0307,'o',026,030,'e',0216,0344,'s','<',0317,0207,0227,0210,'j','.',0224,0134,05,0241,0247,0251,026,'f','g',0260,0312,0330,':',':','<',0312,06,0341,0221,'-','N',0234,0326,'g',0201,0323,0272,'_',0215,0246,02,0203,0242,'}','!',0233,0367,0200,0373,'{',0305,'R',0255,0254,012,030,0245,'f','?',0325,0206,0356,0257,'3',0300,0250,0307,0200,0212,0341,'5',0262,020,'.','3','_','5',02,0362,'J','7',0212,'s',033,0334,0134,'/',022,0215,0265,0226,0236,'q',0345,0276,0330,0303,0225,0245,0300,'N',';',0221,0217,'D',0277,'f',020,03,012,0301,0254,'B','8',0350,0346,'0',0263,0304,0311,0334,0346,04,'i',0344,0222,0227,0264,'r',0363,0206,020,'M','I',0225,031,022,'`',0323,'$','9','=',0340,'3','6','"','}',010,0331,0257,02,0375,0343,021,025,0265,0325,0201,'C',024,0254,0266,0216,'H',0371,'r',0354,0200,'{','L',0360,'>',0177,0200,0302,0243,'?','!','*',0346,035,0305,0273,0342,'X',0343,0340,0263,'y','>',0134,'X',0262,0327,'_',0323,';',0344,0261,'0',0231,'`',0370,'Y',0252,0324,0314,0373,0272,'A',0,0215,0214,0370,0361,0271,0211,0373,'G','G',0232,'P','[',0237,'b','_','*',0327,0230,'x',017,0334,')',0361,'0','`',0355,'N','x',0134,'K',03,0375,'c',021,0222,0326,'*',0303,0301,0212,'V','c','W','$','r',0327,0220,'=',0246,'p',0317,'?',0302,0277,'k',0234,0217,0366,'U',0277,0327,'U',0335,0340,07,0242,0214,0230,0302,'J',0230,'^',0332,'>',0225,0302,0341,0363,0321,0203,0363,'P',0240,'F',0307,0220,0315,0206,'4',0334,'0',0367,0213,01,'1',0315,0213,0342,0363,031,0203,'>',0356,0220,'8',032,0314,0206,'5',0371,0341,020,'n',0330,0351,0337,0232,'y',0207,0222,'A',0310,0235,07,0336,030,'A','2','I',0223,0221,022,'t',0202,03,'/','8','p',0217,0222,'s',010,'X','X','?',0254,'c',0224,'S',025,'a',0206,0305,'+',035,';',0313,'K',030,',',0335,011,'H',0255,0367,'_',014,'7','h',0377,0,'2',023,0257,'q',0305,0200,0235,0214,0331,0376,'_',013,'6','e',0203,'1',0226,' ','F','c','t','p','N','6',021,'@',036,0240,0307,0354,0217,03,034,0227,0277,'g',0244,0274,014,027,'`','w','"',05,'%',0222,0220,'n','h','{','G',0215,0236,036,'n','.',0357,0345,'N',0355,014,0303,0300,'Q',0271,'d',0371,'K',0337,0226,07,'(','A',03,021,0372,0306,'#','d',0305,'X',01,0261,012,0307,'H',0362,0322,0362,016,0256,0201,015,0205,0256,0353,0341,0216,'$',0367,0272,020,034,016,0200,0343,0363,0316,0243,'2',0245,'p',0343,0342,035,0247,0372,'_','#',022,'>',0217,0177,0207,012,')','W','6',0364,037,'1','v',')',0366,0203,'I',0214,'>',0217,0201,0200,0265,0345,0231,'3',030,0344,'d',0363,'F','8',0362,'I',0354,0200,'}',0340,'1','&','I','2',0213,0202,'c',017,'!','r',07,0336,026,034,0262,'q',0260,'!',0261,0177,'Y',0304,014,0253,'W','X',015,0203,025,'b',0204,'y','`',04,';',0350,020,031,0337,0231,'^',01,01,'F','?',011,03,'X',0350,034,0201,0342,0334,035,030,0324,':',033,'E',0261,'+',0326,0270,'Q',0324,0320,0245,0346,'/',0277,032,0242,'P',0206,0342,033,'0','8','i',025,0340,'~',015,'`','=','m',0345,011,0234,0300,'N','r','D',':',0255,0365,0210,'F',0371,0214,0305,'F',0267,0354,0276,'w','s',0236,'D',0356,'q',0314,0360,'5',0327,'2','f','K','~',0244,0341,'Z',0341,0377,0,0250,0374,035,'j',0326,'#','p','1','V','!','w',0226,'&',0303,0340,020,0311,0343,0346,0257,01,06,0253,0316,014,'8','(',032,'r',0256,0223,021,'i',0376,0226,0372,'p',0342,0221,0260,0352,0307,0332,0346,'2','d','|',0316,'-',016,'I',0357,'j',0212,0256,'k',0353,'o',0207,0342,'E',0251,'o',0217,0247,03,0320,0235,'5','v','D','F',0355,0330,0367,'x','`','/',0330,0356,0212,0236,'{',0353,0315,06,0340,0311,'%','>',0204,'}',0341,0323,'6','I',020,0236,'A','1',047,0266,'A',0367,0202,0317,024,'&',036,015,020,'.',07,0315,0211,0331,0226,0255,'c',0224,03,025,'c','7',0243,0227,022,'x','y',02,01,'/',0221,'x','&',02,0307,'S',0374,'C',0300,'j',06,0234,0260,0,'|',0242,0377,0,'N',',','w',0205,033,'8',0260,'i',0244,0352,0277,0344,'x','n',0340,032,0266,0242,'>',0312,0341,0,0211,'c',0243,'0','I',0236,0376,0312,0231,0274,030,047,'m','3',0251,020,036,0327,'N',0374,0356,0367,0214,0206,'w','X',0246,'>',036,0313,'r',0307,0321,0260,0353,0303,0256,'8',0364,0332,'k',034,' ',030,0253,034,0252,'9',0274,0270,'1',0303,0311,'!',0276,0343,'R',0370,'2',0320,0342,0337,0274,'!',0220,03,0,'9',0224,0276,0266,0253,0246,0263,0225,0303,0212,0363,03,0246,'n','#',0273,0304,0201,0275,'?',0330,0360,023,0354,0252,0336,'A',0250,04,0350,0332,017,0230,014,'%',0216,0214,032,012,'u',0374,'"','7',06,'c',0315,'0',0210,'j','J',0376,0307,0316,013,02,0352,'>',04,0311,'C','5',0216,0311,'G','7',0227,'6',024,';','$',0250,0201,0253,0253,0340,0270,'7',':','{',0230,0,0,0240,0320,0346,0233,'y','z',0211,')','X',031,0260,'3',0204,022,0314,'L','/',0327,'N','+','q',0177,'#',017,0267,011,'*','u','5',04,0235,025,016,'B','3','.','c',0223,011,011,'Y',0267,'!',033,'`',021,0322,'^',0206,0235,06,'s',01,'>',0214,0271,0307,0260,032,0274,0240,033,0251,06,0337,023,'2',016,0323,0227,0240,0324,'|',0231,'C',0,0315,0336,'g','/',0313,0363,0371,'L',012,'9',0365,')','A',0177,'P',0375,0370,'F',0367,020,0372,0277,0336,047,0350,'G',0326,0374,'4','<','V',0275,0217,0331,0312,0250,0336,0273,'E',0237,'x',0332,'`',036,0326,'K',0211,0324,'`',0244,0314,'~',0243,',',0347,0227,'*',0,'{',0300,'U',030,0263,'=',0371,0223,'N',0257,0320,0223,'B',0255,':',0376,0216,034,0323,0367,0272,0342,0351,0306,0341,0250,'U',0202,0274,0307,'8',0232,04,0304,0306,'c',0360,'7',0210,0300,0211,0230,0302,0360,'"','Z',']',0261,0227,0323,'d','%',0362,0245,'(',017,'x',0204,026,0260,02,0,0326,'G',0371,0365,0372,'.',0316,0227,'s',0371,0341,0252,0357,0373,'/',0355,0305,0372,0375,0247,015,0337,0367,0207,0336,034,0323,0,0331,'0','{',0313,0225,032,'?','$',0251,0304,0336,'d',')','8',0342,0344,0346,'>',0217,0343,'B',0245,0,037,'x',0215,0225,'@','k',011,023,0342,0271,0177,0257,0243,0247,0367,04,0341,0263,'~',0361,'q','~',0363,'i',0303,'B',0376,'W',0371,016,'v',0212,024,0214,'H','g','?',0252,0342,012,'%','1','?','`','E',0203,0351,'}',015,'j',0263,033,'^',06,04,0224,031,'@',0326,025,047,'0','4',0377,0,'_','I','n',0313,0344,'8','h',0337,0274,0134,'X','=',0303,037,033,'B',0335,'~',0201,0250,05,035,0273,016,0342,'_',0257,0244,0354,0211,014,0233,0306,'8',0223,033,0346,'v',0211,'_',0257,034,0374,010,010,'|',0227,0371,0360,'m',0242,0203,027,0311,'2','X','"','}','!',0325,0342,0203,0334,0376,'8','Y','&',0247,0265,'>',0374,'X',013,0206,'g',0237,0204,023,'k',0356,'#',0357,017,0241,'b','k','F','o',0264,047,0357,'q','%',0340,'4',0315,0340,0253,'K','O',':',0370,0332,0304,0201,'T',0311,0256,0260,010,0223,'R','Y','H','j','@','5',033,0346,017,'g',0350,0314,027,'|','&',0311,'W',0355,0265,0305,'N',0305,'o','l','~',0374,' ',0322,0317,'v',0244,0235,'3',037,0242,'b',0340,'p','>','l','`',0355,'-','|','*',0305,'0',024,0325,0372,02,0340,0357,0270,'a','}','T',033,'X',0323,'1','_',0224,'~',0353,0216,025,'j','}',016,'T',024,035,'(','p',0364,':','~',0274,0270,0264,0234,047,'j',0177,0234,'O',0365,0225,'{',0375,011,023,0201,0300,0371,0261,07,0246,0325,0257,0201,'Y',013,02,0323,0241,0364,'B',030,032,0302,0256,026,0215,0360,0202,'Y',0264,024,0227,0256,0216,0342,031,'K','u',0354,'K',0347,013,031,'/',0240,0200,06,'3',0213,0261,'u',0355,0303,0334,047,0335,0373,0361,'a',',','-','u',032,0373,0307,0200,0233,'4',0251,'z','0',0366,017,0240,'T',0340,'p','>','l','A',0351,0265,'k',0341,0254,'L',0213,0340,0372,'T','q',';',0266,'w',0233,',',0340,0332,0365,0351,'1','w',0220,'~',0353,0206,0226,0265,'9',0246,0266,0254,'7','k','.','!',';','7','q',030,'3','h',016,0266,0250,0271,0220,'?','.',02,'Z',0306,0354,0216,024,'*',0217,0336,'|',0367,'(',07,017,0225,0210,0345,'6',0257,013,0362,'L',0213,0340,0372,'q','t',0210,032,0214,0254,'Y',0300,026,'6','C','m',0337,0241,0362,0203,0322,0335,0270,'v','K',0345,0324,0244,06,0266,'1','{',017,025,0216,0321,0324,'8',0205,0225,037,05,0210,0352,0304,'x','n','h','N','^',032,013,0350,0236,'p',0346,0271,'@','8','|',0254,'w',')',0265,'E',0251,0332,' ',0265,0350,'}','U',0265,'+','U',0251,'1',0202,0337,'Q',020,0222,0223,'$',0227,'}',0313,'5',0367,'o','2',0272,0202,'g',0312,0362,'$',0343,'C',0357,0302,0244,'+',0331,0261,0305,0355,017,014,'z',0,0256,'&','#',0270,03,'G','"',0275,'#',0301,'`','4',0260,'j',0361,'*',014,'1','S','Q','9',0217,'P',017,0344,0261,0334,0246,0325,026,0213,'p','&','D',0262,026,0275,0271,'e',0275,0373,023,'X','!',0271,'p',021,'Q',0243,023,0366,0212,0322,':','<',0321,0246,0314,031,'w',0267,'x',0227,0234,014,'^',0346,0263,'<','e',0352,0317,0240,'`',0246,0223,0252,'%',0361,0271,0264,'J','v','"','[','-',0346,0364,'0',0351,0205,0371,0360,0253,0302,'%','V',030,025,'O',0257,0264,0256,'<','3',0302,0373,'<',037,0264,'x',011,0210,0353,'3',0327,0355,'C',0226,0325,0,0376,'C',036,0312,'m','Q','@',0253,'A',021,'P','d',036,'Z',0300,0312,0202,'~',0341,'z','D',' ','S',' ',0216,035,0342,0323,0275,'b',02,'v',0232,'s','Q','Y',0234,'E','c','I',0251,'*','_','j',05,0211,0365,031,0213,024,04,0306,'X','#','+',0344,'!','@',0230,0337,025,021,0373,'G',022,0370,'m',0200,'A',032,0215,0274,0357,0220,'O',0201,'G','z',0303,0336,020,010,0242,0315,0236,026,0305,'q',035,030,0257,0326,0320,0317,0222,0235,0301,0376,'C',036,0312,0355,'P',031,'Z',015,'X',0356,0223,'K',0313,033,'5','0',02,'V',0260,'x',0375,0220,'-',0243,'8','8','F','b',0351,0374,0301,0351,0364,'}',0330,0325,0245,0350,0366,0216,02,0311,'J','!','z',0302,0320,'s',0210,0266,'k','p',0322,0267,037,0202,'H',0310,'~',030,021,0265,0207,016,'*',0324,0223,'v','_','~',021,0240,05,0134,0202,023,'"',0235,'}',0347,0220,0361,'6',0340,0134,'!','F',0311,'g',0304,'t','0',06,0353,0331,'=',0236,'B','w',07,0371,014,'K','+',0265,'C','D',0243,'5',0214,0213,'F',0177,0256,0134,0260,0266,0214,'Y','[',0270,0215,036,032,'i',0317,0344,'>','/',0302,'t',0325,011,011,0320,031,0235,0374,032,0330,0247,'N','1',0254,'z',0274,'u',0207,0233,0234,'I',0201,0264,0264,06,'^','(',02,0306,0316,0223,':',036,'l','2',0240,0365,0204,0277,0,0244,0260,'z','E',036,0314,0216,0232,017,'N',034,02,0237,0241,0217,0363,'P',0200,'(',024,034,0232,'=','(',0263,0266,'q',0341,012,0237,'7','k',0372,'>','o',035,0273,07,0371,'L','O','+',0265,'G','n',06,'*',0304,014,0216,'o','3','W','p','.',0235,'|',032,0221,'|','m',0177,0210,0,0,0240,0323,0201,0320,011,0230,0312,0215,0253,0242,0366,0211,017,'9',0230,'G',027,010,0244,'r',0252,0356,0357,0234,0371,'n',0306,0244,' ','6',0332,',',0254,0262,'b','K',030,'d',0376,0215,'2',0354,0203,'K',0256,06,0207,0363,'/',0207,'X',0361,'>','c',0326,0371,'T',0240,01,0321,'"',0316,'N','u','h','x','k',0320,'u','5','{',0236,'e',0222,0226,'3',0332,0352,'p',0340,0340,0177,0224,0304,0332,0273,'T','0',0302,0265,'c',0224,'K',0231,0271,0201,'q','}','z',0370,'Q',0205,'X',0267,'m',03,0201,0250,032,'q','T','f',0325,0365,036,'s','1','6','D','3','1',0306,014,0376,'B','(',0347,032,0300,0367,'7',0230,0234,'W',0213,0360,0301,0,'`',0342,'?','1',0344,0335,0237,'p',0250,0251,'O',0252,0324,02,0260,'A',0336,0230,0274,0246,'b',0365,0340,'z',0242,'W',016,04,'6','2',0303,0356,031,0372,0303,0307,017,03,0374,0246,'&',0365,0332,0240,'`','5',0253,026,'^',0314,0276,'O','1',0300,'i',0262,0220,' ','(','4',0200,0205,0343,0376,0304,'0','g',0240,'9',':',024,022,'*',0202,031,0370,025,'d',0275,0352,'7',07,'M',0247,'8',0343,0203,'$',0205,0220,'e','4','0',05,0215,0214,'d',020,'0',030,0235,0343,0212,0273,'^',0314,0257,034,023,0225,'$',0301,'_',0203,0347,0230,'T',021,0307,0243,0243,037,'s',0266,'|',0360,0241,'q',0363,01,'!',0330,012,0316,'%',0237,0206,034,07,0371,'L','y',0353,0265,'C',0340,'k','V','c','!',0230,'|',0217,'3',027,0253,0356,'t',0207,0210,'T',04,':','+','6',0230,0232,0207,023,0252,0337,0226,0230,'L',',',0347,'0',0351,01,'F','#',0264,'2',0207,0273,0134,04,0251,')',0347,'<','[',0352,0273,' ','5','x',06,'r',0233,0255,0310,']',0254,0300,0366,'E','D',0346,'(',0363,'F',0202,024,0207,0235,0314,'^','c',027,025,0324,03,0210,'m',0312,0357,'`',032,'y','f','>','d',0253,')',0325,0217,'=','v',0250,01,0261,0312,0314,'S',0273,0271,0325,0346,'P',0236,0313,0232,06,0221,'>','*',0210,0336,037,'v',0361,0326,0134,0356,0301,'B',014,'q',0207,'a',0347,0340,')',0321,0321,027,047,0253,'G',0235,'k','s',0317,'C',016,'Z',0203,023,'8','6',0236,0201,0203,'5','P',047,0217,'c',0244,0260,0253,'d','a',0376,'G',0317,'I',0134,0322,'>',0240,'$','A','F','&',0363,0334,'-',0231,'>','o','c',0244,022,'U',0203,'B',031,'L','b',0262,0244,'_',0367,'/','0',0241,0303,0320,'@',0312,0206,0273,0307,0256,0372,02,'Q',0360,0376,'S',0350,01,030,')',014,0345,07,'s',']','}',0233,0304,'R','%','&',0221,015,0356,0261,'`',0266,'f','i',0315,'T','5','u',0243,034,0202,0325,0257,'(','*','O',0222,'!',0221,'c','1',0336,0242,024,'Y','m',0300,'"',0254,'r','N','v','{',011,03,035,'r','>','-',0231,0236,'o','c',0244,015,0307,0240,'`','C',0341,0256,0232,0237,0373,'7',0231,'w',05,0355,013,'c','f',0335,'c',0202,'U',01,0254,':','`',0261,0351,0333,0350,0252,033,'K','p',0367,'c','u','-',0365,0216,02,0311,'e',0340,0357,' ',0225,'%','<',0321,0254,0243,']',0336,0205,0,0336,014,022,'^',0207,0273,0331,015,'^','(',0346,0216,'F',0314,021,0326,'6',0353,'n','e',0257,0340,'B',011,0346,0366,':','@',0214,0324,'X',07,0201,0201,'b',0376,'f',0321,'s',0250,011,'C',0213,013,'h',0305,0231,022,0230,0372,0177,'?','J',0337,0245,'9',0366,'K',0340,0330,'_','>',010,0372,017,0345,030,0241,'^',016,0217,'8',0343,0277,'m',010,0231,'&','2',0252,0306,0373,'M',0366,'a',0252,0331,0346,'0','2',0342,0307,0224,'%','^',037,0203,0322,0366,0203,0345,0350,024,022,0270,017,0274,0273,'s',03,06,0324,'P',022,0206,01,0305,0370,'x','z',0333,0362,0237,'N',031,015,'H','`',0312,'Y',0253,0250,'v',0211,'P','C',012,'s',0216,0,0365,0332,'5',06,0330,0323,0235,0231,0267,'1',0224,0277,0205,'s',0212,033,0372,'#','i','a','5',0336,'g','3',013,0257,'y','2',0371,0306,0325,0225,0240,'=',0346,'@','_','+',0230,012,03,025,0202,'7',013,01,0323,0341,0231,')',0217,0257,0361,0,024,'`','}','E','J',0360,0330,030,'w','E',0352,033,0353,0332,'#',022,'h',0306,0243,'S','6',021,'R','S',0263,0315,03,'X',0227,'~',0254,')',0326,'7',0314,0232,'C',0250,016,'z',';','e',0362,'N',0241,0355,'X',0215,0303,0260,'H',0370,0340,'d','n',0313,0277,'G',047,'#',0233,'S','p','.',0235,'|','*',0372,0214,']',0373,'C',0304,'*',03,'O',0253,'M',011,033,'"',0307,0,0371,0360,0265,'2',0237,'(','b',0236,'l',015,'A',0251,'/',05,'d','7','G','`',030,0211,0224,04,0200,0301,0262,0377,0,0134,0225,0206,'?',0306,'J',0274,0212,0325,0210,'"',0257,'h',0346,0335,0335,'+',0257,'_',012,0376,'?',0312,020,';',0216,0200,0372,0306,' ',02,0325,0310,0226,0340,0205,0247,0372,'Y','f',027,0260,0374,'L',032,'X',0330,0352,'N',0301,0315,'D',0325,0335,0300,0363,'r',0230,0263,0216,0254,'D',06,0223,'$',0224,0370,0177,023,'O',0347,0215,'0',0346,021,0356,'1','o',026,'!','4',0324,0365,'s','i','v',0201,0276,0,'(',0300,0207,0305,'f',0322,023,0347,0350,0235,0376,0256,0345,'l',0246,'m',0364,'B','g','/',0323,0202,'z',0272,'K',0227,023,'V','D','X',0376,'b',06,047,0261,0342,'k',0213,'I','|','7',0253,'g','5',0311,'(','d',0222,0311,0277,0221,'q',035,'6',0304,0230,'U',0362,0207,011,'Z',0240,0265,0210,0371,032,035,0264,'J',0326,05,0243,0264,0317,0231,'@',021,0361,'~',020,033,01,'@','x','<','A',022,'3',0240,'*',0353,'/',0352,'n',024,0357,0266,0261,06,'+',031,'{','S','X',0241,'6',0354,0313,0240,'i','/',0200,015,0311,0304,0377,0,010,022,0213,0,0343,';',0332,'t',0263,'5','r',0236,'b',0211,0205,0372,'#',0271,'`',0270,0344,0241,'j','}',0230,'7',0300,0206,0325,0206,0364,0325,0340,0332,015,0236,0274,0315,'G',0374,0134,0,0306,0240,' ','9','5',0253,020,'b',0263,0375,0230,011,0213,'i',0352,04,'C',022,0303,024,'y',0346,0214,'R','l',0331,0345,'/',0351,0215,0305,'Z','R',020,0245,0205,024,0366,0336,035,'^',0354,017,0220,0205,0343,'i','q',0213,'?','n',0262,0251,0322,0351,0237,0330,'x','<','k','h',0307,0353,'6',0246,017,'2',0375,0343,'!',0246,0343,0262,0277,']','8',034,0355,0325,':','c',0340,0244,'d','[',0277,',','f',011,0362,0311,0362,0330,'P','7',0212,0325,026,0242,0356,0367,'W',0200,0,017,0366,0267,0224,'.',0350,'t',0177,017,0264,0312,'T','`','g','t',0320,0371,'C',0357,'[',0211,0177,'@',0360,017,'V',0211,0225,0316,'d',016,0315,'e','-',0244,0345,0361,'y',023,020,023,'z','g','c','"','_','!',0270,015,012,0331,0337,'2','.',0326,0357,'Y','@',0255,'%','@','3',0240,0256,'T','S',07,0341,'M',0366,0210,0203,'l','i',0313,'5',021,'m','w',0227,'r',0315,'`','h',0352,'g',0301,0210,026,'"',0267,'x',031,0330,'d','v','c',0347,0246,'@',0305,0311,0260,0206,0372,02,016,'=',0367,'0','?',0225,0300,0271,0326,'~','S',0307,'r',0230,'w',0366,'X','A',0303,06,'b',0316,0351,0370,0214,0352,0340,037,016,'>',0320,'-',0333,';',0346,0306,0,014,0355,'2',0345,0361,0246,016,'m',0221,'S','J',0322,0276,0230,'e','%',0320,035,0360,'K',025,0366,'b','}',0252,0226,027,02,0225,'g',0275,04,0276,'M','G','2','n',0300,0200,'q',0203,013,'i',0336,'W',0205,'D',0225,0243,047,'?','8',0221,0241,0243,0224,'9','0','s',030,0311,033,0327,')','M','8','<',0226,'1',0230,0334,0307,0253,0270,0305,032,033,0374,0312,0340,037,')','R','2',0371,']',0205,0360,0370,'$',013,0346,'1',0326,0303,0274,'J',0352,'v',')',021,0243,0324,0301,0347,0235,'y','E',0312,0177,'"',0177,'I',010,033,0211,'T','%',0336,'j','b',0207,'m',011,0207,0234,'W',0325,'d',0304,0256,'U',0313,0217,0335,'s','C','}','%',0275,',','o',0373,'S','H','[','}',0250,'A','i',0352,0232,01,0327,0357,012,'.',0205,')',0231,0305,017,0256,0273,0371,0210,0225,'1',0375,0270,'!',0346,02,0331,0230,'y',0276,027,0312,0250,0201,0257,013,0325,0337,'i',0373,'@',0204,'g',017,'W',0320,'(','8',0202,'#','o',0244,0262,015,0251,0217,'t',0367,0207,0231,0235,'[',0316,0273,0360,'g',0311,0336,07,'S','!',0264,'8','M',0236,01,011,'@',0365,035,'G','h',0350,'G','B',0270,0353,0300,035,0340,0354,010,'x','F',0356,'q',0241,'`','.','N',0322,0373,0263,'r',0337,0223,0316,'f',']','{',017,0256,0251,0231,0355,0311,'#',0370,0352,01,0353,0264,024,0240,0306,0227,0326,'Y',0134,0206,' ','&','c','=','|',0374,021,0210,'Q',0230,0306,0370,0,'d',0313,'&',0275,035,0371,011,0211,0326,0371,025,016,0274,0230,0307,'O','I','-',022,0341,':',04,0373,0246,'O',0352,0245,0334,0207,'E',07,0254,0352,0272,0255,01,015,'x',0363,'8',0364,0225,'$','Z',012,0361,0250,'=','W','%',0346,0206,'V','f','f','@',0355,012,0317,0332,0322,'{','9','J',0372,0232,0233,0240,022,0377,0,'%',0376,0234,'s',023,0256,0320,'f',0215,0257,'Q','+',0227,'R',0200,0366,0231,'w','E','-',0244,'c',0316,0311,0332,'X','j',0326,'[','8',0330,0323,0375,'2',0241,010,0252,'l',0313,0366,'x',0217,'?',0266,0236,'Q','U','W',035,0247,'R','8','/',0315,0210,0362,'~',0222,0246,026,022,02,0326,'7','$','1','Q',0266,'/',0311,'g',012,020,0351,0276,0262,0271,0371,022,'r','3',0214,'x',07,'l',0304,0225,014,0134,';',0240,0313,0211,'}','L',0251,'e','C',']','}','S','1','/','S','X',01,0227,'u','(',0356,0257,0232,'z',0222,0261,0225,0312,0251,'^',03,0302,',',0276,0237,'<',0246,'v',0224,0210,05,0134,'p','^',0376,'p','I',0255,'o','>','l','(','Q',0227,0321,0347,'$','b',024,036,0252,'(',012,'q',' ',0333,012,07,'a','F','c',0300,'}',017,0317,0326,'T',0350,0134,'[','"',0303,0260,0375,0210,0323,0366,'5','Q',0305,'a',0220,0350,0233,'(',0313,'&','7',0337,0225,0207,0375,0260,0315,'I',0342,020,035,036,0374,012,037,'h',0331,'S',0372,'0',0224,014,'V',0264,0375,0347,'@','w','+',0312,'c','Z',0353,022,0365,0235,'-',030,022,0276,0235,0261,'N','0',0353,0365,'4','^',0321,'k',016,'c',0231,027,021,0275,'Q','Z','|','g','k','`',0177,0302,'W',0374,'F',0225,0226,'C',0345,037,0,0372,'=',0245,'A',0251,0225,'2',0213,021,0323,0322,'(','i',')',0332,'*',012,0377,0,0303,0364,0361,0372,02,'7',015,02,0313,0274,'A',0301,'.',0,'u','!',0201,'U',0201,05,0313,'J',0377,0,0303,0240,'S',0211,05,025,'u',0373,020,'/','f',0240,'b',0222,0236,0252,'H',0271,0357,'A',0361,0377,0,0214,0377,0332,0,014,03,01,0,02,0,03,0,0,0,020,0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'H',' ',020,01,04,022,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'H',' ',020,'I','$',0222,'I','$',0202,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'H',0,022,'I','$',0222,'I','$',0222,'I',04,022,'I','$',0222,'H',' ',0202,'I','$',0222,'I','$',0222,'I','$',0222,'I',0,0202,'I','$',0222,'I','$',0222,'I','$',0222,'H','$',0222,'I',0,02,0,04,020,'I','$',0222,'I','$',0222,'I',' ',022,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,011,'$',0222,'r',0346,'J','H',04,0222,'I','$',0222,'I','$',0200,'I','$',0222,'I','$',0,'@',04,0200,'I','$',0222,'I','$',0220,'H',04,0222,'w','3',0222,'I','$',0222,'I','$',0222,'@','$',0222,'I',04,0202,'I',' ',0222,'H','$',022,'I','$',0220,011,'&','2','I','$',0222,0240,'$',0222,'I','$',0222,'I',04,0222,'I',0,02,0,'$',0222,'I','$',0,0,'$',0222,'I',0,0213,0311,'$',0222,'I',0134,022,'I','$',0222,'I','$',0222,'I','$',022,0,'$',0222,'I','$',0222,'I',' ',020,'I','$',02,'g','$',0222,'I','"','P',011,'$',0222,'I','$',0220,'I','$',0220,0,'$',0222,'I','$',0222,'I','$',0222,'@',04,0220,010,0134,0222,'I','$',0236,0311,'$',0222,'I','$',0222,'I','$',0220,010,04,0222,'I','$',0222,'I','$',0222,'I','$',0200,'A','$',0362,'I','$',0222,'g','$',0222,'I','>',023,0310,04,0222,'@',' ',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I',0,0204,0311,'$',0222,'H',0244,0220,'I','$',0220,0263,'H',022,'I',' ',0202,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0202,'{','$',0222,'I','!',0360,'I','$',0222,'B','$',0205,0331,' ',020,011,'$',0222,'I','$',0222,'I',04,0222,'I','$',0222,'I',0274,0222,'I','$',0222,0301,'$',0222,'I','!','B','@','K',0,01,'$',0222,'I','$',0222,'I','$',0220,'I','$',0222,'@',' ','r','I','$',0222,025,0,0222,'I','$',0221,03,'!','P',0263,04,0222,'I','$',0222,'I','$',0222,'I','$',0222,'I',0,0223,'I','$',0222,'I',',',022,'I','$',0222,'H','I',0222,'O','M',0262,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',022,011,'$',0222,'I',' ',0220,'I','$',0222,'I','$','e','I','$','3',030,'d',0220,'I','$',0222,'I','$',0222,'I',04,0220,011,'l',0222,'I','$',0225,0311,04,0222,'I','$',0222,'B','d',0222,'I','^',0252,0311,'$',0222,'I',04,0222,'I','$',0222,'I','#',0222,'I','$',0222,03,0,0222,'I','$',0222,'I','!','r','I','$',0203,'S','<',0220,011,'$',0202,'I','$',0222,'A',' ',0212,0311,'$',0222,'I',034,022,'I',04,0222,'I','$',0220,0311,'$',0222,'I',':',0265,'I',04,0222,'I','$',0222,'I','$',0220,'c','$',0222,'I',' ',020,'I','$',0200,'I','$',0222,'I',0304,0222,'I','$',0222,'L','B',0222,'I','$',0222,'I','$',0222,'@',0344,0222,'I','$',0222,'H','$',0222,'H','$',0222,'I',04,'#',031,'$',0222,'I','#',0274,0351,'$',0222,'I','$',0222,'H','!',0362,'I','$',0222,'m',04,0202,'I','$',0222,'I','$',0220,'J','>',0222,'I','$',0222,'N',0310,0362,'I','$',0222,'I','$',0223,'I','$',0222,'I',0244,02,'H','$',0222,011,'$',0222,'I','$',0254,'i','$',0222,'I','$',0311,0330,'$',0222,'I','$',0220,027,'$',0222,'I','"',0220,'I','$',022,'I',' ',0222,'I',04,0222,'L',0201,'r','I','$',0222,'H','Y',0216,'I','$',0222,010,024,0222,'I','$',0233,'H','$',0222,011,'$',0222,'I','$',0202,'I',' ',0374,0221,'$',0222,'I','$',0217,0243,'d',0222,'H','#',0222,'I','$',0222,'o',0,0222,'I',' ',0222,'H','$',0222,'@','$',0220,'A',0276,'B','I','$',0222,'I','!','#',0311,' ',033,0311,'$',0222,'H','l',022,'I','$',0222,'I','$',0202,'I','$',0222,'H',04,0232,03,'d',0222,'I','$',0223,']','H',0222,'u','$',0222,'I','!','R',011,'$',0222,'H',04,0222,'A','$',0222,011,' ',022,'I',025,0346,'I','$',0222,'I','%','J','p',0214,0222,'I','$',0201,0300,'$',0222,'I','$',0200,'I','$',0222,'I',04,0220,'I','$',0223,023,0364,0222,'I','$',0222,034,'a',022,'I','$',0222,'S',04,0222,'I','$',0222,'H','$',0222,'I','$',0222,'I',04,0222,'I',05,',','I','$',0222,'I','$','S',0311,'$',0222,'H',0204,02,'I','$',0222,'I','$',0222,'H','$',0222,011,'$',0202,'I','$',0200,'C',0213,0222,'I','$',0222,'I','$',0222,'I','#',0340,011,'$',0222,'I','$',0222,'I','$',022,'I',04,0222,01,'$',0222,'I',04,022,'Y','$',0222,'I','$',0222,'I','$',0220,'A','$',0222,'I','$',0222,'I','$',0222,011,'$',0222,'I','$',0222,'I','$',0220,0,'4',0222,'I','$',0222,'I','$',0222,'Y',04,0222,'I','$',0222,'I','$',022,'I',04,0222,'I','$',0220,'I','$',0222,'I',' ',020,07,'$',0222,'I','$',0222,'I','<',0222,'I','$',0222,'I','$',0222,'I','$',0202,'I','$',0222,'@','$',0222,'I','$',0222,0,0,022,'I','$',0222,'I','$',0323,'A','$',0222,'I','$',0222,'I',' ',0222,'A','$',0222,'I','$',0222,'I','$',0222,'I','$',0,'H','d',0222,'I','$',0222,'P',0231,022,'I','$',0222,'I','$',0200,'I','$',0222,'H','$',0222,011,'$',0222,'I','$',0222,'@','"',022,'I','$',0222,'I','$','_','Y','$',0222,'I','$',0222,'I','$',0220,'I','$',022,'H',04,0222,'I','$',0222,'I',' ',0226,011,'$',0222,'I','$',0222,'M','^','2','I','$',0222,'I',' ',022,'I','$',0222,01,'$',022,'I','$',0222,'I','$',0222,',','$',0222,'I','$',0222,'I','$',0213,0341,04,0222,'I','$',0200,'I','$',022,'I','$',0222,0,'$',0222,'I','$',0222,0,0320,0222,'I','$',0222,'I','$',0222,'I',0247,02,'I','$',0222,'@','$',0222,011,'$',0200,'I',' ',022,'I','$',0222,'I',02,0222,'I','$',0222,'I','$',0222,'I','$',0220,0350,04,0222,'I',' ',022,'I',04,0222,'H',04,0222,011,'$',0222,'I',' ',016,0311,'$',0222,'I',' ',0262,'I','$',0222,'H',0346,'B','I','$',0220,'I','$',02,'I',' ',0202,'H',' ',0222,'I','$',0200,'A','$',0222,'I','!','r',03,'$',0222,'I','$',0233,0276,0204,0222,'A',04,0222,011,'$',0222,'A','$',0222,'I','$',0222,'A','<',0222,'I','$',0234,011,03,0306,'I','$',0222,'I','$','2','A',' ',022,'H','$',0222,'I','$',0222,'@','$',0222,'H','#',0262,'I','$',0222,027,0,02,'F',0344,0222,'I','$',0222,03,'d',0222,'I','$',0222,'I','$',0202,'I','$',0202,'I','$',0234,0311,'$',0222,'I','%',022,'@','$',0255,'I','$',0222,'I','$',034,'A',04,0222,011,'$',0222,'H','$',0222,01,'$',0222,05,'$',0222,'I','$',0254,'I','$',02,'C',0354,0222,'I','$',0222,'M','`',0222,'H','$',0222,'I','$',0220,'I','$',0222,011,04,0222,'I','$',0221,0321,'$',0222,'H',04,0200,0311,'$',0222,'I','$',0242,0200,'$',0202,'I','$',0222,01,'$',0222,'A',03,'R','I','$',0222,'K',0,0222,'I','$',0202,'@',';',0222,'I','$',0222,'I',0370,0202,'I','$',0222,'I','$',02,'I',0,013,'I','$',0222,'I',011,0202,'I','$',0222,'H',04,012,0211,'$',0222,'I','$',0221,0310,'$',0222,'I','$',0220,'I','$',0202,'Y','$',0222,'I','$',016,'I','$',0222,'I','$',0220,'@',022,0222,'I','$',0222,'C',0204,022,'I','$',0222,'I',04,022,'A','<',0222,'I','$',0223,0231,'$',0222,'I','$',0222,'I',04,0223,0343,'$',0222,'I','$',0217,'9','$',0222,'I','$',0222,'I',06,0362,'I','$',0222,'C',0,0222,'I','$',0222,'I','$',0222,0,04,'2','I','$',0222,'I',05,'@','I','$',0222,'H','$',06,'I','$',0222,'I',03,0202,'I','$',0222,'I','$',0222,'I',' ',0222,'{','$',0222,'I','$',0223,0350,04,0222,'I','$',0200,';','$',0222,'I','$',0256,'I','$',0222,'I','$',0222,'I','$',0222,010,' ',0372,'I','$',0222,'I','%',02,'I','$',0222,'A','4',0222,'I','$',0220,'Y',04,0222,'I','$',0222,'I','$',0222,'I','$',0,07,'d',0222,'I','$',0220,0234,04,0222,'H',07,0222,'I','$',0222,'H',020,022,'I','$',0222,'I','$',0222,'I','$',0222,'@','"',0302,'I','$',0222,'I',010,020,'I','$',027,0311,'$',0222,'I','$',0242,'I','$',0222,'I','$',0222,'I','$',0222,'A','$',0222,02,0344,0222,'I','$',0220,011,'$',0222,014,'$',0222,'I','$',0213,0,'$',0222,'I','$',0222,'I','$',0222,'H','$',0,'I',' ','n','I','$',0222,'I',024,022,'H','&',0222,'I','$',0222,'u','$',0222,011,'$',0222,'I','$',0222,'I',0,0222,'I','$',0220,'M','$',0222,'I','$',0223,011,'$',022,'I','$',0222,'I',0304,022,'@',04,0202,'I','$',0222,'I',0,0222,01,'$',0222,'H','$',07,'I','$',0222,'H',0320,0222,011,014,0222,'I','#',0322,'I','$',0200,01,' ',0,0,04,0,'@','$',0222,'H',0,0222,'H',0253,0222,'I','$','<','I','$',0200,0335,0317,0333,0300,'$',0222,'I',' ',0,01,' ',02,01,'$',0222,'I',04,020,'I',' ',033,')','$',0222,'D','$',0220,'A','%',0265,011,' ',0202,'I','$',0222,'I','$',0222,'I','$',0222,'I',' ',0200,'I','$',0220,'@',' ',0222,'I','#',0222,'I','$',0,011,'$',0220,'I',' ',0222,'I','$',0222,'I','$',0222,'I','$',0,'I','$',0222,'I','$',0222,')','$',0220,0311,'$',0222,'I','$',0222,'I',' ',0200,010,'$',0222,'I','$',0222,'I','$',020,01,'$',0222,'I','$',0222,010,037,0222,'I','d',0222,'I','$',0222,'I','$',0222,'H',' ',020,01,'$',0222,'I',0,0200,'@','$',0222,'I','$',0222,'I','$',0202,0311,'$',0362,'I','$',0222,'I','$',0222,'I','$',0222,010,' ',02,'@',04,0,'I',04,0222,'I','$',0222,'I','$',0222,'@','%',0222,011,'$',0222,'I','$',0222,'I','$',0222,'I','$',0222,011,0,02,01,'$',0222,'I','$',0222,'I','$',0222,'I','$',02,'o',0244,0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','#','h','I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0202,'@','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0237,0377,0304,0,'-',021,01,0,01,03,03,02,05,05,0,03,01,01,01,0,0,0,01,021,0,'!','1','A','Q','a','0','q','P',0201,0221,0261,0321,020,'@',0241,0301,0360,' ','`',0341,0361,'p',0200,0377,0332,0,010,01,03,01,01,'?',020,0377,0,0365,0234,0253,0276,017,0336,012,017,0301,'r',0377,0,0312,0277,0253,0210,012,'$',0276,'H',0370,0212,0274,0220,0345,'z','a',03,0344,0276,0323,'J',0301,017,0372,' ',0260,0374,01,'X','0','e',0325,'w',0177,0304,04,011,'+',0332,0210,0254,'+','x',')','3','6','r',0277,0270,0240,'1','M',0,0303,0376,0200,024,0255,0344,012,0357,0222,'u','[',0275,'(',031,'i',0346,024,0254,'a',030,'{',0236,'=','m',0267,0220,'7','k',0275,0331,0325,'n',0364,0331,034,0317,0342,0263,06,0225,0356,0370,0357,'r',0223,0240,0335,0253,0225,0274,0305,0273,0323,'D',0220,024,032,'d',0275,0301,0241,0307,0216,0367,0350,':',015,0332,'>',027,0363,027,0366,':','n',036,03,'-','@',0230,0330,'?',03,0217,035,0237,0202,0345,0320,'7','h','8','J',0356,'/',0215,0216,0232,'t',0200,0312,0324,020,0205,0203,0360,'8',0351,04,0261,'J',030,'*','Z',0226,0206,'|','>','B',0213,0227,'@',0335,0241,047,'+',0270,0276,'6',':','i','R',05,0325,0322,0243,0354,'l',0352,0377,0,0216,0234,0207,'9',0267,0375,0375,'}',021,'-',0317,'X',0267,0257,0324,0335,0341,0263,0266,0134,0272,06,0355,033,')','}',0305,0370,0330,0351,0306,0230,']',']','*',02,07,'.',0277,0363,0325,04,0261,'N','!',02,0310,'F','=',0371,0253,0263,'7',0343,0311,0323,0317,0326,0225,0270,0364,'O',0244,0305,014,0370,'T',0201,0226,']',03,'v',0201,0204,0276,0342,0374,'l','t',0300,'0','%',0134,05,'*',' ',0313,0257,0374,0365,0220,0271,0271,'_',0324,0330,0372,'@',0350,0321,0324,0354,0325,0354,0361,0376,'C',0366,'R','#',017,0320,'|','"','A',0336,0200,'n',0361,'F',016,'W','q','~','6',0351,0237,0,'J',0272,'S','f',0352,'5',0346,0361,0261,0326,026,'4','0',0315,021,0237,0300,'}','R','l',0321,0320,'?',0262,'M',036,'J','L',0342,0364,'y',035,'~',0203,024,'3',0340,0277,0215,'x',015,0336,'(',0,'y',0201,'v','8',0351,0207,'`','J',0272,'R',0366,'U',0347,0315,0343,'c',0256,'l','y','}',0217,0247,'4',017,'o',0361,0134,'f',0365,'9',035,'*',0374,'?',0262,'M','{',0224,0210,0303,0364,035,0374,017,0360,'O',01,0273,0305,014,'?','4','.',0307,07,'L',0,012,'U',0244,'h',0253,0317,0223,0306,0307,0330,'+',016,'_',0327,0323,0261,0337,0362,'A',0263,'V','3',0307,0371,015,';',0224,0271,'k','G','G',0263,0257,0320,'b',0206,'|',03,0330,0376,0271,'x',0243,0201,0352,0216,016,':','g','H','R',0255,'6','E','V','=',0317,0350,0373,025,'h',0351,034,'5','#','o',0372,0332,0257,0353,'&',0307,'m',0375,0351,0301,0302,'h',0326,'(','F',0221,'3',0367,0276,0335,0225,0313,0305,025,'?',0340,07,035,'0',0376,')','V',0227,0262,0253,036,0347,0364,'}',0243,0263,0211,0322,'U',0304,015,'{',0232,0323,0231,032,'N','_',036,0177,'H','-','q','C',022,'I',0371,0373,0277,0340,'J',0273,0305,011,017,0340,07,035,'3','R',')','V',0230,0272,0253,033,0362,0374,'i',0366,'h','#',0364,031,'+',0221,0344,0334,0370,0351,0217,'t',0310,0222,'T','r',0271,'W',0355,0372,0177,024,0335,'G','"','C',0364,035,0376,0347,0373,'R',0256,0361,'D','H',0376,'@','q',0323,'?',0342,0225,'i',0263,0250,0356,'r',0363,0355,0366,0200,'Q','%','I',0206,'J','?','v',0306,0361,0362,'W',0264,'|','p',0363,0324,'C',017,'H',0260,0371,0250,0263,'6',011,0357,0267,0320,'b',0213,0343,0355,0264,'e',0257,0227,0212,0,0277,0310,0203,0216,0231,0377,0,024,0253,'K',0347,'G','s',0227,0237,'o',0266,0201,0263,'H',0250,'h','K','K',0330,'O',0232,0231,'A','z','p',0362,'u',037,021,'2','7','+',020,034,0253,037,016,0324,0271,0307,'#','o',0241,0273,0355,'4','?',0243,0373,'J','<',037,0310,016,':','`',047,0371,'z',0324,'$',0277,0372,0207,0237,'o',0270,0,0372,034,0361,'H',0250,'s','L','E',0377,0,0336,034,0321,'D','O','S',0204,0321,0352,0272,0203,0240,0344,0371,0245,'[',010,0343,0317,'o',0240,0305,014,0375,0214,0377,0,0324,07,0366,0225,07,0341,'v','8','8',0351,0377,0,022,'U',0216,'i','e',0377,0,0220,0274,0375,0321,',','@',0303,0277,0177,0232,'D','p',0224,'N',031,0256,0204,0344,0241,'O',0274,0377,0,'[',0237,0236,0252,03,'&','G',025,0314,0221,'x',0177,0232,'S','!','<',0217,0320,'z',0350,'%','P',05,033,0,035,0216,016,0237,0271,'m','p','s','O',023,0370,013,0317,0336,'`',',','a',0333,0207,0217,'n',0330,'H',033,0362,'b',0217,' ','0',0231,0243,0220,0207,07,0361,0371,0243,'f','L','&',036,0252,'(',0272,'N',037,'4',0257,'h',037,0336,0337,'A',0212,031,0352,':',0225,'@',024,'@',0200,']',0333,0203,0247,0371,017,0205,0330,0346,0230,0267,0222,016,'^','~',0372,'i','>',0203,0333,'u','i',0333,'j','+','#','}','G','s',0252,0354,'J',0310,0341,0256,'x','.',0227,0266,0335,0251,0240,0236,'G',0350,';',0364,0222,'z',0240,012,030,'`','.',0355,0301,0323,0214,'8',0365,']',0212,'b',0360,0366,03,0347,0300,'R',0261,'I',0200,0266,0327,0275,016,0243,0373,'#','g',0254,0323,0206,030,'|',0323,0336,0,'~',0315,'>',0203,024,'3',0376,'o',0331,'P',05,027,0,'W','}',0207,0355,0351,0306,0230,'`',0325,'v',')',02,0300,0354,07,0317,0202,'%',012,0244,0315,'M','7',01,0374,'$',0242,01,0256,':',0251,'@','V','G',025,0315,015,0322,0366,0332,0224,0261,0344,'~',0206,0357,0361,'B',012,0200,'(',0351,0202,0273,0354,'?','o','N','<','A',0203,'U',0330,0244,0315,07,0260,037,'>',014,0237,'@','w',0254,0254,'4','L',0212,0313,0317,0226,0347,'Y',0257,030,'0',0363,0375,'R','!','h',022,0307,033,032,'D',0315,014,'P',0317,0321,020,'*',0,0240,'`',012,0357,0260,0347,'w',0247,034,0,0301,0252,0354,'R','E',0201,0330,017,0237,010,0315,'%','+',' ',0302,'f',0247,0314,'`','h',0177,0232,0320,'T',0221,0302,'u',']',0231,'2','$',0217,'q',0245,04,'Y','T',0273,0273,0226,0215,0251,'#',0307,'D',0323,'s','s',0237,0243,0246,'T',01,0255,020,'0','^',0215,0216,'w','z','}',0270,0206,0253,0261,'I',0233,0360,03,0347,0302,0322,'i','"',0243,'4',0320,'I',0270,0222,'_','&',0215,0273,'S','3',0303,'&',06,0227,033,0235,'c',0250,0311,03,026,'v','k','b',0203,'x','c',0231,0212,0200,'#',0347,07,0207,'w','~',0235,0376,0336,'b',0330,0244,0226,'^','@',0360,0336,0364,021,'D','^','z',0214,'a',0230,'w',047,'F',0325,'d','W',0255,0263,0332,'o','w',0342,0331,0226,'(',0303,0310,0375,0303,0333,0257,'1','W','c',0241,0240,0330,0361,'%',0245,'v','W',0,0262,'^','E',0260,'c',0212,'[',034,0304,0226,012,'N','5',0356,'I','f',0377,0,'l',0271,0245,'w',027,0306,0355,'K',0203,'c','A',0261,0342,0211,0323,031,030,'O',':',0276,'0',0,0320,0246,',',017,'3',0214,0254,'P',0262,05,0231,037,0263,'X',0262,0373,0213,0361,0273,'S',0302,0134,032,06,0307,0213,0367,06,'L',0345,',','n',0315,0313,'^',0343,'V',0330,'3','h','|',0234,0226,0373,024,'w',0225,0267,037,0215,0352,'X','K',0203,'@',0330,0361,0221,'N',0210,0310,0226,'D',0302,'5',0204,020,0,'g','M',0367,0326,020,'o',031,0251,'G',' ','D','q',0353,0244,'u',0356,0244,0250,'q',0,'|',0243,0363,0343,0205,' ',0215,0311,'c','w','c','L','t',0234,0217,'X',0301,012,021,0302,'T',010,0222,'*','7',0371,0366,'|',0266,0245,'j',013,'"','B','<',0216,'(','b',0206,'|','`',020,'J',0321,0205,04,0303,0220,0320,0373,011,0311,0370,'6','f','-',0301,0303,0345,'W','S',0321,'2',0370,0235,036,037,')',0244,0212,035,0374,'X',025,06,'i','^',0364,'}','9','y',0373,' ',0245,0234,'$',0215,06,0202,0353,0270,'3',0334,033,'7',0333,'j','z',0300,0310,0220,0235,0306,0345,'L','P',0317,0211,0243,02,'V',0210,0315,0346,0272,'r',0363,0366,0272,'~','i',022,0332,0306,0342,0201,'^',0260,'e','^','z','<','>','S','A',036,'$','`',0345,'h','Y',0307,'t',0374,0236,'~',0334,0377,0,0232,021,0251,'<','}',0323,017,'|',0237,0236,0372,'"','"','c',0361,0337,0304,03,0224,0256,02,0244,0204,0256,0237,0223,0317,0335,'@',0230,0312,0244,'B',0217,0354,0346,0254,'p',0211,021,0220,'u',0300,0225,0237,015,'>',022,0254,06,0265,'=',047,0215,037,0365,0323,0213,0363,0227,0342,0247,012,'.','"',0334,0356,0323,'5',0273,034,0207,'&',0264,'P',0351,0251,0271,0246,0343,0337,0252,0200,0207,024,'i',024,'A','k',0354,0226,0221,'W','4','1',0301,';',015,'9','7',0363,0253,'$',0236,026,0201,0254,0,0326,0200,0262,'`',0323,0376,0372,'q',0202,'R',0255,'*','x','[',036,0347,0365,0365,02,0247,0261,0206,0343,011,0204,'`',0232,022,0330,'E',0315,0271,'.',0366,0214,0343,'~',0250,010,'i',0300,0310,0350,0325,0247,0317,0263,'&','S','g',']',0232,'V',0240,0310,0344,0237,011,'J','H',0200,'2',0320,'b',047,0247,023,0236,'z','h',0336,02,0353,'L',0243,'%',0335,0377,0,037,0342,'$','X','O','Y',0336,0231,'Y','V',0355,'L','r',033,0344,'8','(',0361,0323,010,0311,0352,'u',0233,0307,04,017,0360,0234,'5',0260,'H','|',0303,0204,0331,0357,0340,0356,01,'P',06,0264,'l','A','y','p','9',0335,0351,0250,022,0322,'X',025,0213,'W','c',0216,0200,'d','+','*','a',')',036,'[',0333,'R',0263,04,0214,'T',0224,0213,'F','Q',0234,0226,0365,0353,027,'G',0321,047,'6',0266,0317,'5','1',0357,'9','C',0257,'<',0264,0244,'R',010,'O',04,'z',0352,0200,'(',0360,02,0273,0247,03,0366,0365,'!',']','V','-','8','9',0336,0226,'n',0364,'_',0251,0341,030,'s','>','d',0350,0332,0234,0203,'d','5',0230,0276,0345,0362,0275,'J',0303,0326,'X',02,'J',0202,0312,'4','w',0213,0227,0316,'f',0233,',',0367,0302,'n',':',0225,'o',01,'u','*',0200,'(',0330,0202,0273,0354,'?','o','R','F',05,'t',0321,0363,'N',036,'W','=','D',015,'g','V','d',0314,'.','N','=','4','(',0351,0237,'S','G','g','g',0254,'_','X','Q',0221,'D',0237,0314,0306,0250,'M','.','9',0230,015,'v',0206,0255,'/','e',030,0360,07,0376,0250,02,0213,030,013,0354,0340,0371,0327,0250,0225,0344,0354,'l',0335,0375,'T',0364,04,0253,0253,0326,0234,'u',0266,0216,'2','a',0230,0211,0314,'a','(',0266,'C','v',0346,'2',0356,':',0303,0134,0320,0215,0306,0215,'3','4',0271,'#',0334,033,'d','7',0205,0240,0244,'!',0376,0317,0336,0240,0334,' ',0376,0305,021,' ',035,0216,016,'=',0372,0214,031,0376,'B',0360,'T',0357,024,0257,0330,01,'P','a',024,'N',0311,'r',0200,'2',01,'m',0361,0240,0322,0332,0352,0355,'N',0314,0200,0217,017,'[','.','F','P','Y',0306,0216,0361,'e',0363,'#','H',0241,0220,0237,0261,0324,'r',047,0335,0377,0,022,'U',0336,'+','j','/',0370,034,'{',0365,026,031,'v',0324,'_',0215,0352,'m',0236,0200,'l','}',0224,'M','!',0240,0266,0356,'K',0365,0215,0242,0212,'2','z',0234,'&','G',0254,'=',0324,'2',021,'#',021,0251,0215,0313,'L',027,0247,0245,0244,027,'N',07,0330,'|',0244,0373,0237,'o',0372,0345,0330,0250,'s',0374,0,0330,0367,0352,'C',0270,'0','j',0273,024,0230,'[','F',0203,'o',0265,'$',0263,0214,'a',0215,06,023,0206,0205,02,'"','Q',0306,0345,'j','p',0337,0252,0316,030,0223,'&','N','J',0262,0327,0200,'L',0333,0312,'7',0357,0210,0253,0361,0223,0325,'l',';',0237,0372,0254,'[',0355,0247,'N','z',06,0357,024,04,'|',0320,0360,'l','u','#','p',0277,0203,0232,'@',0221,'k','h',0337,0273,0366,0342,0241,0261,0374,0371,'m','B',0310,0267,01,'q',0210,06,'C','p',0235,0347,'4',0243,'H',010,0356,'7',':',0250,'6','i',0256,'Z',0343,020,0356,0212,'G','`',0313,0210,0245,0306,'9',022,023,0270,0343,0355,'$',04,0262,0350,033,0264,'@','e','w',027,0343,0250,'G',05,'*',0324,0255,'S','f',0374,0277,0255,0367,'C','Q','[','3',0227,0224,0361,0333,017,0346,0215,0262,027,'5','6','M',037,0354,'u',0203,'!','[','$',033,017,'g',047,'8',0246,'n','K',0234,'1',0252,0324,0344,0373,')',' ','.',']',03,'v',0217,04,0276,0342,0374,'q',0324,'J',0220,'.',0256,0205,'=',0214,0276,0275,0336,'6','>',0362,'w',0343,0215,0304,0331,033,'?',0321,'z','}',010,'/',0276,'2',0255,'G','1',0222,0373,'O','Y',0342,0241,'-',0231,'4','H',03,0210,0302,'f',0263,0356,'P','t','=',0236,033,0367,'/','Y',0307,'_',0277,021,0320,'n',0320,0342,0225,0334,'_',034,'u',024,022,0342,0222,0333,0360,'j',0330,0343,0337,0357,0220,02,0244,'F',021,0334,'t',0251,'M',0276,06,0347,'c',0372,0322,'u','h',021,'$','z',0277,0221,'J',035,021,0321,'4','h','3',0226,0264,'b','Z',037,0243,0205,0332,0307,'X','#',0267,0220,'(',0314,'-',0335,'V',0357,'V','3',0213,'/','c',0357,0351,0340,'9',010,0267,'4','5',0223,0246,0250,0371,'B',0315,01,'+',0332,'M',023,'D','n','=',0372,0256,0304,0232,'7',0247,0305,0252,0214,0356,'w',034,'d',0323,'B',0213,0343,0247,'9','G',0203,0347,0312,0201,0240,02,'Z',0256,0263,0325,015,0200,015,033,034,0373,'R','%','.',0370,023,'H',04,'$',0274,033,0237,0211,'.','h',0322,'B',0314,'K',0236,0346,0344,0332,'}','N',0252,'M',0232,'6','*',0364,'p','n',0177,'g',0231,'|',0364,'A','P','f',0205,0271,01,']',0216,0207,'U','+','I',0372,'7','y',0330,0247,'N','U',0325,0327,0301,037,015,0300,0214,047,0235,016,'@','c','N','X',035,0271,'k',0337,0252,0234,0244,'r','5','e',07,'@',0325,0233,0271,'%','-','b',0335,023,0216,'b','c','}',036,0225,0213,035,'I',0270,06,0315,0271,'x',0367,0251,'?',')','W',0301,0206,031,'*','d','1',0231,0217,';',0217,'P',0355,'X',027,'D',0306,036,0312,'~','z',0232,'&',',',0352,'&',023,0316,0241,'@',0352,'9','?','N','G','R',0375,0,'D',']','>','E',0216,0242,0206,0237,'0','~',012,0231,0342,0376,016,017,010,0346,0232,030,0251,'k',035,0366,'1','m',0261,'4','k',0331,'%',0227,0220,0235,'H','F',04,0226,0210,'c',0231,'I',0362,035,06,015,'{',0362,0307,0307,'M',0220,0354,'j',0266,'*',0364,0347,06,0201,0261,0341,'a','#','v',0334,'M',0206,0245,020,0315,'M',0361,0227,'q',0306,'z','p','Q',010,04,'L',0251,0264,0366,0276,0257,'C',033,0370,033,0233,'Q','6','d',0304,0334,0341,':','1','m',0365,027,'b',0222,'<',036,0300,'|',0357,0341,0250,'A',022,'&','G','r',0233,0355,0341,0240,0317,0342,0363,0206,0204,02,'6',0350,0232,'b','m','N',0242,06,07,'7','{',0240,0364,022,0241,0352,'4','S',0372,014,0237,025,0345,0232,'Q',036,0264,'t','/',0223,0346,0254,0376,0256,0277,'G','_',0272,'0',0277,0223,0232,037,036,0233,0346,0234,0351,0302,012,0372,'R',026,0203,0330,017,0237,017,'q',024,0351,0326,01,'n',0374,0267,'k',0255,'$',0306,0260,'N',0212,0277,'u','{',0336,0317,'!',0322,0305,0312,'W','&','j','*',012,'2',021,'@',0341,'z',0265,0317,0210,0342,0345,'B','&',0311,0227,']',0366,';',0350,0324,0314,0326,'M','V',0311,0243,0320,020,013,0262,0275,0237,0227,0372,030,0210,0306,0231,'_',0201,0367,0253,'w','?','s',0334,037,0307,0371,0245,'6','X','x','R',0236,'h','z',0177,0242,032,'y','a',030,'}',0177,'X','i',0346,0222,0306,033,07,013,0363,0247,0372,'_',0322,0220,0231,')',0360,'B','j',0311,0352,'C','{',0264,01,0201,'"','a','?',0322,0376,'H',0332,0234,0332,0256,'6',0307,'j',020,036,035,034,0303,0263,0271,0365,'"',04,0345,032,0240,0205,0216,024,0365,0377,0,'H','R','K',',','#',033,0217,0276,'y',0240,0325,'`','F',0311,0213,0235,0366,0317,0244,0320,022,'I','G','V','d',0302,'2',0303,0213,07,0223,0247,0372,'L','o',0134,0,020,0217,'+',0330,0276,'1',0134,0177,0361,0217,0377,0304,0,'-',021,01,0,01,02,04,04,05,04,03,01,01,0,0,0,0,0,01,021,0,'A','!','0','1','Q','P','a','q',0321,020,'@',0221,0241,0360,' ','`',0201,0261,0301,0341,0361,'p',0200,0377,0332,0,010,01,02,01,01,'?',020,0377,0,0326,'c',0313,0212,'-',0316,0264,0246,0220,'P','n','t',0244,0350,'J',0321,'E',010,0342,'}',0211,'"','u',0261,'L','5',0376,0230,032,'0',0323,012,017,'G',0353,'Z',0206,012,0303,0314,037,0260,047,'N',0266,'*',0370,0362,0220,0305,0277,'T','"','I',0307,0247,'N',0266,'*',0370,0362,0304,0301,'z',0,03,0216,0316,0235,'j',0370,0362,0364,0244,0223,0361,0234,'{','E',0305,0345,0254,'T',0360,'=',016,'=',0242,0342,0362,0326,'+','L','z',031,'K',036,031,'P',0250,'G',0215,0210,027,0245,'G',03,0215,0214,0271,0341,0341,0215,0213,037,023,'w',031,0,'/','J',0216,'5',',','g','L','y',0253,015,0306,'{',0321,022,0274,06,'(',0227,027,0,'#',05,0251,'c','l',0356,0206,0264,036,010,0344,0206,0206,0301,0336,0336,'&',0352,0327,0212,'`',0301,0315,0233,'m',0375,0347,'k',0360,0321,0364,'a','Z',0237,'4',0241,0345,'x',014,'P',0317,022,0301,0203,0233,'?','^','D',0321,0364,0212,0244,0206,0201,0260,0357,'o',023,'w',020,0301,0203,0232,'?','^','C','W',0206,0213,0325,0372,0360,0243,023,'n',0324,'T',0257,01,0212,021,0341,0230,'0','r',0257,0203,'G',0223,0210,016,'o',0357,' ',0221,0203,015,'w',0232,021,'$',0360,'7','V',0274,047,06,016,'E','|',':','<',0247,0334,0271,'O','m',0332,0260,'#',07,'o',011,'M','*','L',037,'9',0243,0320,'A',0227,0212,0301,'A',0363,0217,'+',0236,0347,0216,0134,'V',023,0210,0336,0201,0222,'O',02,'Z',0371,0224,'Z','U',0350,' ',0313,05,'`',0326,0200,0377,0,0213,0312,011,' ',0241,0222,0207,03,'S','J','E','A',016,'c',0262,0305,'c',0254,037,0232,'x',020,0241,0237,',',0213,'J',0254,'"',0306,'X','+',06,0264,03,0313,01,'s',0300,0204,0230,'4',0210,0240,0304,0314,'I',0253,0250,'{',0320,0262,'I',0340,'n',0362,0210,0223,012,0260,0213,031,'`',0254,04,0264,'C','S',0314,04,'b','V',0265,0314,0224,0212,0202,034,0327,'e',0254,'E',0203,0340,'"',0206,'|',0212,'$',0302,0245,'M',',','e',0202,0200,0225,0243,032,0236,'h',0230,0264,'P',0211,'%',011,016,0273,0323,0321,0353,0315,'I',0253,0242,'{',0321,0262,'I','Z','Q',0273,'=',02,'a','S',0207,'K',031,'`',0240,'%','h',0314,0270,0257,'8',0343,'@',0315,0,0201,0205,']','B',0206,'s',0134,0226,'*',0302,'>',04,'(','g','1',0302,'a','S',0207,'K',031,'`',0240,'%','h',0314,0270,0257,'<',0223,'L','4',0360,0260,015,'#',04,'9',0251,'5',0202,'c','=',0350,0311,'$',0360,'7','e','8','L','*','t',0351,'c','-',030,022,0264,'f',0134,'W',01,'e','I',024,'<','5',0211,'j','o',0337,'9',0311,'b',0254,03,0340,')','C','?','[',0204,0302,0247,'N',0226,'2',0321,03,026,0267,023,0277,03,'I',0246,036,035,0206,0257,016,0271,0251,'5',0200,'c','=',0350,0311,'$',0360,'7','}','.',0223,012,0344,0253,031,'`',0203,032,07,'w','~',014,0313,'J',0322,0202,0307,07,'z','J','=','y',0314,'K',015,'a',0370,'_','j','D',0326,0205,'(','g',0301,0322,'a','I',0312,0266,'Y',03,0306,0260,026,0267,'x','B',015,'$','P',030,'$',0253,0202,'{',0320,0316,'h',010,022,'R',0201,0370,']','N',0215,0372,'>',0264,'l',0235,0375,'<',0,0251,0205,'#',0313,030,'<','j',04,'k','w',0205,0273,'<',025,0340,0237,0267,0315,0351,'T','8',0330,0347,047,'J','i',0205,'M',021,0243,'.',0370,0352,'4','k','w',0206,0244,0320,'A',015,'E','&',0375,0235,'+',037,'z',0227,':',0237,0311,'B','$',0236,'b',0372,0352,'4','k',0277,021,0351,'E','&',0375,017,0343,0265,',','*',037,0337,'M',0374,0265,0305,0361,'M',032,0353,'P',0202,'~','o','R','l',0207,0324,0357,0326,0211,'0',0323,0354,0261,'b',031,'W',0364,'=','J',0302,0270,'}',0236,0236,'D',027,032,0304,'@',0251,032,0177,020,0177,')',0336,0261,0244,'b','7',0317,'8',0333,0370,'B','x',0311,0206,0225,',','q',0373,'u',0255,'0','s',0364,0274,0356,0271,0370,0275,023,'$',0224,')','B','<','a','c',026,0213,'@','/',0220,'r','8','}',0236,0265,0351,0321,0331,0275,'"','Q',0273,0213,'i','_',012,0237,0353,0311,011,0206,0241,0311,0356,0134,0353,0271,'H',015,'#','B',0224,'K',0211,0254,'b',0322,'|','g',0372,0362,0263,'^','7',',',0326,'&',0303,'o','m',0352,0347,022,'P','%',0246,'_',0356,0376,0274,0270,030,'i',0243,026,0316,0356,0364,0,030,0217,020,'P','%',0246,0177,0350,0376,0274,0324,0262,0341,'q',0323,0372,0375,'T',0371,0343,0265,'C',0303,'T',022,0323,0277,0364,'e',0202,0240,'1','k',014,'%',0244,'1','p',0332,0200,0314,'f',0240,0340,0323,030,'p',0246,0211,'o',0233,'V','1',0202,0332,0307,0256,0315,014,0223,0302,0300,'N',0212,'w',07,0366,0314,0217,'}',0355,0343,0212,'<',0204,0327,0374,0345,'P',0213,0243,'g',0267,'L',0324,022,032,'@','C','M','~','i','s',0247,'j',0225,'}',0352,'g',0204,01,'S',012,'G',06,031,'e',0212,0321,'X',0333,0227,0322,0340,'$','u',0257,'@',0233,0235,'7',0351,0351,0265,0,0221,0316,'1','T','|','k','J',030,'?',0277,'N',016,0211,'0',0244,'s',030,'B',0350,0177,'?','^',0232,'Q','9',0177,'C',0327,'z','P',0300,'~','i',0274,0377,0,0271,0304,'@',0371,0312,0233,'1',',',0270,0353,037,0272,02,'h','<',021,0302,'a',0134,0205,0266,'d',0377,0,0365,'e','D','9',0376,'+',037,0353,'.','u',0357,'S','0',0231,0323,'e',0205,0316,0333,'T',0240,'q',0271,'r',0241,011,0340,'.',023,012,0307,'Z','m',0230,0356,0303,0336,0200,010,'2',0336,'T',0273,'w','[','?',033,'Q',')','4','K','g',' ',021,013,0367,0345,'C',034,'+','v','z','v','x',02,04,0302,0247,'N',0226,'3',026,013,032,0320,023,'C','7',012,023,034,',','M','j',0370,',',0331,0371,0266,'p',032,'1','R','w',0134,0353,0271,0317,0375,0240,'-',07,0316,0310,0212,0221,':','X',0314,0365,020,0320,015,017,'!',04,0244,0347,'X','b',0373,0247,'r',0210,0264,034,0351,0303,0302,0347,'m',0252,'D',0351,0251,'q',0363,0212,'=',04,036,'|','R',015,'*',0320,'7',',',0366,0251,0205,010,'K','g','1','H',011,0362,036,'U','7','D','{',0364,0355,0257,06,0300,0,026,0243,'Q',0243,'[',0371,0134,'U',0200,0321,0332,0240,027,'F',0317,'g','4','H',0223,025,0211,0263,0346,0233,'o','O',05,0344,'Y',0371,0347,0317,'J','1',0323,0201,0300,0300,012,03,'Z',',','m',']','|',0274,';',0223,0235,'`',023,0263,0252,'w',0242,'!',0243,0232,0223,0255,'b','I',0367,'N',0234,0271,'P',0207,0223,0201,0,0303,012,0343,0333,0315,06,0200,0334,0263,0331,0347,'R','!','B','j','_',0347,'<',0351,'?',0324,0263,0331,0347,'^',0247,013,0235,'~','G',0,'P','/',0235,0177,'^','r','=',0245,0215,0350,0321,0364,'l',0377,0,'|',0263,0211,0210,0205,0377,0,0212,022,04,033,'v',0336,0271,'g',0316,0235,'j',0342,0363,'t',013,0241,0347,0240,024,0225,0216,'I',0367,0235,0316,'t','"','I',0233,037,'d',0321,0332,0237,05,0344,'l',0377,0,'t','f',0312,0235,'l','T',0343,0307,'6','u',0233,'s',0340,021,'$','4',0221,0314,0304,0355,0265,022,'R',0346,'h',010,025,012,0223,'k','s',0257,'.','t','3',0215,0262,0321,0324,0343,'[','6','U',0207,0275,04,'a',0300,'`',0243,0244,0303,'u','A',030,'}',0236,0237,047,'9',021,0253,0366,'{','Q',0216,'N',0224,0262,'X',0323,0276,'j','c','h',0371,024,02,015,'8','$',04,0223,0346,0224,0247,0340,0267,':',0367,0365,0315,01,016,0224,0362,0341,0247,'N',0371,',',027,'?','Y',0261,'&',0227,'h',0306,0201,0301,0220,'p',0212,0134,'<',0334,'m',0323,0267,0245,'B','B','3','$','F',0271,0211,0363,0235,011,0322,'~','F','D',0277,',','3','>','f','(',0327,010,'&','(',0236,0227,0265,0353,0336,0260,'1','F',0317,0226,0347,0231,':','h',0222,'u',0276,'D',0203,'g',034,0263,032,0334,'2','D',011,0324,0321,0332,0236,'?',023,'g',0263,0313,0323,',',0316,0325,0355,0177,'j',0233,0375,'c',0276,0265,'@',030,'r','Q',0201,'+','A',0316,'u',0341,0261,'J','J',023,'V',0345,0316,0273,0234,0350,'D',0223,'$','A',032,'u',0331,0207,0246,'@','p',0315,013,025,'=','i',0310,'1',0341,033,037,'J',0307,'g',0322,0246,0244,0246,0340,0320,';',0273,0360,0364,0232,'u','n',0377,0,0310,0355,'X',030,'a','~','Y',' ',033,'L',0372,0345,0230,'i',0343,03,'P',0333,0211,'N',0364,'K','G',0366,0353,'B',0222,030,'M','L',0200,'7',0304,0377,0,037,'a',0330,013,033,0374,0345,'L',05,0216,0366,'y',0235,0276,0270,0205,0272,'?',0303,0366,'"','r',0222,0224,0207,'/','G','n','O',0177,0251,'`',0357,'?','b',0201,'B','J','5','%',0333,'s',0247,'.','T',06,0307,0320,'U',016,0177,0257,0261,0264,0304,0251,'t','m',0313,'=',0232,'$',0245,0317,020,011,'p',0177,0217,0261,0371,0326,'$','b',0304,0326,0237,0207,0362,'h',0366,0374,0320,0211,'%','2',0212,047,016,0213,0276,0277,'d',0240,0220,0322,0230,031,0371,'i',0212,010,'0',0364,0377,0,0214,0177,0377,0304,0,'+',020,01,0,01,03,03,02,05,05,01,01,01,01,0,0,0,0,01,021,0,'!','1','A','Q','a','0','q',020,' ',0201,0221,0241,'@',0261,0301,0321,0360,0361,0341,'P','`',0377,0332,0,010,01,01,0,01,'?',020,0377,0,0351,'Z','1','x','7','Z',026,01,0374,'6',',',0324,0332,'r',0354,'6',024,07,0336,0235,'M',0231,05,0336,'a','S',0327,0232,'~',0230,0253,0305,0313,0226,07,'3','F','(','N',',',0336,0363,'F','#',',','Y',0275,0312,0214,024,0376,'8','(',0327,0244,022,0366,0250,0364,'6','Y','O',0265,011,0221,'j','?',0346,0247,'R',0366,'O',0251,'u','c',023,0211,0243,'g',05,0,'B',0347,0345,03,'?',024,030,0231,'I',025,'"',0177,0354,'(',0244,0,0311,'2',015,0245,'/',0241,'@','$',04,0315,'v',0320,0366,0240,'p',0220,0177,'P','7','T','_','9','$',';','1','A','A',0260,0250,'{',0276,'I',0363,'M','K',0275,'K',0275,'h',0216,'g','{',0303,'Y','0',021,0307,0200,0346,0210,'8','t','?',0260,0236,0365,04,0253,'`',0356,0214,'G',0263,'B',0246,05,'l',0357,0201,0355,'C',0314,0377,0,0350,0250,'(','x',0344,0210,'}','T',0253,0360,0366,0,014,'"',0254,'S','W','5',02,':','2',0270,0306,0230,0254,0231,03,027,'p','l','z','x',0317,0320,'M','M','=','F',04,0300,0134,0230,'o','Y',047,0204,0304,0320,0205,0300,0342,0222,0262,'Z','2',';',0307,023,0265,0350,'L',010,0204,'=','a',0267,0257,'S',0232,0204,'C',0346,0247,'q',0366,0177,0263,'Q','I','|',0202,0212,0230,034,0227,0346,0277,0244,0374,0327,0310,')',0374,0326,0264,'z',01,'|','?','V',0242,0222,0275,0231,0344,031,0203,'/',0245,':','@','O','<','N','v','3','{','%','8','*','d',0300,0344,026,'#',0274,0324,0322,0255,'O',0323,0252,0353,'S',035,0204,0374,0222,0216,'0',0353,'Q',0251,'`',0270,0345,0370,'G',0275,'I',0304,0203,034,'Y','H',0366,0242,0376,'y',016,'I',' ','(',0367,'F',015,0277,030,0371,0245,0310,0331,']',0232,0217,010,0250,0241,'`',0247,'f',0233,025,'H',0261,'(',0360,0,0201,'4',0366,0246,011,0326,0345,0236,0261,'5',0213,0375,'R','O','W',0356,0206,'[','[',' ',0354,0232,'?','K',0205,'>',035,0,' ',0316,'s',0351,'M',0226,'f',0340,0256,'O',0335,0342,034,0324,'G',0374,023,'R',014,'$',014,'o',032,0324,0273,0324,0375,'d',0245,'<',0332,025,'S',033,0230,0365,'Q',0330,0262,'!','h','N',0247,'s',0342,0203,0306,0201,0341,';',0231,'=','|',0251,0332,0216,0300,012,'K',0266,0244,0257,0342,017,05,0207,'D',0260,012,'j','(',0302,'1',0356,'s','H',0261,'n','I',0370,'*','N',0206,010,'?','4',']',0203,0331,0276,0372,',',026,020,'3','C','8',0277,0220,'$',' ','w',0272,'9','7',0242,0242,'.',0264,'?','F','N',0200,']','[',01,'@',0272,0230,0346,'-',0243,0306,0226,'/',0336,0231,'%','J',0223,02,01,0263,0202,0354,0336,'b','*',']',0352,'z','E',0332,' ',0242,03,0201,'1',0270,0254,0224,'l',0177,'0',0342,07,'d',0245,'e',0306,' ',07,':',0250,0362,'g',0373,'e','^',0311,037,0236,024,0206,0325,'D','r',04,'?',025,03,0231,0226,'m',0354,0363,0351,'D','0',0344,'$','7',0221,0240,047,'O','Z','m','$','t',0345,0240,0262,05,0357,06,'7',021,0274,'k','Q',0342,'-','H',0260,010,0324,'v','w',0266,0364,'(','0',' ',016,021,'2','x','"',035,';',0,'+','a',0232,05,0237,06,0307,0201,0260,0260,034,0273,024,0321,0205,0202,'l',0376,0371,0362,0207,'q',0341,'!','(','t',015,0321,'_','t',0324,0202,'>',0340,'}',0250,'k','s',0260,'z','a',0240,0340,0243,022,';',0207,0203,0277,'f','E',0334,0365,'g',0350,'T',026,0251,0242,0262,'8',0272,'d','*',0332,0333,0323,'k',0216,0207,'T',0303,'%',0330,'/','s',011,'K','`','T',0245,'N','U',0326,0225,'z',02,'Z','B','"','J','H','v','V',0245,'Y','P',024,0254,0245,0360,'4',024,'9','=','G',0277,0303,0356,020,'Q','g',')','w',0271,0360,'%',030,030,0315,0310,0356,0301,0232,0370,025,0217,0267,0237,0340,'7','~',0364,'@',0262,025,035,0304,0250,'b','D',015,'N',0354,'=','J','a',0271,02,0306,0313,0376,0252,0275,0351,0206,'"',0301,0331,020,0354,'P',0226,'~',0220,'Y',0231,0,034,0222,'R',021,0321,025,0203,'M',0251,0362,0360,'3',0254,'/',0266,0346,';','b',0215,0306,'w',0325,022,0242,0342,'Z',0264,'4',0202,0313,'5','q',0261,0340,011,0260,0214,0273,024,0215,0225,04,0331,0375,0364,0236,'0','p','t',0207,017,0243,'@',04,'d','n','4',0314,'E','-',021,0243,02,'"',0335,0213,0203,0371,0353,0250,'*',0314,047,0333,'7',015,0241,0231,'v',0265,0320,'g',0330,'*',0206,0217,'u',031,'W','e',0301,'j',0231,0251,'_','0',0226,0246,0325,'d',0226,'`',012,0372,'R','n',0260,0271,'H',0231,'D',0271,0302,'T',0354,'b',0270,'L','.',021,0234,025,'C',0251,0303,0216,' ',0265,'@',0324,'T',023,0323,0202,0240,0332,0240,0245,0367,0241,0341,0263,'%',035,'p','3',034,0322,'B','N',0301,0346,0214,0302,'P',0243,047,'T',0224,032,0222,'t','*',0353,'w',',',0233,0300,'1','f',0375,0,'^',0260,0245,'V',0206,'q',011,0231,0200,'5',016,'|',011,'1','-',0251,0320,')',0,'m',015,0261,0375,0363,0323,'c',0305,015,0211,0262,0354,0373,'P',020,' ',010,017,03,0225,020,05,0340,0261,0360,035,'i',0205,'`','2',0322,'T',02,027,0346,047,0330,014,'k',022,'J',01,'c',0367,0267,']','6',013,04,05,0251,'W','/',0230,0315,'5',0341,010,0203,'!','(',0304,0222,'T',04,0335,'(','U',0,'@',013,'1','s','D',0222,0321,')',032,0357,03,0217,0206,'B',0365,'j',015,0250,06,0224,021,0364,'P','R',05,07,0374,'`',0306,0330,0334,'y','/','Q',0355,0246,0351,0265,0202,0370,0233,011,'.','J','^','"',0202,'`','D',0270,0333,0227,'O',0224,047,0264,0211,'S','B','&',0353,0222,07,0372,'_',03,014,'[',032,0255,0,0336,0234,025,0310,'-',0217,0357,0236,0233,0371,'l',02,'U',0245,033,0361,'d',0263,'i',0313,0340,031,'*',0344,'h','-',0337,'j',')','`',0213,'@',' ',0352,0233,'1','J',' ',015,0352,'{','j',0246,'G','q',':',0216,0301,0202,0363,014,'c','N',020,'e','U',0272,0256,0264,0253,0237,'(','K','R',0305,0227,0220,0300,02,0257,05,020,0205,01,'I','I',0272,0341,'N',0354,'9',032,'>',0354,0,0214,'D',0254,015,'V','j',012,0217,0247,0212,0202,0201,03,'B',0345,'h',0213,0204,0311,'@',',',025,'8',03,017,0360,0226,'r','S','E','K',0224,014,'a',0311,'k','%',0235,'<',014,024,'D',0224,'h',0200,'U','r','B',0374,0333,0276,0,0270,'v','5','Z',01,0275,'2','u','0','-',0217,0357,0236,0233,0257,030,011,'V',0201,0370,'J',0334,037,0277,030,0217,0350,'=',0306,0314,'t','#',035,'S','d','i',014,01,0252,0324,'j',0220,0312,026,03,033,06,'.',027,0271,'l','T',0317,0224,'K',024,0337,'#',0232,'4',020,'[',0264,'Z',0315,0312,0177,032,' ',0315,'i','4',0332,',','F',0371,0250,'(',03,07,0326,'%',0266,0245,0220,0340,0206,0272,0203,'%',0211,033,'1','z','+','q',0332,'H',0300,0267,'W',0302,0326,'W',012,0233,012,'U','%',037,'&',0357,0200,037,017,0252,0320,015,'Z','L',0212,0320,'[',015,0335,0336,0232,031,'8',')','V',0216,'r',0310,'7',016,0307,'<',0370,04,0260,']',0245,'m',0202,017,0276,0321,0333,0212,'0',0,020,01,0,'y',0211,0231,0345,'~',0,0325,0257,0312,'U','C',030,')',',','+',0240,0237,'j',032,0233,'c',0205,0270,'B',0315,0,0202,0222,0330,'=',0267,';','x','I',0340,024,0372,'C','6',013,027,'&',027,'C',05,0213,0334,'[',0355,'J',0271,'|',0243,037,017,022,0300,05,0325,0332,0235,016,010,0303,0310,0255,027,0375,'N','(','4',0224,0221,0316,012,0,0323,0353,0306,021,0225,'H',02,0210,0376,0312,'<','N',016,017,0277,0201,0377,0,047,0252,0320,015,'Z','x',0353,0220,033,015,0335,0336,0233,0326,'h',')','V',0207,0263,0367,016,0307,'<',0370,' ',0,0253,'`','5',0256,037,0215,0306,0303,0366,'(',0,01,01,0200,0363,0204,0202,0365,'^',011,036,0227,'{','x','h','m','W','_',0301,0351,0365,0320,'8','h',0371,0326,0207,0271,0333,0371,'*',0354,0315,0300,01,'V',0257,06,0331,'q',013,0360,0344,0340,031,'U','r',0255,'O',0220,0315,06,013,0244,'E',0325,0330,'7','h',0253,0330,0243,0335,027,';',0232,01,06,0304,'E',0,'i',0365,0346,0330,'R',0246,0,0245,014,0245,'8','V',0243,0371,'>',01,0374,0322,0256,'W','c','v',0232,0242,0304,0222,0303,'w','w',0247,06,'d','4',0253,'E',0301,'.',0344,';',034,0363,0340,0355,0225,0,'%','Z','%','N',04,0311,0260,'7',0342,0261,0320,0376,'&',0347,0200,0317,'l',0342,02,01,0332,0330,0305,'1','<',0300,'V',0376,'5','=',0250,027,'l',0312,017,0201,'P',0243,036,037,'r',0222,0213,0371,03,0342,0307,0177,' ',0226,0201,'i',0213,'&','u',0337,'A','B','m',0327,'@',0353,0264,'q','@',025,027,0237,0243,0236,0243,0204,0,0225,'p',024,0316,024,0277,'w',0252,'m',0273,0257,0201,0317,024,0253,0225,0330,0335,0244,0362,0271,0271,'a',0273,0273,0323,0215,0201,036,'U',0241,'O','p',01,0376,'/',0340,0213,0314,012,'U',0330,0242,'(',0300,'R','s',0177,0221,0322,0376,036,0345,'8','k',0372,0233,0274,047,0331,0231,0274,0356,0237,0234,0321,'l',022,0322,0335,0345,0257,'l',0370,04,0207,0203,0220,'l',0225,'r',036,']','d',0315,0267,'R','b','R','"','y',035,'J',02,0336,0214,'5',04,'&','u',0337,'@',0256,'L',010,01,0246,0203,'B',0240,0231,0372,'h',0216,0220,'2',0,']',']',')',037,'T',0265,'w',0253,'m',0273,0340,'i',0307,'*',0327,0203,'w',0212,0277,0264,'7','1',0313,'w',0355,0323,0217,'q',012,'U',0242,0347,0270,020,0177,027,0360,04,0200,026,'T',0350,'Q',':',031,'[',0207,'M',0352,'c',0344,0246,0221,0314,0237,'q',0342,0200,0212,'%',0304,0322,0255,',',0306,'@',0363,0263,0346,0260,'K',0265,0370,'v','|',02,05,030,0360,0373,0224,033,'u','}',0211,0,'4',0357,'G',0250,0312,'B',031,'t',037,'4',03,026,0377,0,0300,'P','*',0300,'e','i',0232,'@',0254,'z',0233,'m',0337,0,'*','2',0237,'c','v',0256,' ',0362,'c',0356,'?','o',0277,'I',0336,0224,025,0326,0200,030,0134,'G',0370,0277,0201,0202,0320,0262,0252,0235,'=','.','H',':',034,0363,0324,'m',0224,0277,'>','}','#',0353,'Z',0227,'=',0222,0212,'h',0326,0236,0357,'/',0307,0200,'%',0242,0260,0233,0215,']',025,0261,0356,'v',0237,'r',0212,0232,'B',0201,0337,0353,0324,05,'X',012,'u',022,'%',0214,0356,0337,'w',0300,014,0234,0247,0330,0346,0245,'W',034,0277,0303,0366,0351,0272,0222,0201,'Q','3',03,017,0204,'A',0372,'j',0,0325,'h',0134,'0',0342,0325,0240,0310,0237,016,0251,06,0363,0346,037,0277,010,'7','/',0261,0346,'@','R','%',0304,0253,032,'0',032,'_',0311,'Z',0235,')',0134,0341,'4',0360,'6',0224,'J',047,0261,0263,0311,'B',0214,'I','T',036,'v',';','y','R','Q',0333,0352,026,012,0325,'M',0330,0306,0355,0367,'i','e',0227,'4','s',0316,'h','-',022,'y',0363,0371,'>',0335,'4',0376,'Q','@',0240,0205,0203,0374,036,010,'=',0261,' ',')','G',04,01,0204,'>',015,0212,0204,0333,'#',01,033,0274,'H',0273,0320,'r','c',0262,0225,0243,'a',0220,'M','#',0251,0337,01,0361,0340,'0','?',0313,':',03,0365,'D','>',0202,'k','W',0354,'l','+','w',0207,'J',020,'H',0311,0271,'H',0227,05,02,'n','5','y',0366,0307,0263,0267,'^',0345,0373,0320,0356,'8','r',035,0372,0223,'~',0274,')',0227,',','z',0233,'}',0332,'U','U','e','u','h','O',0234,0225,'2','T',0363,0177,0243,0355,0323,'M',0245,037,'p',0361,'G',0340,',','X',0374,036,017,'x','-','@',037,0272,0262,'-',0363,013,'?',0260,0361,'~',0203,0221,' ',013,'2',0330,011,0202,011,026,'Z','9','O','L',0337,0376,'c',0303,0333,0367,0301,0321,0305,'&',0364,'-','=',0236,'_',0212,014,0206,'.','[',0274,'x',02,'J',047,0341,0275,036,'K',0324,'A',022,'S','v','2',0255,'O',0236,0234,023,':',0364,0302,'<','@',0236,0241,'k',0326,0333,0356,0322,'"',0252,0267,'V',0200,'I','I','b',0230,0242,'=','4',';',0222,0200,'}',0335,0212,0224,'X',030,0377,0,0300,0360,'h',0264,'O','<',033,0255,'?','l',0300,0307,0347,0276,'M','>',',',0323,07,0264,0230,0250,024,0260,'2',0334,0217,'C','}','z','w',0373,03,'8',0220,0377,0,'5',0360,'Q',015,0321,0241,0377,0,017,0267,'O',030,0230,'X',0250,' ','`','>','h',0322,0213,011,'H',0222,'4',010,'P',0354,'(',0303,'Z','6','`','B',0360,0325,0316,';','Q',0254,'y','9',023,'q',0361,0217,0243,'#','0','/',0343,0252,'o',0316,0224,0214,0212,'2',0256,'Z',0235,0311,012,0301,'K',031,036,0347,0370,0267,'M','4',0252,0,0323,0227,'b',0206,0200,03,016,'x','6','<',031,0203,']','{',0344,0246,'n',0367,'K',',','"','-',015,0266,013,0276,'l',07,'K',020,033,027,0314,014,033,0225,0337,0245,027,0303,022,'c','y',0350,0205,037,'(',0243,'j',03,0330,026,'y',0336,')',020,05,0307,016,0347,'P',0302,0244,0270,0237,'F',0325,0277,0352,0277,0350,'x','>',0204,'t',0203,0225,0252,0211,0214,022,0230,'^',0332,0234,0237,'H','-',04,0243,' ',0330,0377,0,012,'r',0310,0225,'Y','V',0217,'r','I','@',024,0335,0371,'t',0377,0,0213,'t',0323,'{','4',0300,'n',0354,'P','C',011,'p',0272,0354,'l','x','<',0300,0242,016,0371,0347,'c','Z',0273,'1','q','w',0313,0370,0251,0277,0177,'!',04,'L','p',0337,0322,0214,'a',0230,'a',0325,015,0355,'i',0334,'|',0252,0335,'"',0341,'R',0313,'a','#',0302,0324,'t','e',0264,')',012,'&',0214,0351,0321,'X',0245,0315,0222,0,'7',015,0255,037,'4',0252,'[',0353,'S','^',0375,'v',0315,034,'d','+',' ','p',0215,'A',0224,0337,0324,'"',0240,024,0366,'1',0244,0235,'E',0235,0262,0270,'J',0264,0374,'A',']',0331,0247,'z',02,'a','"','H',0323,0374,')',0200,'J',0323,'A',016,';',']',036,'J',017,'G',047,'"','n','?','B','H',0215,'/','!',0330,0376,'E','"','g','J',0222,0255,0,04,0222,0200,')',0372,033,'e','[',0277,']','4',0346,0277,'A',0273,0305,02,020,0362,0245,0326,0306,0307,0202,'E','X',0223,0376,0247,'b',0212,'e',0362,0367,0311,0340,0342,0247,014,'D','E',0316,'c','v',0256,01,0363,'&','I','8',0234,0211,'|',0301,0303,04,'t',0134,'E','B','Q','d',04,0257,'#',022,'?',017,0202,0255,'$',0314,0254,'&',0355,'(',0313,02,'D','d','J','$',0304,0244,'I','V',0316,0343,0265,'7','F','Q',0270,0356,'=','W',0346,0215,0325,0277,'J',035,07,'/','#',0367,'x','4','5','s','G',0253,'F',0207,0,0211,0134,'z',':',0234,0235,'q',0366,'y',')',03,0241,0374,0212,0177,015,'P',0225,'w',0243,'`',0222,'P',05,'(','e',0266,025,0273,0372,0351,0270,'d',0335,0320,'n',0273,'Q','0',07,'*',']','~',0274,011,'!',0344,'+',';',033,0360,0242,'F',';',0372,0222,'j',0371,07,0205,0325,'6',' ','c',0206,032,'l','Z',0242,'!',0224,0345,'K',0327,0310,'f',0234,0371,0203,013,0204,'-',0357,0355,'4','D',0302,021,'0',0235,011,0355,0243,'p','6',017,'X',036,0264,0302,0272,0311,0270,'A',0354,'A',0342,0241,0242,047,';','C',0327,0226,0224,033,0374,0244,0203,0204,'h','D','6',035,'M',0307,'F',0210,0274,0220,'.','x','~','q',0333,0252,0373,'f','W',011,'V',0207,'h',05,0236,0315,';',0321,0267,011,'D',0211,'L',0210,0246,011,'*',0313,0255,0213,'w','n',0235,0350,026,0314,0310,011,0303,0324,0212,0311,031,' ',0177,0235,0251,07,0212,0224,0252,0352,0320,'f','I','(',02,0233,0263,'X',0302,0367,'x',0343,0246,0341,0263,014,';',0255,021,'!',0245,0313,0277,0256,'<',011,0300,'t',0307,0372,'?','j','%',0207,0,'e','j',0256,0253,0345,'p',0264,':','Q',0260,'B',0247,0253,'"',0360,'y','J',01,035,'@','2','"','a',032,';','G',0331,013,'u',0270,0221,0347,'P','Q','7',010,0230,'p',0216,0324,0323,0344,0210,0205,017,0223,0273,0207,0265,'G','t',030,0304,0222,'#',0222,0231,0211,010,'D',0221,0242,06,0216,'p','~',03,0306,';','R','&',010,034,'#',0325,'*','_',0356,'.',0355,0263,'R','x',0323,'I',0373,0216,'O',04,012,0355,0344,0376,0206,0214,0204,0213,0230,'N','7','9',':','Q',02,'c','$',017,0363,0265,'#',07,'R',0225,']','Z','8',0315,'%',0,'S',0300,'k',030,'^',0357,034,'t',0347,0204,']','}','S','G',0210,'R',0345,0337,0327,036,01,0206,0354,017,0373,037,0265,'A',0343,07,0362,0356,0371,0234,'P','c',0311,0373,01,0322,'b',0374,'Q',')',0355,0345,0207,010,0306,0345,'w',0231,0274,0355,'0','h','z',0333,0261,0367,036,0324,0263,036,'@',0200,0275,022,06,'g',0324,'T','A','f',0301,02,022,0322,')',0322,022,'T',022,'|',0232,0234,0225,0212,']','e','[','Y',0303,0271,'W',0355,'H',0376,'8',0361,0325,'e',0303,'+',0204,'h',0234,026,0300,'g',0263,'N',0345,016,0305,0225,0310,0224,0324,0322,'b',03,'V',015,'h','[','=',0272,'w',0242,0345,0323,' ',047,0177,'<','~',0204,027,03,0253,0366,024,0371,0220,0274,0251,0325,0243,0340,0352,'P',05,',','`',0354,0275,0336,'8',0351,0317,0345,'n',0307,'p',0324,0360,013,0261,0334,'>',030,'"','j',0205,037,0302,0321,0345,0200,0260,07,'B','R',034,0232,0204,047,0263,'K',0211,0244,0221,036,'1',0260,'{',036,']',']',012,03,'=',020,'=',0336,'{',0303,0202,0364,0362,'b','J','I',0340,0362,0314,'|',0350,0232,011,'N',0214,03,0276,0206,'i',0,0211,'#','d','j',0357,'$',0204,0206,0344,0321,0346,0234,012,0340,0330,'n',':',0224,'3',0266,07,'%','E',0202,'K',0227,0261,0263,0347,0275,'"','0',0331,0352,0224,'$','Y','U',0335,0266,'h',0245,0210,0276,0201,0333,'s',0223,0300,031,031,0276,0227,0333,'f',0215,'"','K',0210,'n','7',';','y','t',0337,0222,0340,'u','s',0261,'N',0244,0316,0312,0235,'Z',03,'&',0245,0,'S',020,014,0341,'[',0274,'l','t',0347,0363,0252,0235,0303,0372,0251,0373,0253,0247,0250,'|','%','w','K',0331,015,016,'7','h',0307,'H','X',07,'K',03,'=',0313,'^',0336,0241,'<',0245,033,0252,0260,017,0316,017,'Z','`',04,021,0260,'?',0237,'0',0311,'I','y',0213,023,0345,024,'j',0302,0343,'e','?','/',' ','M',010,'i',0271,'N',010,0363,05,02,'<','c',0204,'-','Y',0267,'Z','T',0315,015,0246,027,037,0315,'f',0215,0224,'I','P',0206,0341,0277,'4',0226,'S',02,0303,'q',0324,0352,0247,0247,0225,0302,'<','4',0331,02,0300,'>',0337,0311,'@',0354,0345,'r','%',':','H',0230,0204,0357,'P','v',0342,0375,0277,0222,0210,'%',0134,010,0357,0341,0277,'S',0,'_',0223,'b',0236,0316,0235,0225,':',0321,0257,025,'(',03,'z','R',0211,0272,'Y','[',0274,'q',0323,0265,035,'u',';',0207,0365,'W',0356,0345,'}','C',0340,0206,0226,'I',011,'@','"',01,'`',06,0207,'I',0305,'+','}',0244,0225,0270,0236,01,'>',0236,'c',0320,0330,0202,010,'O',0267,0231,0257,0344,0374,'0','X',';',0212,0256,0217,' ','R',';',0210,014,0200,0367,0217,'O','(','6','4','!','"','T',02,'d',0246,0317,0306,0357,0212,'G',0251,020,017,0375,0245,'e',032,'6',']',0306,0211,05,0233,035,0306,0303,'N',0370,0353,035,'e','Y','t',0366,0266,'y','(',014,' ','Z',0201,0311,0271,0315,'E',02,'Y',0233,0251,0355,'l',0366,0242,'f','-','a',0325,'8','x',0247,'-','F','e','M',01,0361,'P',0200,')',0225,0234,05,0225,0273,0307,035,'8','"',033,0261,'g',0373,'J',011,0,']',0275,'S',0340,0304,'J',012,036,'o',0362,'h',027,0210,024,01,0261,0324,0267,030,0306,0260,0244,0207,025,0207,0226,'s',0367,':',021,017,'D',0363,'$','I',';','/','"','|',0266,0252,012,020,'t',0345,0210,0227,0233,'y',0330,0215,'&',0211,'8',0177,024,0341,'U',0322,0216,016,0215,014,'c','B',022,'4','`',0321,0316,0203,0370,'v',0244,0360,0360,'8','G',0253,0200,030,'<',047,0255,012,'$',',',022,0213,'k',0377,0,'7',0255,01,0320,0230,0360,036,04,0224,'@',024,0377,0,'_',03,'e','~',0272,'k',0212,0351,0313,'?',0276,'(',0200,01,'w','.',0357,0203,015,012,023,016,0300,0333,0212,'6',0306,0200,' ',016,0251,031,'*',07,0220,0374,0325,0206,0336,0370,'#',0340,'<',0262,0300,'3','h','F',0317,0273,0315,'(',0256,'P',0366,036,'P',0277,033,022,'#',0217,'q',0253,'q',037,02,07,'@',0375,'l',016,'C','d',0247,0376,034,']',0322,0337,0212,'N',014,0207,012,0331,'(',021,0257,0200,'N',0301,0337,'4',0262,'*','`',0270,0275,'z',0317,0264,015,0345,0255,'D',0374,0323,0332,'%',0334,017,'y','P',0270,0202,0360,0251,'m',015,0300,0371,0351,0230,'c',0245,0313,037,0335,030,'#','r','=',0326,0201,'@',022,0270,012,0341,0370,0314,0354,'?','b',0200,0,0,0260,032,'u',0300,'V','.',04,036,'_','$',0220,04,0213,'#','/',0333,0346,022,'%','k',0244,'I','>',0317,0224,0313,'_',0355,0304,'}',022,0372,'Q',0320,'I',0250,'T',013,0307,0273,'6',':',0237,'4',0235,0306,0352,03,0207,0361,'D',035,0310,0224,0271,0332,0215,'f',0266,0343,0325,0177,0276,')',0340,':',01,010,0375,'@',0243,'S','*','X',0376,0370,0242,'&',013,0272,0255,0327,'z','0',012,'0',01,'v',0204,0233,'`',0373,'m',035,0366,'(',' ',0203,037,'@',':',0323,'@',0311,'D',0374,'<',0267,0200,'=','w','G',0343,0314,0254,0262,04,'m','7',0374,0371,'Z','}',0300,0240,0273,0357,024,'2',0275,'$',0232,':','M',022,03,'Q',0270,0322,0213,0227,0330,0371,0247,0202,0310,'(','N',0364,0250,0200,'X','H','=',0342,0234,0254,0226,'?',0226,0337,'L','$',0224,0212,026,';',033,0264,'O',0307,'w','U',0252,0272,0264,0374,'[',01,'*',0354,'P',0304,'@','>','M',0216,0357,0263,0350,0200,0365,0274,'l',0310,0376,0351,0345,0216,0330,0371,0207,0346,016,'r',0346,'}','S',0313,'a',0346,0274,'w','t',0322,'j',')','#','+',0227,0217,035,'{',0346,0237,' ',':',0240,0334,0242,033,'<',0201,0217,0325,'5','-','.',0273,'G','o',0357,'j','b','h',0263,032,0337,'F',' ','W',022,026,033,033,0264,017,0313,0352,0265,'W','V',0203,0262,07,0225,'8',02,0206,'y','C','H','~','~',0317,0242,'h','v',0205,0355,0345,'l',',',0306,0257,'7',0317,0313,0330,0274,0267,'B','?','%',0353,011,'4',')',0202,020,0307,'&',0315,'F','[',0354,0220,0343,'e','"',' ','a',033,047,0245,' ',01,0334,'8',0277,'5','>',01,'e','"','}',016,027,036,0315,0261,0313,024,'z',0311,0203,'+',0273,0273,'G',0375,'F',0312,0235,012,0237,0247,0222,0340,0350,'}',0317,0322,06,0251,0363,0377,0,'O',0226,021,0313,0346,'l',0334,05,0216,0361,'o',0307,0225,0216,'f',0223,'C',011,0356,036,0324,'u',0222,'i',0225,021,0252,047,'"',0342,0373,0346,0237,0271,'[','$',0226,0353,'Z',0313,010,014,016,0316,0364,07,'$',0222,'@','k',0263,0373,'J',015,023,02,'$','W',0207,0333,0256,0233,']',0346,0271,016,0371,'z','>',017,0372,'#',0274,04,0374,0213,0320,0351,'i',0222,'?',0343,0307,0322,04,0226,0323,030,01,0363,0367,0362,0233,0223,';',034,'L',0363,026,'@','H','a','i',02,'>','<',0246,0200,'F','k',027,'.',0202,047,0350,022,'h',0343,0214,'C','q',0334,'r','=',0251,'0',012,'`',0310,'r','h',0347,0342,0220,'Q','=','*',0301,0241,'A',011,0242,'w','(',0316,'D',03,047,'Y',0344,'j',0314,'4',0232,'A',05,0263,0222,'i',0377,0,'h',0243,'$',0232,0205,'H',03,'u','=',0303,0363,0232,'P',010,0206,0351,0371,0273,'f',0206,'~',0205,0242,'D',0314,0361,012,0217,0237,',',0313,0340,0375,'_',0237,'5',0234,0374,01,'B','G',0220,'*',0241,'8',014,'?',025,0253,'W',036,0344,0375,013,'z',0335,0311,04,'/',0311,0261,'J','u',0347,'j',0271,0245,0202,'[',024,0356,0204,0271,0200,'.',0375,0217,'O',0240,'Q','g',02,'4',0342,0247,'F',0222,0333,'~','i',0305,0,0310,0226,'J','^',037,0311,0221,0357,0243,0275,024,07,0313,0207,'d',0321,0341,0241,0235,'#',0350,'/',0342,021,'Y',0,0340,0330,0221,0367,0362,0345,'5',047,'h','q',0370,0371,0220,'a',017,0255,'^',0365,'|',0206,0265,'?','|',0254,0240,047,0337,0350,'w','8','A',03,0362,'l','R','v',0243,0262,0246,0261,'Y','S',05,0365,'&',0374,0322,0313,0364,'8',0336,0260,'~','(',0370,'V',0311,'-',0243,']','h','K',0340,':',0252,'b','x',0275,'%',0266,0245,'2',0360,'"','x','^','_',0206,0211,'G','Z','4','F','k',0262,'/',0332,0261,027,0375,024,'A',0332,'D','q',0345,021,0262,0242,0216,010,'~','l',0274,0330,'Z',016,'%','.',07,'a','P','m',0344,0316,0225,'P',07,0322,0217,'p','Y',0353,0364,033,0274,'!',0201,0376,02,0223,0265,035,0225,'4',0240,'K','b',0262,0246,0345,0361,0272,'~','i','U','U',0225,0325,0372,'C','x',030,0300,0203,'X',0367,0243,0302,'E',0254,03,0332,0230,02,0201,0221,'P',0217,'z','y',0346,0214,0221,0347,'e',012,0345,0262,0341,0331,'4','h','g',0246,0324,0307,')',0353,014,0202,034,0264,0262,'y','K',0346,'|',0277,0326,0207,0230,0343,0357,0341,'2',0376,0342,')',0252,0225,0310,0213,0244,'~','O','&','T',021,'<',031,'.','}',0210,'R',0376,0324,'3',0326,0337,0215,010,037,0304,024,0370,'(',0354,0253,'J',02,0254,06,0265,047,'y','>','w',0375,0224,0262,0375,'3',035,'~','G','<','<','U',0207,'Z','h',013,'L',0377,0,0264,016,'Q','q',031,0232,'V','t',0357,0236,0316,0252,'s',0341,0303,'|','s',0374,'t',0316,'(',0365,'a',0362,0331,'^',020,'y',0201,'(','Y',0245,0327,0277,0230,0235,022,'6','f',0210,04,'$',0260,0277,0270,0273,0344,'-','E','h',0232,0303,017,'D',0211,0315,'p',0352,0356,0306,0204,07,0370,012,'L',024,'v','U','h',02,0250,06,'V',0225,0262,031,'_',0325,0373,'>',0252,030,011,0251,0241,'m',0364,0242,0247,'X','K','!',0221,033,0211,0265,',','7','J','!',035,0312,'L','!',032,'+','N',016,'s','@',0305,0246,'x',0351,031,0361,'`','K',0236,034,0356,01,0365,0362,0352,'#',0231,'$',025,0266,0223,0351,'A',010,034,030,0261,0370,'y',0225,'j','%',020,'-',';',0241,'>',0264,'#',0311,'3','I','X',' ','^',0346,'#',0326,0234,'a',0357,'!',014,0236,0375,'M',0306,020,01,0374,'A','I',0202,0216,0312,0264,014,0200,022,0256,0224,0355,0361,0270,'_',0217,016,'z','r',03,'Y',0320,'j',0274,'P','F','E',0241,0255,'s',0212,0234,'1','2',0226,0202,'?','O','j',0276,0334,'7',015,0304,0263,0325,'B','D',031,023,'%',024,035,'I',0224,'o',0236,'y',0243,'q',011,047,0344,0212,020,02,0374,0237,024,'=',',',0226,0317,'1',0263,0315,05,'b','K',0241,0270,0334,0243,0316,'0',015,0226,0,0225,0366,0250,0255,0371,0220,022,'=',0305,'y','B',0272,026,0331,011,0244,0311,'<',0342,'M',01,0372,'+',0322,0313,0361,0356,0247,'/',036,'L',0350,'g','d','+','8','A',0301,'1',0270,0320,0317,'K','u',0204,020,0377,0,'P','S',0222,0243,0262,0255,031,'`','J',0270,012,'d','@',0313,'_',0201,0307,0337,0246,0212,'#',0205,03,0220,030,0376,07,05,'>',020,0200,'J',0274,024,024,0210,0201,0204,'r','q','O',0211,0321,0222,0215,0134,04,0320,'%',034,0376,0372,0240,0322,06,032,016,0347,'H',0220,0225,0221,'R',0351,'s',0274,'}',0350,0254,0214,0202,'i',0363,0265,0204,036,0364,0303,0302,01,'m',010,0330,0363,0212,'4',0,022,'&',037,'+','B',0272,0366,0211,'v','C','%',0357,0336,0260,0307,0220,0315,'.',',','w',013,'+',0262,0340,0366,0350,07,0304,0346,'I',0311,0372,01,0243,0252,0367,023,'(','L','s',0237,'_',' ',0305,'N',',',0335,0202,'^','5',0241,0312,0323,047,'G','9','j','2',037,0352,015,'i',0337,0301,0331,'V',0202,'1',0245,'H',02,0236,0241,0261,'`',0361,'8',0373,0364,0334,'I',0201,'J',0264,012,0340,0214,0203,'g',0345,0244,' ','T',0300,032,0320,0251,'g',017,'&',0251,0273,0342,0206,'w',0335,'D',047,015,0371,0254,'z',0242,'O','U','j','P',0205,'r','!','-','F',0203,02,0246,01,0364,0374,0377,0,0265,'e',0215,',',023,0323,0254,0301,0345,0326,016,0361,0265,'h',0223,0254,021,0342,0260,0332,0335,0253,025,0244,'V','C','}',0307,024,'~','j','G',0230,0341,0331,0251,0361,'k','2',0302,'l','3',0205,0321,0202,0343,'D','<',0254,0331,0200,022,0256,0305,':',021,026,0263,'t',0365,0231,0365,0350,011,0322,'i',0240,0326,'p','a','V',033,027,0373,'R','G',0220,0306,'=','$',0300,0277,0270,0241,022,0302,0202,'"','!','j',010,0362,'=',014,0344,0250,0310,0177,0250,'5',0244,0177,07,'e','Z',013,'!',0224,'@',024,0263,'U',05,'e',0177,'=','z','h','@',0243,0,027,'Z','!',0244,0246,0343,0376,0336,022,0354,0273,026,'x',0276,0325,026,0250,0232,0210,0200,0305,'{','q',0251,'P',013,037,'D',0351,0337,024,0361,024,0237,'=',0,037,'(',0265,015,0220,'D','-',07,'6','X','M',0234,0365,0237,':','L',0254,0360,0357,'C',0330,0250,0260,':',031,0240,'r',0215,'h',035,0312,'^',0330,0355,0243,0315,015,'b','d',0210,'M',0315,0316,'J',0371,'x','"','T',0262,010,023,036,0255,0251,0255,0310,0223,'4','E',',','@',047,0231,0362,031,0251,0213,'t',032,0202,'O',026,'^',032,013,0305,03,0,'X',':',011,'5',0224,'2',0242,0353,06,0331,0215,016,'i',0313,0237,' ',0305,'=',0204,0242,0221,0210,'Z',032,03,'W','b',0206,'|',0316,0262,')','H',0177,0253,'k','H','~',016,0312,0264,'b','M','J',0,0336,0231,0343,'@','Y','[',0274,'q',0324,0315,'*',0307,0206,0343,0177,06,'I',' ',',','_',0207,0315,031,'`','@',010,03,0305,'$',0241,'r','0',031,023,0232,0214,'k',0315,'{',0234,'^',')','h','d',032,023,0222,0244,'e',0221,'|',0320,0206,0313,'.',0243,0267,0352,0200,0211,0,0200,0215,'y',0353,'!',0226,04,0333,'<',':',0366,'k','|',0350,'.','w',0332,0234,0204,0314,'B','S',0353,'D',0223,011,0317,0360,0320,'d','0',0343,0232,0260,0231,'7','|',0301,0356,0202,'6','g','J',0302,'<',0260,01,'f','S',010,0301,0264,0300,0366,':','@',0261,'v','6',036,0367,0237,'J','@',01,0350,0303,0262,'S',0222,'-','<','y','W','6','>',0200,0345,0206,04,'N',026,0216,'e',0217,0313,04,0206,0210,0350,0371,'U','>','i','H','?',0253,'k','H','.',016,0312,0264,0231,0220,0234,01,'M',0230,0373,'+','w',0215,0216,0246,'L',0310,'L',0367,'q','@',0,026,012,05,'-',0230,']',0257,0345,0240,'^','c','`',06,0236,'W',0300,'y',01,'a',0354,0334,'R','$',0315,0257,015,0307,'~',')',06,0,034,0217,0346,0211,'"',011,'6',0242,'&',034,'N',036,'G',0255,0255,0334,'J',0260,'?',0276,0364,'~',024,026,0375,0267,0364,0242,0321,012,'F',0265,04,030,0270,'L',0261,0303,'C','Z','s',':','.',024,0240,'-',0204,0227,0326,'4',0362,0245,0263,'L','Y','0',0230,0233,023,'+',0240,'5',037,0250,0255,'x',0223,'n',0256,0275,'-',037,'j',0223,'c',0373,0341,0373,'{',0322,'y',03,024,'G',0317,030,01,'q',0227,'D',0200,0333,0215,'%',0323,0305,0363,0346,'T',0203,0355,'q',0255,'>',0230,'+','*',0322,'1',02,0360,05,'8','v','&',013,0373,033,035,'D','G','b','Z',0266,0355,'@',030,0320,02,0,0243,'I',02,035,0203,'g','-',02,'%',015,0362,0363,0320,'I','"',0206,'y','B','S',0352,'m','J','N','B',04,0226,0275,0312,'d',0223,014,0334,0305,'7','>',0204,030,'i','N',0275,0313,0335,0217,0357,0254,0213,0236,'U','r',0247,'f','+','9','6','v',0177,0270,0240,0346,0304,'J',0213,030,0200,'8',0267,024,0316,012,0260,0330,'7','_',0212,'v','x',0353,0242,':',027,'N','V',0377,0,0255,'G','=','$',0232,027,'D',0221,'6',0375,010,'b',0206,'y',0260,0211,0206,0336,0242,037,'_','+',0260,0376,'!','i',037,0371,0255,07,0341,'H',0,'0',0314,'9',035,'D','u',0241,0232,'j',0231,0205,' ',0373,0134,'k','R','Y',0302,0312,0265,'%',0362,024,01,'N','S','`',012,0376,' ',0352,03,'.',0344,';','(',0311,' ','(',02,0236,'V','F','r',0327,0365,'D','|',030,'9',013,0236,0222,'I','@',0254,'G','5',021,0335,'!','!',0273,'h',0363,'N','x',0241,'8',0365,'8',0242,0310,'x','F',0226,'J',0215,0322,0306,0355,'N',0210,03,0236,0266,'!',0221,'q',024,0345,'k','m',035,0213,0224,'!',026,'D',0311,0302,'i','D',0312,0231,047,021,'4','j','n',0320,',',0274,03,011,0275,025,0302,'Q',027,020,'9','T',')',0312,0221,01,015,0353,0274,013,01,0241,'m',':',0232,'7',0251,0303,026,022,'u','>',0317,0252,0322,'F','.','y',06,032,'d',0343,010,024,0352,0353,0257,0205,0314,024,'f',0177,'l',012,0321,'i',0305,'I','f',013,'*',0320,0251,'9','+',024,0305,'6','"',0377,0,'L','i',0324,'(','j',0206,';',015,0216,'Z','.','g',0200,0373,0322,'W','}',0335,'5',0223,0240,'Q',0306,' ',0243,0273,0355,0324,'o','Q',0315,'&','"','K','x','N',037,0305,')','4','a','.',0343,'c','Y',0355,0306,0225,'k',031,02,0307,'i',0246,'F',030,0217,0260,0365,0244,0316,0,'2',0301,';','{',0373,0323,0300,'L','*',0307,'T',0334,0276,'y',0245,0350,'i',0260,0271,'"',0243,0265,')',020,'u','w',017,0212,'V',0261,'B',0341,'-',0334,'i','M',0304,'P',0213,'=','D',0232,010,'k',0271,011,',',0372,'7',0245,0321,'X',0331,'V',035,0344,0267,'q',0244,0217,'$',0365,0367,'r',05,0376,0206,0205,0372,'-',0341,'`','(',0346,0217,0360,'k','W','&',0231,0263,0371,'>',0335,'H',0350,'d',0245,0267,032,0212,'I',0276,0247,0252,0321,0344,'Q',0327,'Z','>',0200,0306,'-','v','p','u',0363,'P',0356,0202,020,'T','b',0275,0321,'}',0216,'3','S','4',020,0242,0345,035,')',020,02,0345,'1','E','`','J','4',0236,0251,0256,012,035,07,0206,0256,023,0304,031,'*','&',0244,'[',0266,0365,035,'#',023,0245,'P',010,024,'#','q','+','_',0235,0340,020,'y','V',0364,']',015,'(',0221,'7',0352,0244,0324,'X',0267,0345,07,'"','d',0354,'`',0232,'V','-',0343,'/',0230,0334,0201,0177,0241,0243,0321,'x',0240,032,024,016,'J','U',0366,'7','k','(','C','/',0372,'?','n',0242,0354,'}',0335,06,0253,0305,037,0322,']',027,0336,0355,0305,06,'$',017,'*','p',025,'y','9','-',0344,0323,0316,0357,0320,0244,0225,012,0265,'n',0224,0241,'8',0357,0311,'J',0322,'p',',','7',035,'J','>','o','d','p',0320,'C',020,0226,'/',']','>',0365,'2','`',016,'N',0252,'1','H',0232,0225,047,0345,'f',0134,0270,023,0375,0245,035,0202,0224,'H',0233,0224,0301,'9',0203,'^','m',0307,024,'A',0366,'R','G',0252,'/','F',0215,' ','K',0211,0265,'>','Y','=','r',0313,0354,'7','O',0134,'Z',0222,032,0230,'9','U',' ','_',0350,'h',0353,026,0,014,01,'C',',',0322,0256,'W','c','v',0247,0251,'E',0227,034,0273,0275,0134,0271,0210,011,0202,0216,0363,0261,0301,'H','@',0243,0,'e',0243,'&','e',037,'6',0275,0337,037,'I',024,'E','`','X','C','n',0251,'s',']',0321,015,0203,0243,0361,'E',0243,0370,0241,0364,';',0211,'e',0241,013,0260,0202,0316,0343,0326,'N','T',0335,0337,'+',0305,'g',026,04,0334,'v','h',0213,'q',02,0311,0276,0306,0215,0233,'Y',0355,0264,'4','j','z','b','J','G',0271,0215,'s',0276,0321,030,'h',026,0236,0212,'-','l',032,'"',027,0332,0211,030,'"',0300,0300,024,'w',0313,0352,0266,'7','i',0243,',',0262,0330,'7','w','z',0205,0305,0201,'J',0256,0224,023,'p',016,033,'?','/',0204,0235,0244,'=',0203,0354,'}',':',0235,0304,0240,';','S',011,0335,0367,07,'3',0212,'|',0361,'P','@','v','J',':','+',0206,']','S',0214,'w','T',0360,'z',0320,010,'h','1',0231,0353,0357,'D','7','3',0303,0332,0221,0272,0302,'_','|','(',021,0204,'O',0273,0271,0271,0315,'O','M',0356,0206,0247,';',0275,0214,0323,0334,'e',0353,0253,'@',0334,'v','5','Z',01,0275,'+','e','0',015,0206,0356,0357,'P',0310,'Q',0,'e','h',0336,'H',033,047,0347,0301,0325,0211,0307,0305,0253,0303,0346,0214,0,04,01,0200,0372,'v',0260,0242,0310,0244,0227,0236,0307,0363,0232,'r',0233,0242,0307,'u',0251,0315,07,0343,'D','$','N','i','m','9',0233,0363,021,'H',0334,'L','!',011,0325,'I',010,0321,0211,'6',0242,0304,0272,'Y',0343,0376,0323,',',0311,0134,'z','s',0332,0201,0365,0302,0261,0311,0372,'R','F',':',',',035,'o','`','*','e','o',023,0353,0367,'s','E',0274,036,']',' ',0244,'s','E',0365,0237,0337,'=','@',0226,0262,0312,' ',0361,0334,'o',0266,0336,06,'n','G','Z','-','<','n',0320,022,'@',0360,03,'C',0352,0264,0240,0202,02,0305,0223,'q',0247,'g',05,'b','8',':','?',015,'D',0333,'G',024,',',0341,'C',0216,0340,'k','F','X',0357,0207,0221,0352,0256,'D',0221,'a','(',0214,'T',0274,'-',0277,0345,'0',021,'#',012,0321,032,'2',0354,'X','8',037,0344,0321,'&',02,0333,0371,0361,'I','"','F',']',0203,0360,'P',010,'i',013,01,'R',0340,01,0333,'h','[','w',0253,0206,0233,07,0236,0356,'6',0254,'Q',0370,0242,'1','i',0367,037,0212,014,0202,';',07,0326,':','s','&',01,0272,0320,0224,']','d',0266,'x',0315,'c',0363,'4','f','t',0214,0215,'&',0203,023,012,'L',011,0244,'4',0204,'h',0271,0331,';',0325,0274,0262,032,0336,0273,'=','Q','P',0212,'%',0304,0247,'C',0270,0263,'4',0354,0335,')',0204,0346,'h',0213,'9','X',0377,0,017,0232,0237,'0',0341,0316,'a',0277,0360,05,'"','R','V',0352,0324,0333,0265,'G',027,0340,0352,0262,0340,0322,0372,037,0212,'0',0,020,06,012,'J',03,012,';',0276,0373,024,'U',0317,'w',']','d',0352,0277,'T',0332,0222,'P',0256,0311,')',030,0230,'w','V','*','k',')','@',0264,0321,0341,0301,0306,'1','X',0304,'Q',024,'K','&',0245,037,0352,0316,'A',0376,'-','C',016,'B',022,0204,'a','a','E','0','u',0364,0255,0350,0352,0247,06,'J','.','5',022,0306,01,'K',021,0267,'y',0366,0240,022,0327,0341,022,0210,0210,0265,0234,06,'{','9',0362,0211,047,031,0200,011,'Z','o','L','C','b','l','*',0345,'V','>',0230,0273,'J',0245,'U','[',0253,0324,'7',0266,'e','-',0237,0232,06,'$',012,0,'4',0246,0322,0344,0254,'K',0241,0253,0305,06,'/',0273,01,0220,0134,0242,'L','}','C','j',0354,0244,0330,'A',0244,027,'s',0232,')',0310,05,015,'{',0344,0323,0307,'4',0315,0300,0236,0205,0354,0205,0213,033,'V',030,0361,0232,'c',0331,0344,'=','c','w','%',014,'s',0222,'a',0373,'j','s','@',020,0302,':','q',0315,'B',033,'r',037,0317,0255,'@','F','1','9',0365,0352,'=',0200,0,032,0312,0364,'m',0204,015,'E',0225,'1',02,0277,03,'P','$',0362,']','v',0320,'n','?',0226,'=',0374,05,0323,0275,'Y',0177,0307,'P',0300,0313,0217,0340,'s',0341,0275,'1','"',0215,0257,0236,015,0326,0240,'%','"',0134,0375,0345,0370,0240,'C','D',032,034,'n','#',023,0315,034,0320,022,026,0331,0276,'=',047,0275,'n',0223,0270,0332,0214,0246,'J',03,'C','?','F',0324,0332,0221,';',0215,0240,'k','z',0234,03,026,'$','a','C','}',0344,'Y',0336,0223,011,0223,'{','1',0213,'F',0262,0363,'R','y',0213,'%',036,'u','K','e','}',032,'8',0250,0316,'4',0204,0215,'f',0311,'.','b','d',0232,0303,0332,0236,0,013,'B','{','4','G',0241,0273,01,0341,0352,'@','d',0323,'u',0343,0372,'}',0251,0,'0',0227,0222,0242,022,'e',0233,0244,023,0352,'#',0353,0344,'{',0220,'#',0250,'R','x',021,'+','<',0320,037,0263,0355,0323,'8',0333,0304,'-',0373,0250,'R',02,'9','Z',0257,'4',0323,0246,0354,'z',033,0264,0360,'6','I',037,0226,0277,036,'#',024,0371,0344,'G','&',031,0,'X','p',0311,'N',0342,0255,'G','S','%',0316,',',0242,'T','(','`',04,0200,'d','N',0211,0243,'d',0222,04,0307,0245,023,'q','C','=','Y',0251,0251,0310,'R',0204,0275,'Z','U',0362,'P','0',023,'$',0216,0366,0325,0210,0344,0200,0204,'h',031,'9',0227,0265,'&','i',0201,'e',0267,030,0364,02,0231,'O','=',02,0324,')','Y',0313,0260,'M',0326,0305,026,047,05,022,'3','c',0356,0275,02,0362,'D',0300,'$','D','Z','6',0244,0337,0362,033,047,033,'4',0207,'|','$','#','L',0261,020,'"',0364,'Y',0257,'{',0207,0203,0373,0351,0337,0270,0320,'m',0302,0200,010,'f','i',0254,0255,'s',0251,020,0364,'O','o','$',0316,0316,'!',0334,0236,0314,'x','O',0370,0260,'_','A','*','R','z',0361,016,0220,0376,':','(','A',',',0261,0335,'5',0202,01,',','_','Y','i',0330,015,0250,03,'n',0364,0251,0267,'z','?','o',0215,'<',0360,0332,0217,'"',0324,']',0313,'+','y',0274,0245,031,0243,022,0232,'j','$',0277,02,0240,0343,0210,0260,'u',01,0257,0331,'P',0330,0344,'R',0235,',',0313,0210,0232,0304,04,0231,'/',0232,02,'I','s','r',0273,032,0232,0232,0232,0232,0233,'T',0305,'g','G',0314,047,0273,'V',0350,010,0313,0265,0226,0247,0217,0314,'p',05,0325,0354,'5',0222,06,025,0232,'#','b',0367,')',022,0206,0215,'W',' ',023,0324,0253,'*','|',0341,'-',',',0242,'+','y','C','p','.',']',0255,0233,0332,'(',0267,'t','"','3',']',06,0301,'b','j','S',0237,06,'Z',0324,'e',036,0336,0316,'7',024,0301,'J',0302,0222,0334,0177,024,020,012,'.','&',0216,'&',0301,021,0332,'1',0366,0244,'`','A',0204,'H','G',0243,0205,'D',047,'r',0202,'1',04,0221,0233,025,'<','X',0326,0220,0303,0345,0370,0362,013,'W',0361,0310,0220,0224,0360,'e','O',' ','-',0266,037,01,0265,0260,'9',022,0221,0272,'^',017,'m',0311,'R','A',0260,0244,0366,047,0334,0243,0357,0256,0344,'{',0245,017,047,0253,07,0346,0206,'X',0247,0362,0212,'r',0217,0277,0353,0244,0362,']',0377,0,'M',']','*',0336,010,'p','7','h',0256,032,0266,']',0326,0260,0322,0260,0261,0312,0350,'S',0203,'a',0216,027,014,0356,04,0332,'n',035,'$','1','X','W','e',031,0353,02,'p',0304,0254,0234,0264,'@',0223,0310,'A',0343,021,0301,'S','B','i','(',037,0323,'4','?',0223,033,0360,'h',0230,'y',0364,'w',0365,033,'D',0347,0255,'S',04,0375,'H',0236,0356,0226,0241,'w','o',0205,'r',0255,04,026,'g','%','|',0320,'i','Q','G',020,0211,'&',0206,0244,0332,0260,0350,0227,'k','*',036,0264,026,0274,0345,'`','r',0301,'H',014,021,'y',0303,023,0327,0306,'8',0326,0240,0364,0244,0237,'`','*','<',0242,'6','p','/',0310,'t','h','H','{',047,0240,0177,'z','I',021,0261,'!',0222,'}','(',0366,0373,0201,03,0347,0367,0245,'K',0274,'h','g','u',0370,0350,0314,0271,020,0343,07,0332,0220,010,'a',0331,031,'<',0240,0232,'.',0201,')',0326,'3',047,0334,0353,0332,0227,0223,'B',0244,'{',0371,0242,'j',033,036,05,'$',0310,0220,034,'n',0323,0313,'b',0312,'[','U',0177,024,'@','N',022,0242,'d','V','D','o','H',0206,0245,0332,'1',0204,0370,0134,0316,'J','J',0240,0213,0330,'b','I',0262,'r','I','Q','o',0255,0270,0246,'8',0360,0270,'p',04,0322,014,'Q',',',0310,'B',0301,'}','[',0344,0241,';','@',0244,0367,'w','^',0201,0305,010,'v',0201,0310,0224,05,0224,0271,0261,0313,0372,0322,0217,'L',016,021,0346,0235,026,0210,04,0213,0275,')',';','"',',','w',':',016,'Z',0210,0201,'q',01,0373,0350,'[','2',0305,0263,0324,0275,'L','Y',0330,016,0323,'K',021,'w','/',0232,',',0311,0333,0365,0274,'5',0303,0376,0353,0363,'V','P',0363,024,'>','b','n',012,'R','/',02,0355,0275,0224,'W',026,010,017,'C',0301,'*','D','E',027,022,0202,07,0304,033,0211,0246,0324,0270,0325,'J','B',0234,026,'`',0300,0212,0356,'U',0273,'T','8',0334,'7','z','4',0302,0222,'>',0244,0326,0320,06,'2','q',':',016,'X',')',030,'w','Q',0306,014,',','<',0211,'~',032,0212,'o',' ',027,0272,0357,'R',0235,036,0240,0331,'9','`',0216,037,0332,0221,0375,0134,',',0233,0216,0245,027,05,'G','w',0221,027,0245,'M',0276,0305,0316,017,0235,0214,0313,'(','l',0315,0376,0337,'J',0370,031,032,0213,'Z',0346,034,0225,'u',0301,0270,0272,'7',03,'h','U',0316,'n','Q',0206,0271,07,0202,'k',0344,0333,0256,'@','%','$','T','[',0350,0211,'4',0264,0212,'^',0255,0200,0273,'Q',037,0366,'(',011,0331,0274,0360,0205,'[',0203,0304,0315,'2','N','M',0233,0243,'E',017,'b','E','w',0234,0250,0202,'F',':',0311,0235,')','@','@',0267,0206,0341,0324,0342,0230,0266,0324,0277,0315,0342,0217,'`',0241,037,0335,'8',0134,0350,'M',0314,0307,0305,0274,0320,026,0377,0,0203,0365,015,'j',0233,0315,'5','B',0370,' ',0357,013,0257,'-',033,'q','%',0310,0270,0225,0270,033,024,0210,0305,'t',0233,012,'W',0336,0254,0372,0225,0215,06,0340,0207,0253,'K',020,0331,',',0215,'N','*',')','#',0241,'*',0272,0242,0245,0327,0246,035,0302,017,0225,036,0254,0222,0370,',',0317,0243,'Q',0207,0213,0324,0353,030,'>','h',0260,020,0207,'a',0205,014,0363,'D','@',0,0200,013,025,037,'D','Z',02,05,'>',0333,'<',0324,0276,0202,0200,0221,0344,0324,0346,0234,0,0204,'C','q',0342,0234,'4','w',022,0316,0337,0355,'8',0364,0300,'!',037,'"','<','S',0370,0372,0262,'M',020,0245,'l',0326,'B',0335,0306,0245,'L',0263,'#',07,'|',')',0255,0302,01,0201,'Y',0346,'E',0342,0241,'$',0255,'=','O',0330,0304,0264,0225,0264,0320,035,0340,021,0354,0315,',',030,0272,0376,'"',0245,'0','6','_',0305,'`',0275,'#',0370,0247,0210,0221,0262,'~','(','<',0230,0314,'G',0331,'[',0310,0353,0217,0225,05,'H','b',017,037,0205,'d','h',02,'7',0223,'%',047,'#',0303,'"','F',0367,037,024,'P',0274,0200,'c','k',0,'T',0246,'j','>',0230,030,'@',0220,0211,'f',0242,0235,'$',0366,0345,0332,0361,'L','_','P','p',0232,0333,'J','?',0214,0261,0312,'?',0261,'C',0134,',','N',036,'G',0305,'C','e',07,0237,0347,0377,0,05,'(',0205,'f',0222,'y',0250,0244,0232,0210,0250,0243,0353,'!',0241,'@',0253,'k','p',':',0367,0246,'S',0366,0234,033,0255,'J',027,'u',010,025,'o',0315,'H',0370,0250,0231,'G',0367,0255,'7','`',0332,'D','5','>',0233,'o',0357,'j',0373,0377,0,0360,0316,0,0203,011,011,0272,0254,0330,0254,0222,'x',0230,0357,0212,'!',0344,010,0237,0212,0237,'$',0221,0210,0370,0240,0,05,0200,0300,'U',0243,'~',0330,0335,'v',02,0377,0,0374,';',0200,020,0204,'I',0223,0232,0272,0,0241,'$',0356,0343,0355,024,'g','(',0271,'C',0220,'_','z','y','M',0211,'<',034,010,0247,'H',012,' ',0230,0330,'h',0177,0361,0237,0377,0331,};
diff --git a/src/embedded_images/redcross.h b/src/embedded_images/redcross.h
new file mode 100644
index 0000000..a3a0912
--- /dev/null
+++ b/src/embedded_images/redcross.h
@@ -0,0 +1,16 @@
+/* Autogenerated by hxtools bin2c */
+#ifndef REDCROSS_H
+#define REDCROSS_H 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern const unsigned char bin2c_redcross_jpg[60331];
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+
+#endif /* REDCROSS_H */
diff --git a/src/embedded_images/redcross.jpg b/src/embedded_images/redcross.jpg
new file mode 100644
index 0000000..88fffe9
--- /dev/null
+++ b/src/embedded_images/redcross.jpg
Binary files differ
diff --git a/src/embedded_images/redcross.xcf b/src/embedded_images/redcross.xcf
new file mode 100644
index 0000000..1fb13be
--- /dev/null
+++ b/src/embedded_images/redcross.xcf
Binary files differ
diff --git a/src/embedded_images/shred_db.jpg b/src/embedded_images/shred_db.jpg
new file mode 100644
index 0000000..8b46302
--- /dev/null
+++ b/src/embedded_images/shred_db.jpg
Binary files differ
diff --git a/src/embedded_images/shred_db.jpg.c b/src/embedded_images/shred_db.jpg.c
new file mode 100644
index 0000000..2feee30
--- /dev/null
+++ b/src/embedded_images/shred_db.jpg.c
@@ -0,0 +1,4 @@
+/* Autogenerated by hxtools bin2c */
+#include "shred_db.jpg.h"
+/* Autogenerated from shred_db.jpg */
+const unsigned char bin2c_shred_db_jpg[27063] = {0377,0330,0377,0340,0,020,'J','F','I','F',0,01,01,02,0,034,0,034,0,0,0377,0341,032,0266,'E','x','i','f',0,0,'I','I','*',0,010,0,0,0,07,0,022,01,03,0,01,0,0,0,01,0,0,0,032,01,05,0,01,0,0,0,'b',0,0,0,033,01,05,0,01,0,0,0,'j',0,0,0,'(',01,03,0,01,0,0,0,03,0,0,0,'1',01,02,0,015,0,0,0,'r',0,0,0,'2',01,02,0,024,0,0,0,0200,0,0,0,'i',0207,04,0,01,0,0,0,0224,0,0,0,0246,0,0,0,'7',02,0,0,024,0,0,0,'7',02,0,0,024,0,0,0,'G','I','M','P',' ','2','.','1','0','.','3','0',0,0,'2','0','2','3',':','0','2',':','1','2',' ','0','1',':','4','9',':','2','5',0,01,0,01,0240,03,0,01,0,0,0,01,0,0,0,0,0,0,0,011,0,0376,0,04,0,01,0,0,0,01,0,0,0,0,01,04,0,01,0,0,0,0,01,0,0,01,01,04,0,01,0,0,0,0,01,0,0,02,01,03,0,03,0,0,0,030,01,0,0,03,01,03,0,01,0,0,0,06,0,0,0,06,01,03,0,01,0,0,0,06,0,0,0,025,01,03,0,01,0,0,0,03,0,0,0,01,02,04,0,01,0,0,0,036,01,0,0,02,02,04,0,01,0,0,0,0220,031,0,0,0,0,0,0,010,0,010,0,010,0,0377,0330,0377,0340,0,020,'J','F','I','F',0,01,01,0,0,01,0,01,0,0,0377,0333,0,'C',0,010,06,06,07,06,05,010,07,07,07,011,011,010,012,014,024,015,014,013,013,014,031,022,023,017,024,035,032,037,036,035,032,034,034,' ','$','.',047,' ','"',',','#',034,034,'(','7',')',',','0','1','4','4','4',037,047,'9','=','8','2','<','.','3','4','2',0377,0333,0,'C',01,011,011,011,014,013,014,030,015,015,030,'2','!',034,'!','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','2','2','2','2','2','2','2','2','2','2','2','2','2','2','2','2','2','2',0377,0300,0,021,010,01,0,01,0,03,01,'"',0,02,021,01,03,021,01,0377,0304,0,037,0,0,01,05,01,01,01,01,01,01,0,0,0,0,0,0,0,0,01,02,03,04,05,06,07,010,011,012,013,0377,0304,0,0265,020,0,02,01,03,03,02,04,03,05,05,04,04,0,0,01,'}',01,02,03,0,04,021,05,022,'!','1','A',06,023,'Q','a',07,'"','q',024,'2',0201,0221,0241,010,'#','B',0261,0301,025,'R',0321,0360,'$','3','b','r',0202,011,012,026,027,030,031,032,'%','&',047,'(',')','*','4','5','6','7','8','9',':','C','D','E','F','G','H','I','J','S','T','U','V','W','X','Y','Z','c','d','e','f','g','h','i','j','s','t','u','v','w','x','y','z',0203,0204,0205,0206,0207,0210,0211,0212,0222,0223,0224,0225,0226,0227,0230,0231,0232,0242,0243,0244,0245,0246,0247,0250,0251,0252,0262,0263,0264,0265,0266,0267,0270,0271,0272,0302,0303,0304,0305,0306,0307,0310,0311,0312,0322,0323,0324,0325,0326,0327,0330,0331,0332,0341,0342,0343,0344,0345,0346,0347,0350,0351,0352,0361,0362,0363,0364,0365,0366,0367,0370,0371,0372,0377,0304,0,037,01,0,03,01,01,01,01,01,01,01,01,01,0,0,0,0,0,0,01,02,03,04,05,06,07,010,011,012,013,0377,0304,0,0265,021,0,02,01,02,04,04,03,04,07,05,04,04,0,01,02,'w',0,01,02,03,021,04,05,'!','1',06,022,'A','Q',07,'a','q',023,'"','2',0201,010,024,'B',0221,0241,0261,0301,011,'#','3','R',0360,025,'b','r',0321,012,026,'$','4',0341,'%',0361,027,030,031,032,'&',047,'(',')','*','5','6','7','8','9',':','C','D','E','F','G','H','I','J','S','T','U','V','W','X','Y','Z','c','d','e','f','g','h','i','j','s','t','u','v','w','x','y','z',0202,0203,0204,0205,0206,0207,0210,0211,0212,0222,0223,0224,0225,0226,0227,0230,0231,0232,0242,0243,0244,0245,0246,0247,0250,0251,0252,0262,0263,0264,0265,0266,0267,0270,0271,0272,0302,0303,0304,0305,0306,0307,0310,0311,0312,0322,0323,0324,0325,0326,0327,0330,0331,0332,0342,0343,0344,0345,0346,0347,0350,0351,0352,0362,0363,0364,0365,0366,0367,0370,0371,0372,0377,0332,0,014,03,01,0,02,021,03,021,0,'?',0,0371,0376,0212,'(',0240,02,0212,'(',0240,02,0212,'(',0240,02,0212,'(',0240,02,0212,'(',0240,02,0212,')',0311,033,'9',0340,'~','5','P',0204,0246,0371,'b',0256,0300,'m','*',0253,'1',0340,023,'V',022,05,'^','[',0223,0372,'T',0300,0,'0',06,05,'z',0324,'2',0211,0313,'Z',0256,0337,0231,0233,0250,0272,025,0226,0335,0217,0336,' ','T',0202,04,035,'r','~',0246,0245,0242,0275,'J','Y','~',036,0237,0331,0277,0256,0244,'9',0266,' ','E',07,'!','@','?','J','Z','(',0256,0305,025,035,022,'$',')',012,0206,0352,01,0372,0322,0321,'C','I',0253,'0','!','k','u','#',0345,0340,0325,'v','R',0215,0203,'W',0251,0222,' ','u',0307,'~',0306,0274,0274,'f','[','N',0244,'y',0251,'+','K',0360,'e',0306,'m','n','S',0242,0202,010,'$',036,0242,0212,0371,0266,0255,0243,'6',012,'(',0242,0200,012,'(',0242,0200,012,'(',0242,0200,012,'(',0242,0200,012,'(',0242,0200,012,'(',0242,0200,012,'(',0242,0200,012,'(',0242,0200,012,0,'$',0340,014,0232,'z','F',0316,'x',0351,0353,'V','R','5','A',0307,'_','Z',0357,0302,'`','*','b','5','z','G',0277,0371,023,')','$','F',0226,0343,0253,0376,'B',0247,0351,'E',025,0364,'t','0',0324,0350,'F',0320,'F','-',0267,0270,'Q','H','X','(',0311,'8',025,03,0334,'v','A',0370,0232,'U',0361,'T',0250,'/','}',0374,0272,0202,0213,'{',026,':','T','m','2','/','|',0375,'*',0253,'3','1',0344,0223,'I','^','E',0134,0342,'O','J','q',0267,0251,0242,0247,0334,0234,0334,0236,0312,07,0324,0323,014,0362,023,0327,037,0205,'G','E','y',0363,0307,'b',047,0274,0337,0313,'O',0310,0256,'T','I',0347,'I',0375,0357,0322,0244,'K',0202,'X',06,03,0237,'J',0257,'O',0210,026,0221,'q',0330,0346,0264,0303,0342,0361,036,0321,'%','&',0356,0375,'A',0305,'X',0271,'E',024,'W',0325,0230,025,0356,023,0235,0343,0361,0250,'*',0361,01,0206,010,0310,'5','M',0324,0243,020,'k',0347,'3','L','/',0263,0237,0265,0216,0317,0363,0377,0,0202,'m',011,']','X','m',024,'Q','^','I','a','E',024,'P',01,'E',024,'P',01,'E',024,'P',01,'E',024,'P',01,'E',024,'P',01,'E',024,'P',01,'R','G',021,'s',0350,0276,0264,'E',031,'v',0377,0,'d','u',0253,'}','+',0325,0313,0362,0377,0,'m',0373,0312,0237,017,0347,0377,0,0,0211,'J',0332,'!',0,012,'0',06,0,0245,0242,0220,0260,'Q',0222,'p','+',0350,0275,0330,0256,0311,030,0213,'P',0311,'8','^',027,0223,0372,'T','R','L',0317,0300,0341,'j',':',0360,0361,'y',0253,'~',0345,017,0277,0374,0215,'c',016,0342,0226,',','r','N','M','%',024,'W',0212,0333,'n',0354,0320,'(',0242,0212,'@',024,'Q','E',0,025,'j',04,0332,0273,0217,'S',0374,0252,'(','c',0336,'w',037,0272,'?','Z',0265,'^',0346,'U',0204,'w',0366,0363,0371,0177,0231,0234,0345,0320,'(',0242,0233,0274,'y',0201,';',0342,0275,0271,'N','1',0267,'3',0337,'C','!',0325,024,0351,0271,'w',016,0242,0245,0242,0242,0265,'(',0326,0246,0341,'.',0243,'N',0316,0345,012,'*','I','S','c',0361,0320,0364,0250,0353,0343,0252,'S',0225,'9',0270,'K','t','n',0235,0302,0212,'(',0250,030,'Q','E',024,0,'Q','E',024,0,'Q','E',024,0,'Q','E',024,0,'S',0221,013,0260,'Q','M',0253,'p',0246,0304,0347,0251,0353,']',0230,034,'/',0326,'*',0331,0354,0267,'&','R',0262,036,0240,'*',0200,':',012,'Z',')',011,012,'2','N',0,0257,0253,0322,'1',0354,0221,0200,'3',04,0134,0232,0246,0356,'d','l',0237,0300,'R',0310,0345,0333,'=',0273,012,'e','|',0326,'?',034,0353,0276,'H',0374,'+',0361,'6',0214,'l',024,'Q','E','y',0245,0205,024,'Q','@',05,024,'Q','@',05,'I',034,'f','C',0350,0275,0315,':','(','K','a',0233,0205,0364,0365,0253,'=','+',0327,0300,0345,0256,0245,0252,'U',0321,'v',0356,'g',')',0333,'D',' ',01,'F',0,0300,024,0264,'R','3',04,0134,0232,0372,06,0343,010,0335,0350,0221,0220,0331,034,'"',0347,0277,'a','U','2','I',0316,'y',0353,'J',0356,']',0262,'i',0265,0362,0330,0354,'c',0304,'T',0367,'~',025,0267,0371,0233,0306,'6','E',0330,0337,'z',03,0371,0323,0252,0255,0273,021,'&',';',036,0265,'j',0276,0203,05,0210,0366,0364,'T',0236,0373,'3',')','+','1',0222,'&',0364,0307,'~',0325,'O',0241,0253,0365,'^','x',0360,'w',0201,0301,0353,0134,'9',0256,027,0232,'>',0332,';',0255,0375,012,0204,0272,020,'Q','E',025,0363,0346,0241,'E',024,'P',01,'E',024,'P',01,'E',024,'P',01,'E',024,'P',03,0342,']',0322,0,'z',016,'M',0134,0252,0366,0300,'e',0217,'z',0261,'_','M',0225,'S','Q',0303,0363,'w',0377,0,0206,'1',0233,0324,'*',0264,0362,'d',0354,035,07,'Z',0260,0347,'j',023,0350,'*',0217,'S','Y','f',0330,0207,010,'*','Q',0353,0277,0240,0340,0272,0205,024,'Q','_','<','j',024,'Q','E',0,024,'R',0252,0263,0234,'(',0315,'L',0226,0375,0334,0376,02,0272,'(','a','+','W',0370,026,0235,0372,011,0311,'"',025,'V','s',0205,031,0253,'1',0302,023,0223,0311,0376,'U',' ','P',0243,0,'`','R',0327,0275,0205,0313,'i',0321,0367,0247,0254,0277,03,')','M',0260,0242,0212,'c',0312,0251,0356,'}',05,'z',025,'*','B',0234,'y',0246,0354,0211,'J',0343,0231,0202,'.','M','S','w','2','6','O',0340,')',031,0213,0266,'M','%','|',0316,'7',037,',','C',0345,0216,0221,0374,0315,'c',033,05,024,'S',0221,'K',0260,02,0270,'c',027,'&',0243,035,0331,'d',0266,0352,'y','c',0364,025,'b',0221,'@','U',0,'t',024,0265,0365,0370,'Z',036,0302,0222,0201,0317,047,'w','p',0244,'`',031,'H','=',015,'-',025,0273,'I',0253,'1',024,'Y','J',0261,'S',0324,'R','U',0231,0323,'+',0270,'u',037,0312,0253,'W',0310,0343,'0',0357,017,'U',0303,0247,'O','C','x',0273,0240,0242,0212,'+',0224,0240,0242,0212,'(',0,0242,0212,'(',0,0242,0212,'(',02,'{','o',0342,0374,'*',0305,'S',0211,0366,'>','{','t','5','r',0276,0233,'*',0253,031,'P','P','[',0257,0327,'S',031,0255,'D','e',016,0245,'O','z',0252,0320,0272,0366,0317,0322,0255,0321,'[',0342,0260,'T',0361,026,'r',0321,0241,'F','M',024,0266,'?',0367,033,0362,0245,021,'H',0177,0204,0376,'5','r',0212,0343,'Y','=','+',0353,'&','W',0264,'e','a','n',0307,0251,02,0244,'X',021,'z',0362,'}',0352,'Z','+',0256,0226,'_',0207,0247,0252,0215,0375,'u','%',0315,0260,0,01,0200,'0','(',0242,0202,'@',031,047,02,0273,'4','H',0220,0240,0220,06,'I',0300,0250,'^',0341,'G',0335,0344,0324,014,0354,0347,'$',0327,0233,0211,0314,0351,'R',0322,036,0363,0374,'>',0362,0324,033,0334,0225,0356,';',047,0347,'P','u','4','Q','^',05,'|','M','J',0356,0363,'f',0251,'%',0260,'Q','E','(',05,0216,0,0311,'5',0212,'M',0273,'!',0210,01,'$',01,0324,0325,0270,0343,021,0217,'V',0356,'i','"',0204,047,047,0226,0376,'U','-','}',026,']',0200,0366,'K',0332,'T','^',0367,0345,0377,0,04,0306,'r',0276,0210,'(','$',0,'I',0350,'(',0252,0323,0311,0270,0355,035,07,'Z',0355,0305,'b','c',0207,0246,0346,0367,0350,'L','U',0331,'$','R',0357,'r',017,0341,'R',0325,0,'H',' ',0216,0242,0256,0243,0207,'P',0302,0270,0362,0334,'c',0254,0234,'&',0375,0345,0375,'~',05,'N','6',0325,016,0252,'r',0246,0307,0366,'=','*',0345,'E',':',0346,'<',0372,'V',0271,0225,05,'V',0213,0227,'X',0353,0376,'a',07,'f','U',0242,0212,'+',0345,0215,0202,0212,'(',0240,02,0212,'(',0240,02,0212,'(',0240,02,0254,0301,'&','F',0303,0324,'t',0252,0324,0240,0225,'9',07,04,'W','N',023,020,0360,0365,024,0326,0335,'}',05,'%','t','^',0242,0232,0216,035,'C',012,'u','}','t','d',0247,025,'(',0354,0316,'p',0242,0243,0230,035,0231,'R','A',034,0360,'j',0266,0366,'?',0304,0177,':',0341,0305,'c',0326,036,'|',0262,0215,0313,0214,'n',0134,'$',016,0244,017,0255,'1',0246,'E',0357,0237,0245,'T',0242,0274,0352,0231,0305,'G',0360,'E','/',0307,0374,0212,'T',0321,';',0134,023,0367,'F','>',0265,013,'3','1',0344,0223,'I','E','y',0325,0261,'U','k','|','r',0271,'j',')','l',024,'Q','E','`','0',0242,0200,011,'8',03,'&',0247,'K','s',0325,0377,0,'!','[',0320,0303,'T',0256,0355,05,0376,'B','m','-',0310,0222,'6','s',0300,0374,'j',0322,'F',0261,0216,':',0367,'4',0360,0,030,03,02,0212,0372,'<','&',02,0236,037,0336,'z',0313,0277,0371,030,0312,'M',0205,024,'T',022,0317,0325,'S',0363,0256,0214,'F','"',0235,010,0363,'M',0211,'&',0302,'i',0261,0362,0251,0347,0271,0364,0252,0364,'Q','_','+',0211,0304,0317,021,'>','i','|',0227,'c','t',0254,025,'b',0333,'8','o','J',0257,0324,0325,0310,0327,'d','`','w',0357,']',0231,'M',047,'*',0374,0375,021,'3','z',017,0250,0346,0377,0,'R',0325,'%','C','p',0330,'@','=','M','{',0230,0311,'(',0341,0346,0337,'g',0370,0231,'G','r',0265,024,'Q','_',036,'t',05,024,'Q','@',05,024,'Q','@',05,024,'Q','@',05,024,'Q','@',022,'E','&',0306,0347,0241,0353,'V',0352,0205,'Y',0202,'L',0215,0207,0250,0351,'^',0326,'U',0213,0345,'~',0302,'{','=',0277,0310,0316,'q',0352,'M','U','&','M',0217,0307,'C',0322,0255,0322,'2',0207,0134,032,0364,0361,0270,'U',0210,0247,0312,0267,'[',021,031,'Y',0224,'h',0253,06,0330,'g',0206,0343,0351,'H','-',0217,'v',03,0351,'^',03,0313,'q',')',0333,0227,0361,'F',0274,0350,0202,0212,0260,'-',0206,'y','b','~',0202,0236,0260,0242,0366,0317,0326,0264,0206,'U',0210,0227,0305,'e',0375,'y',011,0315,025,'U','Y',0217,0,0232,0231,'m',0317,0361,034,'{',012,0261,0322,0212,0364,0250,0345,'4',0241,0254,0337,'7',0344,'C',0233,0350,'"',0252,0240,0302,0214,'R',0321,'E','z',0221,0212,0212,0264,'U',0221,01,'H','X','(',0311,'8',025,033,0316,0253,0302,0362,0177,'J',0256,0316,0316,'r','M','y',0270,0254,0316,0235,'/','v',032,0277,0300,0270,0301,0275,0307,0311,'1','~',07,03,0371,0324,'T','Q','_','=','V',0264,0353,'K',0232,'n',0354,0325,'$',0266,012,'(',0251,'b',0213,'y',0311,0373,0277,0316,0212,'4','g','Z','j',020,'Z',0203,'v',035,04,'y',';',0317,'N',0325,'b',0216,0224,'W',0326,'a',0260,0361,0303,0323,'P','_','3',06,0356,0302,0252,'L',0373,0337,0216,0203,0201,'S','M','&',0305,0300,0352,0177,'J',0253,'^','N','m',0211,'O',0367,'1',0371,0232,'A','u',012,'(',0242,0274,'C','@',0242,0212,'(',0,0242,0212,0222,010,'$',0271,0231,'b',0211,'w',';','t',031,0305,015,0333,'V','8',0305,0311,0250,0305,']',0262,':','+','C',0373,023,'Q',0377,0,0237,0177,0374,'}',0177,0306,0217,0354,'M','G',0376,'}',0377,0,0361,0365,0377,0,032,0317,0333,'S',0376,'e',0367,0235,0177,0331,0370,0317,0371,0365,'/',0374,05,0377,0,0221,0237,'E','h',0177,'b','j','?',0363,0357,0377,0,0217,0257,0370,0321,0375,0211,0250,0377,0,0317,0277,0376,'>',0277,0343,'G',0266,0247,0374,0313,0357,017,0354,0374,'g',0374,0372,0227,0376,02,0377,0,0310,0317,0245,04,0251,0310,'8','"',0257,0377,0,'b','j','?',0363,0357,0377,0,0217,0257,0370,0321,0375,0211,0250,0377,0,0317,0277,0376,'>',0277,0343,'M','V',0202,'w','R','_','x',0177,'g','c','?',0347,0324,0277,0360,027,0376,'C',021,0303,0250,'a','N',0251,'a',0322,'5',010,0333,0233,'~',017,'_',0235,0177,0306,0235,0366,';',0237,0371,0367,0227,0376,0371,'5',0365,'x',',',0306,0215,'z','I',0312,'j',0353,'}','Q',0315,'S',01,0211,0246,0375,0352,'r','_','&','A','E','O',0366,';',0237,0371,0367,0227,0376,0371,'4',013,'+',0243,0377,0,'.',0362,0177,0337,'5',0325,0365,0232,'?',0316,0276,0364,'g',0365,'J',0377,0,0310,0376,0346,'A','E','[',032,'e',0343,014,0210,'O',0342,0300,0177,'Z','C',0246,'_',016,0226,0304,0377,0,0300,0327,0374,'k',031,0346,'8','H','o','V','?','z','5','Y','n','1',0355,'J','_',0370,013,0377,0,'"',0255,035,'*','f',0323,'5','3',0322,0330,017,0370,032,0377,0,0215,'F',0332,'6',0244,0307,0230,011,0377,0,0266,0213,0376,'5',0305,'W','=',0303,'G',0370,'n',0377,0,';',024,0262,0274,'g','Z','R',0377,0,0300,'_',0371,020,'4',0352,0274,016,'M','@',0362,0263,0365,'<','z',012,0273,0375,0211,0250,0377,0,0317,0277,0376,'>',0277,0343,'G',0366,'&',0243,0377,0,'>',0377,0,0370,0372,0377,0,0215,'y',030,0214,0322,'U',0364,0224,0322,']',0223,'4','Y','f','-',0177,0313,0251,0177,0340,'/',0374,0214,0372,'+','C',0373,023,'Q',0377,0,0237,0177,0374,'}',0177,0306,0217,0354,'M','G',0376,'}',0377,0,0361,0365,0377,0,032,0343,0366,0324,0377,0,0231,'}',0345,0177,'g',0343,'?',0347,0324,0277,0360,027,0376,'F','}',025,0241,0375,0211,0250,0377,0,0317,0277,0376,'>',0277,0343,'I',036,0227,'r','9','{','y',011,0364,0332,'k','|','<',025,'y','r',0306,'K',0357,'3',0236,017,023,'O',0342,0247,'%',0362,'e','X',0241,',','r',0303,013,0374,0352,0327,'J',0237,0354,'w','?',0363,0357,'/',0375,0362,'h',0373,035,0317,0374,0373,0313,0377,0,'|',0232,0372,'l',',','p',0330,'x',0332,'3','W',0352,0356,0216,'y','a',0361,017,0354,'?',0271,0220,'S',']',0304,'k',0223,0370,012,0266,0272,'}',0343,0203,0266,0335,0362,';','7',0313,0374,0352,06,0321,0265,047,'l',0233,0177,0374,'}',0177,0306,0261,0307,'f',0264,'h',0307,0226,022,'N','^',0253,'C','J','y','v','.','j',0352,0224,0255,0376,027,0376,'F','{',022,0314,'I',0352,'i','+','C',0373,023,'Q',0377,0,0237,0177,0374,'}',0177,0306,0217,0354,'M','G',0376,'}',0377,0,0361,0365,0377,0,032,0371,0227,'^',015,0335,0311,'}',0347,'G',0366,'v','/',0376,'}','K',0377,0,01,0177,0344,'g',0321,'Z',037,0330,0232,0217,0374,0373,0377,0,0343,0353,0376,'4',0177,'b','j','?',0363,0357,0377,0,0217,0257,0370,0322,0366,0324,0377,0,0231,'}',0341,0375,0237,0214,0377,0,0237,'R',0377,0,0300,'_',0371,031,0364,'T',0223,0301,'%',0264,0315,024,0253,0265,0327,0250,0316,'j',':',0321,';',0352,0216,'I','E',0305,0270,0311,'Y',0240,0255,015,023,0376,'B',0360,0177,0300,0277,0364,023,'Y',0365,0241,0242,0177,0310,'^',017,0370,027,0376,0202,'k',':',0337,0303,0227,0243,':',0362,0357,0367,0312,'_',0342,0217,0346,0216,0276,0212,'(',0257,014,0375,'H','(',0242,0212,0,'(',0242,0212,0,'(',0242,0212,0,'(',0242,0212,0,'(',0242,0212,0,'(',0242,0212,0,'(',0242,0212,0,'(',0242,0212,0,'(',0242,0212,0,'(',0242,0212,0,'(',0242,0212,0,'(',0242,0212,0,'(',0242,0212,0,0344,'5',0277,0371,013,0317,0377,0,01,0377,0,0320,'E','g',0326,0206,0267,0377,0,'!','y',0377,0,0340,'?',0372,010,0254,0372,0367,'(',0377,0,016,'>',0210,0374,0267,'0',0377,0,'|',0253,0376,')','~','l','*',0346,0227,'<','v',0332,0224,'R',0312,0333,'Q','s',0223,0214,0366,'"',0251,0321,'U','(',0363,047,027,0324,0347,0243,'U',0321,0251,032,0221,0336,'-','?',0270,0353,0377,0,0266,0364,0357,0371,0370,0377,0,0307,033,0374,'(',0376,0333,0323,0277,0347,0343,0377,0,034,'o',0360,0256,'B',0212,0345,0372,0225,'>',0354,0367,0377,0,0326,'|','g',0362,0307,0356,0177,0346,'u',0377,0,0333,'z','w',0374,0374,0177,0343,0215,0376,024,0177,'m',0351,0337,0363,0361,0377,0,0216,'7',0370,'W','!','E',037,'R',0247,0335,0207,0372,0317,0214,0376,'X',0375,0317,0374,0316,0277,0373,'o','N',0377,0,0237,0217,0374,'q',0277,0302,0217,0355,0275,';',0376,'~','?',0361,0306,0377,0,012,0344,'(',0243,0352,'T',0373,0260,0377,0,'Y',0361,0237,0313,037,0271,0377,0,0231,0327,0377,0,'m',0351,0337,0363,0361,0377,0,0216,'7',0370,'Q',0375,0267,0247,0177,0317,0307,0376,'8',0337,0341,0134,0205,024,'}','J',0237,'v',037,0353,'>','3',0371,'c',0367,'?',0363,':',0377,0,0355,0275,';',0376,'~','?',0361,0306,0377,0,012,'?',0266,0364,0357,0371,0370,0377,0,0307,033,0374,'+',0220,0242,0217,0251,'S',0356,0303,0375,'g',0306,0177,',','~',0347,0376,'g','_',0375,0267,0247,0177,0317,0307,0376,'8',0337,0341,'G',0366,0336,0235,0377,0,'?',037,0370,0343,0177,0205,'r',024,'Q',0365,'*','}',0330,0177,0254,0370,0317,0345,0217,0334,0377,0,0314,0353,0377,0,0266,0364,0357,0371,0370,0377,0,0307,033,0374,'(',0376,0333,0323,0277,0347,0343,0377,0,034,'o',0360,0256,'B',0212,'>',0245,'O',0273,017,0365,0237,031,0374,0261,0373,0237,0371,0235,0177,0366,0336,0235,0377,0,'?',037,0370,0343,0177,0205,037,0333,'z','w',0374,0374,0177,0343,0215,0376,025,0310,'Q','G',0324,0251,0367,'a',0376,0263,0343,'?',0226,'?','s',0377,0,'3',0257,0376,0333,0323,0277,0347,0343,0377,0,034,'o',0360,0243,0373,'o','N',0377,0,0237,0217,0374,'q',0277,0302,0271,012,'(',0372,0225,'>',0354,'?',0326,'|','g',0362,0307,0356,0177,0346,'u',0377,0,0333,'z','w',0374,0374,0177,0343,0215,0376,024,0177,'m',0351,0337,0363,0361,0377,0,0216,'7',0370,'W','!','E',037,'R',0247,0335,0207,0372,0317,0214,0376,'X',0375,0317,0374,0316,0277,0373,'o','N',0377,0,0237,0217,0374,'q',0277,0302,0217,0355,0275,';',0376,'~','?',0361,0306,0377,0,012,0344,'(',0243,0352,'T',0373,0260,0377,0,'Y',0361,0237,0313,037,0271,0377,0,0231,0327,0377,0,'m',0351,0337,0363,0361,0377,0,0216,'7',0370,'Q',0375,0267,0247,0177,0317,0307,0376,'8',0337,0341,0134,0205,024,'}','J',0237,'v',037,0353,'>','3',0371,'c',0367,'?',0363,':',0377,0,0355,0275,';',0376,'~','?',0361,0306,0377,0,012,'?',0266,0364,0357,0371,0370,0377,0,0307,033,0374,'+',0220,0242,0217,0251,'S',0356,0303,0375,'g',0306,0177,',','~',0347,0376,'g','_',0375,0267,0247,0177,0317,0307,0376,'8',0337,0341,'G',0366,0336,0235,0377,0,'?',037,0370,0343,0177,0205,'r',024,'Q',0365,'*','}',0330,0177,0254,0370,0317,0345,0217,0334,0377,0,0314,0271,0252,'O',035,0316,0245,',',0261,'6',0344,'l','`',0343,035,0200,0252,'t','Q',']','Q',0217,'*','Q',']',017,02,0265,'W','Z',0244,0252,'K','y','6',0376,0360,0242,0212,'*',0214,0302,0212,'+','b',0313,'B',0373,'e',0234,'w',037,'i',0331,0277,'?','.',0314,0343,04,0217,'_','j',0211,0324,0215,'5','y','3',0247,013,0204,0255,0212,0233,0205,025,'v',0225,0372,'/',0314,0307,0242,0267,0377,0,0341,031,0377,0,0247,0277,0374,0207,0377,0,0327,0243,0376,021,0237,0372,'{',0377,0,0310,0177,0375,'z',0313,0353,'T','{',0376,'g',0177,0366,016,'a',0377,0,'>',0377,0,030,0377,0,0231,0201,'E','I','<','^','E',0304,0261,'g','v',0307,'+',0234,'c','8','8',0250,0353,'t',0357,0251,0344,'J','.','-',0305,0356,0202,0212,0222,010,0274,0373,0210,0242,0316,0335,0356,027,'8',0316,'2','q','[',0177,0360,0214,0377,0,0323,0337,0376,'C',0377,0,0353,0326,'s',0255,012,'z','I',0235,0230,0134,0273,023,0213,'N','T','#','t',0274,0322,0374,0331,0201,'E','o',0377,0,0302,'3',0377,0,'O',0177,0371,017,0377,0,0257,'L',0237,0303,0336,'E',0274,0262,0375,0253,'v',0304,'-',0217,'/',031,0300,0317,0255,'B',0305,'R','z','_',0363,':','e',0221,0343,0342,0234,0235,'=',027,0234,0177,0314,0303,0242,0212,'+',0240,0362,'B',0212,0320,0323,'4',0317,0355,037,'7',0367,0336,'_',0227,0217,0341,0316,'s',0237,0177,'j',0277,0377,0,010,0317,0375,'=',0377,0,0344,'?',0376,0275,'c','<','E','8','>','Y','=','O','K',017,0224,'c','1',024,0325,'Z','P',0274,'_',0232,0364,0352,0314,012,'+',0177,0376,021,0237,0372,'{',0377,0,0310,0177,0375,'z',0241,0251,0351,0237,0331,0336,'W',0357,0274,0317,'3','?',0303,0214,'c',036,0376,0364,'C',021,'N','o',0226,'/','P',0304,'e',030,0314,'=','7','V',0254,'-',025,0346,0275,':','3','>',0212,'(',0255,0217,'4','(',0255,0310,'<','=',0347,0333,0305,'/',0332,0266,0357,'@',0330,0362,0363,0214,0214,0372,0323,0377,0,0341,031,0377,0,0247,0277,0374,0207,0377,0,0327,0256,'w',0212,0244,0264,0277,0346,'z',0321,0310,0361,0362,'J','J',0236,0217,0316,'?',0346,'`','Q','[',0377,0,0360,0214,0377,0,0323,0337,0376,'C',0377,0,0353,0326,'$',0361,'y',027,022,0305,0235,0333,034,0256,'q',0214,0340,0342,0256,025,0241,'S','H',0263,0233,025,0227,'b','p',0211,'J',0274,'l',0237,0232,0177,0223,'#',0242,0212,0222,010,0274,0373,0210,0242,0316,0335,0356,027,'8',0316,'2','q','Z','7','m','N','8',0305,0311,0250,0255,0331,035,025,0277,0377,0,010,0317,0375,'=',0377,0,0344,'?',0376,0275,037,0360,0214,0377,0,0323,0337,0376,'C',0377,0,0353,0326,037,'Z',0243,0337,0363,'=',0177,0354,034,0303,0376,'}',0376,'1',0377,0,'3',02,0212,0330,0275,0320,0276,0307,'g','%',0307,0332,'w',0354,0307,0313,0263,031,0311,03,0327,0336,0261,0353,'X','T',0215,'E','x',0263,0203,025,0204,0255,0205,0232,0205,'e','f',0325,0372,'?',0310,'(',0242,0212,0263,0230,'(',0242,0212,0,'+',0257,0321,'?',0344,021,07,0374,013,0377,0,'B','5',0310,'W','_',0242,0177,0310,'"',017,0370,027,0376,0204,'k',0217,033,0374,'5',0352,'}',037,014,0177,0276,'K',0374,'/',0363,'F',0205,024,'Q','^','Y',0367,'g',021,0177,0377,0,'!',013,0237,0372,0352,0337,0314,0325,'z',0261,0177,0377,0,'!',013,0237,0372,0352,0337,0314,0325,'z',0367,0241,0360,0243,0362,'|','G',0361,0247,0352,0377,0,'2',0305,0207,0374,0204,'-',0277,0353,0252,0377,0,'1',']',0275,'q',026,037,0362,020,0266,0377,0,0256,0253,0374,0305,'v',0365,0347,0343,0276,'$','}',0177,013,0177,06,0247,0252,0374,0202,0253,0337,0177,0310,'>',0347,0376,0271,'?',0362,'5','b',0253,0337,0177,0310,'>',0347,0376,0271,'?',0362,'5',0307,017,0211,037,'G',0210,0376,014,0375,037,0344,'q',024,'Q','E','{',0307,0344,0346,0377,0,0206,0177,0345,0353,0376,01,0375,'k',0240,0256,0177,0303,'?',0362,0365,0377,0,0,0376,0265,0320,'W',0217,0212,0376,'3',0376,0272,037,0243,0344,'?',0362,'/',0247,0363,0377,0,0322,0230,'W','?',0342,'o',0371,'u',0377,0,0201,0377,0,'J',0350,'+',0237,0361,'7',0374,0272,0377,0,0300,0377,0,0245,030,'_',0343,'/',0353,0240,'g',0337,0362,'/',0251,0362,0377,0,0322,0221,0201,'E',024,'W',0260,'~','p','v',0366,'?',0362,017,0266,0377,0,0256,'I',0374,0205,'X',0252,0366,'?',0362,017,0266,0377,0,0256,'I',0374,0205,'X',0257,06,0177,023,'?','X',0303,0377,0,06,036,0213,0362,012,0342,'/',0377,0,0344,'!','s',0377,0,']','[',0371,0232,0355,0353,0210,0277,0377,0,0220,0205,0317,0375,'u','o',0346,'k',0263,03,0361,'3',0347,'8',0247,0370,'4',0375,'_',0344,'W',0253,026,037,0362,020,0266,0377,0,0256,0253,0374,0305,'W',0253,026,037,0362,020,0266,0377,0,0256,0253,0374,0305,'z',023,0370,'Y',0362,030,0177,0343,'C',0325,'~','g','o','E',024,'W',0202,'~',0260,'g',0353,0177,0362,010,0237,0376,03,0377,0,0241,012,0344,'+',0257,0326,0377,0,0344,021,'?',0374,07,0377,0,'B',025,0310,'W',0251,0202,0376,033,0365,'>',023,0211,0377,0,0337,'#',0376,025,0371,0260,0242,0212,'+',0260,0371,0300,0242,0212,'(',0,0256,0277,'D',0377,0,0220,'D',037,0360,'/',0375,010,0327,'!',']','~',0211,0377,0,' ',0210,'?',0340,'_',0372,021,0256,'<','o',0360,0327,0251,0364,'|','1',0376,0371,'/',0360,0277,0315,032,024,'Q','E','y','g',0335,0234,'E',0377,0,0374,0204,'.',0177,0353,0253,0177,'3','U',0352,0305,0377,0,0374,0204,'.',0177,0353,0253,0177,'3','U',0353,0336,0207,0302,0217,0311,0361,037,0306,0237,0253,0374,0313,026,037,0362,020,0266,0377,0,0256,0253,0374,0305,'v',0365,0304,'X',0177,0310,'B',0333,0376,0272,0257,0363,025,0333,0327,0237,0216,0370,0221,0365,0374,'-',0374,032,0236,0253,0362,012,0257,'}',0377,0,' ',0373,0237,0372,0344,0377,0,0310,0325,0212,0257,'}',0377,0,' ',0373,0237,0372,0344,0377,0,0310,0327,034,'>','$','}',036,'#',0370,'3',0364,0177,0221,0304,'Q','E',025,0357,037,0223,0233,0376,031,0377,0,0227,0257,0370,07,0365,0256,0202,0271,0377,0,014,0377,0,0313,0327,0374,03,0372,0327,'A','^','>','+',0370,0317,0372,0350,'~',0217,0220,0377,0,0310,0276,0237,0317,0377,0,'J','a',0134,0377,0,0211,0277,0345,0327,0376,07,0375,'+',0240,0256,0177,0304,0337,0362,0353,0377,0,03,0376,0224,'a',0177,0214,0277,0256,0201,0237,0177,0310,0276,0247,0313,0377,0,'J','F',05,024,'Q','^',0301,0371,0301,0333,0330,0377,0,0310,'>',0333,0376,0271,047,0362,025,'b',0253,0330,0377,0,0310,'>',0333,0376,0271,047,0362,025,'b',0274,031,0374,'L',0375,'c',017,0374,030,'z','/',0310,'+',0210,0277,0377,0,0220,0205,0317,0375,'u','o',0346,'k',0267,0256,'"',0377,0,0376,'B',027,'?',0365,0325,0277,0231,0256,0314,017,0304,0317,0234,0342,0237,0340,0323,0365,0177,0221,'^',0254,'X',0177,0310,'B',0333,0376,0272,0257,0363,025,'^',0254,'X',0177,0310,'B',0333,0376,0272,0257,0363,025,0350,'O',0341,'g',0310,'a',0377,0,0215,017,'U',0371,0235,0275,024,'Q','^',011,0372,0301,0237,0255,0377,0,0310,'"',0177,0370,017,0376,0204,'+',0220,0256,0277,'[',0377,0,0220,'D',0377,0,0360,037,0375,010,'W','!','^',0246,013,0370,'o',0324,0370,'N',047,0377,0,'|',0217,0370,'W',0346,0302,0212,'(',0256,0303,0347,02,0212,'(',0240,02,0272,0375,023,0376,'A',020,0177,0300,0277,0364,'#',0134,0205,'u',0372,047,0374,0202,' ',0377,0,0201,0177,0350,'F',0270,0361,0277,0303,'^',0247,0321,0360,0307,0373,0344,0277,0302,0377,0,'4','h','Q','E',025,0345,0237,'v','q',027,0377,0,0362,020,0271,0377,0,0256,0255,0374,0315,'W',0253,027,0377,0,0362,020,0271,0377,0,0256,0255,0374,0315,'W',0257,'z',037,012,'?',047,0304,0177,032,'~',0257,0363,',','X',0177,0310,'B',0333,0376,0272,0257,0363,025,0333,0327,021,'a',0377,0,'!',013,'o',0372,0352,0277,0314,'W','o','^','~',';',0342,'G',0327,0360,0267,0360,'j','z',0257,0310,'*',0275,0367,0374,0203,0356,0177,0353,0223,0377,0,'#','V','*',0275,0367,0374,0203,0356,0177,0353,0223,0377,0,'#',0134,'p',0370,0221,0364,'x',0217,0340,0317,0321,0376,'G',021,'E',024,'W',0274,'~','N','o',0370,'g',0376,'^',0277,0340,037,0326,0272,012,0347,0374,'3',0377,0,'/','_',0360,017,0353,']',05,'x',0370,0257,0343,'?',0353,0241,0372,'>','C',0377,0,'"',0372,0177,'?',0375,')',0205,'s',0376,'&',0377,0,0227,'_',0370,037,0364,0256,0202,0271,0377,0,023,0177,0313,0257,0374,017,0372,'Q',0205,0376,'2',0376,0272,06,'}',0377,0,'"',0372,0237,'/',0375,')',030,024,'Q','E','{',07,0347,07,'o','c',0377,0,' ',0373,'o',0372,0344,0237,0310,'U',0212,0257,'c',0377,0,' ',0373,'o',0372,0344,0237,0310,'U',0212,0360,'g',0361,'3',0365,0214,'?',0360,'a',0350,0277,' ',0256,'"',0377,0,0376,'B',027,'?',0365,0325,0277,0231,0256,0336,0270,0213,0377,0,0371,010,0134,0377,0,0327,'V',0376,'f',0273,'0','?',023,'>','s',0212,0177,0203,'O',0325,0376,'E','z',0261,'a',0377,0,'!',013,'o',0372,0352,0277,0314,'U','z',0261,'a',0377,0,'!',013,'o',0372,0352,0277,0314,'W',0241,'?',0205,0237,'!',0207,0376,'4','=','W',0346,'v',0364,'Q','E','x',047,0353,06,'~',0267,0377,0,' ',0211,0377,0,0340,'?',0372,020,0256,'B',0272,0375,'o',0376,'A',023,0377,0,0300,0177,0364,'!',0134,0205,'z',0230,'/',0341,0277,'S',0341,'8',0237,0375,0362,'?',0341,'_',0233,012,'(',0242,0273,017,0234,012,'(',0242,0200,012,0353,0364,'O',0371,04,'A',0377,0,02,0377,0,0320,0215,'r',025,0327,0350,0237,0362,010,0203,0376,05,0377,0,0241,032,0343,0306,0377,0,015,'z',0237,'G',0303,037,0357,0222,0377,0,013,0374,0321,0241,'E',024,'W',0226,'}',0331,0304,'_',0377,0,0310,'B',0347,0376,0272,0267,0363,'5','^',0254,'_',0377,0,0310,'B',0347,0376,0272,0267,0363,'5','^',0275,0350,'|','(',0374,0237,021,0374,'i',0372,0277,0314,0261,'a',0377,0,'!',013,'o',0372,0352,0277,0314,'W','o',0134,'E',0207,0374,0204,'-',0277,0353,0252,0377,0,'1',']',0275,'y',0370,0357,0211,037,'_',0302,0337,0301,0251,0352,0277,' ',0252,0367,0337,0362,017,0271,0377,0,0256,'O',0374,0215,'X',0252,0367,0337,0362,017,0271,0377,0,0256,'O',0374,0215,'q',0303,0342,'G',0321,0342,'?',0203,'?','G',0371,034,'E',024,'Q','^',0361,0371,'9',0277,0341,0237,0371,'z',0377,0,0200,0177,'Z',0350,'+',0237,0360,0317,0374,0275,0177,0300,'?',0255,'t',025,0343,0342,0277,0214,0377,0,0256,0207,0350,0371,017,0374,0213,0351,0374,0377,0,0364,0246,025,0317,0370,0233,0376,']',0177,0340,0177,0322,0272,012,0347,0374,'M',0377,0,'.',0277,0360,'?',0351,'F',027,0370,0313,0372,0350,031,0367,0374,0213,0352,'|',0277,0364,0244,'`','Q','E',025,0354,037,0234,035,0275,0217,0374,0203,0355,0277,0353,0222,0177,'!','V','*',0275,0217,0374,0203,0355,0277,0353,0222,0177,'!','V','+',0301,0237,0304,0317,0326,'0',0377,0,0301,0207,0242,0374,0202,0270,0213,0377,0,0371,010,0134,0377,0,0327,'V',0376,'f',0273,'z',0342,'/',0377,0,0344,'!','s',0377,0,']','[',0371,0232,0354,0300,0374,'L',0371,0316,')',0376,015,'?','W',0371,025,0352,0305,0207,0374,0204,'-',0277,0353,0252,0377,0,'1','U',0352,0305,0207,0374,0204,'-',0277,0353,0252,0377,0,'1','^',0204,0376,026,'|',0206,037,0370,0320,0365,'_',0231,0333,0321,'E',025,0340,0237,0254,031,0372,0337,0374,0202,047,0377,0,0200,0377,0,0350,'B',0271,012,0353,0365,0277,0371,04,'O',0377,0,01,0377,0,0320,0205,'r',025,0352,'`',0277,0206,0375,'O',0204,0342,0177,0367,0310,0377,0,0205,'~','l','(',0242,0212,0354,'>','p',0377,0331,0377,0341,014,'w','h','t','t','p',':','/','/','n','s','.','a','d','o','b','e','.','c','o','m','/','x','a','p','/','1','.','0','/',0,'<','?','x','p','a','c','k','e','t',' ','b','e','g','i','n','=','"',0357,0273,0277,'"',' ','i','d','=','"','W','5','M','0','M','p','C','e','h','i','H','z','r','e','S','z','N','T','c','z','k','c','9','d','"','?','>',' ','<','x',':','x','m','p','m','e','t','a',' ','x','m','l','n','s',':','x','=','"','a','d','o','b','e',':','n','s',':','m','e','t','a','/','"',' ','x',':','x','m','p','t','k','=','"','X','M','P',' ','C','o','r','e',' ','4','.','4','.','0','-','E','x','i','v','2','"','>',' ','<','r','d','f',':','R','D','F',' ','x','m','l','n','s',':','r','d','f','=','"','h','t','t','p',':','/','/','w','w','w','.','w','3','.','o','r','g','/','1','9','9','9','/','0','2','/','2','2','-','r','d','f','-','s','y','n','t','a','x','-','n','s','#','"','>',' ','<','r','d','f',':','D','e','s','c','r','i','p','t','i','o','n',' ','r','d','f',':','a','b','o','u','t','=','"','"',' ','x','m','l','n','s',':','x','m','p','M','M','=','"','h','t','t','p',':','/','/','n','s','.','a','d','o','b','e','.','c','o','m','/','x','a','p','/','1','.','0','/','m','m','/','"',' ','x','m','l','n','s',':','s','t','E','v','t','=','"','h','t','t','p',':','/','/','n','s','.','a','d','o','b','e','.','c','o','m','/','x','a','p','/','1','.','0','/','s','T','y','p','e','/','R','e','s','o','u','r','c','e','E','v','e','n','t','#','"',' ','x','m','l','n','s',':','d','c','=','"','h','t','t','p',':','/','/','p','u','r','l','.','o','r','g','/','d','c','/','e','l','e','m','e','n','t','s','/','1','.','1','/','"',' ','x','m','l','n','s',':','G','I','M','P','=','"','h','t','t','p',':','/','/','w','w','w','.','g','i','m','p','.','o','r','g','/','x','m','p','/','"',' ','x','m','l','n','s',':','x','m','p','=','"','h','t','t','p',':','/','/','n','s','.','a','d','o','b','e','.','c','o','m','/','x','a','p','/','1','.','0','/','"',' ','x','m','p','M','M',':','D','o','c','u','m','e','n','t','I','D','=','"','g','i','m','p',':','d','o','c','i','d',':','g','i','m','p',':','8','0','0','1','1','d','e','d','-','1','d','8','3','-','4','f','6','e','-','8','a','a','c','-','9','d','6','c','1','b','5','b','1','f','7','c','"',' ','x','m','p','M','M',':','I','n','s','t','a','n','c','e','I','D','=','"','x','m','p','.','i','i','d',':','f','7','d','e','2','c','2','a','-','2','0','e','7','-','4','7','2','c','-','8','7','4','7','-','6','8','6','7','a','4','4','1','8','e','8','3','"',' ','x','m','p','M','M',':','O','r','i','g','i','n','a','l','D','o','c','u','m','e','n','t','I','D','=','"','x','m','p','.','d','i','d',':','c','4','f','1','2','1','6','2','-','7','b','0','6','-','4','0','e','2','-','9','8','3','a','-','f','7','e','1','a','3','8','3','3','b','b','4','"',' ','d','c',':','F','o','r','m','a','t','=','"','i','m','a','g','e','/','j','p','e','g','"',' ','G','I','M','P',':','A','P','I','=','"','2','.','0','"',' ','G','I','M','P',':','P','l','a','t','f','o','r','m','=','"','L','i','n','u','x','"',' ','G','I','M','P',':','T','i','m','e','S','t','a','m','p','=','"','1','6','7','6','1','6','6','5','6','9','0','7','7','6','8','8','"',' ','G','I','M','P',':','V','e','r','s','i','o','n','=','"','2','.','1','0','.','3','0','"',' ','x','m','p',':','C','r','e','a','t','o','r','T','o','o','l','=','"','G','I','M','P',' ','2','.','1','0','"','>',' ','<','x','m','p','M','M',':','H','i','s','t','o','r','y','>',' ','<','r','d','f',':','S','e','q','>',' ','<','r','d','f',':','l','i',' ','s','t','E','v','t',':','a','c','t','i','o','n','=','"','s','a','v','e','d','"',' ','s','t','E','v','t',':','c','h','a','n','g','e','d','=','"','/','"',' ','s','t','E','v','t',':','i','n','s','t','a','n','c','e','I','D','=','"','x','m','p','.','i','i','d',':','d','e','2','7','4','d','3','3','-','d','6','7','a','-','4','1','7','d','-','9','b','e','8','-','8','1','7','e','8','c','c','d','1','2','b','a','"',' ','s','t','E','v','t',':','s','o','f','t','w','a','r','e','A','g','e','n','t','=','"','G','i','m','p',' ','2','.','1','0',' ','(','L','i','n','u','x',')','"',' ','s','t','E','v','t',':','w','h','e','n','=','"','2','0','2','3','-','0','2','-','1','2','T','0','1',':','4','9',':','2','9','+','0','0',':','0','0','"','/','>',' ','<','/','r','d','f',':','S','e','q','>',' ','<','/','x','m','p','M','M',':','H','i','s','t','o','r','y','>',' ','<','/','r','d','f',':','D','e','s','c','r','i','p','t','i','o','n','>',' ','<','/','r','d','f',':','R','D','F','>',' ','<','/','x',':','x','m','p','m','e','t','a','>',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','<','?','x','p','a','c','k','e','t',' ','e','n','d','=','"','w','"','?','>',0377,0342,02,0260,'I','C','C','_','P','R','O','F','I','L','E',0,01,01,0,0,02,0240,'l','c','m','s',04,'0',0,0,'m','n','t','r','R','G','B',' ','X','Y','Z',' ',07,0347,0,02,0,014,0,01,0,'1',0,06,'a','c','s','p','A','P','P','L',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0366,0326,0,01,0,0,0,0,0323,'-','l','c','m','s',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,015,'d','e','s','c',0,0,01,' ',0,0,0,'@','c','p','r','t',0,0,01,'`',0,0,0,'6','w','t','p','t',0,0,01,0230,0,0,0,024,'c','h','a','d',0,0,01,0254,0,0,0,',','r','X','Y','Z',0,0,01,0330,0,0,0,024,'b','X','Y','Z',0,0,01,0354,0,0,0,024,'g','X','Y','Z',0,0,02,0,0,0,0,024,'r','T','R','C',0,0,02,024,0,0,0,' ','g','T','R','C',0,0,02,024,0,0,0,' ','b','T','R','C',0,0,02,024,0,0,0,' ','c','h','r','m',0,0,02,'4',0,0,0,'$','d','m','n','d',0,0,02,'X',0,0,0,'$','d','m','d','d',0,0,02,'|',0,0,0,'$','m','l','u','c',0,0,0,0,0,0,0,01,0,0,0,014,'e','n','U','S',0,0,0,'$',0,0,0,034,0,'G',0,'I',0,'M',0,'P',0,' ',0,'b',0,'u',0,'i',0,'l',0,'t',0,'-',0,'i',0,'n',0,' ',0,'s',0,'R',0,'G',0,'B','m','l','u','c',0,0,0,0,0,0,0,01,0,0,0,014,'e','n','U','S',0,0,0,032,0,0,0,034,0,'P',0,'u',0,'b',0,'l',0,'i',0,'c',0,' ',0,'D',0,'o',0,'m',0,'a',0,'i',0,'n',0,0,'X','Y','Z',' ',0,0,0,0,0,0,0366,0326,0,01,0,0,0,0,0323,'-','s','f','3','2',0,0,0,0,0,01,014,'B',0,0,05,0336,0377,0377,0363,'%',0,0,07,0223,0,0,0375,0220,0377,0377,0373,0241,0377,0377,0375,0242,0,0,03,0334,0,0,0300,'n','X','Y','Z',' ',0,0,0,0,0,0,'o',0240,0,0,'8',0365,0,0,03,0220,'X','Y','Z',' ',0,0,0,0,0,0,'$',0237,0,0,017,0204,0,0,0266,0304,'X','Y','Z',' ',0,0,0,0,0,0,'b',0227,0,0,0267,0207,0,0,030,0331,'p','a','r','a',0,0,0,0,0,03,0,0,0,02,'f','f',0,0,0362,0247,0,0,015,'Y',0,0,023,0320,0,0,012,'[','c','h','r','m',0,0,0,0,0,03,0,0,0,0,0243,0327,0,0,'T','|',0,0,'L',0315,0,0,0231,0232,0,0,'&','g',0,0,017,0134,'m','l','u','c',0,0,0,0,0,0,0,01,0,0,0,014,'e','n','U','S',0,0,0,010,0,0,0,034,0,'G',0,'I',0,'M',0,'P','m','l','u','c',0,0,0,0,0,0,0,01,0,0,0,014,'e','n','U','S',0,0,0,010,0,0,0,034,0,'s',0,'R',0,'G',0,'B',0377,0333,0,'C',0,03,02,02,03,02,02,03,03,03,03,04,03,03,04,05,010,05,05,04,04,05,012,07,07,06,010,014,012,014,014,013,012,013,013,015,016,022,020,015,016,021,016,013,013,020,026,020,021,023,024,025,025,025,014,017,027,030,026,024,030,022,024,025,024,0377,0333,0,'C',01,03,04,04,05,04,05,011,05,05,011,024,015,013,015,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,0377,0302,0,021,010,01,0,01,0,03,01,021,0,02,021,01,03,021,01,0377,0304,0,035,0,01,01,0,03,01,01,01,01,01,0,0,0,0,0,0,0,0,010,05,06,07,04,03,011,02,01,0377,0304,0,034,01,01,0,01,05,01,01,0,0,0,0,0,0,0,0,0,0,0,06,02,03,04,05,07,01,010,0377,0332,0,014,03,01,0,02,020,03,020,0,0,01,0252,'@',0,0,0,0,0363,0325,0347,'%',0333,'Q',0310,0267,'6',0371,0316,0316,0215,'s',047,0317,0207,0276,'l',026,'*',0354,0332,';',0224,0324,'^',0367,0276,0217,'@',0,0,0,0,0,0,03,023,'w',0311,0316,'I','f','{',0222,'Z',0302,0344,'R',0,0,'u','M','=',0313,0256,05,0225,0376,0200,0,0,0,0,0,03,0371,047,0271,025,0231,'n','Y','c',023,0177,0301,0226,0261,0357,'b',0322,0335,0352,'z',0212,0367,'<','*',0275,0226,0375,0347,0273,'*','#','y',0276,'0',0263,0240,0331,'=',0313,'G','t',0,0,0,0,0,03,03,0177,0310,0302,'m',0215,0313,'7',026,0306,0331,0205,'U','5',026,0277,0334,'4','W','=','T',0372,0,0302,0335,0363,0363,'g',0250,'a',015,0207,026,0257,0320,0376,'o',0227,0220,0243,0320,0,0,0,0,03,'F',0316,0242,033,0236,0343,'k',0231,'T',0372,'(',0366,0233,0212,0337,0245,'c','7','}',024,0372,0,02,'[',0225,'X',0230,0345,'v',0,0244,0342,0327,0352,0330,0235,0360,0,0,0,0,032,'F','u','0','w','@',0305,0303,0337,0247,'h',0303,0252,0333,0202,0344,0357,0270,025,0200,0,034,0347,'c','D',031,0320,0261,'~',025,'y',0330,0364,'W','x',0346,0366,0327,0336,0237,0177,'B','9',0306,'^',0343,0207,'P',0,0,0,03,025,'w',0317,0317,0336,0211,0211,0253,'f','S',0274,'k',0353,0272,0240,'9','Y',0353,036,0200,0,032,'F','m','0',0217,'@',0305,0302,'d','S',0326,'4',0327,'.','X',036,'T','!','?',0305,0346,';','k','}','g','M','r',0347,0201,0345,0,0,0,0,'#',0271,0226,'?',03,0221,0331,0330,'1',0252,0277,0271,0336,'V',0305,0217,'P',0,01,0312,0266,0266,0342,0271,0316,'6','"',0377,0,0233,0236,05,'w',0267,'>',0312,0313,0332,0367,0224,0355,'m',0302,0275,03,024,'[','p','<',0256,0313,0246,0270,0,0,0,'5','|',0232,0177,':',0372,'^',047,0317,0337,'-',0350,026,'W','b',0323,0334,0,01,0342,0253,0311,'f','Y','f','u',0223,'Y',0376,'=',0363,'{',0327,'W','s','@',0262,0263,0366,'=',034,012,'A','j','<',0232,0343,015,0247,016,0257,0320,0316,'q',0227,0353,0247,0320,0,0,016,01,' ',0265,037,'M','q',0267,'L',012,0377,0,'C','9',0316,'X',0,'y','j',0363,0205,'o',0255,0314,'R',0253,032,0336,']','#',0265,0350,'.',0330,0260,0334,0214,0205,0277,'G',0206,0277,'?','=',0372,'6','&',0253,0231,'H',025,04,'N',0375,'A',026,0276,0,0,01,':',0310,0354,0311,'3',',','}',0323,02,0273,'b',013,0223,0262,0342,0373,0207,0277,0346,0205,0260,0247,0222,'n','-',0361,'m',0355,0274,'=',0372,'F','w',032,0252,0302,'!',0177,0271,0350,0256,0201,0374,0221,0204,0333,033,0210,'o',0355,'f',0361,0352,0244,0342,0327,0245,031,0216,'?',0262,0337,0277,0241,'|',0337,'/','i',0306,0250,0,0,032,0276,'M','?',0236,'}','#',023,0307,'s',0300,0,03,'5',0217,'U',023,032,0273,'F','F',0257,'d','h',0364,017,0231,'!',0314,'q',0347,0371,'%',0237,0357,0317,'m',0370,026,'O',0134,0324,0134,0201,':',026,047,'<',0331,0321,'D',0306,'o','V',0361,014,0200,0,0,011,0276,'I','f','O',0230,0343,0201,0376,0233,'F',025,']','+','Y','_','d',0322,']',0354,032,'z',0375,'4',0372,0,0301,0336,0362,'5',0233,'c','r',']',0315,0277,0364,0256,0241,'y','4',',','z',0350,0233,0344,0226,'d',0371,0216,'?','d',0321,']',0267,' ',0371,' ',0,0,037,0301,04,'t',034,'N','s',0264,0243,0276,0306,0357,'W','P',0374,0217,'O',0200,0,037,0341,0303,'7',0266,0244,0311,0205,0214,'6','E','>',0212,'=',0257,'a','y',035,0357,'C','t',010,'N','{',0213,0312,0367,026,0351,'h',0265,0372,0252,047,'|',0,0,0,'i',0231,0224,0376,'~','t','l','O',0237,0257,0320,'N','s',0227,0273,'a','T',0,0363,'U',0347,032,0335,'[',0233,'$',0366,'t',015,0225,03,'j',0303,0252,0316,0204,'d',0364,0215,'m','`','q','m',0325,0270,0242,'w',0213,0365,0247,0337,0320,0236,'q',0227,0271,'a',0324,0,0,0,04,0321,'&',0263,'+','K',0361,0367,'m','}','t','$','v',0357,0266,0217,'p',0327,'|',0347,0273,'*','9','F',0336,0336,'*',0377,0,0203,0325,'G',0264,0134,'f',0365,';',026,0275,0356,0243,0320,'4',034,0372,' ',0376,0201,0213,0215,0275,0345,033,027,0277,'Y',0304,0257,0200,0,0,0,'>','^',0240,'^',0203,0211,0317,0266,'t',0,0,0314,'X',0253,0274,'G',0256,0321,0321,0253,0273,026,'=','@',016,'k',0262,0242,' ',0236,'b',0342,'/',0371,0324,0265,027,'.','X',036,'O',0246,0237,'@',0,'|',0374,0253,03,'g','`',0,03,'G',0316,0246,'$',0235,0343,0377,0,'&','Z',0313,'=',0217,0356,0357,0201,'W','Q',0325,0134,0352,'Z',0252,0376,0364,0200,03,0214,'n','h',0226,'e',0326,'|',0365,'y',0273,'`','U','g','B','r','2','6',0300,014,0365,0355,0177,0323,0332,'F',0273,'c','e',020,0305,0273,0330,0,0,0,0,0,0,0,0,0,02,0336,0224,0360,'M',0212,0376,0264,'k',0266,'6','Q',014,'[',0275,0200,0,0,0,0,0,0,0,0,0,'-',0351,'O',04,0330,0257,0353,'F','&',0336,'d',0331,0243,0352,0,0,0,0,0,0,0,0,0,0,012,'O','y',0313,0362,0327,'0',0300,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,034,0277,02,'U',0255,0330,0332,0216,0327,0264,0202,'z','j',0267,0363,0362,0256,037,0252,0236,0214,0345,0355,0177,'X',0330,'D',06,0245,0215,0270,0347,030,'R','q',0321,0363,'c',033,'n','N',0234,'r','}','|',0277,07,'g','`',';',0206,0326,05,0364,0366,0237,'5','7','8',0246,0256,'v','6','K',0372,0256,0241,0237,025,0,01,'(','G',0272,0377,0,'.',0300,0227,013,0262,'Y',0363,0346,'R',0346,'/',0232,0233,0220,'D','G',0350,0221,0277,'e',0307,'l',')','/',024,034,'o','Y','6',0230,0364,']',0134,'S',0233,0336,'Q',0331,'6','p',0221,036,0306,0273,'^',0203,0211,'"',027,0274,0273,0347,'o','M','V',0361,'v',0362,0241,'8',0237,0320,'c',0250,0347,0304,'j',0371,017,' ',0,01,'(','G',0272,0377,0,'.',0300,0227,013,0262,'Y',0363,0346,'R',0346,'/',0232,0233,0220,'D','G',0350,0221,0277,'e',0307,'l',')','/',024,034,'o','Y','6',0230,0364,']',0134,'S',0233,0336,'Q',0331,'6','p',0221,036,0306,0273,'^',0203,0211,'"',027,0274,0273,0347,'o','M','V',0361,'v',0362,0241,'8',0237,0320,'c',0250,0347,0304,'j',0371,017,' ',0,01,'(','G',0272,0377,0,'.',0300,0227,013,0262,'Y',0363,0346,'R',0346,'/',0232,0233,0220,'D','G',0350,0221,0277,'e',0307,'l',')','/',024,034,'o','Y','6',0230,0364,']',0134,'S',0233,0336,'Q',0331,'6','p',0221,036,0306,0273,'^',0203,0211,'"',027,0274,0273,0347,'o','M','V',0361,'v',0362,0241,'8',0237,0320,'c',0250,0347,0304,'j',0371,017,' ',0,01,'(','G',0272,0377,0,'.',0300,0227,013,0262,'Y',0363,0346,'R',0346,'/',0232,0233,0220,'D','G',0350,0221,0277,'e',0307,'l',')','/',024,034,'o','Y','6',0230,0364,']',0134,'S',0233,0336,'Q',0331,'6','p',0221,036,0306,0273,'^',0203,0211,'"',027,0274,0273,0347,'o','M','V',0361,'v',0362,0241,'8',0237,0320,'c',0250,0347,0304,'j',0371,017,' ',0,01,'(','G',0272,0377,0,'.',0300,0227,013,0262,'Y',0363,0346,'R',0346,'/',0232,0233,0220,'D','G',0350,0221,0277,'e',0307,'l',')','/',024,034,'o','Y','6',0230,0364,']',0134,'S',0233,0336,'Q',0331,'6','p',0221,036,0306,0273,'^',0203,0211,'"',027,0274,0273,0347,'o','M','V',0361,'v',0362,0241,'8',0237,0320,'c',0250,0347,0304,'j',0371,017,' ',0,01,'(','G',0272,0377,0,'.',0300,0227,013,0262,'Y',0363,0346,'R',0346,'/',0232,0233,0220,'D','G',0350,0221,0277,'e',0307,'l',')','/',024,034,'o','Y','6',0230,0364,']',0134,'S',0233,0336,'Q',0331,'6','p',0221,036,0306,0273,'^',0203,0211,'"',027,0274,0273,0347,'o','M','V',0361,'v',0362,0241,'8',0237,0320,'c',0250,0347,0304,'j',0371,017,' ',0,01,'(','G',0272,0377,0,'.',0300,0227,013,0262,'Y',0363,0346,'R',0346,'/',0232,0233,0220,'D','G',0350,0221,0277,'e',0307,'l',')','/',024,034,'o','Y','6',0230,0364,']',0134,'S',0233,0336,'Q',0331,'6','p',0221,036,0306,0273,'^',0203,0211,'"',027,0274,0273,0347,'o','M','V',0361,'v',0362,0241,'8',0237,0320,'c',0250,0347,0304,'j',0371,017,' ',0,01,'(','G',0272,0377,0,'.',0300,0227,013,0262,'Y',0363,0346,'R',0346,'/',0232,0233,0220,'D','G',0350,0221,0277,'e',0307,'l',')','/',024,034,'o','Y','6',0230,0364,']',0134,'S',0233,0336,'Q',0331,'6','p',0221,036,0306,0273,'^',0203,0211,'"',027,0274,0273,0347,'o','M','V',0361,'v',0362,0241,'8',0237,0320,'c',0250,0347,0304,'j',0371,017,' ',03,0377,0304,0,'+',020,0,01,03,03,03,04,02,01,05,01,01,0,0,0,0,0,05,03,04,06,0,07,020,01,02,027,026,' ','5','6','0','4',024,021,022,023,025,'@','1','`',0377,0332,0,010,01,01,0,01,05,02,0371,0134,'9','I',0242,'E',0256,0240,'A',0332,0220,0274,'d',0226,0325,0334,0350,0373,0335,024,'2',0375,'j',0332,'I',0336,0335,'Y','L','M',0217,0324,05,0341,'W','n',0366,'/',0233,0222,'k',0376,'R','e',0231,0207,'m',' ',0274,':',0376,0244,0315,'>','2',0267,'}',0277,0227,'n',0215,025,0323,'_',0327,'O',0361,'n',0335,0246,0315,0262,0253,0260,0335,0225,022,'*',0354,0303,0234,016,022,0360,0272,0242,0355,01,'W','z','0',0264,01,0333,0323,'{',0177,037,'m',0246,0350,'P','-',0332,036,0264,0303,035,'6',0305,0255,0224,0177,'n','+',0374,'&','N','3',0,0316,']','?','{',047,0337,0200,'1','b','R','5','@','Z','A',0354,'t','l',0325,026,'h',0366,032,'#',0260,'H',0234,0,'2',0264,'|',0260,0367,0310,0223,'e',0363,0313,'%',0255,'b',0214,'N',0237,'y','#','}','M',0333,0252,0351,'h',0225,0247,0375,0264,0335,0272,'M',021,0356,0273,0262,'}','7',0353,0233,'G','(',0376,025,0276,'i','d',0251,0264,'P','q','b',0316,'M',0276,0250,0364,'q',0344,0225,0364,'V',032,0306,',',0337,0276,'m','.','J','*','1','u',0324,'t',0275,'%','o','W','V',023,'H','-',0275,0262,0321,'9',012,'r','`',0277,'!','B','h',07,'a','$',0220,0270,0222,0224,0250,0234,'Q',0314,0251,0370,'P',0215,0,'1',0357,0225,'K',032,'E','X',0230,'0',0344,0351,012,0267,0321,015,'d',0244,0364,0331,0246,0233,'n',04,'c',0246,0316,'U',0271,0224,'t',0361,0257,0222,0353,'J','?',0261,'%','@',0202,0270,0220,023,0,011,0264,'t','o','|',0306,'|',0322,'0',0231,'R',0256,0215,'=',0250,0264,']',0314,0244,0210,0221,'M',0302,0260,0253,0232,0301,027,0221,034,'[',')','G',0367,0201,0376,')','1','}',01,02,'Q','M',0313,')','V',0322,'/',0240,' ',0275,0317,036,0240,'=',0274,0266,0353,0252,0352,0267,0357,0334,0246,0372,0212,'D',']',0312,0236,04,010,0322,'>',0303,027,'|',0316,0326,0241,'q',031,':',0254,'p',0313,'G','I','>','m',0360,0336,'G','[',0222,03,'P',0321,'[','L',0311,0273,0134,0272,'E',0222,'2',033,0270,0311,0225,032,0221,020,0220,0257,0210,'m',0266,'r','w','V',03,0333,0213,'k',0207,0317,'Q',032,0316,'L','y','Y','!',0234,0332,'9','G',0305,'z',033,'~',0341,'u',017,'=',0244,'l',0360,0351,0230,'R',0233,0177,'=',0265,'9',0224,010,'i',0251,013,0256,011,0236,0205,0257,021,07,'4','H',0303,0323,012,0340,'@','G',0247,0134,0304,0355,'s','@',0375,0232,0353,0246,0335,'.','<',0343,0373,0367,024,024,'B',0347,'I',0134,'(','*',010,0307,'i',0233,0265,'X',':',0215,035,'J','F',033,0340,0222,0204,0331,'!',012,0361,0242,0254,035,'w',0214,014,0370,0312,0261,0353,'=',0246,0224,0300,'s','a','m',0363,0277,'~',0324,0266,0134,033,0207,0251,'l','l',0331,0252,0233,0255,0344,'7',0246,0230,'n',0333,0246,0375,0263,0230,0326,0261,0223,0225,'f',0312,0357,'H',0247,0303,'u',0342,'_',0220,0216,0177,0355,'2',0213,027,'!',0250,0333,'B',']',0335,010,0265,01,0207,0323,'v',0310,0263,'G',0260,0271,0246,'@',0232,0314,0256,013,0251,'>',0352,0323,'O',0327,'[','u','o',0377,0,0254,0323,027,0247,'z',0177,0201,'V',0237,'n',0272,0313,0376,035,0373,'6',0253,0262,'u',027,0335,027,'3','V',0244,0273,']',0217,0323,'l',0222,'=',0372,0353,0246,0335,'%','7','M',0220,0255,012,0230,'x','m',0325,' ',0202,0216,0226,0201,0333,0235,0241,0273,'.','<',0207,'C',0322,'*',0263,02,0365,0334,0347,0342,0226,0307,023,0223,0206,'r',0331,'V','N',021,'W','z',012,0303,0344,'[','$',0301,';',034,'9','I',0242,'G','n',0270,0241,0265,'!',0234,025,0221,0353,0200,021,0247,0362,'W','Q','8','3',030,0252,'y',0271,'S',015,01,016,0244,0323,0334,0262,0221,020,'Z','G','@','|','w','b','%',0374,0211,0324,'Z','Z',0362,'(',0351,0265,0347,037,0271,'=','o',' ','j','w','z','[','m',0320,0225,0332,'4',0362,0237,0225,'x','Q','L','6','j',0263,0325,0343,026,0215,'E','u','d',0305,0270,0346,0371,0227,0313,0233,'E',030,021,' ',0271,'W',0265,'j','b',':',0271,'q',0362,'*',0226,0325,0223,0233,'E',0367,'E',0314,0367,015,016,0364,0272,0240,0254,0352,0352,0320,'x',0360,0360,011,'v','L',047,015,'"',0250,025,'*',0344,0323,0352,0203,'B',0225,0225,'<','n',0335,'6',0210,'w','(',0246,0324,'S',0352,'1','U',0324,'b',0253,0250,0305,'W','Q',0212,0256,0243,025,']','F','*',0246,033,'C','J',03,'n','h',0266,0335,0337,0214,0255,'"',011,0332,0373,032,0303,']',0270,0321,0215,0272,'a',0255,013,0211,0303,06,0322,'&',0302,0266,'O',0250,0305,'W','Q',0212,0256,0243,025,']','F','*',0272,0214,'U','K',0256,'v',0250,'h',0343,'G','N',0326,0374,'e','j','/',030,'L',0313,0241,0344,0201,012,'g',0324,'b',0253,0250,0305,'W','Q',0212,0256,0243,025,'I',0251,0265,'d',0361,'#',0365,0377,0,0366,'G','=',0177,022,'?','_',0377,0,'d','s',0327,0360,']',0256,0367,0242,0270,0320,0275,'q',0241,'z',0343,'B',0365,0306,0205,0353,0215,013,0327,032,027,0256,'4','/',0134,'h','^',0270,0320,0275,'q',0241,'z',0343,'B',0365,0306,0205,0353,0215,013,0327,032,027,0256,'4','/',0134,'h','^',0270,0320,0275,'q',0241,'z',0343,'B',0365,0306,0205,0353,0215,013,0327,032,027,0256,'4','/',0134,'h','^',0270,0320,0275,'q',0241,'z',0343,'B',0365,0306,0205,0350,'C',']',0354,0205,0177,0341,'$',023,0244,0200,021,0345,'D','+',0225,020,0246,0313,'~','K','j','S',0177,0361,0247,0312,0210,'W','*','!','A','.',02,'&',0211,0342,'I','!',0331,034,'g',0312,0210,'W','*','!','Q',0271,016,0311,033,'<',033,0270,010,0205,047,0312,0210,'W','*','!','I',0357,0376,'D',0351,0312,0337,0214,0333,0225,020,0256,'T','B',0243,0363,0244,0217,0221,0356,0270,0376,0313,0201,'~','2',0234,0375,'|','A','=',0257,027,'G',0302,'b',0327,'x','L','N',0375,0257,015,0276,0275,024,0361,0230,0267,036,0313,0335,'q',0375,0227,02,0374,'e','9',0372,0370,0202,'{','^','.',0217,0204,0305,0256,0360,0230,0235,0373,'^',033,'}','z',')',0343,'1','n','=',0227,0272,0343,0373,'.',05,0370,0312,'s',0365,0361,04,0366,0274,']',037,011,0213,']',0341,'1',';',0366,0274,'6',0372,0364,'S',0306,'b',0334,'{','/','u',0307,0366,0134,013,0361,0224,0347,0353,0342,011,0355,'x',0272,'>',023,026,0273,0302,'b','w',0355,'x','m',0365,0350,0247,0214,0305,0270,0366,'^',0353,0217,0354,0270,027,0343,')',0317,0327,0304,023,0332,0361,'t','|','&','-','w',0204,0304,0357,0332,0360,0333,0353,0321,'O',031,0213,'q',0354,0275,0327,037,0331,'p','/',0306,'S',0237,0257,0210,047,0265,0342,0350,0370,'L','Z',0357,011,0211,0337,0265,0341,0267,0327,0242,0236,'3',026,0343,0331,'{',0256,'?',0262,0340,'_',0214,0247,'?','_',020,'O','k',0305,0321,0360,0230,0265,0336,023,023,0277,'k',0303,'o',0257,'E','<','f','-',0307,0262,0367,0134,0177,'e',0300,0277,031,'N','~',0276,' ',0236,0327,0213,0243,0341,'1','k',0274,'&',047,'~',0327,0206,0337,'^',0212,'x',0314,'[',0217,'e',0317,0377,0304,0,'9',021,0,01,01,04,02,017,010,03,0,03,01,0,0,0,0,0,01,02,0,03,04,020,06,021,05,022,025,026,' ','0','4','5','Q','S','r',0201,0221,0222,0341,'1','2','3','A','q',0261,0262,0301,023,'!','@','"','C',0321,'`',0377,0332,0,010,01,03,01,01,'?',01,0307,025,0200,0337,0220,0265,0261,'j',0313,'V','Z',0330,0260,'y',0247,0371,0353,0251,0213,0315,014,'I','8',0204,'*',0257,0345,'R',0364,'1','5',0314,012,0330,';','-',0370,0303,'Z',0206,0265,014,'P','<',0246,0203,'X',0376,'"','j','e','.',0271,0204,0222,0301,0330,0363,0302,'?',0251,0203,'Q',0376,025,'*',0325,0211,0256,'i','F',0234,'C',0303,0345,0200,0354,0371,'c',0324,'m','X',0232,0344,05,'l',023,'k',0210,'R',0255,'g','i',0376,'5',0314,032,0306,'4',0376,0230,0232,0344,0224,0333,'0',025,'b',012,0255,'X',0232,0344,0204,0327,'%',0212,0214,0220,'j','8',0325,0237,')',01,'[',01,'V','!','J',0251,0211,0256,'I','M',0263,01,'T',0227,0331,'4',032,0306,',',0232,0204,0320,'*',030,0205,'/','D',0322,0233,'f',02,0251,0274,'?',0252,0246,015,'G',026,0363,0262,'I',025,0234,'2',0360,'y','1','$',0315,'(',0257,0265,0273,'0',024,'k','8',016,0317,0226,')',0347,'d',0222,'j','-','l',014,0253,015,'n',030,0274,0320,0304,0223,'0','+','d',0242,0254,025,0252,0277,0324,0200,0255,0226,0237,0327,0352,']',0214,015,'c',022,'E','a',0273,'1',0,022,0301,0336,0234,'%',0256,0277,0320,0232,'S','T',0224,'*','2','v','|',0261,'K','O',0236,015,0251,'`',0354,0260,'@',0302,'&',0246,'R',0353,0232,023,'W',0354,0315,0344,0235,0366,0342,0324,'*','2','v','|',0261,'%','`','v','1','5',0315,'(',0253,0267,01,'f',0263,047,'c',0317,026,0241,'X',0232,'M','c',010,0254,06,'*','&','`',022,0311,'H','N',02,0325,'T',0322,'*',030,0305,0247,0316,'I','U',0253,'~','@',0337,0220,'7',0344,'b',0360,0265,'u',0340,07,'z','p','T',0253,'V','&',0271,'!','>','x',0345,012,0216,030,04,0260,'w',0245,0200,03,05,'J',0265,'b','k',0222,'S','^','!',')','R',0324,022,0221,'Y','-','s','#',0265,012,0345,'-','s','#',0265,012,0345,'-','s','#',0265,012,0345,'-','s','#',0265,012,0345,'-','s','#',0265,012,0345,'-','s','#',0265,012,0345,',',0253,027,034,'G',0200,0256,'R',0327,'&',0310,'j',025,0312,'Z',0344,0331,015,'B',0271,'K',0134,0253,'!',0250,'W',')','k',0223,037,0250,'W',')','k',0221,035,0251,'W',')','a','b','c',07,0372,025,0312,'Z',0346,'G','j',025,0312,'Z',0346,'G','j',025,0312,'Z',0346,'G','j',025,0312,'Z',0346,'G','j',025,0312,'Z',0346,'G','j',025,0312,'Z',0346,'G','j',025,0312,'X',0330,0330,0377,0,047,012,0345,'-','r',0254,0206,0241,0134,0245,0256,'M',0220,0324,'+',0224,0260,0261,'1',0376,'n',025,0312,'Z',0346,'F',0217,0364,'+',0224,0265,0314,0216,0324,'+',0224,0265,0314,0216,0324,'+',0224,0265,0314,0216,0324,'+',0224,0265,0314,0216,0324,'+',0224,0262,0222,0244,'(',0245,'B',0242,047,'c','2',0347,033,'I',0367,0376,0333,047,0227,'?',0332,'W',0274,0354,'f',0134,0343,'i','>',0377,0,0333,'d',0362,0347,0373,'J',0367,0234,033,0324,0270,0211,'v',0365,']',0211,' ',0360,'-','}','P',032,025,0303,0253,'_','T',06,0205,'p',0352,0327,0325,01,0241,0134,':',0265,0365,'@','h','W',016,0255,'}','P',032,025,0303,0253,'_','T',06,0205,'p',0352,0327,0325,01,0241,0134,':',0265,0365,'@','h','W',016,0255,'}','P',032,025,0303,0253,'_','T',06,0205,'p',0352,0327,0325,01,0241,0134,':',0265,0365,'@','h','W',016,0255,'}','P',032,025,0303,0253,'_','T',06,0205,'p',0352,0327,0325,01,0241,0134,':',0265,0365,'@','h','W',016,0255,'}','P',032,025,0303,0253,'_','T',06,0205,'p',0352,0327,0325,01,0241,0134,':',0265,0365,'@','h','W',016,0255,'}','P',032,025,0303,0253,'_','T',06,0205,'p',0352,0327,0325,01,0241,0134,':',0265,0365,'@','h','W',016,0255,'}','P',032,025,0303,0253,'_','T',06,0205,'p',0352,0327,0325,01,0241,0134,':',0265,0365,'@','h','W',016,0255,030,0365,'/',0342,'^','=','O','b',0211,'<','O',0376,026,0307,'X',07,0226,'E',0307,0347,'J',0300,'k',0321,'}',0255,034,032,0364,'_','k','G',06,'z',0217,0306,0265,'#','D',0222,'-',0215,'M','z','/',0265,0243,0203,'^',0213,0355,'h',0340,0321,0364,'q',0344,014,':',0242,024,0360,032,0277,0355,'S',0261,'v','5','V','M',0351,'t',0225,'U','P',0255,0257,'E',0366,0264,'p','k',0321,'}',0255,034,032,0312,'X',0325,'X',0307,0241,0322,0225,']','b',0271,0300,'Q',0307,0221,0320,0351,0210,'K',0300,'+',0377,0,0265,'5',0350,0276,0326,0216,015,'z','/',0265,0243,0203,'(','Z',0232,0244,0351,037,0221,'i','F',0226,0275,027,0332,0321,0301,0257,'E',0366,0264,'p','k','#','`',036,'X',0347,037,0235,'K',07,016,0213,0346,0375,0346,'q','^',';',0317,'S',0357,047,'}',0361,':','C',0233,036,0356,0371,011,0321,'<',0261,'{','?','b','t',0263,',','F',0317,0331,0235,036,0315,0216,0267,0374,0214,0336,'w',0314,0241,'|','w','~',0243,0336,'t',0243,'7',0357,030,'t','_','7',0357,'3',0212,0361,0336,'z',0237,'y',';',0357,0211,0322,034,0330,0367,'w',0310,'N',0211,0345,0213,0331,0373,023,0245,0231,'b','6','~',0314,0350,0366,'l','u',0277,0344,'f',0363,0276,'e',013,0343,0273,0365,036,0363,0245,031,0277,'x',0303,0242,0371,0277,'y',0234,'W',0216,0363,0324,0373,0311,0337,'|','N',0220,0346,0307,0273,0276,'B','t','O',',','^',0317,0330,0235,',',0313,021,0263,0366,'g','G',0263,'c',0255,0377,0,'#','7',0235,0363,'(','_',035,0337,0250,0367,0235,'(',0315,0373,0306,035,027,0315,0373,0314,0342,0274,'w',0236,0247,0336,'N',0373,0342,'t',0207,'6','=',0335,0362,023,0242,'y','b',0366,'~',0304,0351,'f','X',0215,0237,0263,':','=',0233,035,'o',0371,031,0274,0357,0231,'B',0370,0356,0375,'G',0274,0351,'F','o',0336,'0',0350,0276,'o',0336,'g',025,0343,0274,0365,'>',0362,'w',0337,023,0244,'9',0261,0356,0357,0220,0235,023,0313,027,0263,0366,047,'K','2',0304,'l',0375,0231,0321,0354,0330,0353,0177,0310,0315,0347,'|',0312,027,0307,'w',0352,'=',0347,'J','3','~',0361,0207,'E',0363,'~',0363,'8',0257,035,0347,0251,0367,0223,0276,0370,0235,'!',0315,0217,'w','|',0204,0350,0236,'X',0275,0237,0261,':','Y',0226,'#','g',0354,0316,0217,'f',0307,'[',0376,'F','o',';',0346,'P',0276,';',0277,'Q',0357,':','Q',0233,0367,0214,':','/',0233,0367,0231,0305,'x',0357,'=','O',0274,0235,0367,0304,0351,016,'l','{',0273,0344,047,'D',0362,0305,0354,0375,0211,0322,0314,0261,033,'?','f','t','{','6',':',0337,0362,'3','y',0337,'2',0205,0361,0335,0372,0217,'y',0322,0214,0337,0274,'a',0321,'|',0337,0274,0316,'+',0307,'y',0352,'}',0344,0357,0276,047,'H','s','c',0335,0337,'!',':',047,0226,'/','g',0354,'N',0226,'e',0210,0331,0373,'3',0243,0331,0261,0326,0377,0,0221,0233,0316,0371,0224,'/',0216,0357,0324,'{',0316,0224,'f',0375,0343,03,0377,0304,0,'<',021,0,0,04,02,06,07,06,04,05,04,03,0,0,0,0,0,01,02,03,04,0,020,05,021,022,024,'1','3',023,' ','!','0','2','R','q',025,'"','A','Q',0201,0221,'#','@',0241,0261,'B','C','D','S',0360,06,'4','a',0321,'`',0301,0341,0377,0332,0,010,01,02,01,01,'?',01,0336,0224,0242,'a',0250,0260,0215,024,0345,']',0242,025,'u',0204,0350,'D',0203,'0',0302,'0','J','=',0251,06,0260,047,0375,0375,0340,033,0242,030,020,'=',0243,'@',0227,'(','{','A',0330,0266,'S',0210,0201,0366,0373,'C',0212,024,'*',0255,01,0364,030,'9',014,0231,0254,0234,'*',037,0226,'M','#',0254,'k',')',0205,'c',015,0350,'_',025,0307,0320,'!','&',0351,' ',025,'&','Z',0267,024,0223,';',0312,'v',0213,0304,037,0312,0276,'U',0245,020,'u',';',0313,0354,017,0254,'$',0212,'h',026,0312,'a','T',0325,']','4',02,0265,015,'T','+','M','"',']',0211,0205,0177,'H','R',0231,'p','n',032,0202,015,'H',':','>',047,030,0276,'9',0375,0301,0367,0204,')','u',0310,'o',0213,0336,011,0322,0255,'4',012,0351,013,0201,0276,0377,0,'$',0212,'*','.','k',011,0205,'p',0316,0215,'M',0257,'x',0333,'M',0374,0302,'n',036,'"',0324,'>',' ',0303,0212,'a','e','6','%',0335,017,0254,030,0302,'q',0254,0303,0252,0212,'b',0252,0205,'L','<','f',0345,02,0271,'H','S',030,'9',014,0231,0204,0206,0304,'>','A',0243,'C',0273,'=',0222,0341,0342,'0',0335,0262,'m','I','a','9',030,0305,' ','Z','0',0354,0207,0224,0270,0217,'q',0277,0277,0372,0203,030,'L','5',0233,'^',0206,'k',0372,0203,'z','j','S','-',0177,'P','_',']',0373,'F',0207,'v',0245,0202,0341,0343,010,0242,'F',0344,04,0323,015,0222,'r',0351,'6',0244,0266,'x','t',0361,'G','f',0254,0330,'y','n',031,'4','3',0265,'*',0360,0361,0202,0224,010,'P',')','p',0220,0322,'E',07,0232,037,0303,0207,0257,0363,'d',0214,'P','8',011,'M',0200,0303,0266,0342,0325,'Q','L','w',0251,0246,'e',0216,04,'&','#',015,'[',021,0252,'v',013,047,0217,010,0320,0225,0216,'>',020,0262,0307,0134,0366,0324,035,0273,0206,0215,024,'v','{','$',0204,020,'#','t',0301,'2','J',0222,'{','v','N',0311,'x',0206,'T','k',0253,0312,'=',0356,' ',0225,'(',0322,0360,0225,0242,0361,027,'{','D','4',0321,0223,'N','l','G',016,0222,'p',0271,'[','&','*',032,027,0134,0356,'T',025,017,0270,'e','G',0250,0354,'k',0300,0276,'p',0212,'$','n','K',011,0206,0311,'<','v','F',0204,0264,'8',0370,'B',0252,0231,'c',0211,0317,0210,0312,0214,'P',0304,'t','[','>','3',0245,032,']',0325,0264,'^',023,'n',0333,'#',0247,'X',0251,0371,0300,0,024,'*',011,'R','n',0257,013,'Y','/',011,'u',0312,'S',034,'l',0224,'+',030,'g','D',01,'{',0356,'=',0240,0,0,'*',011,'<','z','F',0205,0333,0217,0224,',',0261,0334,036,0332,0203,0266,'t','2','"','u',0264,0276,05,0233,0266,0340,0351,'!','L','`',0305,022,010,0224,0330,0356,0250,'R',0200,0256,'&',0362,011,'>','T','P','n','c',0206,':',0305,'(',0234,'j','(','C','j',035,'U','6',0253,0335,017,0254,' ',0325,'&',0301,'R','a','7',0264,0241,020,0254,0211,'m','7',0332,016,0241,0225,'5',0263,0215,'c','2',020,0312,030,012,0134,'F',032,'7',06,0251,02,'a',0251,'L','4',0250,'o',05,0365,0335,'P',0206,0251,'c',027,0374,'I',0363,'{',0322,'"',0230,'c',012,'3','p',0217,031,06,',',0217,0224,025,0262,0347,0332,'R',017,0264,047,'D',0272,'>','!','T','#','B',0246,']',0252,0232,0270,'I',04,0220,012,0223,'-','S',']',0302,'m',0313,'i','A',0207,0224,0252,0213,0367,023,0330,'_',0256,0255,030,0303,'@',032,'U','8',0207,0351,'%',0327,'+','t',0305,'C','E',032,0374,0332,'q','*',0243,0307,0367,0222,0204,'*',0245,022,033,01,0207,'(',013,'e','E','1',0334,0266,0134,'[',0252,012,07,0204,'&','r',0252,'P','9','p',035,0302,0256,022,'@','+','P',0325,'C',0232,'h','G','b',01,0352,'0','u',014,0241,0255,034,'k',035,'L','b',0215,0243,'t','_',031,'l','|',03,0313,0377,0,'d','"',0,025,0214,'R','/','o','G',0250,0274,'!','*','=',0325,0351,032,0307,0210,'1',0225,'6',0210,011,012,0257,0216,033,0252,'!',0345,0203,']',0317,0200,0341,0252,'w',0215,0323,0342,'8','B',0264,0322,05,0340,012,0341,'j','Y',0302,0273,013,0335,0203,030,'L','5',0233,'U','$','T',0134,0326,023,012,0306,031,'Q',0244,'k',0337,'>',0323,'K',010,0244,0251,035,'7',0302,'K',0207,0357,':',020,07,'H','q',0360,0252,'T',0317,0366,0336,0273,0254,'"',0217,'w','z','K','o',020,'c','*','a',03,011,'A','r','x','c',025,0210,0356,032,'Q','J',0255,0336,'W',0272,'_',0254,'"',0202,'m',0313,'e','0',0252,'F','1','H',026,0214,';','"',0220,0244,0205,0307,0303,'K',0207,0357,0251,'E',0266,0320,'!','X',0342,'m',0262,0246,0325,0330,'D',0275,'w','m',034,0213,'U','A','@',0202,034,0252,024,016,0134,06,014,'P','0',011,'F',036,'6',026,0252,0212,'z',0245,'(',0230,'j',',','7',0242,'W','W','i',0373,0241,015,0250,0364,033,'m',0,0254,'|',0346,0345,0332,'M','K','Y',0306,035,0276,'U',0330,0355,0330,'_','-','J','1',0225,0345,'K','f',0341,011,010,0201,'B',0261,0207,'k',0336,'V','2',0233,0312,'!',0345,0221,0273,0237,0322,'N',0331,'&',0360,0265,033,020,0203,'P',0213,'W',0335,'0','G','b','8',0346,017,0257,0372,0202,'P','j',017,031,0303,0371,0355,011,'P',0315,0311,0307,0266,023,'E','4','B',0244,0313,'T',0316,'r',0246,'[','G',032,0202,035,'S','?',0205,0277,0274,034,0346,'P','m',030,'k',035,'F','m',016,0354,0366,'K',0207,0210,0302,'I',025,022,02,'d',0300,'%','K',0274,0250,'.',0344,0365,0336,0200,0325,0264,'!',0203,0273,0332,'U',0217,020,'c',0256,0252,0351,' ',025,0250,'j',0241,0305,'4',01,0261,0,0365,030,'Y',0302,0253,0215,'j',015,'z',0254,0330,0250,0354,0333,'6',027,0316,021,'D',0215,0311,0243,'L','6','J',0220,'~',015,013,'d',0274,'C',06,'0',0230,'m',033,0134,'F',0255,0243,032,'d',0271,0202,'4',0311,'s',04,'i',0222,0346,010,0323,'%',0314,021,0246,'K',0230,'#','L',0227,'0','C',':','@',0215,'U',03,0201,0203,0374,0300,'R',0214,'G',0363,0213,0356,021,0332,'l',0177,'x',0276,0341,03,'L',0321,0345,032,0264,0241,07,0247,0230,027,03,0327,012,0177,'Q','%',0371,'a',0356,'0',0255,'4',0252,0277,0230,01,0322,05,0302,'b','5',0211,0376,0261,0246,'K',0230,'#','L',0227,'0','F',0231,'.','`',0215,'2',0134,0301,032,'d',0271,0202,031,0246,0320,0335,0367,013,024,03,0312,0260,0202,0322,014,010,026,'J',0251,'j',0352,021,0332,'l',0177,'x',0276,0341,016,0351,0266,0250,0227,0341,034,014,'=','a','G','E','T',0302,'s',0237,'h',0306,0231,'.','`',0215,'2',0134,0301,032,'d',0271,0202,'4',0311,'s',04,0,0327,0264,'&',0266,'Q',0272,'|',0352,'9','E',0351,'5',0262,0215,0323,0347,'Q',0312,'/','I',0250,'[','D',022,0204,0134,'U',0213,0212,0261,'q','V','.','*',0305,0305,'X',0270,0253,027,025,'b',0342,0254,0134,'U',0213,0212,0261,'q','V','.','*',0305,0305,'X',0270,0253,027,025,'b',0342,0254,0134,'U',0213,0212,0261,'q','V','.','*',0305,0305,'X',0270,0253,027,025,'b',0342,0254,0134,'U',0213,0212,0261,'q','V','.','*',0302,'e',0262,'@','(',0377,0,0301,'V','t',010,0232,0310,0204,'v',0201,'y','c',0264,013,0313,0,'5',0205,'s',0355,02,0362,0307,'h',027,0226,022,'x',012,0234,011,'T',0327,'X',021,'-',0241,0216,0320,'/',',','v',0201,'y','a',05,0201,'b',0332,011,0252,0360,022,'8',0222,0250,0355,02,0362,0307,'h',027,0226,'b','5',05,'q',0332,05,0345,0216,0320,'/',',','"',0350,026,'5',0220,015,'w',0271,0263,047,010,'H','p',0233,'L',0342,0315,0376,'X','u',0233,014,0261,0353,'7','y',0306,0230,'a','#',0360,0214,0331,'f',0353,0275,0315,0231,'8','B','C',0204,0332,'g',026,'o',0362,0303,0254,0330,'e',0217,'Y',0273,0316,'4',0303,011,037,0204,'f',0313,'7',']',0356,'l',0311,0302,022,034,'&',0323,'8',0263,0177,0226,035,'f',0303,',','z',0315,0336,'q',0246,030,'H',0374,'#','6','Y',0272,0357,'s','f','N',020,0220,0341,'6',0231,0305,0233,0374,0260,0353,'6',031,'c',0326,'n',0363,0215,'0',0302,'G',0341,031,0262,0315,0327,'{',0233,'2','p',0204,0207,011,0264,0316,',',0337,0345,0207,'Y',0260,0313,036,0263,'w',0234,'i',0206,022,'?',010,0315,0226,'n',0273,0334,0331,0223,0204,'$','8','M',0246,'q','f',0377,0,',',':',0315,0206,'X',0365,0233,0274,0343,'L','0',0221,0370,'F','l',0263,'u',0336,0346,0314,0234,'!','!',0302,'m','3',0213,'7',0371,'a',0326,'l','2',0307,0254,0335,0347,032,'a',0204,0217,0302,'3','e',0233,0256,0367,'6','d',0341,011,016,023,'i',0234,'Y',0277,0313,016,0263,'a',0226,'=','f',0357,'8',0323,014,'$','~',021,0233,',',0335,'O',0377,0304,0,'L',020,0,01,03,01,02,05,015,015,07,03,02,07,0,0,0,0,02,01,03,04,0,020,021,05,022,'!','1','A',023,' ','"','Q','a','q','r','s',0222,0223,0261,0262,0341,'#','0','4','5','B','R','t',0201,0202,0221,0241,0301,0321,024,025,'$','2','3','@','b','C','S','c','%','T',06,'`','d',0204,0322,0342,0360,0377,0332,0,010,01,01,0,06,'?',02,0357,0244,0353,0356,03,'-',0216,'s','5',0271,022,0224,'Z','3',0234,0342,0177,'a','6','>',0365,0253,0242,'D','b','(',0355,0235,0346,'_','*',0271,0314,'(',0372,047,0370,0227,'S',0352,0335,'[','9',0322,'O',0204,0351,'-','d',0224,0362,'o','8',0265,'{','8','N','F',0361,0236,':','{',0226,0205,0274,'/',035,014,'?',0276,0302,0134,0251,0276,'4',022,'b',0272,'/','2','y','D',0307,0366,0312,0374,0311,01,035,0275,0262,0134,0373,0324,0255,'`',0210,0377,0,0367,017,0247,'@',0375,'k','T',0233,')',0311,05,0374,0327,'"','o','&',0216,0360,'-',0272,'K',0366,011,013,0212,0350,0337,0220,'W',0317,0253,0323,'*','~',0315,'H',0225,04,'S',':',0255,024,'|',020,0203,')',0355,'2',027,0364,0307,'{','n',0225,0371,0262,016,'C',0253,0244,0327,'6',0366,0325,0272,0234,'8',0316,'I','?',0340,'7',0335,0277,'H','S',036,'f',010,0355,'~','r',0370,'d',0370,0325,0362,035,0221,',',0267,'K',024,'~',037,'Z',0270,'p','c','E',0306,'*',0237,'M',']',0367,'T','o','P',']','N',026,016,0306,0207,'%','2',0212,'c',')',02,0356,'e',0267,0356,0367,0316,0371,'Q',022,0344,0277,0312,'o','G',0273,'7',0273,0366,'K','&','k',0310,0320,'h','M','$',0273,'H',0224,'M',015,0361,'`','_',0221,0221,0134,0245,0302,0333,0267,026,024,'u',' ','O',0314,0351,'d',01,0365,0320,0271,0204,'M','g',0275,0237,021,'6','-',0247,0326,0205,0246,032,06,'[',034,0300,0330,0334,0232,0331,'s',017,0362,0262,0332,0226,0375,0261,0347,'3',0235,0265,0331,017,0234,':','R',0231,0224,0301,'c','2',0350,0241,012,0376,0303,'U','{',0272,'H','?',0322,'a',027,')','v','R',0311,0230,0346,'1','y',' ',0237,0224,023,'i',',',06,0231,'l',0235,'t',0326,0341,0,'K',0325,'h','%','a',0254,0253,0235,'"',012,0365,0227,0345,'B',0323,'-',0213,'M',012,0134,' ',011,'r','&',0274,'0','+',05,'}',0327,'9','"',0357,0200,0374,0375,0332,0302,0300,0322,013,'`',0346,0316,'=',0372,013,'H',0367,0375,']',0336,0350,0371,0344,'e',0235,'&',0277,'J','r',0134,0267,'5','G',0217,0334,0233,0211,'b','F',0210,031,0262,0233,0213,0371,'A','7','j',0346,'G','U',0224,0251,0263,0220,'I',0262,']',0355,0244,0357,012,'H',0250,'S',']','K',0231,'o',0346,0273,0224,0343,0316,0222,0233,0246,0270,0304,'K',0245,'l',0134,'.',0230,0337,'j',0277,'U',026,0266,0331,0377,0,0354,0266,'6',0363,'E',0210,0343,'d',0204,'$',0232,025,')',0231,'c','r',';',0371,']',04,0362,'O','O','}','z','d',0223,0304,'e',0244,0275,'W',0345,'N','K',0220,0267,'h','m',0275,0,';','V','j','-','l',030,014,0256,0275,0240,'S',0353,'A',022,033,'x',0215,0246,'u',0322,'K',0266,0275,0343,'U',0177,'f',0361,0376,0223,011,0234,0327,0351,'N',0314,0226,'x',0356,0237,0270,'S','i',',',0325,'^',037,0364,0370,0353,'{',0213,0347,0257,0233,'X',0267,'&','.','k',0250,0221,0261,0272,034,0216,0350,0316,0346,0330,0372,0254,'F',0336,'+',0241,'J',0330,'9','~','a',']',05,0337,'~',0353,'`',0377,0,015,025,'{',0245,0336,'S',0235,0226,'3',012,'2','l',0317,'9','h',024,0322,0253,'M',0303,0212,'7',010,0376,'b',0134,0346,0273,'k',0336,025,0226,0356,0223,0204,027,'3','(',0271,07,'t',0250,0345,'K','u',']','x',0364,0256,0215,0304,0261,030,'e','1','Z',034,0256,0275,'v','@','J','j',034,'P',0304,'e',0264,0367,0356,0331,')',0307,'n',023,0217,'s',0215,0222,0350,'[',0356,0370,0337,'u',0277,'e','x',0257,0231,021,020,'V',0377,0,'(','4','/','{',0231,'7','&','3','a',0260,0277,0316,0314,0237,032,'#','2','R','2','[',0325,'W','J',0330,'2','^',013,0246,'K','D','2',0277,'8',0216,0201,0327,0223,0362,']',026,'Y',034,0346,'k','r','Q','F',0300,0327,0260,0326,'e',0222,0277,0235,'w',0266,0251,'H',0225,'H',0225,'o','U',']','6','b','4',0232,0234,'`',0375,'W',0325,'2',017,'m',04,'H','m',0342,'6',0231,0327,'I','.',0332,0332,0326,016,025,0356,0262,0213,030,0223,'h',07,'/','M',0326,0261,'5',0274,0250,'+','s',0201,0347,016,0224,0246,0244,'2','X',0355,':','(','B','I',0245,';',0324,'F',021,'n','G','_',0275,'w','Q',022,0310,021,0134,'L','f',0210,0361,0215,'6',0321,'2',0374,0265,0304,0354,0207,'A',0226,0207,'9',0270,'W','"','Q','5',0203,03,0355,0257,'f',0325,013,'#','i',0365,0255,'V','t',0202,'w',0315,014,0302,';',0311,'k','r',0246,0343,'E',0201,0237,0371,0271,0275,0271,0273,'A',032,'+','B',0303,01,0230,06,0327,'d',0310,'=','M',0226,0207,030,0212,0237,0232,0346,'D',',',0215,0207,0232,032,023,'X','X',026,'A',0355,0234,'{',0376,'#',0363,0367,0367,0254,036,0375,0337,0221,0345,013,0367,0323,0377,0,'[',030,0232,'b',0246,0322,'^','&','#',0236,0345,'J',025,'c',010,0261,'y','y',016,026,'!','{',0226,0274,'!',0256,'Z','R',0243,0270,'N',' ',022,'y',':',0262,'_',0356,0245,0324,0215,0331,0205,0264,0323,'w','t',0335,'D','0','#','5',014,'r',0354,0317,'f',0177,'J',0325,'&',0312,'r','I',0177,'2',0310,0233,0311,0242,0335,'B',024,'r','|',0364,0335,0230,'w',0327,'E',04,0234,'!',0213,'6','b','e','A',0376,0230,'/',0317,'X',0252,0253,'r','&',0232,0373,014,'#',0377,0,'O','i','r',0222,0177,'T',0266,0367,0254,'b',024,'q',0275,0307,012,0353,0374,0324,0322,0265,036,'D',06,0356,',',036,0336,')',0335,0345,0267,0245,'W',0247,0326,0266,'5','!',0202,0304,'u',0242,'B',022,'M',0272,'b','k','y',024,0222,0343,017,'4',0264,0247,'y',0223,04,0362,')',0246,0300,0274,0322,0320,0264,0354,'w',0301,'[','y',0242,0305,'!',']',0276,0361,0251,0302,0212,0344,0202,0376,011,0221,'7',0327,'E',013,0270,'a',0373,0377,0,0351,0330,'^',0222,0372,'P',0261,021,0200,0216,0312,'y',' ',0227,'k',010,0314,0220,'@','R',0365,'U',0321,'G',0203,0260,'i',0252,'B',0314,0353,0311,0375,']',0304,0376,'=','6',' ',0212,')',022,0344,'D','M','5',0253,0311,024,0373,0301,0364,0331,0377,0,0215,'<',0332,'Q','$',0274,'W','"',0242,0323,0215,012,'~',025,0336,0350,0302,0356,'m','z',0254,0225,0203,0325,'{',0223,0255,0352,0251,0270,'I',0330,0277,016,0365,0367,0314,'P',0356,0215,0245,0322,021,'4',0217,0235,0352,0326,0242,'1',0203,'d',0235,0376,'R',0266,0250,0236,0365,0244,'Y','N','3',010,'v',0225,'q',0313,0334,0237,'Z',022,0221,0252,'O','q','?',0270,0267,017,0271,'(','Z','a',0240,'e',0241,0314,0,'7','"','k','V','D',0327,0305,0226,0364,'_',0234,0267,021,'4',0322,0260,0315,0361,'p',0177,0366,0264,0237,013,0351,'e',0311,0225,'h','0',0236,021,'o',0361,'k',0225,0246,'W',0372,'{',0253,0273,'n',015,05,0273,'V','W',011,'S','z',0354,0277,'+',033,0273,'C','&',0253,0336,0210,015,020,0204,0222,0345,'E',0323,'D',02,0237,0203,'{','f',0301,'n','m','z',0254,'<',031,'-',0226,0217,'W',0331,'2','f',010,0253,0215,0346,0327,'s','h',03,0202,'7','k',0357,'U',0271,'(',0330,0301,0327,'N',0227,0233,037,0372,'c',0353,0323,0352,0242,0221,'5',0362,'}',0305,0333,0314,0233,0311,0242,0300,'i',0220,047,035,'5',0270,'@','R',0365,'Z',011,0370,'H','Q',0311,0331,0301,0254,0350,0327,'n',0261,0304,'h',0261,0243,'F',0356,'M',0356,0355,0257,0277,0242,0311,0370,'A','S',' ',0212,'0','+',0277,0225,'z',023,0275,0273,020,0266,'.',0246,0311,0243,0363,'J',0234,'a',0340,'V',0335,'l',0261,'H','W','B',0320,':',0331,'(','8',013,0214,'$',0232,026,0231,0225,0221,036,'M',0203,0303,0264,'z',0322,'u',0367,'A',0226,0307,'9',0231,0134,0211,'J',020,0320,0260,0213,0337,0303,' ','{',0376,0224,0242,0373,0372,0224,0177,0366,0354,0344,037,'^',0335,0272,0214,'6',0257,'O',')',0322,0310,01,0276,0265,0216,'?',0210,0232,0251,'q','H','$',0350,0332,0326,',','(',0307,0370,0371,')','v','L',0355,0206,0335,0202,0330,012,0221,0222,0334,0202,0232,'V',0243,'C',0271,'5','[',0261,0235,'T',0322,'k',0237,0276,'}',0365,024,'6','c',0222,'H',0242,'g','M',05,'a',';',033,025,0306,0334,0310,0343,047,0230,0251,'>',0321,02,'K','g',0245,032,0305,'$',0371,'V','H',0263,0271,01,0377,0,0225,'~',027,06,0272,0342,0377,0,0225,0304,036,0213,0350,0222,'>',0243,010,'4','j','c',0214,'^',0365,0254,'y','r',0235,0222,'_',0344,'+',0356,0264,'Y','a',0242,'y',0322,0314,0,0227,0252,0320,0277,0206,'O','S',017,0366,0255,0256,0311,'w',0327,'G',0252,0205,0210,0314,0203,014,0216,'`',04,0271,'5',0232,0241,0334,0344,0243,0375,026,'<',0355,0335,0352,'v',0134,0223,0325,036,'u','o','%',0263,0357,0231,'A',0334,'Z','_',0303,0242,0371,'E',0347,'z',0273,0351,'6',0340,0241,0201,'%',0312,'+',0245,'(',0331,'D','_',0262,';',0263,'`',0267,'6',0275,'Z',0375,'N',024,'W','$',027,0360,034,0211,0276,0272,'(',0134,0302,0262,021,0201,0376,0313,031,'K',0337,0233,0246,0265,'8','1',0201,0235,0262,0316,'E',0276,0272,0334,0134,0217,0316,'$',0330,'0',0213,0361,'-',0244,0247,'%',0313,'q',']','x',0376,033,0211,'f',';',0227,0267,0203,0332,'^',0350,0347,0235,0374,'R',0233,'e',0220,'F',0332,'m','1','D','G','2','&',0274,0234,'p',0220,0,'R',0365,'"',0134,0210,0225,0343,'(',0234,0360,0327,0214,0242,'s',0303,'^','2',0211,0317,015,'x',0312,047,'<','5',0343,'(',0234,0360,0327,0214,0242,'s',0303,'N','F',0134,'%',015,037,035,0233,047,0253,016,'B',0245,'M','M','r','m','e',0257,0323,'/','u','!',0212,'4',0210,0276,'{',0355,0212,0373,0225,'k','g','/',07,'G',0343,'f',07,0311,'V',0205,'f',0177,0304,0220,'C',0316,026,015,027,0342,0253,0362,0244,'R',0225,032,'c',0211,0345,'H',0222,'+',0360,0315,'H',0333,'S',0241,'4',011,0230,'A',0321,'D','J',0361,0224,'N','x','k',0306,'Q','9',0341,0257,031,'D',0347,0206,0274,'e',023,0236,032,0361,0224,'N','x','h',0342,0340,'f',0325,0327,'s',',',0245,035,0210,0360,'v',0350,0336,'{','T','u',0323,'[',0310,0313,'*',0255,'~',0231,'{',0253,0361,0263,032,0301,0361,03,0363,023,0206,0202,'e',0270,'(',0264,0324,'X',0263,0241,0264,0303,'i','p',0212,'<','5',0343,'(',0234,0360,0327,0214,0242,'s',0303,'^','2',0211,0317,015,'x',0312,047,'<','4','.','6','H','`','I','z',020,0256,'E','K','p',0227,0243,'9',0325,'_',0336,0340,0337,'F','o',0252,0226,0341,'/','F','s',0252,0277,0275,0301,0276,0214,0337,'U','-',0231,035,0273,0265,'G','Y',' ',033,0366,0325,'+','<','~','_','e','g',0217,0313,0354,0254,0361,0371,'}',0225,0236,'?','/',0262,0263,0307,0345,0366,'V','x',0374,0276,0312,0317,037,0227,0331,'Y',0343,0362,0373,'+','<','~','_','e','g',0217,0313,0354,0254,0361,0371,'}',0225,0236,'?','/',0262,0263,0307,0345,0366,'V','x',0374,0276,0312,0317,037,0227,0331,'Y',0343,0362,0373,'+','<','~','_','e','g',0217,0313,0354,0254,0361,0371,'}',0225,0236,'?','/',0262,0263,0307,0345,0366,'V','x',0374,0276,0312,0317,037,0227,0331,'Y',0343,0362,0373,'+','<','~','_','e','g',0217,0313,0354,0254,0361,0371,'}',0225,0236,'?','/',0262,0241,0307,'r',0355,'Q',0246,'D',012,0355,0264,'O',0371,025,'b',034,'S','u','P','P',0261,0220,0256,0257,0,'s',0234,'J',0360,07,'9',0304,0246,0235,0273,027,'T',024,'+',0267,0354,'"',0317,'r','_','^',0,0347,'8',0225,0340,016,'s',0211,'L',0303,030,'f',0332,0271,'~',0311,'O','5',0311,0177,0312,0326,0344,033,'*',0362,031,0342,0134,'+','v',0216,0312,0360,07,'9',0304,0257,0,'s',0234,'J','r','@','2',0254,0240,036,'%',0304,0267,0350,0355,0265,0350,'e',014,0334,'V',0356,0331,'!',0347,0275,'/',0371,0327,0200,'9',0316,'%','x',03,0234,0342,'P',0226,'k',0322,0373,035,'v',0354,'m','L','T',0256,0336,0257,0,'s',0234,'J',0360,07,'9',0304,0244,0210,021,'M',0245,'Q','R',0306,'R',0277,'^',0134,'P',0333,023,0211,016,0213,035,0340,0255,0260,'}',0276,0242,0333,033,0322,023,0252,'V',0311,0364,0205,0352,0215,0263,0275,0216,0242,'Z',0327,05,',',0227,0304,0237,'E',0243,0305,026,0274,0270,0241,0266,047,022,035,026,';',0301,'[','`',0373,'}','E',0266,'7',0244,047,'T',0255,0223,0351,013,0325,033,'g','{',035,'D',0265,0256,012,'Y','/',0211,'>',0213,'G',0212,'-','y','q','C','l','N','$',':',',','w',0202,0266,0301,0366,0372,0213,'l','o','H','N',0251,'[',047,0322,027,0252,'6',0316,0366,':',0211,'k',0134,024,0262,'_',022,'}',026,0217,024,'Z',0362,0342,0206,0330,0234,'H','t','X',0357,05,'m',0203,0355,0365,026,0330,0336,0220,0235,'R',0266,'O',0244,'/','T','m',0235,0354,'u',022,0326,0270,')','d',0276,'$',0372,'-',036,'(',0265,0345,0305,015,0261,'8',0220,0350,0261,0336,012,0333,07,0333,0352,'-',0261,0275,'!',':',0245,'l',0237,'H','^',0250,0333,';',0330,0352,'%',0255,'p','R',0311,'|','I',0364,'Z','<','Q','k',0313,0212,033,'b','q','!',0321,'c',0274,025,0266,017,0267,0324,'[','c','z','B','u','J',0331,'>',0220,0275,'Q',0266,'w',0261,0324,'K','Z',0340,0245,0222,0370,0223,0350,0264,'x',0242,0327,0227,024,'6',0304,0342,'C',0242,0307,'x','+','l',037,'o',0250,0266,0306,0364,0204,0352,0225,0262,'}','!','z',0243,'l',0357,'c',0250,0226,0265,0301,'K','%',0361,047,0321,'h',0361,'E',0257,'.','(','m',0211,0304,0207,'E',0216,0360,'V',0330,'>',0337,'Q','m',0215,0351,011,0325,'+','d',0372,'B',0365,'F',0331,0336,0307,'Q','-','k',0202,0226,'K',0342,'O',0242,0321,0342,0213,'Y',0377,0304,0,'*',020,0,01,01,06,05,05,01,0,03,01,0,0,0,0,0,0,01,021,0,020,'!','1','Q',0360,'A','a','q',0201,0241,' ','0',0221,0301,0361,0261,'@',0321,0341,'`',0377,0332,0,010,01,01,0,01,'?','!',0356,0202,0211,'(',0261,0263,'%',0212,0373,021,'=','(',021,0242,0261,0324,'T',0366,'(','8','m',047,0201,0370,'2',0312,'S','6','Q',033,0250,'>',0331,'&',0371,0356,'t','1','J','B',0233,0343,0211,0262,'h',0301,'P',0352,'*',015,0323,0370,0300,'L','s','E','@',0231,'9',06,'(','(',011,'b',0254,0237,0203,'F','[',0250,0376,0220,0330,';',04,0306,024,0210,0224,0,'i',0216,'Z',06,0,0210,011,020,'G',0360,0307,0204,024,0204,0,'2',016,'8','D',0373,036,'5','o',0353,0210,0220,022,014,0203,0342,0337,020,0203,06,0244,0206,0354,'R',' ','.',037,'P',0301,025,030,0210,07,0210,025,0232,0375,0204,0271,0226,'_',06,'2',0177,014,032,'F',022,'I','j',04,'H',0324,030,'P',0275,'q',022,024,0243,'(',0254,0362,0376,020,0274,'X','N',0363,0221,'b',0306,06,016,'p',047,0370,0226,0263,'z',0212,'"','y','{',0320,')','`',0352,'(',0240,0221,0240,0216,0350,032,'0','s',0262,0,026,0303,0244,0210,0242,037,022,'H',01,0231,'(','7','y',0311,'&','t','@','h',027,'Q',0351,0264,'_','Z',032,0347,0374,05,' ',04,'!','8',0365,'4',014,'K','a',0316,0240,'(',014,034,0243,'`',0,0214,0200,'`',0300,'`','C','[','_',0217,'8','2','#',0264,020,0250,0,0353,0212,015,' ',0261,0237,0271,0320,0230,0354,'I',0250,0336,0230,0314,032,0367,0316,'&',01,'J','z','!',0211,0366,0214,'K',0346,0304,0340,0340,014,0,0243,0226,0347,06,0356,0213,0360,'b',0301,'`','q',0134,0376,01,0272,0366,010,013,0207,0306,'4',0250,0346,'L','`','k',0237,0220,0251,'.',020,0204,'H',0264,047,0251,0342,'3','q','x','M',030,'B',0203,0345,0214,'R',012,'a','&',0206,'c','"',';',0240,0264,0263,0246,'h',031,0223,01,0253,'!',0220,'a',016,'H','=',0234,'K',0201,0344,0302,0307,0372,0213,0,0302,'c',033,0273,0213,0211,0354,',',0350,0340,'o',0371,03,023,0355,03,033,0211,0262,0330,013,0,034,'<','Z',022,0245,'0',017,0274,0265,014,016,'"',020,0201,020,'J','4',0352,0202,022,0213,0220,'|',020,0343,'+',0240,',',0377,0,014,'`','r','9','4',0373,0207,025,'P','!','u','5','%',0321,0332,'q',04,037,0204,'?',0241,0213,'M',0370,0265,'W','8',0361,'.',0302,':',047,'4',0230,'i','3',0313,'M','<',0222,03,0,'`',05,034,'}',0311,0213,'W',0276,'N',01,0201,0341,'(',014,'K',022,0251,'.','Z',0310,0244,0332,'6',026,0340,0364,'s',0315,'9',0253,'x',0230,'!',0323,'>',0331,0332,'@',0364,'H','N',017,0220,'c',0316,'Q',0345,'$',0211,'%',0320,0202,'(','U',0345,'T',0346,'r',0353,04,0235,'T',0201,0242,0272,0300,0323,'G',0207,'3',035,030,0203,0334,']','I',031,0222,0134,0276,0306,'`',0325,05,'r','4',0320,04,0261,0307,023,022,0360,0220,' ',0324,0,0255,0370,013,0325,'Y',':','c',0375,06,'`','0','I',0343,0214,02,0216,0324,020,'f',0324,0341,0344,0217,016,0215,0364,'@','P',031,'0','r','0','n',0300,' ','A',0322,'8',0250,0242,'7',0205,0201,0233,022,0242,'&',0376,0355,'A',0233,'B','$','*',035,0230,'`','5',0233,0305,0232,0264,02,020,'9','0',0314,0331,0205,'n','P','T',032,0346,'s','x','F',024,';',0,031,'p',0216,'Y',0205,0364,'9',0223,0321,06,0260,0317,0177,0334,';','B',017,'P','5','D','w',03,0377,0,0323,0212,'I',05,0304,024,';','0',',',0245,016,0256,0331,'k','{',0333,'L',0306,04,'_',021,'V',04,017,0224,'T',036,'e','h',0254,'(',0243,'@',0332,0210,0220,'n',013,'D',0262,0250,'X',0360,033,036,'?',05,026,010,'*',0310,06,'R',0250,010,'v',0300,0317,0231,0360,0300,' ','@',0361,0266,011,'I',024,0,'4','F',0305,0230,0200,'_',0206,036,'h',0342,'R',024,'H',0206,'#',0220,021,'h','*',0262,0,0263,'*',0210,'*',0340,'C',0270,'c',0,0262,'c',047,'"',0251,0373,'8','#',0262,'~',02,0371,'0','c',0346,0341,0213,0330,030,'`','v','"',0275,0320,0377,0,'H','n',',','F',0253,0224,0263,0375,'4',0212,0222,0235,0256,'g','>',0203,0220,'A','t',01,'R','X',0350,'u',0200,033,0217,0361,'6','%',0363,0,0324,0221,0220,01,0206,'T',0257,'V','(',037,'y',0350,0303,0320,'.',0,'C',027,'<','l',0316,'z',0312,032,'%',0134,0251,0344,0311,'3','`','C','^',07,'k',013,0230,0134,'p',0265,'H','r',0323,0240,02,'H','"','K',07,0220,012,'8',014,034,0262,0326,'p',0245,033,0300,0302,0201,0243,034,'n',0350,022,'X','a',0201,05,015,0220,035,'#','f',0240,0244,'M',0301,'h',0300,0230,'S',0,';',0344,'?',0306,0256,' ',0200,'I',0,06,'-','%',0352,0213,0241,'q',0254,0234,030,0305,037,0210,020,0375,037,027,033,0244,0322,0210,0237,0244,'v',0216,'D',0201,0224,01,0230,',','S',0321,024,'.','c',0314,0212,'h',0225,'p',0327,0306,0206,02,'"',0243,0201,02,031,0214,0330,'Z','h',047,0343,0254,0344,0,05,'$',0310,'1','o',021,'P',0237,0237,0343,0344,0322,0332,'R',0340,0322,'@','d',035,036,0252,'$','(',03,016,'K',0266,032,0361,030,'W',0241,0,015,'`','`','@',0363,0360,07,'j',027,0344,'H','x',0371,'{','i',0343,025,0304,0231,'l','d','r',',','s',0356,0237,0202,'a',0202,0300,'3',0,025,04,'0','Z','`','g',012,'{',031,0215,'z','A',0300,0224,'P',0331,0222,0310,0234,'`',0260,'g','3',0236,0302,0304,0324,'Y','(',0237,'-',0317,030,',','x',0300,',',0204,0330,'`','8',0232,0263,03,0203,0232,0364,01,0212,0230,0221,020,':',0314,0206,0347,07,016,0322,0206,')','$',0,014,'u','!',0327,0202,',','R','C',' ',';',0230,'p','D','e','#','f','G','$',0243,0220,0360,0204,0222,022,'9',031,0307,'6','4',0343,'q',0262,'$',0376,031,024,'I',0314,'M',021,0321,'2','<',030,0214,0216,0231,020,'n',0216,03,'F',0332,'(',047,035,03,015,0236,'a','V',0206,0366,01,0261,0347,'c',0302,'>',036,'A',0200,0370,'Q','(',0350,'!',' ',022,024,0211,'U','@',0304,0261,0375,0252,0337,0301,'@','$',0341,'9',021,0273,'t','`',0317,'N',0351,'<',030,'j',0202,'L',026,'6','"',0204,0305,'q',0221,0254,0236,016,'=','`',0353,':',025,0217,01,0271,0215,0315,0311,'6',0331,0225,0263,'#',0322,0204,030,0335,'1','=','&','+','S','K',',','3','c',0343,010,0223,' ',0300,030,01,'G','#','8','f',0201,'/',0326,0247,06,037,'N',0206,'@',0244,07,'Y',0364,0371,'0',04,0311,'8',06,0266,0275,0265,0265,0355,0255,0257,'m','m','{','k','k',0333,'[','^',0332,'4','V','p',0275,0344,'d','X',0211,')','$','(','G',0221,'6',0372,0366,0225,0316,0207,0205,022,0312,0215,'T',0264,':','.',0364,0230,021,0224,017,'D',0370,0221,0301,0206,'n',0323,'D',0300,013,'[','^',0332,0332,0366,0326,0327,0266,0266,0275,0265,0265,0355,0210,'j',02,'I',0260,'3','f','a',0253,0,0230,0302,'H',0314,0267,0327,0264,'G',0346,0367,01,0314,0265,'n','z',0223,0210,0306,'$',0342,'Z',0332,0366,0326,0327,0266,0266,0275,0265,0265,0355,0207,0247,0313,0202,'$','A',0304,0177,0301,0220,' ','@',0204,0304,0213,'P',')',01,'|',0267,0323,0263,0351,0331,0364,0354,0372,'v','}',';','>',0235,0237,'N',0317,0247,'g',0323,0263,0351,0331,0364,0354,0372,'v','}',';','>',0235,0237,'N',0317,0247,'g',0323,0263,0351,0331,0364,0354,0372,'v','}',';','>',0235,0237,'N',0317,0247,'g',0323,0263,0351,0331,0364,0354,0372,'v','L','H',0265,'B',020,'S',0307,0374,'*',02,'X','0',0216,'H',0327,0347,0246,0277,'=','2','f',0224,0134,0250,0205,'G','B','8',0244,'U',032,0374,0364,0327,0347,0246,0200,'{',0343,020,0246,'D','x','H','3',035,010,0212,0262,0374,0364,0327,0347,0246,'2',014,0307,'F',02,0257,'D','=',0361,0200,'C','"','2',0374,0364,0327,0347,0246,0204,'p','H',0242,0271,'s','J','.','E','B',0243,'_',0236,0232,0374,0364,0310,011,'`',0302,031,047,']',0266,0205,0367,0312,']','z',0243,0371,0375,0222,'@',0207,'=',0346,0365,'G','_','+','}',0266,0203,0256,0333,'B',0373,0345,'.',0275,'Q',0374,0376,0311,' ','C',0236,0363,'z',0243,0257,0225,0276,0333,'A',0327,'m',0241,'}',0362,0227,'^',0250,0376,0177,'d',0220,'!',0317,'y',0275,'Q',0327,0312,0337,'m',0240,0353,0266,0320,0276,0371,'K',0257,'T',0177,'?',0262,'H',020,0347,0274,0336,0250,0353,0345,'o',0266,0320,'u',0333,'h','_','|',0245,0327,0252,'?',0237,0331,'$',010,'s',0336,'o','T','u',0362,0267,0333,'h',':',0355,0264,'/',0276,'R',0353,0325,037,0317,0354,0222,04,'9',0357,'7',0252,':',0371,'[',0355,0264,035,'v',0332,027,0337,')','u',0352,0217,0347,0366,'I',02,034,0367,0233,0325,035,'|',0255,0366,0332,016,0273,'m',013,0357,0224,0272,0365,'G',0363,0373,'$',0201,016,'{',0315,0352,0216,0276,'V',0373,'m',07,'G',0377,0332,0,014,03,01,0,02,0,03,0,0,0,020,0222,'I','$',0222,'H','R',0363,0305,0344,0222,'I','$',0222,'I','$',0222,07,0277,0377,0,0377,0,0304,0222,'I','$',0222,'I','$',0212,0277,0374,0367,'/',0374,0222,'I','$',0222,'I','$',017,0372,014,0222,'o',0363,0222,'I','$',0222,'I','#',0277,'C','$',0222,'_',0376,0222,'I','$',0222,'I',0,0374,0311,'$',0222,0317,'8',022,'I','$',0222,'I','/',0222,'I','$',0231,'s',0207,'"','I','$',0222,'H',0270,'r','I','$',0257,0345,037,0362,'I','$',0222,'O','>',0222,'I','<',0334,0311,07,0367,0311,'$',0222,'I',0300,0222,'B',0277,0374,0310,'O',0376,0311,'$',0222,'C',0346,0244,'i',0372,'"','I','|','}',0371,'$',0222,'O',0377,0,0377,0,0376,0217,0222,'e',0323,'G',0271,'$',0222,'H',0377,0,0347,0324,'d',0223,037,'d',0213,0251,'$',0222,'H',0237,'2','I','$',017,'u',0244,0217,0211,'$',0222,'I',020,'B','I',')','?',0221,'$',035,'I','$',0222,'I','"',0370,0204,0177,0362,'Y','%',0377,0,'I','$',0222,'I','$',0377,0,0377,0,0374,0206,'I',07,0346,0311,'$',0207,0377,0,0377,0,0263,0311,0345,0377,0,0377,0,0347,0313,0377,0,0314,0236,0333,'m',0266,0333,'m',0266,0333,'m',0266,0333,'|',0236,0333,'m',0266,0333,'m',0266,0333,'m',0266,0333,'|',0203,'m',0266,0333,'m',0266,0333,'m',0266,0333,'m',0204,0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'}',0234,'[','Y',036,0316,'K',0262,0217,'o','$',0222,';','t',0326,0351,035,0256,'M','n',0232,0327,'$',0222,';','t',0326,0351,035,0256,'M','n',0232,0327,'$',0222,';','t',0326,0351,035,0256,'M','n',0232,0327,'$',0222,';','t',0326,0351,035,0256,'M','n',0232,0327,'$',0222,';','t',0326,0351,035,0256,'M','n',0232,0327,'$',0222,';','t',0326,0351,035,0256,'M','n',0232,0327,'$',0222,';','t',0326,0351,035,0256,'M','n',0232,0327,'$',0222,';','t',0326,0351,035,0256,'M','n',0232,0327,'$',0377,0304,0,047,021,0,01,02,04,05,05,01,01,01,0,0,0,0,0,0,0,01,0,021,020,'!','1',0360,' ','A','Q',0241,0361,'0','a','q',0261,0301,0321,'@','`',0377,0332,0,010,01,03,01,01,'?',020,0352,0222,0323,'+',0273,'#',0220,021,'>','k',0274,0273,0210,06,0205,'i',020,' ',0207,037,0314,'@',034,0254,0244,0250,036,0203,0226,'4',0376,'P',0211,'!',011,0311,0211,'(',010,0212,0226,'@','5','@','y','.',0312,'(','d',0214,0260,0324,0177,020,01,0312,'$',0242,0221,0244,0241,'5',' ',0,0220,0302,'L','&','&','x',' ','A',016,'?',0200,0,0345,034,0334,0300,02,'K',04,0314,0324,06,0220,0307,0223,0301,0233,0353,0204,034,0242,033,0230,034,0330,'!',04,0272,0,016,0350,0227,'.','`',010,0352,'@',026,'.',020,0336,0352,0220,07,'(',0346,0346,04,'$',0,'a',0320,020,'9','D','7','0','r',0346,0220,0243,'P',0302,'L','h','z',0257,0226,'@',0306,0301,0,'X','t',03,0346,0210,'N','`','B','d',0,'a',0,04,0336,'2','c','Q',0323,'x','*',0260,0233,032,0234,'d',0200,034,0247,'e',031,013,0262,0,0260,0213,035,0310,0317,0220,'.',034,'t',0212,0210,'4',0214,'D',0201,'2',0204,0224,0312,0253,022,'M','B',0,03,010,0222,0,'r',0246,0330,'3','}','!',0220,0302,'l',0205,021,'N',021,025,'J','#','D','4','*',0201,0211,'M',0202,014,0306,'g',015,022,0220,'!',0260,'A',0233,'$',01,'$',0341,011,0341,0321,'z',010,0202,'L','z',024,0,0263,0220,0,03,014,'=',0201,026,0256,'k',017,035,011,0207,0244,'x','3',010,'-',02,'"',0262,'A',0326,'h',06,0220,0302,0,'r',0211,'(',0244,'{',0242,'-','a',0325,'|','t',0,0356,0350,0313,'&','(',0206,0346,0,022,'X','*',0312,0260,'Q','2',0205,'n',0230,'-','"',010,',','P','-','0',0246,0330,'I','i',0225,'D',0232,0256,0304,0353,05,0345,'0','1','a','X','U','1',016,0243,0322,0300,0205,'$','3',0202,0354,0242,'9',04,'M','$',0211,'T','b',01,'%',0202,0316,'P',0,014,'0',0,034,0242,023,0230,'g','z',0336,';',035,0,'#','f','R',0,'0',0200,';',0242,023,0230,'?','s','D',03,'H','c',':','b',0,0,07,'$',0232,0,'3','%','X',0277,025,0213,0361,'X',0277,025,0213,0361,'X',0277,025,0213,0361,'2',0255,'|','+','3',0342,0263,'>','+',0263,0342,07,0265,0331,014,0373,0336,0312,0331,'v','M','Z',0354,0254,'_',0212,0305,0370,0254,'_',0212,0305,0370,0254,'_',0211,0273,'_',';','"','b',0346,0323,0262,0263,'>','#',0214,0215,0232,' ','3',013,'^',0312,0305,0370,0254,'_',0212,0305,0370,0254,'_',0210,0351,0210,' ',0202,030,0202,'*',010,0310,0210,0330,0264,0377,0,'m',0213,0134,'l','Z',0177,0266,0305,0256,'/',0256,0344,'j',0260,011,'n',0362,0134,01,'8',02,'p',04,0340,011,0300,023,0200,047,0,'N',0,0234,01,'8',02,'p',04,0340,011,0300,023,0200,047,0,'N',0,0234,01,'8',02,'p',04,0340,011,0300,023,0200,047,0,'N',0,0234,01,'8',02,'p',04,0340,010,0372,0354,'G',0253,020,0207,0357,'?',0360,0243,'a',0256,'C',020,'M','<','.','i',0372,0271,0247,0352,'w','K',0270,0207,0360,'Z',014,0272,0313,'.','i',0372,0271,0247,0352,07,0301,0262,0,0202,0134,'>',0242,017,0307,'0','C',0346,03,'K',0312,0346,0237,0253,0232,'~',0240,'~','9',0200,033,'2',032,'~','"',017,0203,0344,'A','$','1','|','.','i',0372,0271,0247,0352,'u',0322,'Z',014,0351,'g',0,0376,'K','.','i',0372,0271,0247,0352,';',015,'p',030,02,'+',0347,035,013,0251,033,'V',0250,'n','G',0274,'N',0357,0361,0366,0374,'[',0273,0223,0356,026,0255,021,0251,'u','q',0320,0272,0221,0265,'j',0206,0344,'{',0304,0356,0377,0,037,'o',0305,0273,0271,'>',0341,'j',0321,032,0227,'W',035,013,0251,033,'V',0250,'n','G',0274,'N',0357,0361,0366,0374,'[',0273,0223,0356,026,0255,021,0251,'u','q',0320,0272,0221,0265,'j',0206,0344,'{',0304,0356,0377,0,037,'o',0305,0273,0271,'>',0341,'j',0321,032,0227,'W',035,013,0251,033,'V',0250,'n','G',0274,'N',0357,0361,0366,0374,'[',0273,0223,0356,026,0255,021,0251,'u','q',0320,0272,0221,0265,'j',0206,0344,'{',0304,0356,0377,0,037,'o',0305,0273,0271,'>',0341,'j',0321,032,0227,'W',035,013,0251,033,'V',0250,'n','G',0274,'N',0357,0361,0366,0374,'[',0273,0223,0356,026,0255,021,0251,'u','q',0320,0272,0221,0265,'j',0206,0344,'{',0304,0356,0377,0,037,'o',0305,0273,0271,'>',0341,'j',0321,032,0227,'W',07,0377,0304,0,'*',021,01,0,0,02,010,06,03,01,01,01,0,0,0,0,0,0,01,0,021,020,'!','1','A','Q','a',0221,0261,'q',0201,0241,0301,0321,0360,' ','0',0341,0361,'@','`',0377,0332,0,010,01,02,01,01,'?',020,0373,'C',022,0255,0201,'[',02,'H',035,'Z',023,'u',010,'&','j','e',' ',0357,0332,'$',0347,0316,'{',0214,'X',0313,0223,0304,'+','n',0223,0304,035,'.','H',0360,'D',0362,0277,0251,0301,0362,'s',0205,'R',0205,0243,0376,'b',0215,0310,0206,'e','/',0351,'[',0343,'X',0224,0227,05,0274,0333,'^','o',0320,'.',025,'s','3','=','U',0237,027,0374,0200,0254,0210,0221,0360,0253,0336,'7',0312,0370,0226,'8',0313,0273,'k',0316,0231,023,014,0355,0344,'Z',0362,0206,'f','s',0330,0356,0364,'"',0243,'<',022,'o','Y',0235,'!',0211,0362,'U','m','(',013,0312,0203,'B',0305,0250,036,'I',0336,0336,0264,0325,015,0367,013,0307,';','N','e',0337,0342,027,'0',0333,'7',02,010,'8',0335,0307,0253,0355,0341,'e','3',0205,'n',05,'k',0313,0314,0210,'E','.',0246,0253,0271,'V','c',011,030,0255,0355,'o',0306,0321,024,032,0323,'~','=',0216,015,0317,0267,'L',0203,'f','I',047,0227,0370,',',0372,'8',01,0335,0300,0355,'6','%',0203,0213,'z',0347,'C',0263,02,0325,0211,0255,'Y',0212,0336,'K',0270,0265,0344,'B','f',0232,0332,0266,0374,0326,'i',0342,'w','=',0265,0370,'M',03,0254,0253,0261,0354,0362,0373,0354,0220,'-','`','w','p',';','M',0211,'t',06,0253,0213,0237,0266,'Q','9','5',0266,027,0256,']',0333,0264,0211,0361,0220,0260,'X','y','s',0330,0253,0350,'7','d',0332,0313,03,'6',0355,'`',0362,0220,'$',031,'Q','=','s',037,'v',035,'U',0320,'T','L',021,0340,0333,026,'4',0332,'8',0227,'=',0234,0347,0366,0234,'S',0260,0367,'+',0340,0335,0275,0356,'/',0266,027,024,'[',0241,0330,0307,'7','(',']','8',0272,'d','e',0364,'X',0240,'-','n','?','[',0216,0323,'`',0221,0220,'j',0267,0256,'o',0345,0224,024,0356,0256,0254,0214,'|','g',0302,'&',0316,'q','W',')',0324,0271,0340,0363,0334,'h',0230,0225,0341,0231,'y',0334,0316,0253,0376,0322,'Q',0331,'b',0347,0267,032,',',0353,',','1','n',017,'l',0233,016,015,'n',0201,'p','e',0375,'k',0372,'&',015,'E',0270,0262,'1','z',035,' ',0344,0240,0352,0342,0342,0321,'j',0325,'c',027,0301,'|','&','s',0364,0322,0202,'Q','2','a','2',0227,'k','y','S','Q',0326,0211,0223,'y',0343,'.',037,'Z',023,'z',0276,027,0272,'A',0203,0220,'T','p',0241,0331,'.',014,0333,0337,031,'|',0315,0251,',',010,0222,'V',0270,',',0346,0226,0360,'*',0315,0262,01,04,0202,0211,0376,0263,0263,0271,0300,0364,0207,0223,0213,0246,'F','T',0251,'V',035,'Z',0266,0235,'6',0264,'Z','8','%',0236,034,0230,'*','$',031,'&','g',0324,0256,'-','%',0305,'C','i',0320,0346,0312,'I',034,'V','U','f','N','|',0276,'A',030,0255,0305,'l','H','_','"',0337,03,0235,'f',021,'U','~','-',0357,027,0265,0230,024,0363,0306,']',0344,0345,0256,020,0321,011,'j',0322,01,0315,'$',021,'_','C','j',0342,0266,0370,'2','>',022,0203,0251,0253,0261,0355,0246,'?','U','y','[','6',0211,0346,0205,'S','*',0211,';','&','~','N',034,'d',02,0360,0231,0251,'3',0254,'f',0240,0331,0241,0210,0245,0254,0241,'R','Q',0315,0354,'M',0211,'2',0235,'U',025,031,0227,0257,')','D',0260,0270,'-',0346,0332,0363,0246,0247,'r',0354,'^',05,0376,0316,'&','=',0330,'f',0335,0300,0346,0277,013,'j','!',0245,0370,06,017,'-',0370,025,'c','C',0177,'Q',0325,0270,'8',0376,0331,025,0236,012,'|','.','K','&',0315,'0',0240,'I',0230,'I',0213,0231,0354,'q','.','}',0341,0364,0336,'~',0254,0304,0274,0346,'B',0223,'0',0231,0364,'J','K',0212,0336,'E',0257,'"','&','R',0372,0234,0213,'5',0237,010,'l',0304,0275,0257,0340,012,0221,'l','T',07,0330,0233,0263,016,'6','B','$',0220,'C','O',0360,0363,'q',0361,0201,0305,0201,0223,'2',014,022,0343,0261,0347,0274,0350,02,0330,'t','3','z',047,'_',0252,0262,'6',016,034,0367,0343,0360,'P','&',0301,0323,0344,0231,0272,023,'b','b',';',0320,0325,0257,0244,'M',0220,031,'[',0253,'^',0222,0204,0314,0253,'j',0326,0374,'g',0227,0261,0233,0201,0233,022,0211,030,0367,034,'<',0333,0204,0250,'P','M',0262,021,0257,'R',0327,0325,0233,0322,0322,0134,'O',0214,0352,0331,0241,01,0236,016,0377,0,'P',0252,'d',021,0256,0347,0261,0347,0274,0350,'p',033,0203,'+',0236,'M','O',',','!',0264,'3',0371,0333,012,0235,0341,0300,0273,0213,0304,030,'3','.',0352,0361,'o',0241,0334,0260,0265,'a',0205,0245,'}',0277,0300,0313,0134,012,'P',0316,0371,0302,0343,'J',0371,0312,0211,'a','s','l','w',0372,0353,'9',',','L','K',0374,0231,0302,03,'0',0230,0300,'3','1','$',0360,'a',011,'e',0243,0210,0331,0341,0314,0370,0206,'%','[',02,0266,'$',0374,0362,0335,'<',0312,'$','9',0245,'[',0312,0343,0225,'y',0323,'h',0265,0301,'k',0313,014,0354,0211,'i',0310,'j',035,0361,'}',03,0340,0222,034,'|',0333,0216,0356,0134,'h','r',0362,012,0336,04,'=',0310,0265,'p',',',0375,0317,0354,0256,'z',0235,015,0347,';',0263,0343,'A',012,0233,04,0273,0311,0354,0340,'H','q',0234,0307,'I',';',0307,0364,'`','B',0250,'d','.',0360,0220,')','|','d','h','W',0326,'%',0301,0300,'n',0332,0363,0245,'q',02,0366,010,023,0257,027,'c',0273,0244,'!','R','^',0327,0360,0261,'`',0344,036,'[',0216,0320,'X','K',0322,'|','Z',016,0177,'[',0321,'q',0316,0327,'.','/',0332,0210,'I','$',020,0245,0307,025,0317,'=',0347,0363,0226,027,025,0274,0213,'^','Q','6',0235,'y',034,0217,')',0302,'&','>',0272,034,013,017,0215,0302,026,0366,030,0276,0260,'v','@','u','q','q','h',0264,0255,0221,0206,'o','b',0370,'N',0223,'Z',0327,027,0346,01,'I',04,0177,'h',0217,0355,021,0375,0242,'?',0264,'G',0366,0210,0376,0321,011,0324,'X','&','V','k',0314,0316,0,023,0332,0316,'=',0253,0274,'0',0134,0251,0275,'B','P',0214,0271,011,'w','H',0255,0201,0222,035,'C',0314,'O',0,'8',' ',0353,'9',0365,0204,'B','W',047,0230,0376,0321,037,0332,'#',0373,'D',0177,'h',0217,0355,020,0331,0304,04,0361,0256,0243,0257,010,'>','x',0260,'<',0321,0355,']',0342,'B','K','d',0202,034,'Q',0351,'o',010,032,015,'b',0314,0217,0355,021,0375,0242,'?',0264,'G',0366,0210,0,'$',0306,0236,0265,0267,0373,'z','V',0324,0365,0255,0277,0333,0322,0266,0245,0255,0242,'&',0244,'g',032,0376,'F','q',0257,0344,'g',032,0376,'F','q',0257,0344,'g',032,0376,'F','q',0257,0344,'g',032,0376,'F','q',0257,0344,'g',032,0376,'F','q',0257,0344,'g',032,0376,'F','q',0257,0344,'g',032,0376,'F','q',0257,0344,'g',032,0376,'F','q',0257,0344,'g',032,0376,'F','q',0257,0344,'g',032,0376,'F','q',0257,0344,'g',032,0376,'F','q',0257,0344,'g',032,0376,'F','q',0257,0344,'g',032,0376,'F','q',0257,0344,'g',032,0376,'F','q',0257,0344,'5',0264,0,0320,0377,0,0205,0233,'k',031,0215,'c','1',0254,'J',0261,'P',0262,026,'3',032,0306,'c','X',010,'2','~',047,'H',' ',0234,0331,'F','c','X',0314,'k',010,0202,'R','e','J','A',0262,0361,'8',0314,'k',031,0215,'`','f',015,023,0254,021,0230,0326,'3',032,0304,0333,'O',0236,0331,'O','N','m','E',0265,';',0355,0232,'}',014,0232,'}',014,0212,'v',0333,024,0331,'Q',0323,0273,'S',0266,0374,0366,0312,'z','s','j','-',0251,0337,'l',0323,0350,'d',0323,0350,'d','S',0266,0330,0246,0312,0216,0235,0332,0235,0267,0347,0266,'S',0323,0233,'Q','m','N',0373,'f',0237,'C','&',0237,'C','"',0235,0266,0305,'6','T','t',0356,0324,0355,0277,'=',0262,0236,0234,0332,0213,'j','w',0333,'4',0372,031,'4',0372,031,024,0355,0266,')',0262,0243,0247,'v',0247,'m',0371,0355,0224,0364,0346,0324,'[','S',0276,0331,0247,0320,0311,0247,0320,0310,0247,'m',0261,'M',0225,035,';',0265,';','o',0317,'l',0247,0247,'6',0242,0332,0235,0366,0315,'>',0206,'M','>',0206,'E',';','m',0212,'l',0250,0351,0335,0251,0333,'~','{','e','=','9',0265,026,0324,0357,0266,'i',0364,'2','i',0364,'2',')',0333,'l','S','e','G','N',0355,'N',0333,0363,0333,')',0351,0315,0250,0266,0247,'}',0263,'O',0241,0223,'O',0241,0221,'N',0333,'b',0233,'*',':','w','j','v',0337,0207,0377,0304,0,'(',020,01,01,0,01,03,03,04,03,0,02,03,0,0,0,0,0,01,021,'!',0,'1','A',020,'Q','a',' ','0','q',0201,'@',0221,0360,0241,0321,'`',0261,0301,0377,0332,0,010,01,01,0,01,'?',020,0367,'Q',0251,0311,0240,0252,0240,011,0335,0325,0263,0362,'x','e',0253,'p',027,'Z',0342,' ',0257,0204,0224,'3',0262,0364,0255,'y',0251,'K',0355,023,0306,0232,'W',022,'9',0317,0333,0326,015,0314,0240,0375,0232,'-','-',02,021,';','G',0372,'4','{','.',0320,'&',024,'(','L',0252,0324,0331,';',0343,'s','P',015,0221,0344,'F',0212,0210,0210,0203,0370,0325,'U','a','4',026,033,0341,013,0343,'E',0251,0253,0251,0334,'R','q',0302,047,0316,0251,0261,'Q','G',030,'a',0205,0215,0210,0366,'3','U','$','H',010,0260,'q',031,0275,0356,0300,0344,0210,0205,021,0331,037,0303,'C','B',',','1','U',0134,0,'s',0247,0361,'e','5','f',0310,0211,'M',0361,0230,0216,'f',0260,'i',0254,0200,'v','3',0300,03,0307,'R',0262,0305,030,0354,0354,0270,'r',0203,'F',025,'y','W','v','Q',0372,'-',015,'+',07,'S',0312,'E',0373,'Z','N','I','k',0373,'B','/',0255,'/',0134,0345,0317,0334,':','6',0233,'d','Y',0210,'N',0306,0341,'p','N',0263,02,0271,'8',0336,'J',0242,0361,'N',0177,011,'y',0304,0327,010,0246,0341,0246,0306,0306,'X',013,0246,0217,0312,' ','6',035,0334,0316,',',0302,016,0245,0325,0306,'(',0363,0221,'v',0375,06,0222,0254,0240,030,0331,'C',0266,0330,033,0352,'H',0250,'c',0314,0,0,'z','Z',0324,'(',0200,'^','v',')',017,'#',0250,0200,034,'^',0300,'N',021,0337,'f',0267,015,033,0371,'O',030,'X','8',015,021,0310,0211,0307,0340,'/',03,05,017,0223,';','j',0214,0330,025,05,0306,0255,0273,'V',0225,0261,0335,0312,0356,0253,0320,'R','q',0253,0370,02,0256,0236,'`',0262,'y',0347,0272,0317,',',0306,'P',0272,01,0363,026,'^',0301,0,'|','z',0302,'X',023,0220,'9','~','L',027,0373,'z',024,0344,'F',0270,032,027,'b','J','y','(',0367,0314,0323,010,'0','2',0256,'s',07,0207,01,'P','o',0302,011,0200,0370,'C','`',037,'-','U','u',024,020,05,036,0311,0216,'c',014,0241,0206,024,0207,0321,037,'G','}',0317,023,'p','R',0317,0261,0275,0263,'U','L','H',0216,'q','v',0250,034,0250,0240,'w',012,0371,017,'u','W',0240,'L','!',026,010,'A',0346,0215,0351,01,'z','6',0310,0361,'L',037,0220,017,0326,0216,'R',0254,'1','L',034,'P',0367,'d',0330,'N',0206,']',0203,0313,0,0345,06,0255,'s',0251,0222,0257,0234,0363,0310,'.','0',032,032,027,'P',0326,0354,'x',011,0366,'0',027,'A','c','E',03,010,'E',0313,'7',0343,0,0,036,0306,015,'p',0270,'M',0327,0316,'+',0202,0201,'R','b',0216,'Q',0203,0257,'j','L',017,0225,0252,0256,0207,'t',0210,0201,0213,'y',0303,';','v',0251,0240,0244,0222,01,'$','#','i','1','4',0231,0337,0361,06,'%',0360,047,0371,0367,0246,0333,0334,0255,'S',0303,'J','c','*',0354,0320,0200,'F',0216,'D',0367,022,'B',0225,0236,'$','F','n',05,0335,0311,'0','t',0241,025,0257,'9',0344,033,07,0355,'C','#','P',0206,'g',025,0357,'y','N','6',0,' ',07,0261,0346,'B','`',0230,026,0315,0220,0360,'l','`',0213,0373,'"',036,0360,027,'0','c','~','U',0350,0325,0273,0312,'@',0370,'I',0272,0271,0300,')',0316,'|','9',0331,'o','*',0253,0335,0350,0352,020,0326,'h',0213,'L',0300,'w',0300,0303,0255,0263,'a',0327,'8',0263,'`',0252,0255,013,0267,0333,037,' ','f','E','Q',0310,0323,0342,0352,0205,013,0362,024,'9','U','U',0363,0322,0261,030,0260,0203,011,05,'F','r',0340,'l',0372,0340,0377,0,'S',017,0313,0272,0360,031,'x',0326,012,'V',036,'n','G','2',037,'w',03,'.',0221,'x',0225,'u','P',0312,0252,0252,0357,0322,'m',06,0243,0204,0361,0134,0330,0276,'X','g','F',0363,0233,0243,0362,0344,0370,0,0,0,0353,0275,0343,'r',0321,035,0242,'K',0277,0207,0250,0243,'`','3',034,0355,0330,024,0300,0216,'4',0317,0352,0254,04,0237,0247,'n','=',0247,034,'@',0271,015,'x',0277,0344,'t',0330,'D','=',0235,0316,'#','[',0240,0,0,020,016,'=','(','S',01,013,0312,01,0254,'U','X',0334,0245,'(','I',015,0210,'4',0325,0313,'5','B','%','{',' ','3',05,01,'W','~',0253,0316,0207,0334,'x',0306,'o',0260,0330,0332,'a','z',025,0227,'*',0345,032,0252,0252,0252,0257,'^',')',0363,0212,0270,'9',']',0200,0312,0240,'o',0242,'F',0266,'7',0251,0363,0305,'N','Q',0317,0240,0200,0303,'C',013,0271,0273,0360,0263,0372,036,0322,'y',032,'<','`',0374,0377,0,0323,0241,0331,'Q','S','d',0330,'(',0205,'.',024,0272,0247,'Z','$','%',0246,'C','g','a',023,'"',0231,0320,0300,0214,':','C',0331,'(',020,0371,037,0341,0245,07,'<',0345,'9',0306,'|',0262,'q','t',011,0305,'B','C',0213,0205,0206,'`',0354,0316,'2',0372,0210,0312,010,017,05,0203,0,':',0212,'b',0344,0304,0261,0334,07,'+',0230,0312,0343,'K','R','D','H','3',0204,' ',0360,0314,010,022,0350,0,0,010,07,035,0134,0202,015,0,'U','W','`','4',026,033,'L',0212,036,0134,'w',013,0364,'h',017,010,'j',0376,'Y','.','Y',0314,0206,'P',0321,0265,'8','x',0315,011,'W','w',0262,0347,035,026,'-','Q',0222,0223,0344,0304,'M',0222,0216,0372,'%','x',0236,0234,'z',014,'2',024,0312,'9',0366,'s',')','_',0314,0221,'3',02,'S',0224,0234,0350,0206,0207,'q','d','~','N','D',0302,'"','a',0366,016,0313,0212,0253,0242,0225,0205,0215,0310,0320,0310,'f','J','>',030,0327,0310,017,017,'[',04,07,0251,0312,0231,'N','U','^','_','A',0346,0302,0346,0225,'F',0,'3',']','*',0346,'Y',0134,'o',0310,'w','s',0320,047,0225,0272,0352,01,0225,'T',0,0337,'U',0310,0312,0213,'t',014,'[',026,'a',01,'P','*',025,0310,'U','D','D',0344,'M',031,0235,0302,0245,0262,'3',0225,0311,0312,0364,'&',0224,0361,0304,'8',0363,'+','}',0250,0342,'v',0206,0274,'L',0337,02,'x',0266,'z','&',01,'D',0,0252,0352,0264,'`',026,'|','Z','Y',0357,0262,037,0301,0273,0321,0323,02,0263,'G','i','Q','x',']',030,034,026,'F',0,'@','z','j','s',06,0322,0254,'[',01,0300,'{',0260,0316,0261,'P',0230,0342,'p',0344,'{',0201,0216,'h',023,'O',0344,'@',0252,0235,0200,0321,05,'@','M',0271,0216,0360,'8','8',0365,037,'#',0212,'U',037,0207,0364,0235,025,'t','!',0357,037,'h',010,013,026,025,0,0356,'"',0223,'O',034,0257,'(',0262,0347,0204,0345,'S',0265,06,0222,'w',014,'(',0305,',','D','m','^','u','8',034,0330,0225,0372,036,0264,0134,0345,0300,015,0325,'v','4','T',0272,0252,0316,'d',022,0313,0271,'0',0211,'I',0246,0252,'m','|','k','}',0,'9','j',0257,'A','J',0265,013,0354,014,0256,0231,'$',017,'K',0366,'T',0247,0234,0274,0205,0352,0333,'D',0345,'+',047,0211,'c',0270,016,'E','<',0364,'D',0343,0215,0300,0202,'w',031,0177,0337,0333,'H','1',0234,'S','4',0263,'j',0313,0222,'d',035,020,'*','Z',';',022,'c','s','s',016,0346,0221,'X',0373,017,'-',0334,'A','>','4','P',0213,036,'B','X','V',012,'x',0211,0270,0372,'^',0243,011,'`',0252,0240,017,0227,'S','x','P',0363,0246,0312,0357,0340,'1',0311,0244,':','/',0300,'c','/',0333,'N',0301,0325,0352,0356,'>',011,0231,0240,0347,06,0256,06,'2','U','Z','8',035,0335,'/',0332,0254,'S',0,0352,'Y','%',035,0364,0134,0333,'&',0336,'|',027,'V','P',0376,0230,01,0312,0250,07,0235,06,05,'W','H',0327,02,024,'d',0276,0342,011,'|',0214,0207,'0','y',0305,0374,0230,037,'B','2',0222,'5',024,023,047,'@','.',0352,'&','4',0370,0,'A','o','4',0357,0330,0321,0376,012,013,0376,0372,06,'H',0305,' ',0357,')','|','S',0347,'W','y','N',05,0250,0242,'.','r','o','c','9',0322,0272,0211,0214,';',0331,0207,0300,016,0267,035,0276,'7',0200,'_',0365,0254,07,02,0307,'a',07,'A',0270,0321,0333,0261,0250,036,'a','{',0206,033,0256,0352,0345,'r',0276,0202,024,0324,0222,0343,016,'j',036,'M',0214,0272,'U',035,0332,0256,0300,'p',0,034,0,'t','V','B',012,'@',0251,'.','r',013,0233,'w',0367,'a',0364,'l',014,'@',';',0210,0246,0255,'I',032,0344,0345,'7','f',0271,'O','X','T',030,'X',024,0275,0213,0312,015,'&',0217,010,'1',06,0242,0266,0214,'x','&',0245,0134,'X',' ',0362,'9',0260,0260,0270,017,'J','o','~','d','Z',014,'r','m',0314,'!',0312,'p',0377,0,'2','?',0204,0272,03,0374,0252,0272,0201,0206,01,016,'6',0357,023,0260,'n',0350,'%',0334,':',0200,0200,0354,07,0254,0350,05,0302,0225,'L',0,'*',0270,03,0326,'<','x',0361,0343,0307,0330,0306,0340,'X',0301,'x',0367,0304,0335,0303,'N',0376,0355,'a',030,0312,03,0310,0243,0306,0277,0244,0377,0,0315,'l','8',0303,037,'#','~',0303,'@',',',031,024,0235,0347,0373,0232,0303,0262,'*','/',0300,0177,'*',0370,0322,0327,05,0270,0336,0352,0303,0212,0377,0,'y',0320,0202,0261,0272,'6',0,03,0343,0324,'<','x',0361,0343,0331,0266,037,'>',0312,'l','Y',0205,0305,03,0244,0203,0300,'[',0356,0246,'W','_',0322,0177,0346,0221,0240,0236,0377,0,020,0212,0316,'d','y','c','D',0345,'X',0270,'7','F',0321,'T',0252,0252,0265,0364,0217,036,'<','y',0320,013,0205,'(',0230,'A',021,'0',0217,0374,015,'2','d',0311,0231,0322,0211,0270,0204,0214,'(',0254,0374,0325,0353,0327,0257,'^',0275,'z',0365,0353,0327,0257,'^',0275,'z',0365,0353,0327,0257,'^',0275,'z',0365,0353,0327,0257,'g','Z','&',0342,022,024,0252,'3',0376,013,'N',')',0374,012,0312,'[','N',0254,'X',0233,0240,'+',0241,034,0230,0262,0313,0322,0233,',',0376,'`','Y','~',0272,0261,'b',0217,034,'T',0303,0240,'-',021,0367,0324,0235,0365,'#',034,0262,'8',0310,0373,0352,0305,0213,';',0352,'F',031,'`','1',0201,0365,0325,036,'8',0211,0207,022,'H',03,0353,0253,026,'0',0331,'g',0367,0,0313,0367,0320,0335,021,'M',012,0344,0314,0262,'^',0254,'X',0303,0212,0177,02,'2',06,0367,0334,0251,'O',0371,0375,0376,0375,'=','Z',0251,0377,0,'?',0267,0360,'i',0324,0251,'O',0371,0375,0376,0375,'=','Z',0251,0377,0,'?',0267,0360,'i',0324,0251,'O',0371,0375,0376,0375,'=','Z',0251,0377,0,'?',0267,0360,'i',0324,0251,'O',0371,0375,0376,0375,'=','Z',0251,0377,0,'?',0267,0360,'i',0324,0251,'O',0371,0375,0376,0375,'=','Z',0251,0377,0,'?',0267,0360,'i',0324,0251,'O',0371,0375,0376,0375,'=','Z',0251,0377,0,'?',0267,0360,'i',0324,0251,'O',0371,0375,0376,0375,'=','Z',0251,0377,0,'?',0267,0360,'i',0324,0251,'O',0371,0375,0376,0375,'=','Z',0251,0377,0,'?',0267,0331,0247,'S',0377,0331,};
diff --git a/src/embedded_images/shred_db.jpg.h b/src/embedded_images/shred_db.jpg.h
new file mode 100644
index 0000000..28fa222
--- /dev/null
+++ b/src/embedded_images/shred_db.jpg.h
@@ -0,0 +1,16 @@
+/* Autogenerated by hxtools bin2c */
+#ifndef SHRED_DB_JPG_H
+#define SHRED_DB_JPG_H 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern const unsigned char bin2c_shred_db_jpg[27063];
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+
+#endif /* SHRED_DB_JPG_H */
diff --git a/src/embedded_images/tick_erased.jpg b/src/embedded_images/tick_erased.jpg
new file mode 100644
index 0000000..3710146
--- /dev/null
+++ b/src/embedded_images/tick_erased.jpg
Binary files differ
diff --git a/src/embedded_images/tick_erased.jpg.c b/src/embedded_images/tick_erased.jpg.c
new file mode 100644
index 0000000..6bbfc2a
--- /dev/null
+++ b/src/embedded_images/tick_erased.jpg.c
@@ -0,0 +1,4 @@
+/* Autogenerated by hxtools bin2c */
+#include "tick_erased.jpg.h"
+/* Autogenerated from te.jpg */
+const unsigned char bin2c_te_jpg[54896] = {0377,0330,0377,0340,0,020,'J','F','I','F',0,01,01,02,0,'v',0,'v',0,0,0377,0341,047,'6','E','x','i','f',0,0,'I','I','*',0,010,0,0,0,07,0,022,01,03,0,01,0,0,0,01,0,0,0,032,01,05,0,01,0,0,0,'b',0,0,0,033,01,05,0,01,0,0,0,'j',0,0,0,'(',01,03,0,01,0,0,0,03,0,0,0,'1',01,02,0,015,0,0,0,'r',0,0,0,'2',01,02,0,024,0,0,0,0200,0,0,0,'i',0207,04,0,01,0,0,0,0224,0,0,0,0246,0,0,0,'O',02,0,0,05,0,0,0,'O',02,0,0,05,0,0,0,'G','I','M','P',' ','2','.','1','0','.','3','0',0,0,'2','0','2','3',':','0','2',':','1','2',' ','2','0',':','5','9',':','4','7',0,01,0,01,0240,03,0,01,0,0,0,01,0,0,0,0,0,0,0,011,0,0376,0,04,0,01,0,0,0,01,0,0,0,0,01,04,0,01,0,0,0,0367,0,0,0,01,01,04,0,01,0,0,0,0,01,0,0,02,01,03,0,03,0,0,0,030,01,0,0,03,01,03,0,01,0,0,0,06,0,0,0,06,01,03,0,01,0,0,0,06,0,0,0,025,01,03,0,01,0,0,0,03,0,0,0,01,02,04,0,01,0,0,0,036,01,0,0,02,02,04,0,01,0,0,0,017,'&',0,0,0,0,0,0,010,0,010,0,010,0,0377,0330,0377,0340,0,020,'J','F','I','F',0,01,01,0,0,01,0,01,0,0,0377,0333,0,'C',0,010,06,06,07,06,05,010,07,07,07,011,011,010,012,014,024,015,014,013,013,014,031,022,023,017,024,035,032,037,036,035,032,034,034,' ','$','.',047,' ','"',',','#',034,034,'(','7',')',',','0','1','4','4','4',037,047,'9','=','8','2','<','.','3','4','2',0377,0333,0,'C',01,011,011,011,014,013,014,030,015,015,030,'2','!',034,'!','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','2','2','2','2','2','2','2','2','2','2','2','2','2','2','2','2','2','2',0377,0300,0,021,010,01,0,0,0367,03,01,'"',0,02,021,01,03,021,01,0377,0304,0,037,0,0,01,05,01,01,01,01,01,01,0,0,0,0,0,0,0,0,01,02,03,04,05,06,07,010,011,012,013,0377,0304,0,0265,020,0,02,01,03,03,02,04,03,05,05,04,04,0,0,01,'}',01,02,03,0,04,021,05,022,'!','1','A',06,023,'Q','a',07,'"','q',024,'2',0201,0221,0241,010,'#','B',0261,0301,025,'R',0321,0360,'$','3','b','r',0202,011,012,026,027,030,031,032,'%','&',047,'(',')','*','4','5','6','7','8','9',':','C','D','E','F','G','H','I','J','S','T','U','V','W','X','Y','Z','c','d','e','f','g','h','i','j','s','t','u','v','w','x','y','z',0203,0204,0205,0206,0207,0210,0211,0212,0222,0223,0224,0225,0226,0227,0230,0231,0232,0242,0243,0244,0245,0246,0247,0250,0251,0252,0262,0263,0264,0265,0266,0267,0270,0271,0272,0302,0303,0304,0305,0306,0307,0310,0311,0312,0322,0323,0324,0325,0326,0327,0330,0331,0332,0341,0342,0343,0344,0345,0346,0347,0350,0351,0352,0361,0362,0363,0364,0365,0366,0367,0370,0371,0372,0377,0304,0,037,01,0,03,01,01,01,01,01,01,01,01,01,0,0,0,0,0,0,01,02,03,04,05,06,07,010,011,012,013,0377,0304,0,0265,021,0,02,01,02,04,04,03,04,07,05,04,04,0,01,02,'w',0,01,02,03,021,04,05,'!','1',06,022,'A','Q',07,'a','q',023,'"','2',0201,010,024,'B',0221,0241,0261,0301,011,'#','3','R',0360,025,'b','r',0321,012,026,'$','4',0341,'%',0361,027,030,031,032,'&',047,'(',')','*','5','6','7','8','9',':','C','D','E','F','G','H','I','J','S','T','U','V','W','X','Y','Z','c','d','e','f','g','h','i','j','s','t','u','v','w','x','y','z',0202,0203,0204,0205,0206,0207,0210,0211,0212,0222,0223,0224,0225,0226,0227,0230,0231,0232,0242,0243,0244,0245,0246,0247,0250,0251,0252,0262,0263,0264,0265,0266,0267,0270,0271,0272,0302,0303,0304,0305,0306,0307,0310,0311,0312,0322,0323,0324,0325,0326,0327,0330,0331,0332,0342,0343,0344,0345,0346,0347,0350,0351,0352,0362,0363,0364,0365,0366,0367,0370,0371,0372,0377,0332,0,014,03,01,0,02,021,03,021,0,'?',0,0367,'{',037,0371,07,0333,0177,0327,'%',0376,'B',0254,'U','{',037,0371,07,0333,0177,0327,'%',0376,'B',0254,'P',01,'E',024,'P',01,'E',024,'P',01,'E',024,'P',01,'E',024,'P',01,'E',024,'P',01,'E',024,'P',01,'E',024,'P',01,'E',024,'P',01,'E',024,'P',01,'E',024,'P',01,'E',024,'P',01,0322,0271,0235,'_',0305,0366,0366,'N',0320,'Z','(',0236,'Q',0301,'l',0374,0252,0177,0255,'e',0370,0257,0304,0340,0371,0226,'v',0262,0204,0205,'?',0326,0312,017,0336,0366,07,0322,0274,0306,0377,0,']','w','c',035,0257,0312,0237,0337,0356,'k',012,0370,0210,'Q','W',0233,'"','s','P','Z',0235,0226,0243,0342,0253,0351,0201,'7',027,0376,'J',0177,'u',033,'`',0375,'9','5',0316,0313,0257,0331,0206,047,'|',0222,'1','9','$','/','S',0370,0327,'.',0356,0322,'1','g','b',0314,'{',0223,'M',0257,'.',0246,'i','6',0375,0305,'c',0232,'X',0211,'t',':','3',0342,'(',07,'H','d','?',0210,0245,'_',021,'[',0223,0363,'E',' ',0374,0215,'s','t',0370,0242,'y',0345,'X',0343,031,'f','8',025,0222,0314,'1',015,0331,'~','D',0373,'y',0236,0203,0244,'k','3','Y',0272,0335,'Y','K',0362,0260,0344,'v','o',0250,0242,0247,0360,0256,0201,0366,0331,0243,0264,'$',0371,'1','!','2','8',0365,0366,0374,'h',0257,'z','7',0345,0134,0333,0235,0252,0366,0324,0364,0373,037,0371,07,0333,0177,0327,'%',0376,'B',0254,'U','{',037,0371,07,0333,0177,0327,'%',0376,'B',0254,'U',014,'(',0242,0212,0,'(',0242,0212,0,'(',0242,0231,'4',0321,'[',0304,0322,0315,'"','G',032,0214,0263,0273,'`',01,0356,'h',01,0364,'W',0222,0370,0253,0343,0347,0207,'4','g',0222,0333,'G','V',0325,0256,027,0202,0361,0374,0260,0203,0376,0361,0373,0337,'Q',0305,'x',0227,0211,0376,'-','x',0273,0304,0354,0351,'6',0244,0366,'v',0215,0377,0,'.',0366,'d',0306,0270,0367,'#',0223,0371,0343,0332,0200,'>',0247,0326,0374,'q',0341,0257,017,'d','j','z',0305,0254,'2',017,0371,'g',0274,'3',0376,'B',0274,0347,'W',0375,0243,'<','?','k',0271,'4',0275,'6',0362,0371,0200,0312,0263,'b','$',047,0323,047,0221,0371,'W',0314,0324,'P',07,0262,'j','?',0264,'o',0211,0247,0227,0376,'%',0372,'f',0233,'i',026,':','H',036,'V',0317,0327,' ','c',0247,'j',0346,'n','>','4',0374,'@',0270,0220,0261,0327,0332,'1',0222,'B',0307,'m',012,0201,0236,0337,'s',047,0361,0315,'p','4','P',07,'c','q',0361,'W',0307,'W',',',0246,'O',023,'^',0202,06,07,0226,'V','?',0375,04,014,0324,0220,0374,0134,0361,0344,021,0210,0323,0304,0227,'E','G','w','D','s',0371,0225,'&',0270,0252,'(',03,0321,0254,'>','9','x',0366,0312,'M',0322,'j',0261,'^','&',0322,'<',0273,0213,'X',0361,0365,0312,0205,'9',0374,'k',0251,0322,0377,0,'i','-','b',',','.',0255,0241,0331,0134,015,0334,0275,0263,0264,'D','/',0320,0356,0311,0353,0334,'W',0210,'Q','@',037,'U','h',0337,037,0274,'#',0251,'8',0216,0354,']','i',0316,'N','?','~',0200,0257,0346,0244,0340,'}','k',0320,0364,0237,020,'i',032,0354,'>','n',0227,0251,'[',']',0256,01,0375,0324,0200,0220,017,0250,0352,'+',0341,'*',0226,0336,0346,'{',';',0204,0270,0266,0232,'H',047,0214,0345,'$',0211,0312,0262,0237,'P','G','"',0200,'>',0372,0242,0276,'R',0360,0247,0307,'_',024,'h','^','U',0276,0245,' ',0325,0254,0327,03,0367,0377,0,0353,'@',0377,0,0177,0277,0374,013,'$',0372,0327,0270,0370,'C',0342,0347,0205,0274,0134,0311,'o',025,0327,0330,0257,0333,0245,0255,0321,012,'X',0372,')',0350,0337,'A',0315,0,'w',0225,0311,'x',0263,0304,'B',0332,'7',0260,0265,0223,022,0221,0211,0134,0177,010,0364,0317,0255,'_',0361,'&',0276,0272,'U',0267,0223,011,06,0352,'A',0362,0377,0,0262,'=','k',0306,'u',0315,'U',0244,'w',0267,0215,0311,'$',0376,0361,0363,0324,0372,'V','5',0353,'F',0214,'9',0244,'D',0346,0240,0256,0312,0272,0266,0246,'n',0344,0362,0242,047,0311,'S',0377,0,'}',032,0313,0242,0212,0371,0252,0265,'e','V','N','r','8','%',047,047,'v',024,'Q','E','f','H',01,0223,0201,0326,0272,0337,017,'h',0222,0231,'c','U','M',0327,'S','p',0253,0375,0321,'Y',0372,'&',0226,'X',0255,0314,0253,0237,0371,0346,0270,0353,0357,'^',0311,0341,'}',04,'i',0266,0302,0346,0341,0177,0322,0245,035,'?',0270,0276,0237,'Z',0366,0260,030,'N','_',0336,0317,'~',0207,']',012,'_','i',0232,032,'6',0225,036,0221,'`',0260,'!',0334,0347,0346,0221,0375,'M',025,0243,'E','z',0247,'I','^',0307,0376,'A',0366,0337,0365,0311,0177,0220,0253,025,'^',0307,0376,'A',0366,0337,0365,0311,0177,0220,0253,024,0,'Q','E',024,0,'Q','H','H','U',',',0304,0,'9','$',0366,0257,012,0370,0241,0361,0275,'l',0332,'}',017,0302,0262,',',0223,0355,')','6',0240,017,021,0237,0356,0247,0251,0367,0355,0357,0330,03,0276,0361,0347,0305,015,027,0300,0266,0245,'f','&',0357,'Q','`','|',0253,'H',0230,'d',0237,0366,0217,0360,0217,0314,0373,'W',0315,036,'3',0370,0225,0342,037,032,0312,0351,'}','r','a',0261,'-',0225,0263,0204,0341,06,':','g',0373,0337,0215,'r',0227,'7','3',0336,0134,0311,'q','s','+',0315,'4',0207,'s',0310,0355,0222,0307,0334,0324,'T',0,'Q','E','w',0236,016,0370,'I',0342,'o',027,0254,'w',021,0333,0375,0213,'O','~','E',0325,0310,' ','0',0365,'U',0352,0337,0312,0200,'8',':',0265,'e',0246,0337,'j','R',030,0354,',',0256,'.',0234,'r','V',010,0231,0310,0374,0,0257,0247,0274,'7',0360,017,0302,0332,'C','G','>',0251,0346,0352,0267,012,'>',0354,0247,'l','Y',0377,0,'t','u',0374,'N','=',0253,0323,'l',0264,0333,035,'6',05,0202,0306,0316,0336,0332,'$',030,'T',0206,'0',0200,017,0240,0240,017,0222,'4',0337,0203,'>','8',0324,0227,'p',0322,'~',0316,'3',0200,'n','$',013,0237,0347,']',035,0277,0354,0347,0342,0231,'T',031,0265,015,'6',03,0214,0341,0235,0233,0237,'N',026,0276,0236,0242,0200,'>','u',0217,0366,'i',0277,'1',0251,0223,0304,0266,0312,0370,0371,0225,'m','Y',0200,'>',0307,'p',0317,0344,')',0227,037,0263,'V',0252,0250,015,0257,0210,0254,0344,'|',0362,'%',0201,0220,'c',0352,013,'W',0321,0264,'P',07,0313,'w',0277,0263,0317,0213,0355,0324,0233,'y',0364,0373,0242,016,'0',0262,0225,0343,0327,0221,0134,0246,0253,0360,0267,0306,'z','F',0363,'q',0241,0134,'I',032,0214,0357,0200,011,01,0372,01,0317,0177,'J',0373,'>',0212,0,0370,012,'H',0336,031,'^',')','Q',0222,'D','b',0254,0214,'0','T',0216,0240,0216,0306,0233,'_','r',0353,0236,022,0360,0377,0,0211,'-',0314,':',0276,0223,'k','t',0244,'`','3','&',035,'~',0214,'0','G',0340,'k',0312,'|','I',0373,':','i',0227,01,0345,0360,0366,0241,'-',0243,0366,0202,0340,0371,0211,0371,0365,0240,017,0234,'+','s',0302,0376,035,0272,0361,016,0250,0221,'C',0230,0341,0215,0203,'K','7',0367,07,0267,0275,'_',0325,'>',034,0370,0237,'E',0326,0342,0322,0357,0264,0331,021,0246,'m',0261,0316,0240,0264,',','=','C',0364,0350,011,0307,'_','j',0364,0375,';','O',0264,0360,0216,0204,0226,0320,0200,0322,0177,023,036,0262,'?',0257,0322,0246,'R','P',0213,0224,0266,'B','m','%','v','Y',0325,'5',03,'c','j',0226,0311,',',0222,'M',0260,'.',0371,034,0263,0,';',0222,'{',0327,'2','I',047,047,0255,'>','Y','^','i','Z','I',033,',',0307,'$',0323,'+',0346,0361,'X',0207,'^','w',0351,0320,0340,0251,'7','7','p',0242,0212,'+',0230,0314,'+','O','I',0323,'M',0334,0276,'d',0203,0367,'*','y',0377,0,'h',0372,'U','k',033,047,0275,0270,021,0257,012,'9','f',0364,025,0351,'^',026,0360,0352,0337,0314,0252,'P',0255,0224,'8',0335,0217,0342,'>',0225,0350,0340,'p',0236,0321,0373,'I',0354,0277,023,'z','4',0371,0235,0336,0306,0277,0204,'<','>',033,'n',0243,'r',0237,'"',0377,0,0251,'B',':',0377,0,0265,']',0275,'"',0252,0242,04,'P',02,0201,0200,07,'j','Z',0367,0216,0320,0242,0212,'(',02,0275,0217,0374,0203,0355,0277,0353,0222,0377,0,'!','V','*',0275,0217,0374,0203,0355,0277,0353,0222,0377,0,'!','V','(',0,0244,'$','(','$',0220,0,0344,0223,'K','^',015,0361,0277,0342,'{','[','y',0236,025,0321,'.','1',')',030,0275,0236,'6',' ',0247,0375,'3',037,'^',0364,01,0227,0361,'{',0342,0374,0367,'w','7','>',034,0360,0355,0302,'-',0220,06,';',0253,0250,0311,0335,'!',0356,0252,'{',016,0240,0372,0327,0206,0321,'E',0,025,0273,0341,0177,07,0353,'^','0',0324,'V',0313,'H',0264,'2',034,0374,0362,0266,'D','q',0217,'V','n',0325,0323,0374,'8',0370,'Q',0251,0370,0336,0346,'+',0273,0214,0332,'h',0312,0337,0274,0234,0217,0232,'@','?',0205,07,0277,0257,'j',0372,0247,'C',0320,'t',0317,016,'i',0261,0351,0372,'U',0244,'v',0366,0350,':',' ',0301,'c',0352,'O','s','@',034,'7',0201,0376,014,'h',036,025,0215,'.','o',0243,'M','O','R',0340,0231,'f',0134,0242,037,0366,'T',0373,0367,'<',0327,0245,0200,0,0,014,01,'E',024,0,'Q','E',024,0,'Q','E',024,0,'Q','E',024,0,'Q','E',024,0,'R','3','*',')','f',' ','(',031,'$',0366,0245,0256,'3',0305,0372,0366,'3',0246,0333,'?',0375,'v','p',0177,0361,0332,0,0305,0361,'O',0210,0205,0374,0315,0206,0333,'g',016,'v',0377,0,0264,'}','O',0364,0257,'?',0271,0212,0357,'T','V',0273,013,0362,03,0204,'N',0370,0245,0326,'u',023,'q',')',0202,'3',0373,0244,'<',0237,0357,032,0237,'E',0232,0345,0224,0241,0301,0201,';',0236,0337,'J',0371,0334,0327,031,047,0356,'S',0331,'o',0346,'x',0371,0246,'*',0245,':','N','T',0255,0247,'~',0246,031,05,'I',04,020,'G','c','I',']','5',0305,0225,0256,0242,0205,0343,'e',0337,0375,0365,0376,0265,0203,'u','g','5',0243,0355,0221,'x',0354,0303,0241,0257,'6',0235,'e','=',':',0234,0330,0134,'}','<','G',0273,0264,0273,'2',0275,'>',030,'^','y','V','8',0306,'Y',0216,05,'0',014,0234,016,0265,0322,0351,0366,0221,'i','v','o','w','t','B',0260,']',0314,0315,0374,013,']',0330,0134,';',0257,';','t',0352,'z','4',0340,0346,0354,'l',0370,'w','A','y',0346,0216,0316,0334,'|',0315,0314,0222,'z',016,0346,0275,'r',0306,0312,035,'>',0316,';','h',027,010,0203,0361,047,0324,0327,01,0360,0227,0305,0232,'7',0211,'4',0353,0304,0263,'V',0216,0372,011,'H',0225,'_',0253,0246,'~','V','_','l','v',0365,0315,'z','=','}','$','b',0242,0224,'c',0261,0336,0222,'J',0310,'(',0242,0212,0241,0205,024,'Q','@',025,0354,0177,0344,037,'m',0377,0,0134,0227,0371,012,0261,'U',0354,0177,0344,037,'m',0377,0,0134,0227,0371,012,'u',0335,0324,'6','6','s',']','N',0341,'"',0205,013,0273,036,0300,012,0,0341,0276,',','x',0361,'|',025,0341,0206,0373,'4',0200,'j','w',0200,0307,'l',07,'%','8',0345,0361,0355,0374,0353,0344,031,'$','y','d','i','$','v','w','r','Y',0231,0216,'I',047,0251,'&',0272,'?',036,'x',0252,0177,031,'x',0272,0363,'V',0221,0211,0211,0217,0227,'n',0204,'}',0310,0206,'v',0217,0324,0237,0251,'5',0315,'P',01,'^',0217,0360,0247,0341,0224,0236,'9',0324,'Z',0352,0370,'K',026,0217,'n',0300,'H',0353,0307,0232,0337,0334,07,0371,0237,'z',0347,'<',015,0341,013,0257,032,0370,0232,015,'*',0337,')',027,0337,0236,'P','?',0325,0306,':',0237,0257,0245,'}',0231,0242,0350,0326,'>',037,0322,'-',0364,0315,':',05,0206,0332,05,0332,0252,0243,031,'=',0311,0367,047,0232,0,0261,'e','e','m',0247,'Y','C','g','g',02,'A','o',012,0204,0216,'4',030,012,05,'O','E',024,0,'Q','E',024,0,'Q','E',024,0,'Q','E',024,0,'Q','E',024,0,'Q','E','A','y','w',025,0215,0244,0227,'3',034,'"',014,0237,0177,'j',0,0314,0361,'&',0264,'4',0233,022,'#',' ',0334,0313,0304,'c',0323,0324,0327,0217,'k',0232,0233,'F',0255,022,0271,'3','I',0313,'1','<',0201,0376,'5',0261,0342,035,'q',0347,0226,'[',0353,0203,0363,'1',0304,'q',0347,0240,0354,05,'p','r',0312,0363,'J',0322,'9',0313,'1',0311,'5',0347,'c',0361,'^',0316,'<',0221,0335,0230,'V',0251,0312,0271,'V',0343,'+',0241,0322,012,'M',0246,0274,031,0303,'r',016,':',0363,'Q','Y',0330,0333,'[','[',',',0367,0200,026,'~',0212,'F','z',0373,'T',0267,'v',0361,0333,0315,021,0264,0375,0325,0303,0234,0,':',021,0356,'+',0345,0252,0324,'S',0367,'W',0336,'|',0306,'3',023,'O',021,0373,0210,0335,';',0350,0372,']','~',0204,'6','z','m',0335,0255,0360,'!',0361,020,'<',0220,'z',0217,0245,'C',0254,'_',031,0244,'6',0352,'0',0250,'y','>',0246,0237,'>',0257,'y',016,0350,'^','5','I',07,04,0325,'m','6',0305,0257,0356,'K','>','|',0265,'9','s',0353,0355,'[','a',0350,'N',0265,'E','}',0372,033,0341,'0',0265,0252,0326,'U',0253,0245,'t',0264,0267,0346,0134,0321,'4',0355,0344,']',0314,0277,'(',0373,0200,0367,'>',0265,0304,'|','E',0361,'Y',0274,0234,0350,0366,'R',0376,0342,'3',0373,0366,'S',0367,0333,0373,0277,'A','^',0302,'<',')',0251,'k',0336,034,0324,'S','K',0231,'m','f',020,0224,0267,'v',034,027,0364,0366,0343,0214,0366,0315,'|',0307,'y','i','q','a','y','5',0245,0334,'M',025,0304,'.','R','H',0334,'r',0244,'u',025,0365,0324,'(','F',0214,'9','Q',0364,0220,0202,0202,0262,'5','|',')',0342,'{',0377,0,010,0370,0202,0333,'V',0260,'o',0236,'&',0371,0343,047,013,'*','w','S',0365,0257,0263,0274,'5',0342,013,'/',024,0370,'~',0323,'X',0260,'b','a',0270,'@',0333,'N','7','#','w','S',0216,0340,0361,'_',013,'W',0260,0374,011,0361,0347,0366,'&',0272,'<',';',0177,'6',0333,033,0367,0304,014,0307,0210,0346,'8',0,'{',06,0351,0365,0307,0255,'l','Y',0364,0365,024,'Q','@',05,024,'Q','@',025,0354,0177,0344,037,'m',0377,0,0134,0227,0371,012,0361,0257,0332,033,0305,0362,0351,0372,'E',0247,0206,0255,033,017,'}',0231,'n',0230,'6',012,0306,0244,'a','q',0376,0321,0316,'}',0227,034,0346,0275,0216,0321,0326,'=','.',011,034,0200,0253,012,0222,'O','a',0266,0276,'2',0370,0205,0342,'F',0361,'W',0215,0265,035,'H','I',0276,03,047,0227,07,0240,0215,'x',030,0372,0365,0374,'h',03,0227,0247,'G',033,0313,'"',0307,032,0226,'w','!','U','G','r','i',0265,0351,0277,04,'|',032,'|','K',0343,'$',0277,0271,'B','t',0375,'0',0211,0237,'#',0207,0223,0370,027,0363,0344,0375,'=',0350,03,0334,0276,022,'x',026,'?',06,'x','R','6',0270,0211,'F',0253,'z',04,0227,'O',0216,'@',0376,024,0372,0,0177,'<',0327,0240,'Q','E',0,024,'Q','E',0,024,'Q','E',0,024,'Q','E',0,024,'Q','E',0,024,'Q','E',0,025,0347,'~','.',0327,05,0345,0323,'[','D',0370,0265,0200,0374,0315,0236,031,0273,0237,0240,0256,0217,0305,'Z',0327,0366,'u',0227,0331,0241,'o',0364,0231,0306,06,'?',0205,'{',0232,0361,0315,'w','Q',0311,'6',0261,'7',0375,'t','?',0322,0261,0257,'Z','4','`',0346,0310,0234,0324,'U',0331,0235,0251,'_',033,0353,0222,0303,'"','5',0341,07,0365,0252,0253,034,0205,014,0201,030,0242,0236,'N','8',0245,0202,'#','<',0351,020,0352,0307,025,0321,'J',0216,0252,0226,'6',0251,0265,'v',0374,0362,021,0300,037,0343,'_',')','_',020,0334,0357,'-',0331,0341,0342,0361,0236,0312,'j',';',0267,0253,0362,']',0311,0342,0362,'n',0342,0206,'a',0363,'l',031,'_','c','U',0322,06,0212,0346,'K',0353,0307,'Q',0264,'|',0240,036,024,'T','o',0247,0275,0215,0273,0313,'k','<',0201,0224,'d',0203,0202,017,0341,'X',0323,0336,0134,']',0220,0262,'H','[',0321,'G',02,0270,0351,0323,0347,'o',0225,0351,0370,0236,'>',027,012,0353,'9',':','3',0367,036,0236,'i','o','e',0352,'=',0274,0315,'O','P',';',07,'.','x',0366,025,0335,'x','w','B','7','s',0305,'e',0,0333,032,0363,'#',0372,016,0347,0353,'Y',':',036,0222,0361,04,'E','M',0327,'3',020,'1',0375,'+',0327,0364,'-',036,'=',036,0300,'F',0,'3','?',0315,'+',0372,0237,'O',0240,0257,0256,0300,'a',025,030,']',0356,0317,0257,0303,0321,'T',0342,0222,'/','Z',0332,0305,'g','m',035,0274,013,0266,'4',030,02,0274,017,0366,0203,0360,'8',0214,'E',0342,0353,'(',0270,',','!',0275,03,0266,'x','G',0374,0370,0374,'E','}',05,'U','u','=','>',0337,'V',0323,'.','t',0373,0270,0326,'K','{',0210,0332,'7','V',031,04,021,0212,0357,':',017,0202,0251,0360,0315,'%',0274,0361,0315,023,025,0222,'6',016,0254,';',020,'r',015,'j','x',0247,'B',0227,0303,'^','&',0277,0321,0345,0311,'6',0322,0224,'V','#',0357,'/','P',0177,'*',0310,0240,017,0265,0376,036,0370,0246,'?',027,0370,'6',0307,'S',04,'y',0373,'|',0271,0327,0373,0262,'/',07,0374,'k',0250,0257,0231,'?','g',0257,023,0313,0247,0370,0252,0347,'@',0232,'P',',',0357,0342,'2','"',0221,0322,'e',0306,'1',0351,0225,0334,017,0321,'k',0351,0272,0,'(',0242,0212,0,0363,0337,0212,'>',' ','o',017,'|','&',0272,0226,'&','+','q','u',02,'Z','D','A',0301,05,0306,011,07,0331,'w',037,0256,'+',0344,'*',0367,0217,0332,023,'W','a',0247,'x','w','G','R','v',0230,0215,0303,0340,0372,0,0,'#',0277,'Z',0360,'z',0,'+',0353,0357,0203,'~',032,'_',017,'|','>',0262,'g',0210,'-',0315,0360,0373,'L',0247,0271,0335,0367,0177,'L','W',0313,036,027,0321,037,0304,'~','(',0323,'t','t',0310,0373,0134,0353,033,025,'8','!','3',0226,'#',0203,0321,'A','5',0367,',','Q','$',020,0244,'Q',0250,'X',0321,'B',0252,0216,0200,016,0,0240,07,0321,'E',024,0,'Q','E',024,0,'Q','E',024,0,'Q','E',024,0,'Q','E',024,0,'T',027,0227,'Q','X',0332,'K','s','3',05,0216,'5',0311,0317,0177,'j',0236,0270,037,032,0353,'K','$',0306,0311,034,010,'`',0371,0245,'n',0305,0277,0372,0324,01,0312,0370,0217,0134,'y','%',0226,0362,'S',0373,0331,'N','#','_','A',0333,0362,0256,031,0231,0235,0313,'1','%',0211,0311,0253,':',0205,0343,'^',0335,'4',0207,0205,034,'(',0364,025,'k','G',0265,'G','w',0271,0227,033,'#',0351,0237,'Z',0371,0254,0303,027,0355,'$',0332,0331,'l','y','x',0314,'R',0247,027,'Q',0354,0210,'!',0265,0274,0266,'d',0272,020,'6',0325,'9',0374,'>',0225,0252,0272,0261,0271,0375,0325,0254,',','f','#',0370,0272,'/',0275,'X',0216,0371,'M',0263,0134,0312,'6','D','[',011,0352,'E','V',0324,'m','!','H',0215,0344,'O',0345,'8',0344,025,0350,0325,0343,0271,')',0312,0323,'Z',0237,'9','*',0321,0304,'U','Q',0304,0323,0264,0266,'O','V',0257,0331,0367,033,'}','(',0261,0323,0314,06,'B',0363,'K',0234,0223,0357,0324,0323,'4','=',';','q',027,'r',0257,03,0356,03,0374,0352,0235,0205,0254,0232,0235,0346,0351,'X',0262,016,']',0217,0362,0257,'L',0360,0246,0202,'/',0356,04,0322,0246,'-',' ',0350,'1',0303,036,0302,0275,0354,0257,05,'o',0336,0314,0372,',',0273,07,0354,'c','y',';',0266,0356,0375,0177,0310,0333,0360,0216,0204,'-',0241,032,0205,0302,'~',0371,0307,0356,0324,0217,0272,0276,0277,'S',']',']',0,0,0,03,0,'Q','^',0371,0352,0205,024,'Q','@',037,'<',0376,0321,0276,030,021,0335,0351,0336,'&',0202,'5',02,'Q',0366,'[',0222,':',0226,034,0243,037,0303,'#','>',0313,'^',015,'_','i','|','K',0320,0227,0304,'?',017,0365,'k','-',0240,0310,'!','2',0305,0222,'F',035,'>','a',0323,0351,'_',026,0320,06,0237,0207,'5','w',0320,'|','G',0247,0352,0261,0222,032,0326,'u',0220,0343,0256,'3',0317,0351,0232,0373,0232,0326,0341,'.',0355,'!',0270,0214,0345,'%','@',0343,0350,'F','k',0340,'j',0373,03,0340,0306,0264,'5',0237,0206,032,'^','Y','L',0266,'{',0254,0344,0,0364,0331,0367,'G',0375,0360,'T',0376,'4',01,0337,0321,'E',024,01,0362,027,0306,'m','O',0373,'C',0307,0357,010,'$',0255,0225,0244,'0','{','g','`','c',0217,0373,0353,037,0205,'y',0355,'m','x',0273,'Q',0376,0325,0361,'v',0253,'x','$','I',021,0356,031,'c','t',0350,0310,0247,'j',0221,0377,0,01,02,0261,'h',03,0326,'?','g',0335,')','o','~',' ',0275,0353,0252,0262,0331,'[','3','.','z',0207,'n',01,037,0206,0357,0316,0276,0247,0257,04,0375,0232,0254,0224,'[','k',0267,0333,'N',0342,0361,0302,'N','x',0300,04,0364,0374,'M','{',0335,0,024,'Q','E',0,025,'^',0356,0372,0326,0306,'?','2',0346,'t',0211,'{','n','<',0237,0245,'s',0272,0357,0213,'R',0321,0336,0326,0303,022,'L',0274,'4',0207,0225,'S',0355,0352,'k',0316,'u',']','|',011,0331,0356,'&','y',0356,017,'Q',0234,0343,0374,'*',047,'R','0',0134,0322,'v','B','r','Q','W','g',0244,0134,'x',0336,0306,'6',0304,'0',0313,'/',0277,0335,025,'L','x',0364,0356,0347,'O',0371,'s',0327,0315,0347,037,0225,'y',025,0306,0265,'w','1',';',037,0312,'_','E',0353,0371,0325,'d',0276,0273,0215,0303,0213,0211,'r','=','X',0232,0340,0226,'g','I',';','$',0331,0203,0304,'F',0372,037,'B',0350,0372,0365,0246,0260,0254,'"',0312,'J',0243,'-',033,'u',0307,0257,0275,'j',0327,0214,0370,'W',0134,0305,0354,027,'$',0205,'x',0234,011,06,'{',036,'3','^',0312,010,'`',010,0350,'y',0257,'B',023,0214,0342,0245,035,0231,0272,'i',0253,0241,'h',0242,0221,0230,'"',0226,'c',0205,03,'$',0325,014,0311,0361,016,0254,'4',0255,'5',0235,'O',0357,0344,0371,'c',036,0376,0277,0205,'x',0226,0277,0250,031,034,0333,'+',022,'s',0231,016,'z',0237,'J',0351,0374,'c',0342,'#','s','s','$',0341,0276,'E',0312,'@',0276,0336,0277,0326,0270,0230,0364,0351,'g',0263,'{',0267,0220,')',0345,0276,'n',0376,0246,0274,0254,0317,026,0251,0307,0331,0247,0253,'8',0361,'x',0230,'R',0212,'R','v',0276,0205,'H','b','i',0245,'T','P','y','<',0343,0265,'_',0324,'#',']','=',0274,0233,'y',0230,07,'_',0235,'3','K',0244,'^','[',0333,'3',',',0253,0265,0237,0370,0377,0,0245,']',0324,'4',0241,'t','L',0360,'7',0316,'y',0301,'<',032,0371,0311,0324,0265,'K','K','D','x','U',0361,'|',0230,0245,012,0276,0354,'?',06,0377,0,0340,022,0304,0266,0267,0272,'|','*',0314,'6',0240,04,0200,'q',0202,'=','k','2',0376,'C','}','~',0260,'[',0235,0312,'0',0240,016,0231,0365,0254,0347,'G',0211,0312,':',0225,'a',0301,06,0272,'m',017,'L','1','"',0312,0310,'L',0362,'p',0243,0270,07,0265,'u',0340,0260,'^',0326,0256,0372,035,'8','<',0271,'F',0257,'?','3','k','[','y','_','s','c',0303,0372,033,'O',',','V','6',0343,0223,0314,0217,0216,0236,0244,0327,0255,0331,0332,'C','c','i',035,0264,013,0266,'4',030,036,0376,0365,0233,0341,0315,025,'t',0213,034,0270,06,0346,0134,031,017,0247,0265,'l',0327,0326,'F','*',')','E','l','{',0351,'$',0254,0202,0212,'(',0246,'0',0242,0212,'(',01,0256,0213,',','m',033,0214,0253,02,0244,'z',0203,'_',012,'x',0212,0307,0373,'3',0304,0332,0265,0206,'s',0366,'[',0311,0241,0317,0256,0327,'#',0372,'W',0335,0265,0361,0357,0306,'{','F',0264,0370,0257,0255,0203,033,'*','J',0321,0312,0204,0256,03,06,0215,'r','G',0250,0316,'F','}','A',0240,016,012,0276,0213,0375,0232,0357,0213,'h',0372,0335,0201,'#',011,'p',0223,'(',0372,0256,017,0376,0202,'+',0347,'J',0366,0277,0331,0272,'b',0236,047,0325,0342,030,0304,0226,0253,0237,0301,0215,0,'}',')','E',024,'P',07,0300,024,'Q','E',0,'}','3',0373,'8',0333,',','~',016,0324,0256,03,022,'f',0274,0301,036,0230,'Q',0376,'5',0354,0325,0343,'_',0263,0215,0312,0311,0340,0335,'J',0334,')',015,015,0346,'I','=',016,'T',0177,0205,'{','-',0,025,0317,'x',0273,'T','{',015,'9','a',0205,0212,0313,'9','#','#',0250,'Q',0326,0272,032,0343,'|','y',021,')','e','/','`','Y',0177,'<',037,0351,'@',036,'e',0254,0352,0215,01,0373,'<',07,016,'G',0314,0336,0225,0316,022,'I',0311,'9',047,0275,'X',0277,',','u',013,0215,0335,'|',0306,0376,'u','^',0276,'g',025,'Z','U','j',';',0354,0217,'>',0244,0334,0245,0250,'Q','E',025,0314,'f','h','h',0367,036,'F',0240,0231,'8','W',0371,'M','{',0327,0206,'/',0376,0337,0241,0302,'Y',0263,'$','_',0273,0177,0303,0247,0351,0212,0371,0331,'I','V',014,016,010,'9',025,0353,'>',02,0326,024,0134,'$','l',0330,0216,0351,'@',0372,'8',0377,0,'&',0275,0254,0256,0255,0342,0351,0276,0232,0235,'x','y','h',0342,'z','E','r',0336,'3',0326,'E',0225,0227,0330,0243,'l','I','(',0314,0215,0375,0324,0377,0,0353,0327,'C','{','y',025,0205,0234,0227,'3','6',021,06,'~',0276,0325,0341,0336,'-',0326,0345,0275,0273,0220,026,0371,0345,'9',0177,0366,'G','e',0257,'B',0275,'e','J',016,'l',0336,'r','Q','W','f','i','-',0254,0352,'d',0223,0373,0204,0355,0355,0377,0,0327,0253,0272,0204,'3','N',0360,0332,'D',0233,'a','?','y',0207,'L',016,0325,0317,0303,'<',0266,0362,07,0211,0312,0265,'t',026,':',0264,'W','8','I','q',034,0237,0241,0257,0215,0305,'J',0244,0252,'{','W',0251,0362,0231,0232,0304,0252,0253,021,025,0314,0227,'N',0317,0271,'G','X','[','x',0314,'V',0360,0306,'<',0305,034,0221,0351,0351,'H',0222,'_','i','H',0245,0300,'h',0233,0242,0223,0320,0326,0227,0366,'d','K','}',0366,0242,0304,0217,0274,'U',0275,'}','k','2',0342,'I','5','}','@','C',021,0375,0332,0234,017,0247,'s','J',0212,'u','Z',0247,025,'q','a','&',0261,'*','4','"',0271,0243,'o','y',0276,0357,0242,'$',0322,0355,036,0376,0355,0256,0356,'9','@','s',0317,'s','^',0255,0340,0355,017,047,0373,'J',0341,'x',034,'B',0247,0365,'o',0360,0256,'{',0303,':',07,0333,0356,'c',0267,'P','V',0326,',',031,033,0333,0323,0352,'k',0324,0343,0215,'"',0215,'c','E',012,0212,'0',0,0354,'+',0354,'0',0270,'u','B',0232,0212,0334,0372,0252,'T',0325,'8',0362,0241,0324,'Q','E','t',032,05,024,'Q','@',05,024,'Q','@',05,'|',0251,0373,'A',')','_',0211,'y',' ',0200,'l','b','#',0337,0226,0257,0252,0353,0345,'O',0332,011,0231,0276,'%',0200,'I','!','l','b',03,0330,'e',0250,03,0312,0353,0325,0177,'g',0277,0371,')','g',0376,0274,'%',0376,'k','^','U','^',0253,0373,'=',0377,0,0311,'K','?',0365,0341,'/',0363,'Z',0,0372,0252,0212,'(',0240,017,0205,0374,'U',0247,0177,'d',0370,0257,'T',0262,020,0210,'R',';',0227,0362,0343,07,'!','c',047,')',0377,0,0216,0221,'Y',025,0337,0374,'d',0323,0177,0263,0376,' ',0315,' ','?','-',0345,0254,027,0,'c',033,'~','@',0247,0353,0312,023,0370,0327,01,'@',037,'B','~',0315,'w',0252,'m','u',0333,035,0337,'8','x',0346,'+',0216,0304,021,0234,0376,06,0275,0356,0276,'Z',0375,0236,0365,'e',0262,0361,0364,0266,'.',0312,0253,'{','l',0312,0271,'<',0227,'^','@',037,0206,0343,0370,'W',0324,0264,0,'V',017,0213,0255,0274,0375,06,'G',035,'b','`',0377,0,0322,0267,0252,'+',0230,026,0352,0326,'X',037,0356,0310,0205,'O',0342,'(',03,0347,'=','n','/','+','R','s',0331,0300,'j',0316,0256,0217,0304,0366,0215,021,'R',0303,017,023,0230,0337,0353,0376,'E','s',0225,0363,'X',0332,'|',0225,0344,0273,0352,'y',0365,'U',0246,0302,0212,'(',0256,'S','0',0255,0377,0,016,'^',030,0331,0240,0336,'U',0201,0336,0204,034,020,'}',0253,02,0235,034,0217,014,0213,'"','1','V','S',0220,'E','o',0207,0255,0354,'j',')',0227,011,'r',0312,0347,0242,0370,0207,0305,027,'7','V',011,0366,0246,0134,'F','8','U',0343,'{','z',0232,0363,0271,'d','i',0245,'i',034,0345,0230,0344,0232,0222,0346,0362,'{',0266,015,'3',0356,0307,'A',0330,'T',025,0256,'3',025,0355,0345,'e',0262,'*',0255,'N','w',0344,024,'Q','E','q',0231,026,0306,0243,'r','m','M',0266,0342,0312,0334,03,0337,036,0225,0322,0350,':','D',0213,0345,0305,032,'o',0272,0230,0201,0217,'O','j',0310,0320,0364,0377,0,'6','O',0265,'H','>','E',0373,0240,0367,'>',0265,0354,036,020,0321,'>',0313,'o',0366,0373,0205,0304,0322,0217,0335,0203,0374,'+',0353,0365,'5',0354,0345,0270,'E',05,0355,'Z',0325,0354,'t','a','p',0360,0247,'y','%','k',0233,'Z','>',0227,036,0223,0247,0245,0272,'`',0277,'Y',037,0373,0315,'W',0350,0242,0275,'c',0260,'(',0242,0212,0,'(',0242,0212,0,'(',0242,0212,0,'+',0343,0377,0,0215,'W',0262,'^','|','V',0326,03,'9',')',07,0225,014,'`',0377,0,010,021,0251,'#',0376,0372,',',0177,032,0372,0371,0335,'c','F','v','8','U',031,047,0320,'W',0302,0276,'#',0275,032,0227,0212,'5','k',0361,0234,0134,0336,0315,'0',0317,'_',0231,0311,0376,0264,01,0231,'^',0325,0373,'7',0302,'_',0305,':',0264,0270,0134,'G','j',0275,'z',0362,0335,0253,0305,'k',0350,0237,0331,0256,0304,0215,'+',0134,0324,012,0360,0323,0244,'*',0334,'s',0205,0334,'G',0352,0264,01,0356,0324,'Q','E',0,'|',0357,0373,'B','i',015,0375,0237,0341,0355,'a',024,0355,021,'}',0235,0310,034,'d',0200,'F','O',0341,'^',021,'_',0134,0374,'W',0320,'_','^',0370,'I','r',0260,0250,'k',0213,'8','c',0273,0217,'=',0302,014,0260,0377,0,0276,'w','~','8',0257,0221,0250,03,'g',0302,'z',0323,'x','w',0305,0272,'V',0254,030,0252,0332,0334,0243,0310,'@',0317,0311,0234,'8',0307,0272,0222,'+',0356,'H',0344,'I','c','Y','#','`',0310,0340,'2',0260,0350,'A',0257,0200,0253,0354,037,0203,0336,'%','_',021,0374,'>',0261,0334,0340,0334,'Y','/',0331,'e',031,0344,'m',034,'~',0230,0240,016,0372,0212,'(',0240,017,'-',0361,0366,0227,0213,0353,0220,07,023,0257,0232,0277,0357,017,0376,0270,0375,'k',0313,0353,0336,0274,'o','e',0347,'i',0221,0335,'(',0346,027,0303,0177,0272,'x',0376,'x',0257,020,0324,0240,0373,'=',0374,0251,0216,011,0310,0372,032,0362,'3','J','Z','*',0213,0320,0345,0304,'G','i',025,'(',0242,0212,0361,0316,'P',0242,0212,'(',0,0242,0212,'(',0,0253,026,'V',0217,'y','r',0261,'/','N',0254,'}',05,'W',0,0223,0200,'2','M','v',0276,035,0321,'e','w',0212,0332,'4',0315,0304,0304,'n',0377,0,'d',0177,0200,0256,0274,036,037,0333,'T',0327,'e',0271,0255,'(','s',0263,0242,0360,0236,0202,'/','n','P',0262,'b',0316,0337,033,0275,030,0366,025,0351,'@',0,0,03,0,'U','m',':',0306,'-','6',0306,';','X',0207,0312,0203,0223,0352,'{',0232,0265,'_','H',0225,0264,'G','x','Q','E',024,0,'Q','E',024,0,'Q','E',024,0,'Q','E',024,01,0312,'|','H',0327,07,0207,0374,01,0253,0337,07,'U',0227,0310,'1',0305,0273,0241,'v',0340,017,0326,0276,'+',047,047,'&',0275,0367,0366,0215,0361,':',0264,0232,'w',0206,' ','s',0225,0377,0,'K',0271,0301,0300,0356,021,'O',0257,'s',0371,'W',0201,'P',01,'_','^','|',023,0321,06,0213,0360,0307,'O','f','@',0263,'_',0263,'^','I',0202,'N','w',0340,')',0347,0375,0205,'N',0237,0375,'z',0371,'S','A',0322,0244,0327,'u',0373,015,'.',' ',0305,0356,0247,'X',0376,0134,'d',02,'y','<',0372,014,0232,0373,0236,0316,0331,',',0354,0240,0265,0214,'a','!',0215,'Q','@',0364,03,024,01,'=',024,'Q','@',025,0255,021,'d',0322,0340,'F',031,'V',0205,'A',07,0323,'m','|',']',0343,0357,015,0277,0205,'<','i',0250,0351,'d','b','%',0220,0311,07,030,0375,0333,'r',0277,0227,'O',0302,0276,0322,0261,0377,0,0220,'}',0267,0375,'r','_',0344,'+',0306,0277,'h','O',06,0266,0241,0243,0333,0370,0236,0315,011,0232,0307,0367,'W','J',0243,'%',0242,047,0206,0377,0,0200,0237,0320,0373,'P',07,0315,0265,0352,037,03,0274,'b','|','9',0343,'5',0323,'n',033,026,032,0256,'!','r','O',021,0310,'3',0261,0277,023,0362,0237,0250,'=',0253,0313,0351,0321,0310,0361,'H',0262,'F',0305,']',010,'e','a',0324,021,0336,0200,'>',0375,0242,0270,'?',0205,036,'8','O',032,'x','J',047,0230,0250,0324,'l',0361,015,0322,'g',0251,03,0207,036,0304,'~',0271,025,0336,'P',04,027,0266,0311,'y','e','5',0263,0217,0226,'D','+','^',015,0342,'{',047,0206,'@',0354,'0',0321,0261,0215,0353,0350,012,0363,'?',036,0351,'8',0275,0220,0201,0362,']','&',0345,'?',0355,016,0277,0322,0260,0304,0322,0366,0264,0234,010,0251,036,'h',0264,'y','M',024,0254,0245,030,0253,014,020,'p','i','+',0345,0366,'<',0360,0242,0212,'(',020,'Q','E','O','g','j',0327,'w','+',012,0367,0352,'}',05,'8',0305,0311,0250,0255,0330,0322,0273,0262,'4',0264,'+',037,'6','O',0264,0310,'>','D','?','.','{',0232,0366,'o',011,0350,0277,'`',0263,0373,'T',0351,0213,0211,0207,'B','9','U',0364,0256,'o',0301,0272,012,0134,0316,0263,':',0177,0242,0333,020,0,0354,0315,0351,0375,'k',0321,0353,0351,0360,0364,025,032,'j','(',0364,')',0303,0222,'6',012,'(',0242,0267,',','(',0242,0212,0,'(',0242,0212,0,'(',0242,0212,0,'*',0246,0251,0250,0333,0351,032,']',0326,0241,'w',' ','H','-',0343,'i',035,0211,0300,0,014,0325,0272,0360,'/',0332,07,0307,'8',0206,'/',011,0351,0363,0217,0234,0371,0227,0305,'O','8',037,'u','=',0275,'O',0320,'P',07,0212,0370,0247,'_',0237,0304,0376,'%',0276,0326,'.','3',0272,0346,'B',0312,0247,0370,'W',0240,037,0226,'+',036,0212,'t','q',0264,0262,',','h',0273,0235,0310,'U',03,0271,'4',01,0354,037,0263,0337,0206,'$',0324,'|','Y','>',0273,'4','`',0332,'i',0361,0225,'F','=',0346,'l','c',037,'E',0311,'?','Q',0370,'}',';',0134,0237,0303,0217,012,0217,07,0370,'*',0307,'M','l',0233,0202,0276,'l',0344,0367,'v',0344,0217,0303,0247,0341,']','e',0,024,'Q','E',0,'W',0261,0377,0,0220,'}',0267,0375,'r','_',0344,'(',0277,0262,0267,0324,0264,0373,0213,033,0270,0326,'K','{',0210,0332,'9',021,0206,'C',')',030,'"',0213,037,0371,07,0333,0177,0327,'%',0376,'B',0254,'P',07,0304,'^','8',0360,0274,0376,020,0361,'m',0366,0221,',','n','"',0215,0367,'@',0354,'8',0222,'3',0312,0220,'{',0360,'q',0365,04,'W',';','_',']',0374,0134,0360,012,0370,0323,0303,'M','-',0244,'J','u','k','%','/','n',0330,0345,0307,'R',0237,0217,0363,0257,0221,0244,0215,0342,0221,0243,0221,031,035,011,'V','V',030,' ',0216,0240,0212,0,0351,0274,03,0343,033,0237,04,0370,0242,015,'N',034,0264,015,0373,0273,0230,0307,0361,0306,'O','?',0210,0353,0370,'W',0331,032,'.',0261,'c',0257,0351,026,0332,0246,0233,':','O','k','p',0233,0221,0324,0347,0352,017,0241,07,0202,';',021,'_',07,0327,0245,'|','(',0370,0237,047,0202,'5',01,'c','~','Y',0364,'K',0211,'3','(','Q',0270,0302,0307,0215,0340,'w',0355,0221,0351,0353,0322,0200,'>',0265,0254,0217,022,'i',0207,'S',0322,035,020,'f','h',0317,0231,037,0324,'v',0374,0253,'F',0326,0352,013,0333,'X',0256,'m','f','I',0240,0225,'C',0244,0210,'r',0254,017,'B',015,'M','@',037,':',0353,0266,'&',033,0217,0264,'*',0235,0216,'~','a',0350,0325,0217,'^',0317,0342,0217,011,'<',0263,'I','q','g',017,0233,014,0274,0311,010,034,0251,0365,036,0325,0302,'M',0340,0351,025,0310,0362,0256,0223,0333,'f',0177,0245,'x',0370,0254,0276,'R',0237,'=','>',0247,'-','J','-',0273,0304,0344,0350,0256,0234,0370,'B','o',0372,'x',037,'X',0215,':','?',07,'L',0314,024,0255,0313,023,0330,'D','E','r',0377,0,'g',0342,';','~','&','~',0302,'}',0216,0134,02,'H',0,'d',0232,0354,0374,'3',0241,0313,',',0261,0300,0211,0376,0221,'1',0371,0216,'>',0352,0326,0256,0225,0340,'+',0302,0312,0311,'i',0345,0177,0323,'I',0316,'1',0370,'u',0375,'+',0320,0364,'M',06,0337,'F',0211,0266,037,'2','w',037,'<',0204,'~',0203,0320,'W',0243,0203,0300,0373,027,0317,'=',0315,0351,'Q',0345,'w','e',0353,033,'8',0264,0373,'(',0255,'a',030,'H',0306,'>',0247,0271,0253,024,'Q','^',0211,0270,'Q','E',024,0,'Q','E',024,0,'Q','E',024,0,'Q','E','a','x',0267,0305,'z',0177,0203,0364,033,0215,'R',0375,0301,0362,0324,0230,0341,014,03,'J',0335,0224,'f',0200,'2',0276,'$',0370,0352,0337,0300,0336,031,0226,0350,'2','6',0243,'8','1',0331,0304,0303,'9','|','u','#',0320,'u',0375,'+',0343,0253,0353,0353,0235,'J',0372,'{',0333,0311,0236,'{',0231,0334,0274,0222,'9',0345,0230,0326,0267,0213,0374,'[',0251,'x',0317,'^',0227,'T',0324,0234,'n','?',',','Q','/',0335,0211,';','(',0377,0,036,0365,0203,'@',05,'z',0357,0300,0257,02,0177,'o','x',0207,0373,'z',0376,0337,'v',0237,0247,0260,'h',0203,016,'$',0233,'9',037,'P',0275,'~',0270,0367,0257,':',0360,0307,0206,0357,0374,'W',0256,0333,0351,'Z','|','e',0244,0225,0200,'g',0306,'D','k',0335,0217,0260,0257,0264,'|','5',0341,0353,'?',013,'x','~',0323,'H',0261,'_',0335,'[',0306,024,0271,03,'t',0215,0335,0216,';',0223,'@',032,0324,'Q','E',0,024,'Q','E',0,'W',0261,0377,0,0220,'}',0267,0375,'r','_',0344,'*',0305,'W',0261,0377,0,0220,'}',0267,0375,'r','_',0344,'*',0305,0,025,0363,0357,0307,037,0206,'[','M',0307,0213,0364,0210,0216,'8','k',0350,020,023,0355,0346,0,':','q',0327,0267,031,0365,0257,0240,0251,031,'U',0324,0253,0,'T',0214,020,'{',0320,07,0300,'4','W',0265,0374,'_',0370,'B',0332,'3','?',0210,'<','7','k',0273,'N',047,'7','6',0261,016,'`',0377,0,'i','G',0367,'=','q',0323,0351,0234,'x',0245,0,'z','O',0303,'?',0213,027,0376,012,0273,'K','+',0343,'%',0336,0212,0374,'4','Y',0313,'C',0376,0322,0177,'Q','_','S',0350,0372,0316,0237,0257,'i',0261,'j',032,'m',0312,0134,'[','H','2',0254,0207,0364,'>',0206,0276,017,0256,0213,0302,'>','5',0326,0274,027,0252,'-',0346,0225,'r','B',0236,'%',0267,'s',0230,0345,'_','B','?',0257,'Q','@',037,'n',0321,'^','u',0340,0177,0214,'^',037,0361,'p',0206,0326,'y',06,0237,0252,'8',0307,0331,0346,'n',035,0273,0354,'n',0207,0371,0327,0242,0320,01,'E',024,'P',01,'E',024,'P',01,'E',024,'P',01,'E',024,'P',01,'E',024,'P',01,'E','E','q','s',05,0245,0274,0227,027,'3','G',014,'1',0215,0317,'$',0214,025,'T','z',0222,'k',0306,'<','y',0361,0356,0307,'O',0216,'K','/',012,030,0357,'.',0210,0301,0273,'e','&','$',0372,016,'7',037,0322,0200,'=',03,0307,037,020,'t','o',03,'i',0306,'k',0351,'D',0227,'n',017,0221,'j',0234,0274,0207,035,0375,07,0271,0257,0222,0374,'_',0342,0375,'S',0306,0232,0334,0232,0226,0247,'.','z',0210,'a','S',0362,'B',0277,0335,'Q',0374,0317,'z',0313,0324,0265,'K',0355,'b',0372,'K',0335,'F',0356,'k',0253,0231,011,'-','$',0255,0270,0237,0360,036,0303,0212,0251,'@',05,'M','i','k','=',0365,0334,'6',0266,0261,'4',0263,0314,0341,'#','E',0352,0304,0364,024,0330,' ',0226,0352,0342,'8',' ',0215,0244,0232,'F',012,0210,0203,'%',0211,0350,05,'}','I',0360,0243,0341,'-',0277,0204,0355,0343,0326,'5','x',0326,']','i',0327,'*',0247,0221,'n',017,'a',0376,0327,0275,0,'j','|','(',0370,'q',037,0201,0264,'C','-',0336,0331,'5',0213,0260,032,0341,0300,030,0214,'v','E','=','q',0353,0352,'k',0320,0350,0242,0200,012,'(',0242,0200,012,'(',0242,0200,'+',0330,0377,0,0310,'>',0333,0376,0271,'/',0362,025,'b',0253,0330,0377,0,0310,'>',0333,0376,0271,'/',0362,025,'b',0200,012,'(',0242,0200,021,0321,']',012,':',0206,'V',030,' ',0216,010,0257,04,0370,0237,0360,'<',0317,'$',0332,0337,0205,'#','E','c',0363,'O',0247,0216,01,0365,'h',0375,0377,0,0331,0375,'k',0337,'(',0240,017,0201,'&',0202,'[','i',0232,031,0342,'x',0245,'C',0206,'G','R',0254,0247,0334,032,0216,0276,0301,0361,0367,0302,'}',023,0306,0361,033,0215,0277,'b',0325,'T','|',0227,'q','/',0336,0366,'q',0374,'C',0337,0250,0375,'+',0346,0237,031,'|','=',0327,0274,025,'t','W','P',0265,'g',0264,'?','r',0356,'%','&','6',0372,0236,0307,0330,0320,07,')','^',0213,0341,'/',0214,0376,'(',0360,0302,'%',0274,0223,015,'F',0315,'x',021,0134,0222,'Y','G',0240,'n',0265,0347,'T','P',07,0325,0336,030,0370,0361,0341,'M','u',0274,0235,'A',0244,0322,'.','p','?',0343,0353,06,'&','>',0316,':',0177,0300,0200,0353,0336,0275,'*',0326,0366,0326,0366,'%',0226,0326,0342,')',0321,0206,'C','F',0341,0201,037,0205,'|',017,'W',0264,0335,'g','S',0321,0345,0363,'4',0333,0373,0213,'V',0316,'O',0225,'!','P','O',0270,0350,'h',03,0357,032,'+',0344,035,'7',0343,'_',0216,0264,0345,'U',032,0262,0334,0250,' ',0221,'s',012,0276,'G',0246,'z',0343,0351,']','5',0207,0355,'!',0342,'H',0234,0375,0277,'H',0323,'.','c',0333,0201,0344,0357,0211,0263,0352,'I','f',036,0274,'`','P',07,0323,024,'W',0317,0221,0376,0323,'3',04,0304,0236,024,'F','o','U',0277,'*','?','/',',',0323,'e',0375,0246,'.','I',036,'W',0205,0242,'Q',0337,'}',0351,'o',0375,0220,'P',07,0320,0264,'W',0313,0367,0337,0264,'_',0213,047,'w',026,0226,':','U',0254,'g',0356,0376,0351,0335,0307,0324,0226,0301,0374,0205,'r',0272,0247,0305,0257,033,0352,0300,0254,0372,0344,0321,0243,014,025,0201,'V','0',0177,'!','@',037,0134,'j',0276,' ',0321,0364,'H',014,0332,0246,0245,'k','i',030,'8',0314,0322,0205,0347,0322,0274,0233,0304,0277,0264,'V',0217,'i',0272,037,017,'X',0315,0177,'&',016,'.','&',036,'T','`',0366,0300,'?','1',0374,'@',0257,0234,'n','n',0356,'o','f','3',']',0134,'K','<',0247,0253,0312,0345,0230,0376,'&',0241,0240,016,0237,0305,0177,020,'<','G',0343,')','O',0366,0255,0373,0233,'|',0345,'m','b',0371,'b','_',0370,010,0353,0323,0251,0315,'s',024,'Q','@',05,'i','h','~',037,0325,'<','I',0250,0245,0206,0223,'g','%',0315,0303,'v','Q',0302,0217,'R','z',01,']',0347,0201,0276,012,0353,0276,')','h',0256,0365,025,'m','7','L','a',0273,0314,'q',0373,0307,037,0354,0257,0365,'5',0364,0227,0205,'<',033,0242,0370,'3','J','[',015,'"',0333,'`',0353,'$',0316,'w','I','+','w','f','?',0320,'`','z',012,0,0345,0276,032,'|',047,0260,0360,'E',0262,0336,'^',0371,'w','z',0324,0212,'<',0311,'q',0224,0213,0375,0230,0362,01,0374,'z',0232,0364,0212,'(',0240,02,0212,'(',0240,02,0212,'(',0240,02,0212,'(',0240,012,0366,'?',0362,017,0266,0377,0,0256,'K',0374,0205,'X',0256,'V',0317,0307,0376,017,'K',033,'u','o',024,'h',0341,0204,'j',010,'7',0261,0360,'q',0365,0251,0277,0341,'`',0370,'7',0376,0206,0255,033,0377,0,03,'c',0377,0,032,0,0351,'(',0256,'o',0376,026,017,0203,0177,0350,'j',0321,0277,0360,'6','?',0361,0243,0376,026,017,0203,0177,0350,'j',0321,0277,0360,'6','?',0361,0240,016,0222,0212,0346,0377,0,0341,'`',0370,'7',0376,0206,0255,033,0377,0,03,'c',0377,0,032,'?',0341,'`',0370,'7',0376,0206,0255,033,0377,0,03,'c',0377,0,032,0,0351,'*','9',0355,0341,0272,0201,0340,0270,0211,'%',0211,0306,0326,'G',0134,0202,'=',0305,'s',0377,0,0360,0260,'|',033,0377,0,'C','V',0215,0377,0,0201,0261,0377,0,0215,037,0360,0260,'|',033,0377,0,'C','V',0215,0377,0,0201,0261,0377,0,0215,0,'p','^','*',0375,0237,0264,035,'P',0311,'q',0241,0314,0372,'e',0301,04,0210,0276,0364,'D',0375,017,' ','}','+',0304,'<','O',0360,0313,0305,0236,023,'f','k',0375,'.','I','-',0201,0300,0272,0265,0375,0354,'G',0361,034,0257,0247,0314,05,'}','Y',0377,0,013,07,0301,0277,0364,'5','h',0337,0370,033,037,0370,0320,'~',' ','x','0',0202,017,0212,'t','b',017,0375,'>',0307,0376,'4',01,0361,')',05,'I',04,020,'G',04,036,0324,0225,0365,0246,0270,0237,010,0274,'C',0270,0352,032,0207,0207,'Z','F',04,'y',0261,0335,0306,0216,'?',020,'k',0316,'5',0237,0205,0377,0,015,0356,031,0237,'G',0370,0207,0247,0331,0222,'s',0345,0317,'s',024,0310,07,0240,0303,')',035,0271,'$',0320,07,0211,'Q',']',0266,0241,0360,0350,'Z',0274,0206,0323,0306,036,024,0275,0214,'7',0311,0263,'T','D','f',031,0340,0220,0330,0,0343,0266,'O',0324,0327,'=',0250,'h','3','i',0320,0274,0262,'^',0351,0223,05,' ','m',0266,0276,0216,'V','?','@',0244,0223,'@',031,'T','Q','E',0,024,'V',0375,0257,0204,0356,'n',0212,0201,0252,'h',0221,0202,'3',0231,'u','8','W',037,0134,0267,0351,']','.',0233,0360,0303,'O',0235,0363,0251,'|','@',0360,0255,0242,'v',0362,'o','D',0315,0371,'|',0243,0365,0240,017,';',0251,' ',0202,'k',0231,'V','(','"',0222,'Y',033,0200,0221,0251,'b',0177,01,'^',0365,0242,0374,':',0370,'I','a',0265,0365,'/',031,'Y','j','R',0,'7',06,0277,0216,'$',0317,'r',02,0234,0343,'=',0211,'5',0350,0372,'F',0263,0360,0307,'A','@',0272,'^',0253,0341,0313,0134,'w',0216,0346,' ',0177,'<',0320,07,0202,'x','S',0340,0217,0212,0274,'H',0253,'=',0334,07,'H',0264,'=',036,0355,010,0220,0363,0332,'>',017,0347,0217,'j',0367,'o',011,0374,036,0360,0257,0205,0274,0251,0315,0232,0352,027,0310,'C',013,0213,0265,017,0265,0207,' ',0252,0364,04,'v','=','k','o',0376,026,017,0203,0177,0350,'j',0321,0277,0360,'6','?',0361,0243,0376,026,017,0203,0177,0350,'j',0321,0277,0360,'6','?',0361,0240,016,0222,0212,0346,0377,0,0341,'`',0370,'7',0376,0206,0255,033,0377,0,03,'c',0377,0,032,'?',0341,'`',0370,'7',0376,0206,0255,033,0377,0,03,'c',0377,0,032,0,0351,'(',0256,'o',0376,026,017,0203,0177,0350,'j',0321,0277,0360,'6','?',0361,0243,0376,026,017,0203,0177,0350,'j',0321,0277,0360,'6','?',0361,0240,016,0222,0212,0346,0377,0,0341,'`',0370,'7',0376,0206,0255,033,0377,0,03,'c',0377,0,032,'?',0341,'`',0370,'7',0376,0206,0255,033,0377,0,03,'c',0377,0,032,0,0351,'(',0256,'o',0376,026,017,0203,0177,0350,'j',0321,0277,0360,'6','?',0361,0243,0376,026,017,0203,0177,0350,'j',0321,0277,0360,'6','?',0361,0240,016,0222,0212,0346,0377,0,0341,'`',0370,'7',0376,0206,0255,033,0377,0,03,'c',0377,0,032,'(',03,0377,0331,0,0377,0341,014,'w','h','t','t','p',':','/','/','n','s','.','a','d','o','b','e','.','c','o','m','/','x','a','p','/','1','.','0','/',0,'<','?','x','p','a','c','k','e','t',' ','b','e','g','i','n','=','"',0357,0273,0277,'"',' ','i','d','=','"','W','5','M','0','M','p','C','e','h','i','H','z','r','e','S','z','N','T','c','z','k','c','9','d','"','?','>',' ','<','x',':','x','m','p','m','e','t','a',' ','x','m','l','n','s',':','x','=','"','a','d','o','b','e',':','n','s',':','m','e','t','a','/','"',' ','x',':','x','m','p','t','k','=','"','X','M','P',' ','C','o','r','e',' ','4','.','4','.','0','-','E','x','i','v','2','"','>',' ','<','r','d','f',':','R','D','F',' ','x','m','l','n','s',':','r','d','f','=','"','h','t','t','p',':','/','/','w','w','w','.','w','3','.','o','r','g','/','1','9','9','9','/','0','2','/','2','2','-','r','d','f','-','s','y','n','t','a','x','-','n','s','#','"','>',' ','<','r','d','f',':','D','e','s','c','r','i','p','t','i','o','n',' ','r','d','f',':','a','b','o','u','t','=','"','"',' ','x','m','l','n','s',':','x','m','p','M','M','=','"','h','t','t','p',':','/','/','n','s','.','a','d','o','b','e','.','c','o','m','/','x','a','p','/','1','.','0','/','m','m','/','"',' ','x','m','l','n','s',':','s','t','E','v','t','=','"','h','t','t','p',':','/','/','n','s','.','a','d','o','b','e','.','c','o','m','/','x','a','p','/','1','.','0','/','s','T','y','p','e','/','R','e','s','o','u','r','c','e','E','v','e','n','t','#','"',' ','x','m','l','n','s',':','d','c','=','"','h','t','t','p',':','/','/','p','u','r','l','.','o','r','g','/','d','c','/','e','l','e','m','e','n','t','s','/','1','.','1','/','"',' ','x','m','l','n','s',':','G','I','M','P','=','"','h','t','t','p',':','/','/','w','w','w','.','g','i','m','p','.','o','r','g','/','x','m','p','/','"',' ','x','m','l','n','s',':','x','m','p','=','"','h','t','t','p',':','/','/','n','s','.','a','d','o','b','e','.','c','o','m','/','x','a','p','/','1','.','0','/','"',' ','x','m','p','M','M',':','D','o','c','u','m','e','n','t','I','D','=','"','g','i','m','p',':','d','o','c','i','d',':','g','i','m','p',':','e','5','9','4','4','0','5','e','-','8','d','f','0','-','4','a','b','a','-','a','2','d','a','-','c','d','f','c','8','0','0','2','8','5','9','e','"',' ','x','m','p','M','M',':','I','n','s','t','a','n','c','e','I','D','=','"','x','m','p','.','i','i','d',':','1','8','b','0','1','7','6','2','-','f','7','1','8','-','4','c','5','f','-','a','a','5','c','-','7','b','4','d','0','8','a','7','2','7','d','4','"',' ','x','m','p','M','M',':','O','r','i','g','i','n','a','l','D','o','c','u','m','e','n','t','I','D','=','"','x','m','p','.','d','i','d',':','8','0','6','1','1','f','0','9','-','1','5','8','1','-','4','b','9','c','-','b','4','5','8','-','9','8','4','f','e','e','c','e','1','2','6','0','"',' ','d','c',':','F','o','r','m','a','t','=','"','i','m','a','g','e','/','j','p','e','g','"',' ','G','I','M','P',':','A','P','I','=','"','2','.','0','"',' ','G','I','M','P',':','P','l','a','t','f','o','r','m','=','"','L','i','n','u','x','"',' ','G','I','M','P',':','T','i','m','e','S','t','a','m','p','=','"','1','6','7','6','2','3','5','5','8','9','3','2','7','1','8','8','"',' ','G','I','M','P',':','V','e','r','s','i','o','n','=','"','2','.','1','0','.','3','0','"',' ','x','m','p',':','C','r','e','a','t','o','r','T','o','o','l','=','"','G','I','M','P',' ','2','.','1','0','"','>',' ','<','x','m','p','M','M',':','H','i','s','t','o','r','y','>',' ','<','r','d','f',':','S','e','q','>',' ','<','r','d','f',':','l','i',' ','s','t','E','v','t',':','a','c','t','i','o','n','=','"','s','a','v','e','d','"',' ','s','t','E','v','t',':','c','h','a','n','g','e','d','=','"','/','"',' ','s','t','E','v','t',':','i','n','s','t','a','n','c','e','I','D','=','"','x','m','p','.','i','i','d',':','9','2','f','7','c','d','9','7','-','d','a','8','4','-','4','9','3','e','-','b','e','0','8','-','b','c','6','3','2','3','4','5','b','d','7','2','"',' ','s','t','E','v','t',':','s','o','f','t','w','a','r','e','A','g','e','n','t','=','"','G','i','m','p',' ','2','.','1','0',' ','(','L','i','n','u','x',')','"',' ','s','t','E','v','t',':','w','h','e','n','=','"','2','0','2','3','-','0','2','-','1','2','T','2','0',':','5','9',':','4','9','+','0','0',':','0','0','"','/','>',' ','<','/','r','d','f',':','S','e','q','>',' ','<','/','x','m','p','M','M',':','H','i','s','t','o','r','y','>',' ','<','/','r','d','f',':','D','e','s','c','r','i','p','t','i','o','n','>',' ','<','/','r','d','f',':','R','D','F','>',' ','<','/','x',':','x','m','p','m','e','t','a','>',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','<','?','x','p','a','c','k','e','t',' ','e','n','d','=','"','w','"','?','>',0377,0342,02,0260,'I','C','C','_','P','R','O','F','I','L','E',0,01,01,0,0,02,0240,'l','c','m','s',04,'0',0,0,'m','n','t','r','R','G','B',' ','X','Y','Z',' ',07,0347,0,02,0,014,0,024,0,'3',0,'&','a','c','s','p','A','P','P','L',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0366,0326,0,01,0,0,0,0,0323,'-','l','c','m','s',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,015,'d','e','s','c',0,0,01,' ',0,0,0,'@','c','p','r','t',0,0,01,'`',0,0,0,'6','w','t','p','t',0,0,01,0230,0,0,0,024,'c','h','a','d',0,0,01,0254,0,0,0,',','r','X','Y','Z',0,0,01,0330,0,0,0,024,'b','X','Y','Z',0,0,01,0354,0,0,0,024,'g','X','Y','Z',0,0,02,0,0,0,0,024,'r','T','R','C',0,0,02,024,0,0,0,' ','g','T','R','C',0,0,02,024,0,0,0,' ','b','T','R','C',0,0,02,024,0,0,0,' ','c','h','r','m',0,0,02,'4',0,0,0,'$','d','m','n','d',0,0,02,'X',0,0,0,'$','d','m','d','d',0,0,02,'|',0,0,0,'$','m','l','u','c',0,0,0,0,0,0,0,01,0,0,0,014,'e','n','U','S',0,0,0,'$',0,0,0,034,0,'G',0,'I',0,'M',0,'P',0,' ',0,'b',0,'u',0,'i',0,'l',0,'t',0,'-',0,'i',0,'n',0,' ',0,'s',0,'R',0,'G',0,'B','m','l','u','c',0,0,0,0,0,0,0,01,0,0,0,014,'e','n','U','S',0,0,0,032,0,0,0,034,0,'P',0,'u',0,'b',0,'l',0,'i',0,'c',0,' ',0,'D',0,'o',0,'m',0,'a',0,'i',0,'n',0,0,'X','Y','Z',' ',0,0,0,0,0,0,0366,0326,0,01,0,0,0,0,0323,'-','s','f','3','2',0,0,0,0,0,01,014,'B',0,0,05,0336,0377,0377,0363,'%',0,0,07,0223,0,0,0375,0220,0377,0377,0373,0241,0377,0377,0375,0242,0,0,03,0334,0,0,0300,'n','X','Y','Z',' ',0,0,0,0,0,0,'o',0240,0,0,'8',0365,0,0,03,0220,'X','Y','Z',' ',0,0,0,0,0,0,'$',0237,0,0,017,0204,0,0,0266,0304,'X','Y','Z',' ',0,0,0,0,0,0,'b',0227,0,0,0267,0207,0,0,030,0331,'p','a','r','a',0,0,0,0,0,03,0,0,0,02,'f','f',0,0,0362,0247,0,0,015,'Y',0,0,023,0320,0,0,012,'[','c','h','r','m',0,0,0,0,0,03,0,0,0,0,0243,0327,0,0,'T','|',0,0,'L',0315,0,0,0231,0232,0,0,'&','g',0,0,017,0134,'m','l','u','c',0,0,0,0,0,0,0,01,0,0,0,014,'e','n','U','S',0,0,0,010,0,0,0,034,0,'G',0,'I',0,'M',0,'P','m','l','u','c',0,0,0,0,0,0,0,01,0,0,0,014,'e','n','U','S',0,0,0,010,0,0,0,034,0,'s',0,'R',0,'G',0,'B',0377,0333,0,'C',0,03,02,02,03,02,02,03,03,03,03,04,03,03,04,05,010,05,05,04,04,05,012,07,07,06,010,014,012,014,014,013,012,013,013,015,016,022,020,015,016,021,016,013,013,020,026,020,021,023,024,025,025,025,014,017,027,030,026,024,030,022,024,025,024,0377,0333,0,'C',01,03,04,04,05,04,05,011,05,05,011,024,015,013,015,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,024,0377,0302,0,021,010,02,'T',02,'@',03,01,021,0,02,021,01,03,021,01,0377,0304,0,035,0,01,0,02,02,03,01,01,0,0,0,0,0,0,0,0,0,0,06,07,05,010,03,04,011,02,01,0377,0304,0,033,01,01,0,02,03,01,01,0,0,0,0,0,0,0,0,0,0,0,05,06,02,03,04,01,07,0377,0332,0,014,03,01,0,02,020,03,020,0,0,01,0332,0220,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,01,0326,'"',04,0134,0301,0235,'0',0,0,0237,0226,'H',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'8',0312,0374,0201,034,07,016,0254,'q',0261,0372,':','<','Z',0272,0274,0332,0370,'u','c',0363,0217,0237,'Y','{',0331,0350,0316,'I','`',0356,0355,'t','g',0260,0340,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,07,'D',0250,'L',031,0207,0211,0346,0216,0300,0361,'b',0243,'t','~','y',0340,0,01,'$',0260,'w','K',0354,0362,033,012,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,03,0246,'S',06,03,0233,0134,'6',0245,033,0213,0216,0347,0,0,0,0375,0367,0333,06,0363,'/',':',0355,0335,'n',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,07,021,'L',0221,'.',035,'0','Z',0134,'W',016,0254,0,0,0,04,0246,0311,0337,',',0261,0367,0335,0346,'P',0,0,0,0,0,0,0,0,0,02,'>','W','D',020,0211,021,'s',016,'b',0216,0251,0310,'e',014,0241,'$','%','d',0340,0237,0223,'s',0354,0,0,0,03,0360,0251,'H','?','.',0270,05,'"','#',0207,'N',0,0,0,03,'/',')',0323,'8',0271,'J','Z','~',0373,'6',0,0,0,0,0,0,0,0,01,0324,'*','"',0234,'*','R',' ',0,0,0,0,0,031,'R',0323,'.',02,0352,'$','@',0,0,'+','r',0272,0327,0214,06,0221,021,0322,0343,0324,0,0,0,';',0235,'{','g',0367,'y','y',0326,0334,0255,'0',0,0,0,0,0,0,0,0,0255,015,'y','(',0363,030,0,0,0,0,0,0,0,07,0331,'i',0233,04,'^','G','h',0,'B',0212,0257,0317,' ',0324,0330,0274,'L','_','0',0,0,0,0345,0333,0224,0376,0357,'/',')',0353,0333,'s',0234,0200,0,0,0,0,0,0,03,0344,0245,'M','`','+','`',0,0,0,0,0,0,0,0,0,014,0361,0262,06,0311,031,'B',':','S',0204,':',0255,035,035,0201,0342,0,0,0,037,0276,0373,'9',0271,'J',0310,0245,':','.',0323,0276,0,0,0,0,0,0,0,'*','s','Q','J',0364,0,0,0,0,0,0,0,0,0,0,0,'2',0346,0327,0231,02,'-',011,0311,016,0251,0306,0,0,0,01,',',0263,0310,0311,0254,035,0267,'!','"',0,0,0,0,0,0,0,0217,032,'|','Q',0240,0,0,0,03,0220,0226,022,0303,'<','f',0316,0301,0306,'b','L','1',025,'!',0307,'@',0,0,0,035,0323,'g',0316,0377,0,06,0230,025,'&','#',0347,037,0,0,0,06,'z','k',0256,'e','m',0223,0263,011,0340,0,0,0,0,0,0,05,032,'i',0251,0207,0,0,0,'9',0213,'@',0267,0213,'H',0260,'N',0350,0,0,01,0360,'C','J',0310,0251,'J',0200,0303,0,01,0366,'l',0211,'1',0323,0205,0177,'F',0210,0353,'s',0353,0,0,0,031,036,0375,0363,0333,0264,0264,0307,'/','m',0220,0,0,0,0,0,0,'u',0215,'?','5',0320,0,0,0,0260,'M',0204,'/','S','4',0,0,0,0,0,0,'p',0225,031,0257,0245,',','p',02,0360,'-',0274,'|',0202,'S','"',0261,'q',0274,0340,0,0,03,0237,'v','v',05,0346,'b','C',0321,0235,0324,'s',0,0,0,0,0,0,030,'S','E','J',0274,0,0,05,0264,'m','1','j',0,0,0,0,0,0,0,0,04,'d',0326,'r',0274,'/',0222,047,'X',0217,0215,0327,0370,'@',0,0,07,0357,0276,0316,0356,0222,0331,0311,'-',0367,'a',0224,0,0,0,0,0,0,'#','&',0202,0220,0260,0,0,0236,033,'z','[','`',0,0,0,0,0,0,0,0,0,0303,024,0261,0200,0211,0346,0204,'S',0242,0317,0,0,0,04,0266,0321,'#','$',0237,0355,0267,011,'X',0,0,0,0,0,01,031,'<',0376,'"','@',0,016,0301,0265,'F',0315,0234,0340,0,0,0,0,0,0,0,0,0,0353,024,0221,0203,0345,0327,05,0241,0300,'e','c',0253,0230,016,0373,'?',0317,0276,0200,0,03,';','3',0327,'3',0267,'I',0317,0313,'$',0,0,0,0,0,0,0303,036,'|',0220,0240,0,04,0270,0336,'2',0303,0,0,0,0,0,0,0,0,0,0,025,011,023,0307,0310,015,'"','#','!',03,'R',0224,0305,'P','1',0335,'3',0221,')','k',0367,'S','w','`',0,016,0377,0,'n',0351,0365,0336,'^','K',0237,0267,'!',0364,0,0,0,0,0,03,0254,'h','1','V',0200,0,'-','s','y',0214,0270,0,0,0,0,0,0,0,0,0,0,0200,0225,0251,021,0253,'G','G','`','x',0204,0232,'2',0243,'#',0215,0243,'|','e',0276,'1',047,'z',0300,0310,'Y',0277,0,'9',0266,0345,'`','^','&',':',0275,'[','-','2',0311,0,0,0,0,0,0,'4',0344,0327,020,0,05,0340,'n',0341,0330,0,0,0,0,0,0,0,0,0,0,030,02,0231,'0','q',034,0320,0212,'|','P',0354,'k',0347,0236,0300,'|',0252,'+','-','}',0314,0361,'V',0362,0274,0225,0334,'_','U',0206,037,'3',0364,036,',',0266,'~',0373,0354,0342,0343,')',0224,0225,0351,0326,'s',0340,0364,'@',0313,0,0,0,0,0,05,036,'h',0330,0,02,0360,'7','|',0345,0,0,0,0,0,0,0,0,0,0,035,'R',0220,'0',0374,0372,0353,0352,',','G',016,0254,06,'_',0222,022,'e',013,0363,'X',024,0377,0,0326,':',0233,0273,'g',020,'_','5',0343,0313,'l','.','o',0350,0237,0217,'d',0326,'.',0371,'U',0227,0276,0221,'*',020,']',0346,0362,0200,0,0,0,0,'0',07,0234,0206,024,0,013,'X',0337,'c',0260,0,0,0,0,0,0,0,0,0,0,05,'H','D','|',0362,07,'J',0212,0306,'G','s',0201,0317,0206,0231,0354,07,0312,0177,036,0342,0372,0247,0262,0374,'u',0332,0372,0305,0365,0276,',',0266,'e','d',0272,047,'W','I','X',027,0276,0353,0221,0370,01,0273,'%',0366,0,0,0,0,06,0220,0224,'H',0,022,0303,0321,03,'0',0,0,0,0,0,0,0,0,0,0,04,',',0252,0310,0275,'s',0202,'/','[',0340,034,0372,0371,0344,'q',0325,017,0237,'v','c',':','f',0344,'Q',0324,0356,0346,0230,0350,0364,0215,0276,';','#','k',0354,'o',0316,0301,0274,0314,'t',0267,0347,0252,0247,'D',0,'f',0317,'G',0214,0330,0,0,0,05,'X','y',0374,0,07,'9',0350,'A','b',0200,0,0,0,0,0,0,0,0,0,01,0214,')',023,015,035,0242,05,'J',0211,0374,0363,0317,0337,'<',0234,0301,0374,0317,0367,0317,';',0332,'"','#','2','w',0254,'w','L',0266,';',0246,'W',0360,0375,0367,0331,0345,0326,'[','/','#',0276,0340,'4','@',0257,0200,0,0330,0263,'r','@',0,0,01,0370,'y',0346,'W',0,0,'m','q',0264,0300,0,0,0,0,0,0,0,0,0,0,'|',0224,0271,035,0327,0215,'}','E',0207,0352,'s','k',031,'.','h',0271,0214,'7',0316,'c','r','w','L',0307,025,'r',';','%','q',0225,0304,0320,0240,026,017,0252,0376,022,0313,'<',0204,0222,0301,0335,'h',023,0202,014,'y',0330,'u',0300,07,')',0350,0311,'6',0,0,0,')',0223,'D','@',0,0234,0236,0211,0235,0200,0,0,0,0,0,0,0,0,0,0,'+',0342,0270,'!',0325,'8',0334,04,047,030,031,016,'x',0331,0304,027,0313,'>','2',0337,021,0230,0372,',',0222,'2',0221,0206,0355,0262,0307,'d','m','y',0231,'n',0231,0275,0302,'V','_',0352,0331,0,0325,023,'V','@',0,0275,0315,0335,0,0,0,'<',0372,'+',0,0,'7',0324,0267,0300,0,0,0,0,0,0,0,0,0,0,0304,024,0221,0201,0210,0345,0204,0323,0342,0307,0327,0236,'I','#','j','8','n',0333,016,'z','>',0255,0236,0340,0250,014,'G','e',0226,037,'1',0364,016,0307,'N',0333,02,0365,'1',0232,0333,0225,0332,'s',0200,'t',0217,'7','H',0320,0,0345,'=','$','%','`',0,01,']',036,'x',0,01,'j',0233,0376,0,0,0,0,0,0,0,0,0,0,07,0311,'K',021,0255,030,'W',0264,'X','~',035,'8',011,'L',']','/','%',0313,013,031,0223,0273,'w',0264,0305,0364,'7',0313,'q',0345,0263,0241,0321,'$',0367,0331,0355,0332,'[','-','!',0276,0352,'3','@',0,'k',0241,0246,0300,0,'l',0311,0267,'`',0,01,0246,0246,0272,0200,01,0277,'E',0264,0,0,0,0,0,0,0,0,0,0,04,0,0255,0210,'5','6','+',021,025,0314,'9','p',0325,'a','W',0276,'G',027,0224,0276,0311,0243,'(',0234,0230,0363,0340,0373,0355,0261,')','k',0320,0225,0331,0244,'$',0266,036,0353,'<',0234,0200,0,':',0307,0232,0344,'p',0,'g',017,'L',016,0300,0,034,07,0231,'&',' ',0,'N',0317,'F',017,0320,0,0,0,0,0,0,0,0,0,01,0212,')',02,'9',07,0307,016,0252,'F',0201,0317,0206,0213,06,0275,0362,'?',0337,'5',0341,0373,'-',037,0257,':',0333,':',0342,0322,0267,0134,0274,0247,'L',0346,0345,'+',',',0365,'n',0,0,0,0325,0263,'S',0300,0,0336,0322,0347,0,02,0237,'4',',',0,015,0300,'6','P',0,0,0,0,0,0,0,0,0,0,012,'d',0212,'s','k',0257,'h',0260,0374,'Z',0361,035,0235,'|',0337,'^','c',0330,0327,0311,0331,0325,0307,0313,0216,0231,034,'m','.',011,';',0365,'/',0316,0316,0333,06,0365,'1',0230,0335,0235,0334,'v','@',0,0,'G',017,'4',0316,' ',01,'~',033,0260,0,06,0236,032,0334,0,'9',017,'L',011,0,0,0,0,0,0,0,0,0,0,0,0206,025,'W',0210,'%','2',047,025,031,0316,'2',0234,0260,0363,'(','_',0233,'}','y',0204,'z','F',0345,037,0221,0266,'I',0342,0351,'8','>',0353,'>',';',0252,'Z','w','t',0225,0313,0311,'t',']',06,'t',0,0,0,032,010,'T',0300,03,':','z','j','}',0,017,'8','H','H',0,0263,0317,'A','@',0,0,0,0,0,0,0,0,0,07,'T',0243,010,0234,017,034,'B',0255,032,'9','q',0327,'=',0200,0371,'V',027,0266,0323,0236,0217,0247,0375,0371,0253,0245,0276,'Z',031,'5',0364,'n',0266,0316,0231,'=',0213,0276,'S','e',0357,0262,'I',0370,0,0,0,06,0270,0232,'r',0,07,0242,'e',0202,01,0205,'<',0306,'?',0,06,0326,033,'P',0,0,0,0,0,0,0,0,0,0,'*',0242,05,0313,0256,0275,0242,0303,0361,0341,0210,0314,0361,'@',0347,'x','j',0335,015,0362,0277,'~','`',';',0374,0361,'P',0231,0317,0244,'w',0344,'z','g',0267,'i','i','7',0276,0334,'G',0350,0,0,0,04,'8',0363,'p',0,015,0305,'6','<',02,0243,'4',034,0,015,0376,'-','`',0,0,0,0,'8',016,0241,0370,'w',0216,'P',0,0,0,'G','J','g',0304,022,0231,023,0212,0214,0347,035,0375,021,0275,0315,'<',030,0256,0251,0271,0254,047,0315,0370,0362,0335,0333,0323,033,07,0235,0372,0177,017,'O','m',0203,'y',0230,0313,0365,'l',0273,0316,0350,0,0,0,0,0374,'<',0315,'#',0300,03,'`',0215,0322,0,0326,'c','Q',0,07,0351,0351,0371,0224,0,0,0,07,'P',0203,0221,03,022,01,0314,'Y',0344,0314,0,0,07,0301,'H',020,0250,'>','8','u','R','4','}',0371,0214,0246,'*',0223,0363,0356,'q','Y','[',0256,'{',0202,0265,'%',0214,0245,'a',';',0254,0361,'y','K',0234,0316,0333,047,0236,0232,0353,0267,0311,'@',0,0,0,0,03,'C',0312,'l',0,'X',0307,0241,0300,032,'r','k',0210,0,0224,036,0226,0,0,0,034,'e','~','@','N',017,'<',0304,'E',0363,0341,'b','9',0272,'<','Z','y','6','e','(',0261,0367,'f','d',0272,'/',0203,0266,0,0,020,'"',0254,0347,0302,0274,0242,'C','q','k',0304,'J',0242,0251,'9',0370,0372,0204,'6','g',0351,'X',0216,0311,0261,0372,'~',031,0311,0236,0271,0245,0272,'N','v','Y',0240,0,0,0,0,01,0251,06,0261,0,014,0301,0351,0340,06,0212,024,0260,0,0265,017,'@',0,0,0,'b',0312,0214,0303,0230,'x',0236,'h',0245,'f','?',0245,0311,0250,01,0365,0227,0266,05,0342,'^',0333,0355,0335,';',0,0,'t',0312,',',0257,0351,0261,']',030,'X',0271,0134,'U',032,035,'1',0364,036,0366,0210,0351,'l','G',0317,0373,':',0343,0343,0322,027,'8',0334,0235,0277,0343,0334,0273,']',';',',','+',0324,0306,'w','f','W','Y',0310,0,0,0,0,0,032,0352,'i',0250,0,036,0244,035,0300,'y',0360,'V','@',02,0365,'7','|',0,0,'1','e',',','t','p',0362,035,'T',0215,0301,0303,'r',0,0,031,0211,'^',0233,'V',0345,'+','u',0200,0,'*',0242,0260,0206,0344,0206,'T','"',0346,0360,0237,'6',0341,0317,0246,'9','%','p',0374,'{',0325,0333,0331,'(',0213,0246,'g','x','*','1',')',0177,0242,'a',';',0354,'S',0333,0264,0266,'^','C','}',0332,'e',0200,0,0,0,0,0,'S',06,0211,0,01,0351,'I','+',07,0235,'e','~',0,'6',030,0334,0340,0,0,0245,'H',0376,'>','A',')','q','8',0310,0355,0,0,0,0375,0367,0333,036,0377,0,'5','~',0357,0317,'&',0,'0','E',037,0247,012,0356,0211,017,0232,0210,0251,'H',0243,0251,0260,'i',0337,0251,'L',0341,'~','m',0213,0352,0236,0213,0312,0335,'y','p',0323,0333,0325,0305,0217,0350,0223,0223,'X',0273,0345,'6','^',0373,'@',0234,0,0,0,0,0,0,025,'1',0240,0200,0,'z',':','M',0301,0346,0361,014,0,033,'*','m',0370,0,03,034,'Q',04,'f',0273,0303,025,0255,'G',0200,0,0,011,'m',0236,'F',0344,0260,'v',0330,0340,037,0205,'(','V','5','(',0314,'$',037,014,0376,03,0345,0134,0270,'r','t','w',0313,0362,0343,0317,04,0235,0372,0216,'g',0216,0277,'$',0215,0247,'@','g',0376,0247,0336,0357,0337,'>',0273,0313,0312,0262,0366,0340,0,0,0,0,0,0,05,0134,'y',0370,0,07,0242,0304,0370,036,'s',0220,'0',01,0261,0246,0343,0200,0,'1','E',030,'@',0251,'1',030,0310,0355,0,0,0,03,'!',0337,0272,0314,0274,'L','_',07,' ','!','E','/',031,0317,04,0245,0304,0216,0316,0276,'i','d','E',013,')',0313,'^',0350,0357,0227,0214,0312,'^','&',020,0337,'8',0213,0312,0337,'0',0262,'6','K',02,0361,'1',0231,0354,0333,'x',035,0300,0,0,0,0,0,0,'U',06,0201,0,01,0350,0321,':',07,0236,'e','n',0,'/',0243,'v',0200,0,034,'f',0277,020,'*',0134,'N','&','3',0234,0,0,0,013,016,0367,'3','p',0366,'m',0232,035,022,0214,0303,0312,0316,0213,015,0324,0345,0326,07,0351,0236,0340,0254,0311,0342,0350,0237,'x',0351,0304,0365,0331,'!',0223,'_','C',0226,0332,'$','d','s',0375,0267,01,'(',0,0,0,0,0,0,0,0247,0315,013,0,03,0322,'"','d',015,010,'*',' ',01,'q',033,0344,0,0,024,0251,'[','V','#',0343,'U',0376,020,0,0,0,'$','S',0335,0263,0313,'L',0215,0320,'U',0205,'c','Y',0217,0215,'W',0270,'@',0,'v',0365,'q',0312,'"',0251,'1','I','k',0327,'v','^','B','w','s',0226,0234,'z',0264,0,0,0,0,0,0,0,05,04,'i','@',0,036,0237,031,'`','i','Y',0257,0340,02,'r','z','6',0,0,025,0251,'T',0304,0363,'A',0351,0321,'@',0,0,0,'r',0354,0312,0306,0277,'M','r',0355,0313,0241,0305,0246,0277,0243,'D','~','y',0340,0,0,'9','v',0345,'a','^',0246,'s',0375,031,0335,0247,'(',0,0,0,0,0,0,0,0325,0243,'T',0,07,'l',0365,034,0372,06,0253,032,0250,0,';','G',0250,0207,'0',0,02,'4','R',0372,0261,0255,'~','}',011,0371,0347,0200,0,0,0,'g',0347,';','$',0223,0375,0260,0212,'|','_','O',0223,'P',0,0,04,0316,0333,047,' ',0232,0353,0272,0214,0330,0,0,0,0,0,0,0,032,'J','P',0240,02,'h','z','@',01,'F',0232,':',0,07,0242,0344,0364,0,01,0306,'P','E','w','I',0210,0306,'G','h',0,0,0,0,0,0,0,01,0231,0226,0352,0233,0334,'e',',','b',0302,0,0,0,0,0,0,0,0,'y',0312,'A','@',05,0330,'o','@',04,'4',0363,'t',0,015,0313,'6','$',0,0,'*','"',0256,0256,0360,0305,'k','Q',0340,0,0,0,0,0,0,01,0315,0267,';',016,0367,'1','$',0337,0235,0322,'}',0,0,0,0,0,0,0,01,0211,'<',0304,'>',0,06,0326,033,'P',01,0370,'y',0216,'a',0,05,0340,'o',' ',0,0,'C',012,'s',0217,'U','{','D',0206,0,0,0,0,0,0,0,011,0255,0272,'R','C','3',0325,'w',031,'@',0,0,0,0,0,0,0,05,'"','h',0310,0,033,0360,'[',0200,03,'F',0312,'<',0,'e',017,'M',0316,0310,0,03,0254,'P','e','s','C',0206,0351,'r','j',0,0,0,0,0,0,03,'5','/',0325,'6',0270,'J','Y',0344,0344,0,0,0,0,0,0,0,0,'4',0260,0327,0340,01,0331,'=',';','2',0,03,'^',0215,'0',0,03,'z',0313,0244,0,0,')',0322,0260,0255,'p','F','+',0274,0,0,0,0,0,0,03,0237,'v','v',025,0356,'b','[',0273,';',0210,0,0,0,0,0,0,0,0,':',0247,0231,0206,' ',0,'[',06,0376,0,01,0203,'<',0314,'8',0200,05,0322,'o','X',0,0,'B','J','{',0207,'M','}','F',0207,0,0,0,0,0,0,023,'k',0204,0244,0216,'_',0252,0361,';',0300,0,0,0,0,0,0,0,01,'E',0232,'>',0,06,0343,033,034,0,0,0320,0202,0242,0,034,0247,0244,0244,0250,0,01,0325,'(','B',0271,0241,0303,'t',0271,'5',0,0,0,0,0,03,'5','/',0325,'6',0270,'J','[',04,0300,0,0,0,0,0,0,0,0,07,0237,'e','^',0,'9',0217,'K',0314,0370,0,02,0212,'4',0200,0,015,0220,'7',020,0,0,'*',022,0257,0256,0360,0305,'k','Q',0340,0,0,0,0,07,'6',0334,0254,';',0334,0314,0317,'~','v',0340,0,0,0,0,0,0,0,0,012,0304,0363,0344,0,013,0244,0336,0260,0,0,0353,0236,'k',021,0300,01,0332,'=','"','%',0,0,010,0221,'M','s','k',0256,0250,'0',0337,0217,0,0,0,0,0,'L',0255,0222,'r',0251,0276,0273,0304,0355,0200,0,0,0,0,0,0,0,0,0320,'B',0246,0,03,'~','K','l',0,0,06,0257,0232,0226,0,05,0360,'n',0340,0,03,0214,0241,012,0356,0227,023,0212,0214,0347,0,0,0,0,014,0264,0247,'L',0352,0347,'+','n',022,0300,0,0,0,0,0,0,0,0,024,0311,0242,' ',0,'O',0317,'E',017,0320,0,0,030,0343,0315,'S',014,0,'?','M',0371,'-',0240,0,05,'d','U','q','<',0320,0212,'t','P',0,0,0,03,0223,'f','V',025,0352,'b','u',0323,0262,0330,0,0,0,0,0,0,0,0,01,0320,'<',0345,'"',0200,0,'o','9','v',0200,0,0,03,'Z',0215,'@',0,02,'R','z','.','e',0,0,0306,024,'_',0212,0362,0215,017,0321,0342,0322,0,0,0,04,0272,0323,'#','/',0236,0355,0274,'N',0320,0,0,0,0,0,0,0,0,015,'6','5',0320,0,013,'(',0364,'$',0375,0,0,0,07,0134,0363,0240,0205,0,01,'x',0233,0304,'~',0200,01,'T',0225,0274,'_','<',026,0231,022,0,0,0,'2',0222,']',023,0313,0244,0265,0301,0352,'P',0,0,0,0,0,0,0,0,02,0221,'4','h',0374,0,037,'g',0240,0305,0226,0,0,0,0,'*',0263,'@','O',0220,0,'6',0310,0332,' ',0,':',05,034,'C','k','Q',0361,0232,0357,010,0,01,0373,0357,0271,0251,'~',0231,0205,0252,'J','{',0267,'+','@',0,0,0,0,0,0,0,0,04,04,0363,0354,0306,0200,01,0261,0206,0344,0,0,0,0,0,'4',0360,0326,0340,0,'>',0215,0326,'/',0300,0,'"',0245,'J','c','8',0264,0364,0371,'5',0361,0353,0307,0347,027,0317,0236,'}','z',0354,'t','l',0310,0367,0356,0314,0357,0316,'z','N','O',0320,0,0,0,0,0,0,0,0,0213,036,'}',0221,0200,0,'%',0247,0242,0306,'D',0,0,0,0,01,0323,'<',0366,' ',0,0,'r',0233,0260,'^',0340,0,'c',0314,01,0325,'>',016,'3',0340,0345,';',0306,'d',0313,0200,0,0,0,0,0,0,0,0,021,'c','@',0210,0210,0,035,0203,0320,022,0313,0,0,0,0,0,01,017,'<',0363,'0',0340,0,'}',0233,0206,'l','p',0,0,0,0,0,0,0,0,0,0,0,0,0,020,'3','C',0310,0270,0,03,'q','M',0217,0,0,0,0,0,0,05,'R','h','Q',0326,0,0,'l','q',0267,0307,'`',0,0,0,0,0,0,0,0,0,0,0,0,024,0231,0245,'F','0',0,01,0262,'&',0341,0200,0,0,0,0,0,0,012,'@',0321,0343,0214,0,01,'b',033,0262,'M',0300,0,0,0,0,0,0,0,0,0,0,03,0244,'j','9',0256,'G',0310,0,02,0370,'7','d',0373,0,0,0,0,0,0,0,02,0211,'4',0240,0340,0,0,'v',0215,0242,'6','t',0355,0200,0,0,0,0,0,0,0,0,0,012,'x',0323,0222,'$',0,0,027,0271,0272,0347,'0',0,0,0,0,0,0,0,0,'S',0246,0216,0230,0360,0,0,0224,033,'R','_',0247,'0',0,0,0,0,0,0,0,0,012,0330,0325,022,0242,0,0,01,0261,0346,0341,037,'`',0,0,0,0,0,0,0,0,05,'~','h',0271,023,0,0,01,'(','6','<',0277,0214,0370,0,0,0,0,0,0,07,01,'L',032,0340,'U',0,0,0,'9',0315,0274,'6','D',0,0,0,0,0,0,0,0,0,0,'b',015,'-',')','@',0,0,03,0234,0266,0213,0254,0267,0311,'H',0,0,0,0,030,0342,0252,')',0222,0223,'0','`',0,0,04,0254,0335,0302,0315,0,0,0,0,0,0,0,0,0,0,0,015,0177,'5',020,0303,0,0,0,0,'I',0313,024,0235,0222,0302,'B','d',0216,0361,0300,'b',0314,'9',025,'!','e','z','@',0316,' ',0,0,0,'}',033,012,'m',0321,0223,0,0,0,0,0,0,0,0,0,0,0,0,'0','F',0246,0232,0374,'q',0200,0,0,0,0,0,0,0,0,0,0,0,0261,'M',0300,',',0360,0,0,0,0,0,0,0,0,0,0,0,0,0,' ',0206,0253,0224,0211,0360,0,0,0,0,0,0,0,0,0,0,047,06,0323,0227,0231,0364,0,0,0,0,0,0,0,0,0,0,0,0,0,0,02,036,'k','a','B',0230,0200,0,0,0,0,0,0,0,0,0344,'-',0343,'d',013,0200,0372,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,035,'B',0235,'(',0342,0240,'1',0,0,0,0,0,0,07,'1','e',0227,'q','x',022,'P',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'q',0225,0331,'X',025,0341,02,'"','f','8',0,0,'9',0211,'9','7',',',02,0314,'-',03,'(',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,017,0303,026,'`',0216,0231,0324,'9',0316,0331,0226,'3',0207,'(',0,0,0,0,0,0,0,0,0,0,0,01,0377,0304,0,'.',020,0,0,06,01,02,06,02,03,01,0,02,03,01,0,0,0,01,02,03,04,05,06,07,0,' ',021,022,023,024,'0','@',020,025,026,'1','P','!','"','4',027,'%','&','2',0377,0332,0,010,01,01,0,01,05,02,0376,0342,0356,'R','l','G','6',0244,'I',0245,0354,'o','U',0322,0257,027,'_',0301,05,'8','~',0247,0366,0224,'P',0251,022,'J',0317,0245,'V','Q','s',0351,'E','H',0226,0217,'*',0201,'t','i',0240,0321,0246,'U',0327,0333,'/',0257,0265,'q',0240,0226,'_','I',0314,0237,'H','8','#',0202,0,0360,037,0354,0274,'x',0223,024,'d',0345,0225,0222,'>',0234,'J','$',0216,0226,0223,'Y',']',010,0361,035,0360,0240,'=','B',0224,'N','o',0354,';','t',0233,'$','$','d',024,0221,']',0303,0222,'6','#',0251,05,034,0370,0200,'9',0205,0223,'~',0331,012,0343,'.',0341,0357,0365,0325,'P',0250,0247,'-','(','i','%',0336,'<','+','B','*',0251,0226,'?',0212,')',0236,0222,'H',0313,')',032,0304,'#',0332,'{','o',0354,'1',0221,'Z','y',0226,0253,0255,'A',0346,'p','D',0242,0353,'5',0313,'(','.','r',0275,0221,'q','S','"',0330,0324,0322,0267,')',0325,'u',0371,'L',0326,0211,'m',0234,' ',0247,0177,0260,0245,0244,0262,'}',0225,'1','C','2',0316,0247,0246,0371,0305,'N',',',0263,',','#',0201,0217,0274,0300,0311,0350,0247,03,0227,0306,'#',0303,'S',0263,035,0362,0216,0134,025,0262,'J',0252,'e',0324,0361,'G',0262,0356,'O',0372,0325,'z',047,0266,047,0260,0345,0332,',',0222,0227,0313,'0','Q',0243,')',0232,'d',0234,'j','B',0335,'3','*','>','v','R',0257,'c',06,'/','-','O','0',0324,'V','i',0217,'q',0250,0253,024,'l',0341,'|',026,031,0216,'}','(',0241,'R','#',0247,'&','t',0257,0211,0253,'c',':','U','4',0312,0221,'+',0361,035,0311,0375,'y',0354,0211,011,0,'3',0231,0222,'M',0356,0237,0311,0273,0224,'W',0325,'!',0314,0231,0241,'2','d',0354,'6',0240,0363,014,'T',0206,0233,':','E',0352,';',047,0346,';','R',0210,0360,07,0357,'{',0223,0370,0222,'L',0313,035,0253,'b',0265,'J','&','0',0322,'N',023,'L',0251,023,0324,021,02,0205,0223,'+','E','B',0352,0301,0220,'f','l','^',0344,'L',0354,0204,022,0265,0334,0316,06,030,0351,'6',0222,0315,0365,'3','*',021,0310,0234,0342,0241,0244,0337,'s',0217,0210,03,0210,0307,0262,0355,0210,0321,0251,0336,'.',0305,0231,030,'7',0365,'-','9','"','.',0267,0253,'%',0352,'Z',0314,'o','~','2',']',0344,'3',0212,'v','S','V','X',034,0271,';',0265,0244,0336,0364,0213,0343,0214,'c',0313,0242,0224,'N','h','h',0260,0216,'C',0323,0232,0237,'c','^','k','m',0312,0217,0347,'<',0244,'!',0224,'3','J','|',0333,0341,'k',0212,0354,'n','D',0230,'b','t',0332,'&',020,'{',0303,0377,0,07,0270,0320,0340,0367,'z','_',012,0314,024,0316,'1',025,0205,02,0271,0240,'X','Z',025,0323,07,',','G',0304,0315,0232,0257,0334,0302,0303,0245,012,0315,0353,0260,'j',0221,0214,047,'7',0212,'5',0217,'X',0332,0256,0304,'t',0303,0323,0271,0345,06,0220,032,0225,0227,'w','6',0363,0302,0222,'*','8','R','/',030,0330,'%',02,';',010,0242,01,037,0215,'k',0261,0372,'l',0305,0273,'"','x','L','B',0250,022,024,0270,'9','=','H',0341,0210,0227,032,0225,0303,0263,',',0265,'%',014,0372,035,'M',0245,'(',0234,0325,'z',0361,'a',0233,',',0261,'P','M',0302,0346,'p',0257,0211,0223,'A','t',0241,'J',04,',',04,'O','x',0247,0244,0345,0312,'L',0320,0274,'e','E','d',0374,'P','T','I',0233,010,'B','a','v','-',0265,033,012,0302,035,'?','E','T','S','p',0234,0326,'+',0203,0226,0324,0346,'#',0231,0214,0322,0310,0250,0331,'_',0212,'e','s',0242,'Q',036,'P','~',0363,0272,'S',0304,0212,'&',']','F',0350,025,0272,'Q',0221,0306,0221,'r',0222,'E','A','?','F','^','a',0244,023,033,0245,0361,0335,0265,0177,05,'o',031,0313,0330,'5','^',0306,0320,0320,036,0304,0325,'j','6',0300,0235,0223,015,0270,'m',0252,0345,'I','e',037,'j','Q',0357,'0',0370,0200,'9',0205,0203,'>',0325,'4',020,';',0225,0243,0230,022,'9',0267,0243,'a',0260,0263,0254,0307,'Z','m','o','-','o',0367,0326,'i','r','v',0225,'j',0330,0326,'.',0267,0355,0312,'H',0226,'5',0272,0252,0231,'u','$','^','v',0344,0361,0305,0262,0344,015,'@',0304,0366,')','z','6',033,013,'J',0324,'m',0226,0312,0356,0323,'#',0271,0273,'u',']',0255,'O',0304,'E',' ','"',0212,'m',0222,0366,0234,'8','#','T','_',0276,'<',0203,0207,'.',012,0331,' ',0347,'x',0273,0210,0261,'(',010,'p',037,04,'k','.',0271,0265,']',0211,0347,037,'F','^','Y',0264,034,'}',0256,0322,0352,0327,047,0272,0267,'W','}','h','{','S',0244,'0',0251,0267,0367,047,'e','{',0345,0214,'`','!','^',0272,027,'J',0306,'*',0221,07,'N','Y',021,0300,'.',0330,0355,0315,0271,0233,'Q','t',0251,010,011,0226,032,',','d','W',')','@',0245,0364,034,'8','M',0242,027,0333,0242,0226,0311,035,0324,0232,03,0253,'b',0261,'Q','-','a','Y','{',0226,'9','n',0231,'u','(',0363,0234,0337,021,0252,0254,'o',0203,0220,024,07,'Q',0242,'M','~',0266,'$',0221,0226,'Q',0263,'r',0266,'J','J','E','(',0246,'t','+',023,';',04,047,0243,0225,'o',037,'b',0276,0352,06,':','V',0310,0243,'v',0351,0264,'C',0334,0230,0223,010,0346,0346,'0',0234,0322,'O',';','t',0376,020,'K',0254,0251,010,011,021,0304,0201,0372,0355,'_',0225,0177,0207,',',010,0276,0224,047,'L',0377,0,021,0354,0373,'d',0316,'r',0246,'K',',',0361,0246,0236,'U',0354,0216,'*',0362,0321,'R',0215,0346,0243,0374,0371,'>',0347,0370,0374,'v',0354,'u','@','5',0221,'t',0323,'*',')',0373,0216,0134,021,0242,017,0236,035,0373,0207,013,0225,0272,'J',0252,'e',0224,0370,0214,0377,0,0262,'?',0356,0235,'"','d','U',0323,021,'0',0266,'v',0360,022,'H','G',0210,0352,'-',0227,037,0213,0255,0207,0252,'o',0214,'a','r',0374,'~','K',0315,';','2',0204,04,'T',0304,0263,0211,0311,'-',0264,032,'R',0226,0311,06,0355,0323,'h',0207,0273,'?',')',0336,0256,'#',0300,037,0273,0356,0225,0371,'E','N',0222,0211,'(',012,0221,'T','J',0260,04,'Z','`','q',0340,'B',0250,'n','e','4',0301,0237,'t',0240,07,0,0266,'X','>',0245,0250,0217,035,0230,0252,0341,0367,'Q',0336,0134,0253,'m',0373,0271,']',0265,0212,0343,0233,'D',0264,'D','S','h','H',0377,0,'v',0305,')',0333,'%',0251,'W',0237,'(',0242,'e',0314,0234,'I','@',025,0211,016,04,'Q','V',07,'E',0372,'J',0350,'V',' ',03,0347,0340,'r',0351,04,014,0341,'T','Q','*',011,0311,0276,010,0310,0367,0257,025,0220,'u',0262,026,'Y','x',')','8','Y','t',047,'b',0374,'y',026,0321,0370,0324,06,0324,020,'Q',0322,0324,'z',0222,'U','8',0217,'v','E',0361,'c',0333,',',0261,0234,'*',0371,0337,'j',0220,0217,021,0320,07,021,'f',0330,033,0244,0243,0324,0222,'8',0177,0272,'~',0260,'.',0263,0226,'G','o',0362,01,0314,',','Z','v',0251,'&','C','*','v','P',0210,'%',035,'v',0252,0251,'T',0231,0333,0210,'-','=',0214,0217,0210,'G',0200,'_',0254,0243,'f',0260,0355,0304,'4',0376,05,0367,'L','`','!','f','$',0206,'E',0312,0207,04,0210,0345,0300,0271,'W',0342,'<',0234,0356,'V','7','"','Q',0355,0205,'e',037,'9',0355,0322,0217,'O',0252,0345,0311,0200,0210,'|','E',0262,0345,015,'W','b','z',04,0325,0312,0260,0225,0252,025,0303,'u',032,'/',0261,'%','N',0202,0264,0353,011,'l',0320,036,034,0245,'d',0372,':',0366,0332,'m','h',0366,0231,0306,0355,0323,'h',0207,0273,'e',0223,0345,015,'J','<',0352,0237,0345,0212,0235,'7','?',0275,01,'A','2','*',0222,0356,0227,'h',0330,033,047,'(',0343,0217,0304,'s','>',0341,'M','@','D',0367,0212,0374,0346,032,0237,'(',0355,0304,0226,'O',0251,0234,0360,0344,'i',0377,0,0277,0263,0355,0306,'u',0177,0307,'`','}',0331,'7',0345,0216,'j',0241,0314,0251,0344,0236,'v',0351,0374,0,'s',012,'Q','@',')',0270,'l','f',0306,'d',0374,016,037,017,037,025,020,021,0346,026,0350,031,0312,0251,'$','T','S',0216,'`','y',07,'(','"','F',0351,'|',0275,'f',0224,0203,'K',034,032,0265,0331,0235,0211,0250,'d','T',0253,'M',0226,0305,03,0276,0377,0,'=',0370,0375,'c','n','7',0255,0376,'C','c',0367,'D','@',0241,'1','"','2','.',0227,'X',0255,0322,'Y','S','.',0247,0304,'k','N',0,0351,0321,'[',024,010,0264,0201,0306,' ','x',012,0316,032,030,0357,0326,'8','~',0364,01,0314,',','Z','v',0251,'&',0231,0225,'<','T','q','c',0233,'m',0314,'U',0276,0372,'3','n',026,0235,0350,0275,0337,0231,0247,';',0271,0215,0270,0312,0277,0364,'U',0237,'v',0313,047,0312,']','H',0273,0356,025,0370,'H',0274,0352,024,'9','J','-',0224,'x',0345,'4',0301,'2',0352,'U','0',024,0276,'"',0231,0360,0370,0256,'E','t','I',0271,0313,'t',0335,0267,0261,'C',036,02,'k','d','$',0241,0341,'e',0333,0270,'#',0266,0373,'W',0134,0215,0220,0227,0221,'<',0274,0246,0312,'D',027,0344,'6','P',016,01,0356,'I',0276,',','{','U',024,'2',0252,'J',';',0351,023,0345,03,'r','*',03,0314,032,'z',0364,020,010,0342,0237,0247,',',0247,02,'j','=',0237,'r',0246,0240,'b',0273,0325,0274,031,0246,07,0231,'=',0270,0236,'c',0354,0352,0233,'r',0254,0277,0325,0324,0266,0341,'h','N',0204,0177,0273,'7','#',0366,016,0334,'.',015,0222,'Q','A','T',0377,0,011,0267,'Q',']','(',0325,'D',0264,0335,0372,0210,02,0222,'j',')',0246,0221,0346,'9',0216,'r',0242,'G',013,012,0352,0240,0211,0234,'*',0212,'E','A','6',014,0216,0375,0313,'v',0344,'j',0217,0202,0305,020,'Y',0350,'E','R','2','*','l',0303,'r',0335,0225,0217,'n','k',0223,0353,'K','l','M','3','*',0244,04,'Y','a','a','}',0313,'$',0227,'n',0206,0244,035,0367,'*',0374,'1','i',0334,034,0244,02,0,0207,035,':',0214,0343,0240,023,' ',0242,022,'d','1',035,0274,'3',0223,0,'q',0323,06,0235,0252,'D','(',0234,0321,021,0241,034,0333,0305,0223,0341,0376,0246,0333,0262,012,'H','a',0346,'J','`','9','v',']','d','>',0322,0327,0263,033,'E',0375,0255,0303,0334,'t',0344,0215,020,'t',0344,0356,0334,'J',0273,0351,0227,0341,'2',012,0207,'E',' ','A','5','^',0235,'U',0377,0,0374,0227,0367,0247,015,010,0340,034,'#',0320,'S','Q','L',0376,'+','q',0134,0205,0361,0346,0250,0276,0274,'F',0332,04,0217,0332,'T','>','f',0237,0204,0134,'@',0217,021,0331,0204,'c',03,0227,0334,0262,'I','w',013,0271,'p',015,0221,'9',0305,'C',0374,'F',011,01,'i',05,0272,'H','E','!',0376,'J',0257,0312,'X',0243,034,0305,'p',0360,0210,0220,0306,023,0231,0213,'N',0351,'P',016,'P',0204,0213,0373,07,0,034,03,0307,'p',0214,0373,0212,0316,0334,047,'#',0326,0207,0371,0312,0217,';','J','^',0334,'a',035,0365,0364,0337,')',0327,'M','=',014,0223,'@',0327,0331,0264,0321,024,'"',0241,0342,0233,0221,0372,0366,0232,0220,'u',0334,0255,0360,0315,0267,'r',0253,0250,0361,'G','G','T',0312,03,037,0372,0256,0231,025,0310,0244,0221,'P','M',0321,0371,0327,'M','1','T',0355,0233,0203,'d',0232,'5',';',0305,0331,0264,'#','&',0376,'O',0336,0254,'1',0377,0,'U','9',0263,013,'<',024,'l',0177,'9',0275,0347,',','~',0300,016,'#',026,0314,0261,0361,0236,047,'.',0321,'h','G','v',0255,'/','*',0355,0316,0304,0225,':',07,0203,0233,0357,'|',047,'0',020,0262,'o',0206,'A',0334,0253,0276,0231,'>',012,'Q','9',0242,0322,024,0302,'M',0327,'(','i',0203,0340,'H',01,'b',010,'=',0220,016,']','F','3',0350,0223,'P','Q','}',0202,036,'l',0264,0313,0264,0271,0354,0307,'n',0373,'+',0237,0316,'m','w',0317,'5',0262,0254,0324,'^','Y','<','*','(','T',0211,'#','g',0322,0253,035,'s',0376,0264,0264,0242,')','i','I',0205,'M',0241,'~',0340,'t',0234,0223,0202,013,'7',0344,'u',0244,0324,'2','J','4','p',016,0333,'o',0263,0310,0362,021,0302,0300,0335,'%',024,025,'O',0361,030,0327,0224,0272,'|',0331,'B',0253,0262,'1',0237,'X',0372,0256,'E','u','O',0347,0316,015,0200,0257,'6','C','8',06,'r',0377,0,'9','u','p','Z',0347,0263,030,'7',027,027,'o',03,0371,024,0243,0222,0220,0224,'Z','D',0372,'u','$',0232,032,']',0332,0256,'v',0224,0302,'C','3','s',0335,'#','U','u',0316,0206,0347,0216,0212,0315,0262,0353,0231,0312,0322,'N',0373,0205,'[',' ','.',025,'Z','=','5',023,'9','y',016,0331,0351,0333,0351,07,'$',0134,04,'8',0203,0250,0336,':','1','D',0203,0246,0315,0305,0312,0251,0246,011,022,'*','8',0322,'.','S','!','R',047,0237,'6','#',0315,01,0265,0222,0235,'f',0177,031,'(',0374,0367,'}',0230,'l',0234,0326,0355,0362,'/',0311,034,0335,0323,0245,036,',','s',0202,'e','y','&','e',0274,021,0213,0364,0134,0302,':',0355,'$','w','Y','$','{',0207,022,0216,0372,')',0200,'q',026,'M',0273,'t',0344,035,'t','S','j',0330,']',035,0313,'C',0267,022,'(','d',0314,0326,'H',017,0257,0336,0244,0325,')',0324,0,0342,',','Z','v',0251,'$',0221,0226,'R','1',0201,'c',0233,'z',031,'q','.',0245,'3','m','M','Q','^',0257,0361,0177,'7','5',0313,'f',023,'K',0215,0217,'|',0313,0376,0375,0351,0214,04,'+',0327,0242,0350,0376,0,0377,0,'4',0335,'^',0262,'1',0256,0273,0306,';','&','d','>',0275,0232,0353,02,'D','Y','Q',']','H',0306,0274,'E','e','A',024,0316,'c',':','Y',0253,'p','n',0224,0233,0236,05,0321,0321,':','Z','I',0362,0211,023,0367,0250,0246,'|','~','+',0221,'}',04,0375,034,0250,034,'h',0333,'i',03,0306,0241,0361,'s','0',0236,0333,0263,010,07,0376,0323,'t',0212,0242,0213,015,'L','8',0340,036,'(','e',0270,0222,0252,0363,0201,0276,'D','x',04,0304,0207,0330,'<',0224,'u',0325,'P',0234,'9',0220,'1',014,0231,0212,06,04,0231,'&',0212,0256,0227,06,0351,034,0342,0241,0243,'Z',0365,014,0340,011,0322,037,0333,'&',0242,0351,'`',0,'(','A','E',0367,0356,'=',',',0234,'P','5',033,'m',010,'x',0323,0276,'/','e',0344,0270,'l',0302,'J','p',0234,0335,'*','^','x',0335,'I',017,027,0276,'&',013,'t',']','4','p','-',034,0220,0340,0241,'>',',',0262,'=',04,'$',']','v',0310,0374,'"',0271,0320,'3','W',0344,'_',0341,0323,'R',0271,'/',0327,0251,0327,'L',0200,0221,'$',0235,'u',014,'R',0211,0314,0321,0260,'5','E',0243,'S',0274,'p',0321,0251,031,0267,0364,0262,0230,0377,0,0360,0273,'h',0301,0302,0241,0361,0221,'I',0311,'u',0331,0206,015,0313,'l',0334,0241,':',0204,'1','D',0206,0224,'/','+',0317,033,'5',0272,0355,0353,'/',0272,0315,0264,0365,0331,031,'7','x',0354,'W','Q',0323,0201,'r',0267,0317,0353,'M','d',0204,0232,'!',0301,'@',0324,0203,0256,0202,'z',0212,'i',0310,']','A','E',0366,015,0375,'<',0260,'~','Z','N',0332,'i','y','*',0177,031,'U',0277,'F',0355,0263,023,0271,0355,0356,0233,0347,020,0350,'I',0315,047,0377,0,'?',034,':',0374,0252,'2','v','v','N','P',0221,'A','v',0323,'R',0243,'"',0264,0263,0256,'#',0271,07,047,'n','d',0344,0223,'2','K',',','+',0251,036,0323,0271,'W','U',0310,0276,0272,0236,0246,'h','p',')','U',0366,0304,' ',015,0242,0276,'3','C','a','N',0315,0262,0216,0357,0262,0267,'o',0266,'!',0301,'Y','T',0371,0332,'x',0323,'8',0244,0242,'J',02,0311,0351,0343,0236,0325,021,036,'a',0360,'$',0231,0226,'Q',0272,05,'n',0224,'k',03,'H',0272,'I','"',0242,0237,0251,0234,']',010,047,0261,0243,'q','x',0354,0245,0345,'/',0306,'q','l',';','Z',0270,'3','G','H',0250,013,'%',0272,0302,0337,0257,030,0251,':',0251,010,'p',037,034,'c',0320,'D','V','x',0222,05,'t',0344,0316,0224,0360,0305,0264,0350,0246,'R',0211,0315,023,034,021,0315,'}',0134,0312,0363,0257,'h',0331,'K','h','/','m',0237,'9',0205,0227,'s','R',0333,'D','~',022,'5',015,0307,' ','(','E',0222,024,026,0221,'K',0244,0357,0326,0215,'i',0334,'+',0252,0324,'_',017,'^',0362,0370,'$',0255,0333,'1',013,'0','u','p',0371,0271,'0',0373,':',0266,0334,'+','%',0327,0203,0337,'d','o',0321,0221,0231,'K',0376,'>',0252,')',012,0312,' ',0210,' ',0224,'<','h',0310,0272,')','@',0245,0365,'e',0237,026,'2',',',0306,023,0233,'f',017,'b','<',0277,'"',034,0301,'6',0303,0352,0346,'6','b',031,'N',0306,0327,0276,0320,0333,0250,0311,0352,']','f',0336,0254,'c','N',0212,'i','$','e',0324,0215,'b','X',0366,0276,0266,'^',0223,0354,'j','{','q','d','w','a','M',0331,0227,0242,0373,033,'^',0310,0267,0347,0213,0222,'l',0340,0216,0333,'n','r',0200,'9','n','b',0211,014,0361,'.',0213,0237,'N','1',0247,']','M','V',0342,0372,')',0372,0371,0226,'c',0274,0260,'l','l',0334,0356,0334,'G',0263,'$','s',015,0231,0236,'+',0272,0201,0333,0211,0346,0276,0322,0255,0276,0302,0333,0267,0222,0231,'K',0375,0364,0221,'H','W','Q',024,0212,0202,'p','q',0235,0373,0237,']',0323,0224,0331,0266,0227,0221,'<',0274,0246,0314,'_',025,0366,0227,015,0263,0221,0205,0231,0207,'U','#',' ',0256,0314,'O','9',0365,'V',0215,0366,0226,0335,'F',0217,0222,0353,'5',0364,0243,032,'t',023,'l',0334,0356,0227,'d',0320,0214,'[','z',0371,'v','s',0353,'k',0233,'p',0264,'?','o',021,0273,'(',0302,'}','=',0257,'b','J',0231,05,'j',0363,'e',0261,'@',0356,'t',0200,':','l','r',0211,014,0361,036,0203,0217,'B','1',0247,']',']','W',0243,';','D','=',0214,0215,'?',0367,0366,'}',0210,0242,'w',013,'@','E',026,022,033,'v','^',0203,0373,'*',0346,0334,'9','d',0355,'$','7',0330,0332,0366,0362,023,'(',0377,0,0236,'t',0222,025,0224,'A',020,'A','*',0374,'g','z',0343,0330,0310,0226,'?',0307,'+',0233,'q','<',037,0332,0332,'7',0270,'n',0233,0266,0366,030,'s',0300,'M','l','j',0345,'F','N','k','S',0251,'X',0341,'w','Y','Z',0365,0330,'9','K',0256,0207,0236,'-',0247,'I','6',0215,0216,0361,0303,'F',0304,'f',0337,0330,0311,'V','o',0310,0354,';','q',0215,0177,0350,0353,036,014,0317,']',0352,'#',0267,023,0332,0276,0232,'_','r',0211,0202,0251,0270,'D','[','/','$',0207,'E',0317,0226,'9',0247,'r',0256,0253,0361,0235,0223,0177,'c','(','Z',0276,0202,017,'m',032,0274,'6','[',020,07,0,0360,'H',0260,'J','Q',0204,0314,'Z',0260,0222,0233,'?','Z',0307,026,0337,0311,0241,'w','Z','Y',0362,'-','(',0207,'Y',0277,0221,'2',012,0207,'l',0200,'6','J',0275,031,0335,0257,0354,'>','z',0214,'k',';','=',0201,'k','4',0316,0334,'S','[',0372,'h',017,026,'a',0253,0367,'L',0366,0326,'l',013,0326,'f','#','$',0220,0227,'a',0266,'E',0240,'>','f','b',0362,0213,0306,0375,0262,0376,'8',0246,0234,0205,'f',0320,0357,0134,'6','n','F',0210,'{',031,'b',0345,0366,'N',0366,0320,'+',03,'g',0237,0,02,0207,0211,0303,'t',0335,0241,'o',0255,0251,'W',0233,0333,0215,'/',037,0216,0275,01,0342,033,'l',0254,':',016,0244,'Z',0367,010,0370,0230,'4',0356,0225,0324,014,'g','b',0337,0330,0311,'w',0177,0307,030,0210,0361,035,0205,'(',0234,0324,012,0267,0342,0320,'^','L',0211,'S',013,'D','!',0212,'%',035,0270,0266,0375,0326,015,0257,0232,025,0363,'U',0320,';','e',0244,0243,0307,0216,0377,0,0336,0233,'E',0250,0256,0222,'H',0250,0222,0275,020,'+',')',0354,0134,'m',0255,0352,'Q',0222,'2',013,0313,'>',0333,0210,0352,'?','`',0367,0315,0226,'i',0235,0203,0215,0245,'0',0220,0330,0343,'!',026,'y','=',0263,'p',0377,0,'`','C',0220,0311,0231,'V','(','*','#',020,0200,0353,0351,0222,0327,0323,047,0257,0245,'&',0202,031,'=',026,'%',02,0351,'4',023,'G','M',0243,0334,'<',030,0372,0311,022,020,016,01,0353,0330,0354,'m','+',021,0266,'+',013,0253,'4',0236,0332,0275,'u','{','<',0304,'t','z',021,'L','|',0317,032,'$',0375,0255,0322,0250,0265,'J','[','j','J',0235,05,'1',0366,'F','%',0204,0273,']','G',0267,'y',0243,0325,0331,0233,'_',0211,0241,0257,0304,0322,0320,0324,0213,0257,0304,'t','Z',0222,'z','N',0256,0320,0272,'B','%',0233,0177,'j',0307,'c','g','X',0216,0263,0331,0335,0332,0244,'v',0267,'n',0243,0265,0350,0225,04,0352,'q','>',0205,0252,0266,0336,0323,021,'-',024,0346,021,0376,0322,034,0311,0236,0203,0223,0311,'#',0374,'k','e',0305,0225,'I',0235,0206,0304,0362,0315,'!',0273,026,'Q','~',0265,'/','J',0375,'I','N',0330,0301,0313,'e','Y',0257,0272,0215,0225,024,0216,0323,'w',011,';','C',0370,'W','|',0224,0332,0270,022,'2','N','e',0235,0356,0305,0364,036,0375,'O','S','"','P',013,'d','A','D',0314,0212,0233,0252,0227,0211,032,0242,0265,0233,0204,'u',0251,0277,0276,0361,0352,021,0315,0356,'y','e','i',015,010,0211,0207,'v','9',0307,0306,0237,'T',0204,'*','d',0365,'r',016,'9','%',0210,0253,0240,0243,'e',0267,'5','t',0263,047,025,'<',0303,0246,0216,0320,'|',0207,0271,'j',0311,0261,0225,0355,'X','-','2','6','w',033,0361,0366,'9','=',0200,0310,0242,'F',0351,'z',0367,0254,'z',0205,0245,047,0361,0356,'"',0335,0357,0202,0263,'H',0326,0334,'V',0362,0373,011,035,'"',0262,'n',022,0366,',','y',02,'"',0265,0253,'6','M',0225,0260,0370,'h','8',0300,0362,02,'B',025,'2','{','6',0352,'c','+','k','K',035,'a',0365,']',0357,0202,026,0315,047,'^','V',07,'4',0242,0240,'E','O','G',0316,'%',0351,0256,0272,'m',0222,0234,0313,'P',0321,'z',0260,'d',0271,0251,0337,012,010,'(',0351,'j','.',',','$','p',0373,0222,0261,'-','&',0331,0134,0261,0223,0312,0357,0211,025,0324,'l',0254,'>','U',0235,0213,0324,'V','h',0214,'s',0250,0313,'L','L',0300,'y','^','H',0265,0216,'N','S',',',0300,0307,'j','c','3',0311,':',0324,0234,0333,0371,0245,'<','5',0352,0304,0205,0231,0335,'>',0204,0306,0246,0227,0277,'p',0305,015,'e',0306,'V',035,0344,'#',0277,034,'u',0236,'Z',047,'L',0362,0365,0201,0251,'Z',0347,023,'q','o',0232,'a',0224,022,'e',0212,0331,0364,0226,'K',0255,'-',0240,0277,0327,0207,'J','d','Z',0342,'`','|',0253,'Z','.',0234,0346,'8',024,'A',0336,'p','n',']','<',0315,'2',0353,031,0375,0376,0301,'$',012,0252,'u',0324,0361,024,0242,'s','T','q','+',0251,'-','G','F','5',0210,'i',0374,031,0210,'6','3',0315,'m',030,0205,0354,'v',0216,'C','$',0177,0340,'V','h','r',0266,0203,'U','q',0364,'e',0134,'?',0213,'b',0245,'E','Y',0313,'d',0304,0362,0220,0372,'9',014,0231,0275,0312,0375,'.','Z',0312,'j',0336,'%',0213,0211,0321,'J',04,'/',0362,047,'*','Q','V','"',0317,'a',0207,0215,0265,'%',020,0366,035,'o',']','4',0314,0261,0340,0361,0134,0334,0276,0253,0370,0266,032,023,'@',0,'P',0376,'c',0246,0210,'=','F','c',021,0302,'H',0352,'_',017,0314,0261,0324,0204,'C',0350,0243,0372,')','"','w',012,'E','c','K',04,0256,0241,0360,0253,'D','u',021,'[',0214,0202,047,0364,0224,'L',0252,0226,'C',035,0327,0244,0265,'!',0204,'Z',')',0247,0230,'j','m',01,'y','C',0260,'1',07,'1',0316,0331,'x',022,'E','G',07,'k','R',0232,'x','f','X',0232,0304,0357,'L','0',0201,0304,'c',0261,'=','}',0216,0230,0305,0263,0213,'O',0373,'B',034,'t',0274,'K',027,'@','z','d',021,0364,'4','*',0361,0264,'8',0346,0266,'a','.','?',0256,0227,'D',0244,'@',023,'M',0353,0261,'M','4','D',0210,0220,0177,07,0377,0304,0,'5',021,0,02,01,03,02,05,02,05,03,03,03,05,0,0,0,0,01,02,03,0,04,021,05,022,020,023,'!','1','P','"','0',024,' ','2','A','a','@','B','Q',06,'#',0201,025,'3',0221,'$','D','b','p',0240,0377,0332,0,010,01,03,01,01,'?',01,0377,0,0320,0305,0202,0367,0243,'p',0202,0215,0327,0360,'+',0342,'[',0370,0257,0210,'z',0370,0207,0257,0210,'z',027,047,0356,')',0134,'8',0310,0363,0317,':',0257,'j','i',0335,0275,0233,'n',0347,0316,';',0204,031,'4',0363,'3',0373,'q',0246,0305,0307,0233,0222,'A',030,0246,'b',0307,047,0333,0267,0217,0367,0237,'6',0356,020,'d',0323,'1','c',0223,0355,0303,026,0363,0223,0333,0315,0222,024,'d',0324,0216,'d','9',0366,0343,'B',0347,024,0240,'(',0300,0363,'s','K',0274,0364,0355,0355,0250,',','p','*','4',010,'1',0346,0347,0227,'>',0221,0356,'C',026,0301,0327,0277,0233,0236,']',0276,0221,0356,'A',026,'=','G',0315,0313,047,',','Q','9',0352,'}',0270,'"',0335,0352,'=',0274,0333,'0','A',0223,'N',0345,0316,'O',0267,024,'|',0303,'@','c',0240,0363,'r',0311,0314,'?',0217,'m','T',0271,0300,0244,'P',0203,03,0315,0317,'.','}',03,0334,0212,'>','X',0374,0371,0271,0244,0330,'0',';',0373,0220,'E',0217,'Q',0363,'n',0341,06,'M',0134,0334,010,0224,0313,'%','Y',0353,'I','+','m',0227,0245,02,010,0310,0366,'`',0213,'q',0334,'{','y',0262,'q','R',0311,0314,'5',0255,'A','q','2',016,'_',0322,'(',0202,'*',0307,'U',0226,0324,0341,0272,0255,'[',']',0305,'t',0273,0220,0374,0361,0307,0314,'8',0240,'0','0','<',0334,0362,'g',0322,'8',0353,026,0366,0240,'o','=',033,0204,'3',0311,03,'n','C','V',':',0302,'O',0350,0227,0241,0256,0377,0,'"',0251,'c',0201,'H',0201,06,07,0233,0232,'M',0203,03,0277,033,0211,0204,021,0231,017,0332,0246,0231,0356,0245,0334,0337,'z',0264,0322,0241,'K','}',0262,014,0223,'W',0332,'<',0226,0376,0270,0272,0212,0352,'*',0307,'W',0222,0337,0321,047,'Q','Q',0270,0221,03,0216,'0',0307,0260,'d',0367,0363,'n',0301,06,'M','3',026,'9','<','u',0214,0374,'#','b',0221,0266,'0','j',0264,0270,'K',0210,0203,'/',015,0134,'F','.',0266,0306,'+','N',0323,036,'Y',0277,0272,':',012,03,03,03,0204,021,'~',0363,0347,'%',0223,'y',0371,'&',0210,'M',031,'C',0367,0253,0253,'v',0266,0220,0243,'U',0265,0334,0266,0255,0230,0315,'6',0277,')','L',01,0326,0223,'t',0362,0214,0367,'5',032,0355,'@','8','E',036,0363,0370,0363,0227,022,'~',0301,0306,'y',0322,0335,'7',0271,0251,0277,0250,033,'8',0211,'j',015,0177,0256,'%','Z',0226,033,'m','R','<',0212,0271,0321,0256,'!','>',0221,0221,'B',0312,0341,0216,02,'V',0227,0245,030,017,':','n',0374,021,'K',0234,012,'U',010,'0','<',0334,0262,'l',034,'I',0300,0311,0255,'N',0364,0335,'J','@',0372,'E','A',0244,0334,0316,0234,0300,':','S','.',0326,0333,'Z','M',0261,0265,0200,0334,'=','Y',0352,'0',0335,0364,035,0376,'H',0243,0345,0217,'6','N',0321,0223,'N',0345,0316,'x',0352,'r',030,0255,'X',0212,0263,'A','-',0302,0253,'V',0255,'z','-','b',0344,0307,0334,0326,0225,'c',0361,'R',0357,'o',0244,'V',0251,'0',0202,0330,0326,0235,033,'I','r',0241,'x',0301,026,'=','g',0316,'O','&',0343,0264,'|',0232,0214,'F','k','f','Q','@',0230,0333,'#',0275,027,'{',0251,'r',0347,0275,'C','-',0255,0214,0,06,0255,'F',0374,0336,0311,0323,0351,025,0241,0331,0362,0327,0236,0375,0317,010,'c',0336,'r','{','y',0311,0244,0330,'0',';',0361,'$','(',0311,0253,0215,'w','d',0273,'c',031,025,'k','w',035,0342,'n','J',0324,0364,0226,0317,'6',012,' ',0251,0301,0256,0247,0245,'i',0272,'S',0314,'y',0222,0214,012,0,'(',0300,0244,'B',0347,02,0225,'B',0214,017,'6',0314,020,'d',0323,'1','c',0223,0307,'Z',0277,0377,0,0267,0217,0374,0326,0237,0247,0265,0343,'d',0375,'4',0322,'[','i','q',0342,0227,0372,0202,'"',0330,'+',0322,0276,036,0322,0371,'w',0343,'5',036,0235,'m',021,0312,0257,016,0365,024,'|',0261,0347,'&',0223,'y',0300,0355,0306,'W',0330,0205,0252,'G','2',0310,'Y',0276,0365,0361,'v',0372,'u',0250,011,0324,0324,0367,017,'p',0373,0334,0326,010,0255,02,'f',0346,'4',0177,'n','0','G',0373,0317,0234,0236,'M',0243,'h',0371,047,'N','d','l',0265,'"',030,0334,0253,'V','s','Z','^',0226,'g','<',0331,'{','V',0267,'$','[',0304,'Q',0375,0253,0372,'~',03,0226,0224,0360,0206,'=',0347,0257,'o','8',0354,020,'d',0321,'%',0216,'O',031,'n','a',0203,0375,0306,0305,'E','w',014,0337,'C','U',0336,0231,05,0331,0334,0335,015,'C',0242,0333,'B','w',0267,'Z',0277,0325,0243,0201,'y','p','w',0250,'b',0222,0356,']',0243,0271,0253,'h',026,0332,'!',032,0322,')','s',0201,'J',0241,'F',07,0234,0232,'M',0347,0361,0307,'R',0277,'[','8',0377,0,0362,'5',',',0317,'3','n','s','I','#','F','r',0246,0254,'5',0254,0342,';',0217,0371,0246,011,'p',0230,0373,032,0272,0320,0344,022,0177,'g',0250,'5','a','`',0226,'i',0371,0341,024,'{',07,0347,0316,0134,'I',0201,0264,'q',0222,'A',022,027,'5','w','p',0367,'s',026,'5','m',0246,'C','m',07,':',0344,'f',0212,0363,0244,0304,'b',0231,'J',034,'5','Y','j','R',0332,037,0344,'U',0264,0342,0346,'!',' ',0373,0360,0267,0217,0367,0237,'8',0357,0260,'f',0211,0311,0311,0343,0254,',',0315,'o',0266,'*',0321,0355,'y',0267,031,'a',0332,0265,0353,0242,'1',02,0326,0203,'k',0234,0316,'k','_','X',0327,'n',07,'Z',0263,0322,0344,0236,'E',0334,'=','4',0252,021,'v',0255,'E',037,'0',0371,0331,0244,0336,0334,'o',0356,0305,0234,'%',0376,0365,'c',0253,'G','u',0350,'~',0215,'I',012,'F','I','Q',0336,0265,'r','M',0333,'f',0254,'5','V',0263,'B',0204,'f',0256,'.',036,0366,'m',0315,'V',0221,0362,0340,'U',0240,013,034,012,'D',010,'1',0347,047,0223,'h',0332,'8',0263,04,033,0232,0265,0331,0204,0201,'6',0236,0225,0242,'X',0347,0376,0241,0377,0,0307,015,'W','L','7','_',0335,0217,0352,0243,'e','p',016,0322,0225,0246,'i',014,0254,'&',0237,0204,021,0355,033,0217,0215,'i',0321,'h',0334,0261,0355,0134,0347,0376,'h','N',0343,0357,'Q',0312,'$',0366,0335,0202,014,0232,'b','X',0344,0361,0327,'/',0211,'?',016,0237,0346,0267,023,0320,0326,0233,'w',04,0261,04,'O',0267,0313,04,'{',0216,0343,0343,036,'u','N',0324,0362,'3',0367,0371,'A',0307,'Z',0215,0367,0256,'}',0251,0244,0336,'p','*',0356,0345,'m','b','2','5','[',0353,'3','$',0231,'~',0240,0324,'m',0275,'C','U',0346,0237,025,0330,0353,0336,0256,0354,'%',0264,'>',0241,0322,0221,0332,'3',0271,'M','X','k','Y',0376,0334,0377,0,0363,'J',0301,0306,'W',0202,'!','s',0212,0,'(',0300,0361,'D',0201,0324,0324,0223,0226,0350,0276,0304,017,0265,0261,0354,0317,'&',0321,0264,'Q','8',031,'5',0253,'_','|','T',0273,'W',0351,025,0243,'X','s',0337,0234,0375,0205,'^',0336,'%',0224,'{',0232,0255,'/',0242,0273,0134,0241,0353,'O',032,0312,0273,0134,'U',0376,0212,'W',0327,05,'2',0225,'8','5',0243,'A','$','P',0356,0220,0367,0341,024,'{',07,0212,047,025,',',0246,'C',0370,0366,0221,0267,'(','?',';','0','Q',0223,'L',0305,0216,'M','k','W',0374,0245,0344,'F','z',0232,0265,0267,'k',0251,'D','k','Q',0244,'v',0220,0340,'v',025,0250,'^','5',0344,0333,0276,0337,'j',0320,0354,'N','~','!',0377,0,0307,010,0346,0216,'_',0240,0346,0247,0323,0240,0235,0303,0260,0353,'@','c',0240,0253,'x',0377,0,'y',0361,'w','/',0373,'}',0273,'f',0351,0267,0347,0236,'M',0307,'h',0247,0335,0264,0355,0357,'W',0261,0316,0223,036,'x',0353,'Q',0310,0321,'6',0344,'=','j',0343,'U',0226,0342,036,'S','V',0237,'f','o','&',0333,0366,0244,'A',032,0205,'Z',0326,'/',0276,036,'>','R','}','F',0254,036,'~','x',020,0236,0264,'3',0216,0265,024,'|',0306,0256,0336,'.','o',0367,017,0267,023,'m','q',0363,'M','&',0305,0343,'=',0264,'w','+',0266,'A','W',0332,'D',0226,0336,0270,0372,0257,015,'?','P','k','&',0374,032,0377,0,'T',0200,0300,'f',06,0247,0231,0256,'d','2','7',0336,0264,'{',017,0207,0217,0232,0343,0324,'h',014,0364,025,032,'l',0134,'x',0311,0307,0257,0334,0215,0267,0256,'~','B','p','2','i',0337,'{','g',0344,0357,'W',0372,'2','O',0353,0207,0241,0251,'a',0222,06,0333,' ',0307,015,036,0303,0342,'$',0346,0277,0322,'8','[',0307,0217,'Q',0361,0267,'#',0250,'>',0345,0263,'`',0355,0371,'.','$',0375,0203,0347,0271,0264,0212,0351,'v',0310,'*',']',022,'d',0230,'*','u','S','P','@',0266,0361,0210,0322,0241,0217,'y',0374,'x',0353,0201,0224,0367,01,0332,'s','J','w',014,0216,022,'>',0305,0315,'w',0366,'U','K',034,012,'E',010,'0','<','s',015,0303,036,0354,022,0355,0364,0232,'i',025,'{',0324,0216,'d','9','>',0324,021,0355,033,0217,0220,0231,'v',0271,0375,'<',021,0357,'9','>','F',0345,'{','7',0351,0225,'K',034,012,'U',010,'0','<',0214,0253,0271,010,0375,'4',021,0355,031,'>','N','E',0332,0304,'~',0222,010,0367,034,0237,')','r',0275,0233,0364,'j',0245,0216,05,'*',0205,030,036,'R','U',0334,0204,'~',0216,010,0366,0214,0237,'-','"',0355,'b','?','C',04,'{',0216,'O',0227,0271,'^',0315,0372,05,'R',0307,02,0225,'B',014,017,'.',0353,0271,'H',0375,04,021,0355,033,0217,0231,0231,'v',0277,0275,014,'{',0317,'_','5',':','n',0134,0373,0240,'n','8',024,0213,0260,'c',0315,0310,0233,033,036,0345,0274,'x',033,0217,0234,0232,'=',0353,0323,0333,0212,'=',0347,0361,0347,0246,0207,0367,'/',0262,0220,'3','w',0245,'P',0243,03,0317,0264,'H',0335,0305,'|',':','W',0303,'-','|','2',0377,0,'5',0360,0303,0371,0257,0206,'_',0346,0205,0272,012,010,0253,0330,0177,0360,0311,0377,0304,0,'4',021,0,02,01,02,04,04,04,05,04,02,02,03,0,0,0,0,01,02,03,0,04,021,022,'!','1',05,020,023,'P',024,'0','2','A',' ','"','@','Q',0201,025,'#','B','a','3','C','R','b','p','q',0240,0377,0332,0,010,01,02,01,01,'?',01,0377,0,0300,0317,'*','E',0253,0234,')',0370,0235,0272,0354,'q',0246,0342,0343,0370,0245,036,'-','/',0262,0212,0375,'R',0343,0372,0257,0324,0356,'~',0364,'8',0245,0307,0365,'I',0305,0337,0371,0255,'C',':','N',0271,0223,0277,'O',0304,0241,0213,'E',0324,0324,0274,'B','y','v','8',017,0352,0211,047,'S',0344,'p',0200,'s',0261,0366,0357,0223,0334,'%',0272,0346,'z',0271,0276,0226,0343,'M',0207,0224,01,047,01,'V',0220,'x','x',0202,0373,0367,0273,0253,0245,0266,0134,'N',0365,'$',0257,'3','f','s',0345,0360,0333,'O',0367,0277,0343,0275,0334,0134,'-',0272,'g','j',0226,'V',0231,0313,0277,0227,'c','i',0342,033,'3','z','E','m',0336,0244,0221,'b','R',0355,0265,0134,0334,'5',0313,0346,'>',']',0265,0273,0134,0276,'Q','Q',0306,0261,'(','E',0333,0275,023,0200,0304,0325,0365,0337,0210,'l',027,0322,'<',0270,0343,'i','X','"',0357,'V',0326,0353,'l',0231,'G','{',0342,'7',0231,0377,0,'f','=',0275,0374,0260,'1',0320,'U',0215,0247,0207,0134,'[',0324,'{',0337,021,0274,0351,016,0222,'n','|',0316,035,'g',0227,0367,0244,0374,'w',0273,0313,0241,'l',0230,0373,0373,'S','1','s',0231,0274,0276,037,'g',0326,'=','G',0364,0367,0271,0245,'X','P',0273,'T',0363,'5',0303,0227,'o','.',0316,0324,0334,0276,036,0324,0252,020,'e','^',0364,'H',03,023,'W',0267,'F',0345,0364,0364,0217,'.',030,0232,'g',010,0265,04,'+',02,04,'^',0367,0304,0256,0363,036,0212,'~','|',0260,011,'8',012,0262,0265,026,0311,0257,0250,0367,0273,0373,0276,0202,0344,'_','Q',0363,'8','u',0236,'A',0326,'}',0375,0273,0335,0304,0353,'o',036,'v',0251,0347,',','L',0262,'T','w','X',0350,0325,0277,0223,0303,0354,0372,0315,0324,0177,'H',0357,'l',0301,'F','c','W','w','&',0346,'L','}',0275,0252,0351,']',0266,0333,0224,'S',0264,'u',034,0253,' ',0323,0343,0264,0266,'7','/',0227,0333,0336,0221,'B','.','U',0333,0275,0361,'+',0274,0347,0242,0233,'{',0363,0271,'D',032,0373,0362,014,'W','Q','Q',0134,0343,0243,'|','1','F',0322,0270,'E',0336,0255,0340,'[','t',0310,0275,0357,0210,']',0364,023,'"',0372,0217,'7','|',0213,0215,'1',',','q','5',035,0272,0344,0371,0252,'[','v','M','G','(',0247,'d',0322,0225,0263,014,'y',0330,'Z','x','t',0314,0336,0243,0336,0347,0231,'`','B',0355,'R',0310,0323,'9','v',0347,'u',0376,'>','Q','8','u',0323,0224,0370,'g',0322,0241,0204,0273,'k','[','r',0341,0266,0230,0376,0373,0376,';',0331,'8','j','j',0366,0353,0304,0276,0236,0221,0360,':',0347,0134,')',0224,0241,0300,0322,0271,'C',0210,0243,'t',0344,'a','[',0232,'Q',0200,0303,0225,0215,0257,0211,'|','O',0244,'P',030,'h',';',0337,023,0273,0377,0,'B','~','y',0273,0204,030,0232,'k',0263,0355,'K','v',0177,0225,'2',0245,0300,0322,0236,0335,0322,0262,'7',0332,0240,0267,'*','s','7','(','a','i',0334,'"',0324,'1',',',010,021,'{',0335,0355,0317,0206,0217,'M',0315,023,0216,0247,0226,0325,'4',0235,'F',0245,0201,0330,'b','9','[',0246,'E','2',032,0216,'u',0223,0230,04,0234,05,'Y','Z',0213,'d',0327,0324,'{',0333,0272,0306,0245,0333,'a','W',023,0265,0304,0205,0317,';',0203,0204,'f',0220,'b',0300,'U',0304,0231,06,'E',0250,'"',0352,'5',0134,'6','X',0360,0250,0306,'g',03,0237,015,0264,0313,0373,0357,0370,0357,0234,'J',0353,0252,0335,'$',0330,'|',023,0256,'h',0315,'m','X',0226,':',0322,'4','q','&',0365,',',0246,'S','V',0261,0341,0363,0236,'V',026,0235,'w',0314,0336,0221,0337,'8',0205,0327,'A','2','/',0250,0363,047,012,'{',0277,0233,0345,0250,0344,022,0215,'*','{','r','>','e',0347,014,05,0316,047,'j',03,012,0202,06,0270,'p',0213,'Q','F',0261,' ','E',0357,'s','L',0260,'!','v',0251,'e','i',0234,0273,'s',0271,0227,037,0220,'T','Q',031,015,023,035,0270,0257,030,'>',0325,0222,')',0206,'4',' ',0215,'}',0271,0,'I',0300,'U',0235,0250,0266,'M','w','=',0362,0376,0353,0304,'>','U',0364,0216,'l','r',0251,'4','N','&',0272,0211,014,'x',015,0351,0230,0261,0304,0362,0264,'o',0233,016,'|','6',0323,017,0337,0177,0307,'|',0342,'W',']','5',0351,'.',0347,0340,'q',0231,'H',0242,'0',0345,04,031,0365,';','U',0311,0134,0330,'-','Z','.',0271,0271,'X',0332,'x',0207,0305,0275,'#',0276,0134,'L',0266,0361,0227,'j',0221,0332,'V','.',0333,0236,'m','"',0246,0346,0226,'T','m',0215,'I',02,0311,0255,'-',0252,'.',0246,0245,0270,012,'2',0245,'(','.','p',0250,0323,0246,0270,'T','0',0264,0356,021,'j','(',0226,024,010,0275,0362,0372,0353,0304,'I',0200,0364,0216,'s',0315,0323,032,'o','D',0223,0251,0255,0252,'+',0237,'g',0243,0204,0213,'O','j',0300,0374,0265,014,'"','1',0375,0320,030,0350,'*',0312,0327,0303,'&',0276,0243,0337,'8',0235,0326,'E',0350,0256,0347,0233,'6','Q',0215,';',027,'l','M','$','*',0211,0235,0353,'s',0247,'(',0346,'h',0366,0250,0337,0250,0271,0271,'p',0313,'O',0367,0277,0343,0276,0134,'N','-',0343,'.','i',0334,0310,0305,0233,'~','w','Y',0262,0351,'V',0351,0231,0352,0355,0377,0,0205,'Z','&',047,'5',']',0205,04,'a','Q',0302,0134,0320,030,014,05,'Y',0333,033,0231,'0',0366,033,0320,0,014,07,'|',0276,0271,0361,022,'i',0260,0347,'4',0235,'5',0306,0242,0270,017,0243,'P','@',0272,0212,0237,0374,0206,0242,0234,0305,0245,';',027,'8',0232,0210,'e','@','*','4','i',030,'"',0356,'j',0336,05,0267,0214,' ',0357,0234,'J',0353,0246,0275,'%',0334,0363,'$',01,0211,0253,0246,0315,0206,025,'k',026,'?','9',0345,'q',06,0177,0231,'k','#','}',0252,013,'s',0216,'f',0345,0303,0255,':','K',0324,'}',0317,'l',047,015,0352,'n','%',04,'z',015,'j','N','+','3','z',06,024,'o',0256,017,0363,0244,0342,027,011,0374,0261,0253,'[',0324,0271,0323,'f',0362,0347,0231,'`',0214,0273,'T',0222,'4',0254,']',0267,'<',0356,0245,0304,0344,034,0240,0221,'J',0345,037,017,016,0264,0353,'7','Q',0366,035,0262,0347,0210,'G',017,0312,0272,0232,0236,0352,'[',0217,'Y',0370,'U',0212,0234,0313,'V',0227,036,'&','<',0336,0376,'W',020,0272,0353,0311,0225,'}','"',0244,'q',032,0343,'I','p',0312,'q','4',0247,021,0215,'K',02,0311,'O',033,'F','u',0254,'p',0250,0256,'}',0236,0201,07,'Q',0312,0336,06,0270,0220,'"',0324,'h',0261,'(','E',0333,0265,';',0254,'c','3',035,'*',0357,0210,0264,0337,'$','z',017,'#',0207,'O',0322,0233,')',0330,0371,'<','F',0353,0244,0235,'5',0334,0326,0325,'<',0275,'F',0253,'x',0263,0234,'M','K','(',0210,'T','s',',',0233,'S','(','a',0201,0251,'m',0212,0352,0274,0255,'P',0205,0304,0320,030,0350,'*',0312,0333,0303,'G',0256,0347,'~',0324,0314,024,'f','5','y','v',0327,'-',0377,0,'_','$',034,'5',0250,'$',0353,'F',0257,0367,0370,0345,0221,'a','B',0355,0355,'R',0312,0323,'9','v',0253,0251,'p',0371,05,'"',0227,'8',012,01,'b','Z',0226,'N',0243,'c','V',0261,'b','s',0236,'J',0352,0333,'S',0300,0216,'q',0345,0303,'-','q',0375,0367,0374,'v',0276,'+','>',0,'B','<',0276,023,'.','*','b','>',0337,037,022,0271,0352,0277,'I','v',024,'q',0303,'J','p',0301,0276,'j',07,015,0251,0347,'g',0134,0246,0242,0217,0250,0330,'P',01,'F',02,0256,'e',0312,'2',0212,0217,'6','o',0226,0205,'Z','[',033,0231,'0',0366,0367,0240,02,0214,07,'k',0342,07,033,0226,0362,0354,0245,0350,0316,0247,0342,0277,0271,0360,0361,0340,'7','<',0336,'5',0220,'`','j','X',032,'=','y','E',')',0210,0351,'^','!','2','f',0246,'b',0307,023,'V',0321,'e',031,0215,'*',0227,'9','V',0255,'m',0305,0264,'y',07,'l',0342,'K',0226,0344,0371,0226,0223,'u',0341,017,0360,'3',04,'R',0315,'W','3',0233,0211,013,0237,0206,'[','`',0332,0245,025,'+',0241,0345,'o',026,'s',0211,0345,0303,'-','r',016,0263,0357,0355,0333,'x',0272,'|',0312,0376,'g',012,0233,'+',0230,0217,0277,0301,0305,'.','q','=',05,0374,0374,'r','D',0262,015,'i',0255,0234,'6',02,0221,02,014,05,'X',0332,0370,0211,'1','>',0221,0333,0270,0232,'g',0267,0307,0355,0346,'F',0346,'7',016,'=',0252,'9',04,0250,035,'}',0371,']',0334,013,'h',0363,'{',0373,'Q','%',0216,047,0311,0216,'6',0225,0302,'/',0275,'A',012,0301,030,'E',0355,0322,0247,'Q',012,'}',0350,0214,'4','>','g',016,0274,021,036,0224,0233,'T',0267,'p',0302,'1','f',0253,0233,0206,0271,'|',0315,0345,'p',0353,'^',0222,'u',033,'s',0334,'/',0343,0351,0334,'7',0367,0257,0323,0360,0373,'^',0273,0347,'o','H',0356,'<','^','=',026,'O',0307,0323,'E',033,'L',0341,026,0241,0211,'`','@',0213,0334,'n',0342,0352,0300,0313,0364,0334,':',0327,0242,0235,'F',0334,0367,';',0250,0272,'3','2',0375,047,016,0265,0353,'>','v',0330,'w','N','-',026,0253,'/',0343,0350,0342,0211,0246,'p',0213,'Q','D',0260,0240,'E',0356,0227,0221,'u','`','e',0372,'>',035,'k',0321,'L',0355,0271,0356,0327,'Q','t','f','d',0372,036,035,'k',0326,'|',0355,0260,0356,0374,'Z','-',026,'Q',0377,0,0257,0240,0212,'6',0225,0302,'/',0275,'C',022,0300,0201,027,0273,0334,'E',0326,0211,0223,0350,'8','m',0257,'I',':',0255,0271,0357,'<','B',036,0224,0347,015,0216,0276,'u',0205,0267,0210,0223,026,0364,0216,0365,0304,0240,0352,0303,0230,'n','<',0324,'C','#',04,']',0315,'[',0302,'-',0343,010,';',0335,0334,036,036,'R',0276,0336,'g',014,0265,0310,0275,'f',0334,0355,0337,'/',0355,0274,'D','x',0256,0343,0313,0262,0265,'7',022,'k',0351,025,0267,'}',0342,026,047,036,0264,'_',0237,'#','z',0267,0341,0262,'K',0254,0232,012,0216,'5',0211,'r',' ',0323,0277,0311,'g',04,0272,0262,0321,0341,'P',037,0275,'~',0223,017,0334,0327,0351,021,0377,0,0310,0327,0351,011,0377,0,'*',034,'"','?','v','4',0274,'2',0335,'w',0326,0243,0202,'8',0275,013,0207,0377,0,014,0237,0377,0304,0,'R',020,0,01,02,03,02,010,011,010,06,05,011,011,01,0,0,0,01,02,03,0,04,021,'!','1',05,022,023,' ','"','2','A','Q',020,'#','0','@','a','q',0201,0221,0241,024,'3','B','R','b',0261,0301,0321,'$','C','P','S','r',0202,025,'c',0222,0242,0341,'%','4','5','s',0223,0262,0302,0322,0360,'T','d','t',0203,0224,0243,0263,0342,0361,'D',0377,0332,0,010,01,01,0,06,'?',02,0373,'s',031,0325,0204,047,0246,'(',0313,'j','t',0357,'6',010,0321,'X','h','n','H',0216,'1',0345,0257,0255,0134,0202,'e',0346,025,0214,025,'b',026,'v','}',0266,'T',0265,04,0244,'m','0','Q',')',0375,0241,0370,'F','3',0213,'+','V',0363,0301,0246,0240,0236,0270,0260,0225,'u',010,0321,'k',0274,0305,0210,'H',0217,'G',0272,'5',0207,'t','z',047,0262,'4',0320,017,'T','c',' ',0305,'E',0377,0,'m',027,035,'4',036,0370,0267,'E',0241,'r',07,05,07,030,0256,0210,0326,0304,033,0223,026,0333,0310,'8','v','R',02,'E',0244,0331,0366,0312,0235,'p',0321,'#',0306,'1',0327,'`',0364,'S',0272,'1',0226,'{','7',0305,'5','Q',0352,0216,'J',0202,0370,011,0364,0257,'1',0225,'#','A',0253,'{','v','}',0260,0245,0254,0342,0245,'6',0223,025,0271,0244,0352,0246,'7',0250,0334,'#',031,'f',0247,0223,0313,'/',0362,0302,'P',0201,0214,0245,032,01,011,'h','Z','o','Q',0336,'y',0347,0322,0347,0345,0345,0317,0252,0267,05,'{',0241,'X',0217,0273,'4','F',0306,'Z','?',032,'G',0321,'p','Z',0334,033,0336,'w',027,0334,014,'q',022,'r',0214,0247,0332,012,'Q',0367,0210,0321,0234,'C',03,'s','l',0247,0342,014,'[',0205,']',0374,0241,'#',0341,026,0341,'y',0337,0312,0372,0207,0272,'?',0245,0347,0377,0,0352,'W',0363,0212,0214,'1','=',0377,0,'P',0277,0234,'Y',0205,0237,0374,0306,0276,0370,037,0312,'X',0335,012,'i',037,'(',030,0355,0311,0274,'=',0246,0310,0367,030,031,'|',022,0222,'6',0226,0336,0247,0302,'(',0363,'s','R',0275,'*','F','0',0360,'0',0234,0206,024,'c',030,0330,022,0341,0311,0236,0345,'E','R','B',0206,0361,0312,0344,0232,'<','B',0177,'z','1',0225,0330,'7',0301,'Z',0215,0247,0223,0252,0274,0330,0361,0340,0362,0227,'G',030,0255,'Q',0270,'s',0222,0354,0303,0310,'a',0261,'z',0334,'V','(',0202,0226,0134,'r','}',0301,0367,011,0321,0357,'?',010,0244,0214,0253,'2','i',0365,0227,0306,'+',0345,037,'I',0302,'S',013,036,0250,'^','*','{',0205,0234,0302,0262,0223,'o',0313,'m',0342,0234,')',0200,036,'q',0271,0344,'n','y',026,0367,0210,'J','g',0345,036,0224,'W',0256,0216,'1','?','8',0254,0214,0353,'3',033,'q','R',0255,'!',0331,0177,'"','e','Y','6','z','j',036,0350,'*','Q',0240,021,0214,'n',0330,'9','<','Q','v',0323,01,011,024,02,'<',0241,0321,0305,047,'T','z',0307,0234,')',016,'L',0371,'D',0300,0372,0231,'}','#',0362,020,0244,'`',0366,0221,' ',0337,0256,'t',0327,0362,0214,0254,0334,0313,0263,'+',0336,0352,0312,0271,0260,'R','I','J',0205,0240,0215,0220,0224,0371,'O',0226,0262,'>',0256,'k','K',0306,0370,'J',047,0220,0274,034,0351,0332,'t',0333,0357,0204,0275,'.',0352,036,'i','W','-',0265,'T',034,0323,'.',0311,0343,'N',0261,036,0214,'T',0305,023,0346,0305,0335,'<',0230,'B','E','I',0214,'Q','~',0323,0276,')','s','I',0327,'T',04,'$','b',0244,'X',07,'5',0251,'4',033,0341,'M','J',0237,0322,'3','#','c','G','@','u',0253,0345,012,'K',0263,'>','O','.','~',0242,'_','E','=',0273,0371,0346,'R','B','m',0311,'e','m',0305,'6',036,0261,'q',0204,0265,0206,'e',0361,0177,0336,'%',0305,0235,0251,0371,'@','~','N','a',0271,0206,0217,0244,0331,0257,05,023,'k',0312,0325,037,030,'*','Q',0252,0215,0244,0306,'I',07,'G','i',0337,0312,'U','^','p',0337,0321,011,'i',0261,'U',037,010,'K','H',0331,'y',0336,'y',0252,0232,'J',0274,0266,'t','}','C','F',0357,0304,'v','B',0222,0373,0371,')','m',0222,0355,'X',0236,0335,0377,0,'`',07,0344,0246,0134,0226,'w','z',015,0375,'{',0341,'R',0323,0322,'e','S',010,'M','r',0314,0352,0236,0261,0262,024,0353,0206,0252,'1',0222,'A',0323,'7',0364,'r',0231,'e',0213,'}',021,0,01,'R','v','E','U','k',0313,0326,';',0272,'9',0247,0224,'O','L','%',0224,'l',036,0222,0272,0204,'.','^','G',032,'B','H',0356,'<','b',0372,0316,0316,0316,'T','%',011,'*','Q',0270,010,0342,'p','T',0321,0351,'S','E','#',0274,0306,0224,0232,'X',033,0335,'u','?',010,0265,0351,'$',0365,0270,0257,0362,0306,0226,023,'`',036,0204,023,037,0322,0315,0377,0,'b','~','q','f',024,'g',0373,'#',034,'T',0334,0223,0211,0351,'R',0222,'}',0321,'T','6',0303,0375,015,0273,0363,0244,022,0274,024,0371,03,0356,0350,0277,'t','R','f',']',0331,'r','v',':',0202,0236,'M',014,'2',0234,'g',026,'l',0200,0312,'-','Y',0265,'k',0365,0214,'W',0323,'7',010,'$',0232,0223,0311,0345,026,'4',07,0217,0,0232,'t','i',035,'A',0273,0247,0232,'.','V','G',026,'r','~',0357,0325,0266,'z','w',0365,'B',0246,0247,'_','S',0357,'+','j',0266,'t',015,0334,0222,'[','i',012,'q',0305,0134,0224,012,0223,01,'^','I',0344,0210,'"',0270,0323,'*',0305,0360,0277,0302,'+','?',0204,0226,0263,0352,0313,0247,027,0304,0326,'*','0','z','_','V',0371,0202,'W',0340,'l',0200,0231,'v',032,'a','#','c','h',011,0344,0250,0240,024,'7',030,'V','_',06,'K',0225,'*',0365,0241,'8',0212,0357,020,0243,')','1','1','&',0252,'X','+',0216,0221,0361,0361,0202,0251,'U',0263,'>',0215,0311,'8',0212,0356,'?','8',0304,0235,0224,'v','X',0376,0261,'4',07,0250,0347,04,0244,'c','(',0330,0,0214,'w',05,'f',0334,032,'G',0325,0350,0202,0265,0134,' ',0255,0134,0237,0260,'/','0',0,024,02,'2',0316,0216,'%',';','=','c',0314,0326,0363,0356,'%',0246,0220,'*',0245,0254,0320,010,0134,0226,010,'R',0230,0224,0271,'s',027,'-',0316,0255,0303,0222,'J',0345,0245,012,030,'?','^',0366,0202,'?',0217,'d',05,0341,'9',0225,0316,'+',0356,0332,0320,'G',0317,0335,030,0222,'R',0214,0313,017,0325,0246,0204,0363,'"',0207,'P',0227,020,'o','J',0305,'D',025,'4',0311,0301,0356,0372,0322,0366,017,0331,0272,012,0345,'q','0',0223,'_',0252,0321,'_',0354,0237,0204,')',0267,'P',0246,0234,'M',0350,'X',0241,034,')',0237,0231,'N',0231,0363,'I',';',06,0370,0251,0260,'E',0236,'l',']',0311,0204,'&',0363,01,011,0200,0201,'b',05,0252,'V',0341,011,'m',03,025,')',0260,016,'d',0271,0271,0307,'C','L',0247,0274,0364,010,'(',0266,'_',07,0244,0350,'0',015,0375,'*',0351,0344,'R',0342,0233,0362,031,'S',0365,0257,0213,'O','R','a','+',0310,'y','d',0310,0372,0351,0213,'{',0205,0303,0234,'b','O',0312,'6',0376,0345,0334,0241,0324,'o',0205,'=',0201,0336,0362,0226,0377,0,0331,0335,0261,'}',0206,0343,012,'V',020,'a','l',0241,0205,'P',0264,0342,'h','T',0257,0227,06,'E',06,0317,'K',0223,0240,0264,0305,0276,'p',0337,011,'m',0261,'U',0252,0350,015,0246,0323,0351,'+','y',0346,'K',0233,0234,']',022,',','J',06,0262,0316,0341,05,0371,0223,0212,0332,'|',0323,011,':','(',037,0353,'o','!',0364,'V',0261,'%',0306,0264,0303,0226,' ','|',0341,'.',0255,'>',']',':','>',0271,0321,'b',0177,010,0331,0316,0361,0315,0253,'6','%',';',0341,'N',',',0343,')','V',0223,030,0251,0363,0212,0360,0345,'2',0313,032,'G','T','p','e',0134,034,'z',0377,0,'t','s','%',0316,'M',0256,0211,026,'%',03,'Y','j',0334,'!','S','S','J',0262,0346,0332,032,0255,0215,0303,'=',014,0262,0332,0235,'u','f',0211,'B',05,'I',0204,'M','a',0315,'5','^','$',0322,'l',037,0210,0374,'!','-','4',0204,0266,0332,'E',022,0204,0212,01,0316,0324,0353,0206,0211,'L',027,'W',0330,'7',010,'+','=',0202,'7',0251,'Q','V',0355,0213,'y',034,0242,0306,0200,0361,0340,023,'N',0215,021,0250,017,0277,0231,';','9','6',0274,'F','[',035,0375,02,014,0313,0347,025,0244,0330,0313,';',020,0234,0360,0304,0233,'z','#',']',0345,'j',' ','t',0230,031,'$',0345,0246,0310,0323,0231,'X',0322,'=','[',0207,'=',0311,0266,'x',0204,']',0355,035,0360,'I',0260,010,0257,0242,'.',020,'A',0261,'g',0203,'r',0267,0305,0242,0315,0371,0364,0364,'E',0346,02,'R','(',04,'i','y',0224,'k',035,0375,020,0,024,03,'g','1','q',0367,0226,033,'i',0264,0343,')','j',0270,010,0242,011,'F',017,'d',0361,'-',0357,0366,0217,'N','~','U','u',0226,0301,0311,':','O','m','W','B','a',0271,'I','6','C',',',0243,'`',0333,0322,'y',0351,0225,'h',0351,035,'s',0273,0243,0203,'"',0215,'Q',0177,015,015,0250,0336,'x','(',0241,'Q',030,0315,0332,'7','f',0204,'&',0363,01,03,0264,0357,0205,0314,'<','t','S','p',0336,'w','B',014,0270,0311,'<',0326,0213,0315,'m',012,0337,0324,'y',0222,0260,'<',0222,0376,0212,0321,0343,0326,0237,'M','[',0272,0206,'z','g',047,'B',0232,0301,0211,'=','E',0356,0201,0321,0323,010,'e',0224,'%',0246,0220,'1','R',0204,0212,0,'9',0355,0226,0274,0255,'Q',0361,0202,0245,032,0223,'i','1',0212,0237,'8',0257,016,020,0235,0360,0,0270,'F',0201,0240,021,'C',0242,0256,012,0215,025,'A','I',0331,0303,0214,0257,'8',0257,010,'R',0324,'q','R',0221,'R','N',0310,0321,0262,'Y',0273,033,'O',0306,033,0234,'`',0325,'7','8',0336,0305,0247,'t','3','9','*',0274,0243,'.',0212,0203,0360,0346,036,'E','*',0277,0345,011,0221,0262,0366,0321,0353,'g',0211,0331,0304,0224,0340,0306,0315,0337,'|','w','u','B','P',0204,0204,'!','"',0201,'"',0341,0317,'T',0353,0206,0211,'L',')',0325,0355,0270,'n',020,'V',0250,'+','U',0347,0204,'p',020,'x',022,'U',032,'&',0252,'1','^',014,0262,0307,0341,034,07,07,0313,0253,'@','y',0325,015,0247,'w',017,0221,'M','.',0230,'>','d',0322,0337,0253,'^',0303,0313,0314,'O','L',035,06,0223,'Z','z',0307,'`',0207,0347,'f',0225,0214,0353,0246,0275,']',031,0330,0316,0325,030,'9',0223,0306,0257,0326,0366,'D','6',0313,'(',015,0264,0330,0305,'J',023,'p',034,0373,'$',0331,0342,'Q',0342,'b',0261,'f',0240,0273,'1','*',0335,01,'B','(',0241,'X',0255,0264,0335,033,0200,0205,036,013,'|',0330,0277,0203,'"',0311,0372,'S',0242,0317,'d','o',0315,0375,033,'4',0272,0316,'J',0247,'D',0223,'k',0215,0377,0,016,'[',0364,'|',0272,0253,047,'(',0253,'H',0364,0334,0332,'{','.',0357,0316,'n','N',0134,'P','^',0343,0233,020,0235,0360,0314,0234,0252,'2','l',0266,'(',':','z','O','>',0362,'v',0317,032,0261,'i',0334,'8','2','(',0374,0334,'8',0251,0215,'5','T',0306,0202,0255,0351,0212,033,0267,'E',0370,0247,0246,'5',0204,'b','7','v',0323,0300,020,0230,010,'M',0302,036,0232,'R',024,0264,0266,'.',036,020,0343,0357,'+',031,0305,0232,0346,0313,0317,'K',032,':',0322,0253,0326,'6',0210,0227,0236,0227,'5','i',0324,0327,0250,0355,034,0242,0362,'J',0244,0344,0307,026,0316,0361,0275,']',0231,0315,0262,0322,'J',0335,'q','A',')','H',0332,'a','-','P',031,0307,'t',0237,'p','m',';',0272,0207,'>','S',0252,0277,0321,033,0314,')',0305,0232,0251,'F',0246,',',0327,'7','E','O',05,' ','z',0333,'c',024,0233,'x',022,0312,'w',0332,'b',0267,0247,0177,015,05,0361,'n',0271,0276,02,022,'1',0224,'l',02,027,',',0362,022,0350,'y','4','t',033,0225,0321,013,'b',0325,'J',0271,0246,0303,0233,0323,0273,0254,'g','+',04,'>',0256,'"','g','I',0252,0372,'.','n',0355,0370,'r','u','7','C',0317,'%','U',0225,'k',0212,'`','{','#','o','n','w',0351,0311,0244,'Z','t','e',0222,'G','z',0376,034,0370,0250,0232,01,0266,'*','<',0322,'l','@',0202,0245,'X',04,025,0236,0301,0302,0230,'Q',027,0322,'2',0253,0272,',',0326,'7','@',';',0255,0205,023,0273,0207,',',0261,'o',0242,'8','<',0245,0321,0306,'+','T','n',034,016,'J',0252,0211,'}',':','l',0271,0352,0252,034,'e',0344,026,0335,'m','E','*','I',0330,'s','P',0343,'j','(','Z',016,'2','T','6',030,0227,0234,0263,'+','L','G',0222,'6',',','_',0363,0355,0344,0225,'.',0322,0251,'5','9','V',0323,'K',0302,'}','#',0360,0355,0316,'f','P','T','0','4',0336,'^',0344,'C','l',0262,0200,0333,'M',0244,'%',')','M',0300,'s',0357,'$','l',0332,'u',0376,0134,031,'$',0352,'&',0376,0223,0230,0235,0334,026,013,04,023,0212,'b',0233,'v',0230,0311,016,0336,014,'e','y',0264,0370,0360,'e',0234,034,'J','?','x',0346,014,'9',',',0233,015,021,'2',07,0202,0276,035,0331,0306,'A',0325,'R','Z','w','D','W','c',0233,';',0356,0356,0344,0246,024,0205,'V','^','_',0211,'k',0262,0363,0337,0134,0344,0270,0362,'i','9','7','G',034,0255,0351,036,0212,0177,0326,0376,'|',0247,'=','+',0222,'7',0230,'R',0324,'j',0243,'i','1',0210,0235,'u','x','p',0320,'F',0231,0322,0213,'{',0343,021,0313,016,0376,034,'T',0332,0270,0251,0200,0204,0300,'B','n',020,033,'M',0202,0365,'+','p',0204,0266,0330,0242,023,'`',0314,'z','Y',0364,0343,0262,0352,'J',024,0236,0210,0231,0220,'w',0352,0325,0242,0257,'Y',';',016,'j',0134,'B',0212,'V',0223,'P',0241,0260,0304,0244,0360,0326,'Z','t',0306,0345,0213,0371,011,0247,0320,0254,'W',0334,031,026,0277,021,0371,012,0236,0314,0346,0262,0211,0254,0254,0267,034,0357,'N',0341,0337,0317,0211,'&',0200,'A','P',0363,'I',0261,02,012,0325,0262,012,0325,'y',0341,0312,'*',0375,0221,0355,'l',021,']',0221,'b',0355,0214,'R','c','[',0202,0202,0323,036,0331,0276,02,022,'1',0224,'l',02,02,'o','p',0332,0265,'g','7',0205,0231,'E','^',0225,0321,'v',0233,'[','?','#',0357,'9',0323,'X','%',0303,0242,0360,0313,'5',0370,0205,0376,036,0356,'A',0214,032,0205,'q','r',0251,0306,']',0276,0232,0277,0207,0277,'9',0225,'-','8',0263,'3','|','s',0233,0375,0221,0335,0317,0274,0221,0263,'i',0327,0371,'p','Q',':',0211,0273,0205,'#','y',0200,' ',0225,'h',0246,'(',0221,'A',0300,025,0264,'p',0345,0226,'-',0364,'x','<',0251,0321,0246,0255,'A',0270,'g',0270,0303,0311,013,'i',0304,0224,')',047,'h','1','7',' ',0277,0252,']',022,'N',0324,0354,'=',0331,0262,0223,0315,0353,'0',0340,'U',06,0321,0264,'w','C','o','4',0254,'f',0334,'H','Z','T','6',0203,0234,0343,0316,034,'V',0333,'I','R',0216,0340,'"','j','u',0315,'g',0334,'+',0267,'g','F','l',0244,0252,0223,0214,0315,'r',0216,0376,01,0376,0251,0333,0317,0224,0351,0265,'W','$','o','0',0245,0254,0325,'J','5','&','2','I',0326,'U',0375,'Y',0211,'=','1','Q',0301,0212,0235,'x','*','Y',':','[',0341,'(',0333,0301,'U','y',0264,0337,0323,0301,0225,'p','q',010,0375,0343,0310,0312,'a','v',0323,'j','x',0207,'z',0275,023,0357,0357,0316,'m',0225,032,0271,'(',0242,0311,0256,0353,0323,0340,'i',0331,0234,0372,022,'h',0344,0332,0203,03,0253,'o',0200,0316,0232,0302,0213,'N',0223,0352,0311,'7',0370,'E',0376,'>',0356,'}',0242,'x',0244,'X',0237,0234,025,0230,'*','U',0347,0207,'E','5',0215,'$',0305,'5',0223,024,'H',0244,'c',0273,0335,025,'6',01,05,'F',02,023,01,011,0270,'B','Z','G','i',0334,'!','-','6','(',0224,0362,'3',0222,'*',0247,034,0331,011,047,'b',0266,036,0370,'[','k',030,0253,'I',0305,' ',0354,'9',0256,0311,0250,0321,023,0215,0331,0370,0223,'h',0360,0306,0316,0222,0220,04,0321,0206,0313,0212,0353,'W',0377,0,'<','s','R',0204,014,'e',0250,0320,01,0266,'$',0344,'S',0365,'-',0204,0236,0223,0264,0367,0363,0337,047,'A',0343,034,0277,0240,'p','Y',0250,0233,0270,'j','u',04,'P',012,010,0266,012,0233,0356,0212,0334,0241,032,'z','*',0215,0310,0335,0301,'n',0271,0276,02,'R','*',0243,'`',021,'C','k',0252,0265,'g',0223,0231,')','M',032,0232,0343,0323,0333,0177,0215,'s','d',0247,'A','#','"',0352,'V','i',0273,'o',0204,05,013,'A',0264,'f',0341,'7',0366,'e',0212,07,'R','t','G',0273,'6','E','%','8',0315,0262,'r',0352,0374,0267,'x',0323,0236,0255,0325,0352,0244,'B',0335,'^',0262,0214,'d',0223,0254,'o',0341,011,033,'`','$','@','m',0236,0370,0264,0366,0360,'[','~',0370,'(',0255,'x','2',0313,0374,0274,036,'V',0350,0264,0352,017,0217,')',047,'>',0224,0325,'R',0356,'b','(',0373,'*',0376,' ','w',0347,'`',0307,'I','%','I','o','$',0252,0337,'T',0350,0374,'3',047,'f',0317,0324,'2',0247,';',0204,'T',0332,'s','p',0216,021,' ','V',0306,022,'|','U',0376,036,'{',0344,0350,'<','[','w',0364,0230,'+','=',0202,012,0225,'i','<','5','Q',0267,'d',032,'^','`',0270,'{',' ','6','6',0302,0252,'t','v','B',0255,0322,0335,04,0233,0314,'{',02,0370,0240,0272,'*',0261,0304,0243,'[',0247,0243,0225,0302,'2',0264,0306,'R',0232,'%',03,0332,026,0217,021,0235,'=','$','U','U','0',0350,'X',035,012,037,0372,0234,0311,0260,025,0212,0247,0224,0206,0207,'}','O',0200,'9',0322,'U',0,'-',0372,0274,'i',0322,'l',0360,0247,'-',0244,0342,'S',0326,'c',0371,0313,'_',0266,'#',0371,0313,'_',0266,'"',0250,'P','X',0350,'<',0236,0211,0343,'W','b','~','|',026,'j','&',0356,032,'z',';','c',031,026,0246,0,'Q',0255,'!',020,015,'h','c',024,']',013,'=','0',022,0233,'I',0200,0201,0332,'a','-','6','4',0225,011,'i',027,015,0273,0371,'i',0371,'L',0134,'P',0323,0312,'J','G',0263,'[','<','3','f','e',0375,07,0245,0311,0355,04,'|',0316,'f',014,0225,0373,0307,'T',0357,0354,0212,0177,0213,':','R','U','6','%',0226,0222,0330,0354,034,0236,'3',0316,04,016,0230,0244,0263,'_',0231,0317,0224,'i',0276,0252,'n',026,014,0314,'f',0324,'P',0255,0342,'2',017,'y',0355,0212,0365,0271,022,0245,032,'$','Z','L',')',0317,'F',0344,0216,0210,0311,047,'Y','W',0365,'p',0320,'Z','a','x',0302,0206,'2','i',0274,0337,0301,0210,0273,0267,0305,'q',0205,' ',0241,0263,'S',0277,0203,'(',0241,0246,0257,016,014,'u',0216,'=','w',0364,'t','r',0357,0257,0375,0241,0264,';',0341,0213,0376,034,0334,026,0262,0254,'P',0247,'2',0177,0264,010,0370,0346,'H','K','}',0333,030,0377,0,0264,0257,0375,'s','p','[','4',0306,0306,0231,'n',0243,0243,032,0336,'H',0251,'j',011,'H',0274,0230,'(',0224,037,0363,025,0360,0214,'w',024,'V',0255,0347,0202,0303,0224,'>',0314,'h',0204,0243,0306,'<',0351,0215,'|','n',0205,'E','5','W',0272,022,0264,0232,')','&',0240,0303,'n',0246,0345,012,0362,02,'U',06,0325,'Z',0276,0250,'+',';',' ',0255,'W',0236,034,0251,0274,0335,0300,0245,0233,'A',0315,0312,'+','Q','>','<',036,'T',0350,0320,'N',0240,0336,'w',0363,014,025,'1','K','V',0205,0240,0236,0242,'?',0315,0233,'"',0372,0215,022,0323,0350,'Y','=','J',0314,'u','5',0363,'M','!',036,025,0370,0346,0340,0373,'*',021,0216,0263,0373,047,0221,0306,'p',0333,0261,'"',0363,032,'f',0210,0330,0201,'p',0340,0242,'t',0327,032,'j',0263,'p',0273,'4',021,'a',020,025,0351,'m',0207,030,'7',0240,0343,016,0254,0365,0274,0273,0223,0343,013,'u','f',0252,'Q',0251,0214,'T',0352,'&',02,'D','P','h',0221,0266,012,'o',0244,'o','N',0350,0321,'=',0221,'l','c','7',0335,024,'"',0207,0200,' ','v',0230,011,'M',0200,'@','E',0315,0213,'V','z','!','(','H',0242,'E',0200,'s',011,027,'}','Y',0234,'^',0364,0237,0226,'s',013,0365,0220,017,0207,016,024,'>',0322,'G',0356,047,'5','g',0325,0226,'Y',0361,'O',' ',0134,'U',0246,0344,0247,'y',0205,':',0351,0252,0214,025,'(',0320,010,')','o','E',036,047,0220,03,0321,']',0220,0322,0275,025,'h',034,0377,0,047,'A',0342,0333,0277,0244,0306,'M',':',0352,0360,021,'H',0366,0215,0361,0212,'5',0214,'S','f',0370,0266,0324,0357,0212,0244,0320,0306,'+',0226,035,0374,030,0240,']','y',0212,'G',0266,'o',0204,0241,03,031,'J','4',02,03,'b',0325,'^',0245,'o','<',0305,0343,0352,':',0205,'x',0323,0343,0235,0202,'V',0243,'U',031,'V',0352,0177,'(',0341,0302,0337,0327,'f',0316,'9',0352,0312,0221,0336,0244,0374,0271,05,020,'x',0264,0350,0242,011,'6',01,033,0233,027,016,'I',013,0336,'!',0247,'v',0221,'o','^','i','P',0363,0212,0261,020,0247,027,0262,024,0265,'^','c','*',0241,'f',0310,'*',';','#','y','0',023,0267,'l','d',0205,0346,0376,015,'$',0322,012,'A',0340,0313,'+',0362,0360,'y','K',0203,0214,'V',0250,0334,'9',0226,020,0353,'o',0377,0,'"','s',0260,'G',0374,':','}',0334,'8',0134,0233,'>',0224,0340,0361,0315,0302,'G',0365,')',0367,0347,0314,',','X','B',015,'8',022,0310,0333,'i',0344,0326,0336,0353,'D','9',',','v',0351,0247,0343,0231,'X','*',036,'m','6','"','2','i',0325,'O',0276,06,'5',0320,'1',016,0214,'P',0332,' ',0254,'A','V',0335,0220,'T','o','1',0224,'V',0250,0203,0217,'t','Y','t','S',0321,027,0230,0,'X',04,'c',0254,'q','(',0277,0244,0356,0346,'x','N',0246,0226,' ',0377,0,0334,'N','v',011,0376,0240,'p',0341,'a',0372,0362,'s','g',0321,0276,'^',0277,0274,'>','y',0363,'#',0330,'<',016,'r','h',';',015,0206,033,'x','^',0223,'X','J',0323,'j','T','*','8','|',0235,07,'M',0313,0372,04,'Y',0256,0253,0270,'j',0223,024,':','*',0340,0266,0370,010,0245,0233,0340,'$',0134,'#','&',0223,0242,' ','$','Z','L',04,0355,0332,'a','-','"',0365,'x','B',032,'E',0311,0361,0346,'x','G',0376,'_',0376,'D',0347,'`',0217,0370,'t',0360,0341,'Q',0372,0300,0177,'t','f',0274,'=','i','E',0217,0336,'N','z',0222,'v',0212,'A','I',0274,'Y',013,0351,0267,0224,'B',0266,0355,0203,'.',0243,0246,0335,0335,0134,013,'u','w',015,0233,0341,0307,0335,'6',0233,'L',025,0236,0301,0233,0212,0345,0243,'|','U','&',0243,0203,024,'k',036,014,0262,0257,':',0274,030,0353,034,'r',0357,0350,033,0271,0244,0330,0365,0226,0330,0375,0341,0235,0201,0307,0373,0253,'g',0367,'x','g','U',0261,0324,0266,0261,0373,' ','|','3','e',0223,0367,0310,'[',0177,0273,'_',0207,' ',0370,0330,0243,0215,0337,015,0257,'x',0247,'(',0246,0217,0245,'h',0204,'<',0213,0306,0315,0361,0227,016,0,0215,0265,'7','E',023,'c',010,0325,033,0372,'c','"',0237,0315,0237,'T',0236,0310,'*','6',021,0262,012,0214,'T',0352,'&',0376,017,')','p','h',047,'T','o','<',0325,0206,0307,0326,'M','$',036,0254,'U',0177,014,0351,'&','@',0240,'m',0224,'&',0235,'C',0206,']',0332,'h',0271,',','-',0351,012,'?',0303,'7',04,0271,0372,0364,0243,0366,0264,'~','<',0203,016,0357,030,0246,011,0365,'M','y','D',0254,'^','!','+',027,036,02,0257,'K','d','T',0337,0310,0204,'&',0363,01,011,0200,0330,0261,'7',0251,'[',0204,'%',010,024,'J','E',0,0346,0270,'&','_','a','.','8','|',0,0367,0234,0326,'X','M',0212,'u','a',03,0264,0300,02,0341,0303,0202,'f',0,0321,0343,020,'O','u','>','9',0254,0276,0215,'v',0226,026,':',0304,'!',0304,0334,0241,'Q',0236,0263,0265,0275,'8','R','7',0212,'E','9','L',0232,0316,0201,0270,0356,0212,0225,0216,0241,030,0307,0260,'n',0344,0262,0212,0327,'W',0200,0200,0224,0212,0250,0330,04,04,0375,'b',0255,'Y',0346,0315,'1',0215,'T',0261,'.',0221,'M',0304,0222,'~','Y',0270,'%',0241,0376,0320,0225,0366,'$',0327,0341,0231,0226,0333,'.',0372,'W',0337,0243,0361,0316,0301,'n',0326,0244,'2',033,'5',0336,0235,037,0206,'z',0220,'n','P',0241,0205,0266,'o','I',0244,'/','q',0267,0233,0343,'+','Q','>','<',036,'V',0340,0376,0254,'|','y',0276,024,'x',']',0226,'(',037,0227,'G',0341,0232,0207,010,0376,'n',0312,0334,037,0335,0377,0,026,'f',023,0227,011,0307,'Q','a','E',')',0366,0205,0243,0304,'g','M',0311,0225,'U','R',0357,'c',01,0271,'*',037,'0','y',02,0275,0216,014,'h','m',0316,0316,'l',020,0233,0314,04,'&',0341,024,'>','i','6',0254,0300,0,'P',015,0234,0332,'n','m','z',0254,'4',0247,';',0204,025,'(',0343,'(',0332,'I',0315,0302,0223,0207,'T',0342,'2',0237,'y',0370,'f','P',0335,023,0262,0224,'#','"',0362,0220,'+',0272,0266,'f',0371,'9','U',021,'6',0331,'E','=',0241,'h',0367,036,0376,'A','.',0213,0333,'>',06,026,0235,0264,0250,0346,0330,0352,0327,'W',0200,0204,0266,0201,'U','(',0320,010,'K','B',0323,'z',0216,0363,0315,0324,0300,'$','.','m',0304,0267,'f',0355,'c',0356,0361,0316,0225,'%','8',0253,0230,'*','x',0366,0233,'<',0,0315,'3',0,'h','M',0266,034,0257,0264,',','>',0341,0337,0233,'+','8',0336,0273,016,'%',0301,'m',0364,'0',0323,0355,034,'f',0334,'H','Z','H',0332,016,'{',0215,033,0226,0232,'A','I',0260,0213,'!','i',0331,'[','9',0246,':',0265,023,0343,0301,0345,'N',015,'5','j','t',016,'p',0314,0212,'M','Q','(',0335,0277,0211,'V',0373,0261,'s','Z','a',0261,'W',034,'P','B','G','I',0211,'y','V',0354,'m',0226,0322,0330,0354,031,0262,0363,0311,'N',0224,0253,0264,'Q',0366,'U',0374,'q','s',0220,0302,0215,'^',0223,'V','H',0376,033,0323,0362,0354,0344,026,'F',0253,0232,'p',0207,'?',')',0346,'a',011,0274,0300,'B','n',021,0214,0261,0304,0243,'[',0247,0243,0234,':',0373,0252,0304,'i',0244,0225,0251,'G','`',021,'5',':',0346,0263,0356,025,0365,'t','f',0312,0222,'*',0334,0265,'_','U','z','.',0361,'#',':','r','I','W','>',0331,'E','w',035,0206,026,0332,0306,'*',0320,'q','T',016,0303,0232,0231,'u',0252,0214,0316,0247,'$',0177,027,0243,0362,0355,0344,020,0360,0275,0263,'o','Q',0205,0215,0267,0216,'g',0216,0255,'u','x','B',032,'l','U','J',0204,0264,0213,0206,0335,0347,0234,011,'4','*',0217,'N',0253,023,0362,015,'o',0200,0355,0316,0232,0302,'*',032,'S','+',0304,'G',0341,'O',0361,0257,'v','{',0353,'J','h',0314,0337,036,0233,'6',0235,'o',037,'~','j',034,'m','X',0253,'A',0306,'J',0206,0303,022,0223,0311,0326,'q',':','c','r',0205,0376,'9',0356,'4','}','1','H',')','U',0204,'X','a','i',0331,0263,0230,0343,0253,'Q','>','<',031,'g',07,032,0347,0200,0347,'3',012,'B',0253,'/','/',0304,0265,0331,'y',0357,0315,'C','M',0247,031,0305,0220,0224,0244,'m','1',047,'"',0217,0251,'l','$',0235,0347,'i',0357,0317,023,0210,'M','^',0222,'V','=',0336,0201,0326,0370,036,0314,0347,'p','C',0312,0342,0346,'t',0332,0350,'X',0277,0274,'{',0271,02,0261,0252,0356,0227,'n',0330,'C',0275,0207,0230,04,'&',0363,01,011,0331,031,'U',0216,'%',0277,023,0316,'^','[','j',0244,0323,0374,'K','=',04,0336,'{',06,'r','f',026,0232,0263,'$',0234,0251,0374,'^',0217,0317,0263,0220,'q',0227,'R',026,0323,0211,'(','R','N',0320,'b','n','A',0177,'R',0272,'$',0357,'N',0303,0335,0232,0324,0303,'*',0304,'u',0245,05,0245,'[',0210,0211,'i',0346,0254,0307,032,'i',0365,'U',0264,'g',0345,06,0263,'F',0275,0220,0264,'o',034,0303,'(',0255,'e','{',0241,015,'#','Y','P',0206,0221,0252,0236,'r',0260,0322,0253,')','+',0305,'5',0323,0353,'+',0375,'n',0316,'i','N',047,026,'b','o',0216,'_','W',0242,';',0271,031,'|','2',0322,'m','o',0211,'{',0253,0321,'=',0366,'v',0347,034,036,0372,0251,')','8','h',011,0364,0134,0331,0337,'w','v','z',0220,0253,'R',0241,'C',013,'i','W',0244,0322,016,0345,'[',0313,'T',0352,'&',0376,014,0252,0307,034,0347,0200,0347,'&','Y',0225,0322,'v','l',024,'"',0227,0245,'>',0222,0263,0245,0345,0210,0254,0272,'8',0327,0277,0,0371,0335,024,027,'r','/',0312,'>','1',0231,'y',05,012,0211,0231,027,0374,0343,013,0305,0353,0334,'s',0202,036,'U','g',0245,0250,0207,'}',0255,0312,0317,'D',0300,026,'/','E',']','q',0214,'5',0221,'o','*',022,0233,'I',0200,0201,031,'g',07,024,0337,0211,0347,'/','M','L','/','&',0313,'I',0306,'R',0241,0371,0347,'l',012,'4','m',036,0242,'6',014,0357,'+','u','4',0231,0235,0323,'5',0330,0217,'D','|','{','y','4','a',0226,023,0306,0261,0240,0375,'6',0243,'a',0354,0316,'f','y',0213,'q','l','Z','=','t',0355,020,0314,0344,0262,0361,0331,'u','8',0311,'9',0316,'5',0264,0335,0327,05,'*',026,0334,'D',')',';','/',034,0246,'Y','W',0233,0241,015,'"',0363,0267,'t','!',0246,0305,022,0236,'s',0372,'"','Q','u',0225,'a',0134,'r',0222,'u',0327,0273,0263,0337,0234,0323,'k','M','e',031,0343,037,'=',033,0273,'~','q','A','`',034,0233,0214,0274,0200,0343,'N','$',0245,'I','7',021,017,'J','*',0245,0235,'f','V','}','$','g','y',024,0343,0237,0311,0317,0233,0317,0325,'+',0177,'V','~',']','#','A',0333,0372,0342,0315,'t',0335,0311,0333,0250,'/',0340,0307,'X',0343,0234,0277,0240,'n',0347,'>','G','(',0277,0345,027,0305,0377,0,'t',0237,'[',0257,'t','T',0337,0232,022,0220,'T',0243,'`',03,'l','!',0267,0,0362,0307,0270,0307,0317,'N',0356,0316,'U','Y','$',0375,':','^',0253,'d',0357,0336,0236,0330,' ',0212,021,0260,0347,'7',0201,'p',0202,0364,0305,0222,0317,'+','o',0260,'~',031,0313,'e','[','n',';',0214,')',0247,05,024,0233,' ',0272,0330,0257,0254,'9',032,0257,0213,'O',0214,04,0240,'P','@',0231,'t','q','i',0325,07,0322,'<',0344,0276,0345,027,'2',0273,031,'g',0326,'?','(','z','n','e','y','G',0335,'V','2',0225,0235,0372,'b','e',037,'G',0227,'4','d',021,0254,0275,0375,0234,0277,0351,0231,'D','R',']',0343,0307,0245,'>',0212,0375,'n',0337,0177,'^','p','R','M',024,'-',04,'B','0','v',020,'X','N',021,'@',0320,'Y',0372,0341,0376,'l',0354,0243,'v','>',0237,0336,0202,0225,02,0225,013,0301,0212,0251,0261,']',0342,'=','!',0333,032,0313,0215,'u','G',0234,'1',0256,0250,0271,'J',0353,'1',0240,0200,0230,0342,0232,'R',0206,0375,0220,027,'2','r',0212,0365,05,0321,'A',0316,027,'7','6',0256,0204,'6','5',0226,0255,0302,034,0234,0232,'6',0233,022,0201,'r',023,0270,'g','3','$',0315,0211,':','N','9',0352,'#','i',0206,'e','%',0221,0223,'a',0244,0342,0244,'r',0356,0313,0276,0200,0343,'.',0247,025,'I',';','D',')',0205,'U','r',0316,'i','0',0357,0254,0237,0230,0316,'K',0215,0250,0241,0304,0232,0245,'I','6',0203,011,0220,0237,'!',0254,'$',06,0212,0266,'=',0374,'z','3',0270,0346,0202,0316,0375,0261,'a','q',035,'J',0217,'<',0347,0204,'y',0365,'w','G',0363,0223,0373,021,0374,0353,0376,0337,0361,0213,'f',024,'z',0223,032,'E',0305,0365,0230,0320,'a','5',0336,'m',0347,'J',0232,0233,'_','B',033,032,0313,';',0204,031,0251,0243,'@',',','m',0241,0252,0330,0335,0234,0333,',',0240,0270,0353,0207,025,')','M',0344,0300,'B',0250,0251,0347,0264,0237,'p',0177,'t','t',016,'b',0344,0233,0332,'+',0326,'i',0332,'j','+','|',';',047,'6',0336,'M',0346,0315,017,'O','H',0316,'J',0320,0242,0225,'$',0324,'(','^','!',0254,037,0205,0226,033,0233,0325,'n','`',0352,0271,0327,0270,0375,0215,0224,'|',0345,'&',027,0346,0245,0323,0254,0257,0220,0205,'M','N','.',0246,0344,' ','j',0240,'n',031,0351,0302,0363,0355,'R','i',0301,0304,'6',0257,0253,'N',0376,0263,0314,0361,0232,0243,'x','E',0221,0305,'/',0326,0366,'L','8',0313,0310,'-',0272,0331,0305,'R',025,'x','9',0355,0310,0341,'u',027,0245,'n','D',0305,0352,'o',0257,'x',0204,'<',0313,0211,'u',0245,0212,0245,'h','5',04,'}',0206,0271,'I','<','Y',0254,'%',0273,0320,'k',0361,'|',0241,'s','S','o',')',0367,0327,'z',0225,0236,0214,'/',0204,033,0372,'*',015,'X','i','_','X','}','n',0256,'j','g','d',0322,021,0204,0333,037,0333,015,0335,'p',0244,'-','%',013,'I',0242,0222,0241,'B',016,0177,022,0254,0264,0241,0326,0226,'p',0350,0236,0255,0306,'1',0345,034,0243,0311,0327,'a','v','-','?','`','-',0371,0227,0220,0303,'(',0275,'k','4',020,0271,'L',014,'U','/',',','l','T',0315,0313,'_','V',0357,'|',022,'M','I',0333,0236,0234,'!','>',0202,0234,034,0203,0242,0223,0365,0307,0345,01,')',01,')',02,0200,015,0234,0331,'S',0362,0,'7',0204,0200,0322,'M',0301,0357,0343,0323,013,'i',0324,026,0334,'A',0242,0222,0241,'B',016,'z',037,'a',0305,'2',0362,015,'R',0264,032,021,010,0226,0303,0211,0350,023,'m',0217,0357,017,0224,'%',0371,'w','P',0373,'*',0271,'h','5',07,0236,0255,0226,'U',0345,0323,0243,0352,0233,':',')',0374,'F','2',0263,0317,0343,01,0252,0322,'l','B',':',0207,' ',0211,0351,0364,0226,0360,'p',0325,'M',0305,0357,0341,010,'i',0244,06,0333,'@',0242,'R',0221,'`',034,0340,0314,'K',0342,0261,0204,0222,',','^',0307,':',025,0363,0207,'%',0246,0331,'S',017,0242,0305,'!',0134,0206,'V','F','a','M',0372,0315,0233,'P',0256,0261,011,'g',011,0243,0310,037,0373,0301,'k','G',0345,011,'q',0245,0245,0306,0325,'h','R',015,'A',0347,'*','C',0257,'y','D',0320,0377,0,0363,0261,'j',0273,'w','B',0232,'B',0274,0202,'P',0375,'S','&',0323,0326,0256,'E',0274,'!',0205,0320,'[',0225,0326,'n','X',0336,0347,'^',0341,01,010,01,')',026,0,'6','s',0254,'G',0206,'J','e','>','j','a','"',0324,0374,0304,031,'y',0306,0350,016,0243,0251,0324,'X',0350,'<',0216,'4',0214,0332,0331,033,'[',0275,07,0262,022,0336,026,0225,',',0253,0357,0245,0355,'O',0354,0377,0,0366,'2',0222,'3','m','L',015,0311,'6',0216,0261,0263,0232,')',0307,0234,'K','M',0246,0365,',',0320,010,'R','%','J',0260,0213,0343,'c','V','#',0366,0276,'U',0205,' ','=',0344,'R',0347,0352,0245,0354,0357,'U',0374,0212,032,'e',012,'u',0325,0232,'%',010,025,'&',032,0237,0302,0351,016,'M',015,'$','K','^',0226,0372,0367,0236,'z',0271,'Y',0306,'R',0363,'*',0330,'v','t',0210,0134,0314,0246,'4',0346,017,026,0343,015,'v',0377,0,020,0370,0362,'I','q',0247,024,0323,0211,0271,'h','4','"',022,0227,036,'L',0373,'C',0321,0230,025,'?',0265,'|',04,0317,'J',0275,'$',0255,0252,'O',030,0217,0237,0204,017,'$',0302,014,':','M',0270,0230,0324,'W','q',0267,0226,'+',0232,0231,'j',']',033,0335,'X','L',024,0264,0353,0223,0313,033,030,'E',0235,0346,012,'0','|',0263,'R',')',0365,0325,0306,'/',0345,030,0363,0323,'n',0314,0233,0364,0325,'`',0352,033,'9',',',0204,0223,'8',0324,0327,'u','V','!',035,'f','1',0323,0364,0211,0345,012,'.','a','C',0301,';',0276,0300,0134,0326,014,')',0222,0233,'6',0226,0376,0255,0177,0345,0205,'K','N',0313,0256,']',0321,0261,'[','z',0267,0362,0211,0362,'L','#','0',0312,'S','`','F','=','S',0335,'t',0,0342,0345,0346,0372,']','j',0337,0335,0244,01,'1',0202,'E','6',0251,0247,0276,024,0200,035,0226,0234,'g',0247,025,'$','{',0342,0331,0267,021,0326,0312,0276,'Q','f',023,'H',0374,'M',0254,'|','#',0372,'Z','_',0276,'-',0302,0255,'~','T',0250,0374,'"',0311,0365,'+',0251,0205,0374,0243,0213,023,'S',07,0330,'n',0236,0363,037,'F',0301,'n','9',0375,'k',0241,'>',0352,0307,0321,0345,0245,'e',0321,0322,012,0317,0276,010,'w',011,0274,0224,0356,'g',0213,0376,0354,025,0270,0265,'8',0263,'z',0224,'j','O','&',022,0221,'R','l',0,'B','&','p',0266,'4',0234,0265,0341,0217,0254,'W','_',0253,010,0226,0223,'e',',','0',0213,0222,0237,0260,0214,0274,0364,0272,'_','o','e','o','O','H',';','!','o',0340,0245,031,0351,'{',0362,'G',0316,0247,0374,0320,0244,'-','%',013,'I',0241,'J',0257,037,'`',0205,'0',0326,'F','W','l',0313,0266,047,0263,'|',07,022,0237,'*',0235,0333,'0',0350,0273,0360,0215,0237,'c',023,'7','/',0212,0375,',',0230,'k','E','c',0347,0333,012,'v','K',0371,'J','X','}',0330,0343,07,0345,0333,0331,05,'*',05,'*',026,020,'v','s',0321,0344,0222,0304,'3',0367,0356,0350,0267,0337,0267,0262,022,0354,0367,0362,0224,0300,0330,0261,'F',0307,0345,0333,0333,01,')',024,02,0340,'>',0311,0372,'l',0242,026,0346,0307,'S',0242,0261,0333,012,'s',05,'L','&','m',037,'r',0366,0212,0373,0356,'>',021,0222,0235,0225,'v','Y',0177,0254,'M','+',0325,0277,0234,04,'!','%','k','U',0201,')',025,'&',022,0267,'Z',030,'=',0203,0351,'L','k','~',0315,0375,0364,0204,0270,0362,'?','H','L',017,'M',0375,'Q',0324,0230,0240,024,03,'`',0373,'5','M','L','2',0207,0332,'U',0350,'q','5',020,'U','.',034,0301,0356,037,0271,'5','O','q',0370,'A','T',0242,0232,0302,015,0373,07,021,'}',0307,0347,030,0263,0222,0217,'K',032,0323,0215,'A',025,0346,'A',0266,0220,0247,034,'U',0311,'@',0251,'0',010,0222,0362,'V',0317,0247,'2','q','<','/',0360,0200,0274,'%','8',0271,0223,0367,'l',0214,'D',0367,0337,0356,0212,'H',0311,'5','.','n',0307,03,'H',0366,0337,0366,0231,'J',0322,026,0223,0261,'B',0261,'U',0340,0326,0332,'V',0366,'8',0277,'t','V','K',010,0274,0311,0334,0362,'B',0307,0205,'!','Y',07,'e','f','S',0262,0213,')',047,0274,'E',0134,0301,'S',07,0372,0241,0224,0376,0355,'c',0351,022,0257,'1',0375,'k','e','<',0206,'#','H','S',0213,0365,'R','*','`','%',0254,025,'6','z','K','$',016,0363,032,'l','5','+',0375,'s',0243,0341,'X','I',0235,0302,'i',033,0320,0303,'u',0361,'?','(','I','q',0207,047,026,'6',0276,0347,0300,'P','F','$',0244,0253,'2',0311,0334,0322,02,'~',0334,'!',0351,'9','w','A',0365,0332,06,'-',0300,0362,']',0214,0244,'G',0364,'L',0277,'`',0212,0376,0213,'o',0261,'J',0371,0307,0364,'S',035,0265,'1','f',010,0224,0355,'l',030,0342,'p','l',0243,'_',0201,0204,0210,0242,022,022,':',07,0330,'_',0377,0304,0,'*',020,01,0,01,02,04,04,07,01,01,01,01,0,0,0,0,0,01,021,0,'!','1','A','Q','a',' ','q',0201,0221,020,'0','@',0241,0261,0301,0360,0321,'P',0341,0361,0377,0332,0,010,01,01,0,01,'?','!',0377,0,'s','q',0134,0361,0251,'?',0310,'6','t',0253,0261,037,0313,'5','3',04,0344,0311,0333,0217,012,0221,'w',030,0202,0321,0377,0,'l','"','4',0257,01,'X',017,'d',0376,037,0332,'t',0355,0233,'>',02,'H',0333,0253,033,'_',0326,'5',0236,035,0341,0245,'{',0324,0264,0276,0212,'?',0360,'4','?',0262,0202,'H','n',0232,0274,0341,0231,0231,'H','I',02,0342,0177,0265,013,'V',0300,'b',0264,'*',0345,0314,0276,0261,0273,0253,0340,0223,'d',0313,017,'z','m',0216,0214,0367,0244,0231,')',0315,0362,'6','j',036,0177,0246,0214,0331,'p','5',0177,0331,0321,06,'s','Z',024,0251,0241,0260,0340,'j',0304,032,014,'h',0223,'/',0321,0177,')',0300,'%','0',05,'j',0332,0356,0364,0347,0250,0356,0177,0211,0351,0376,0300,0345,024,0214,0253,032,0255,0276,0347,'z',0220,0276,0353,0247,0315,0177,'.',030,'.','G',0346,0223,',',' ',0315,0250,0244,0301,'=','O','X',0304,'=',0300,'g',0312,'R',0326,033,']',025,'=','b',0242,0246,'|','#',':','|',0212,0375,031,'J','`',0366,0251,026,0326,'_','y','C',0332,'~','[','S',07,0337,0206,0342,0224,0240,013,0225,'5','c',0263,'L','n',0362,'>','&',0227,0244,'L',0334,'~','t',0254,021,0214,0263,0331,0240,034,0272,0342,0272,013,0371,0251,'"','t',0376,0354,0373,'T','*','K','s',0256,0226,032,',',0323,0202,'H',0371,0200,025,'`','1','Z','D',0210,0370,0231,0365,0345,'N',0257,'8','j','*','p','^',0327,0226,0221,0327,'o',0273,'J',0,0,'@','e','Q',020,0316,0357,'{',0233,0352,'J',0305,0310,03,0325,0245,0350,0220,0226,0235,0241,':',0252,'M',0262,'I',035,0224,0303,0330,0323,0311,0371,013,0233,0267,0354,0364,023,0220,0314,0257,016,0360,0336,0227,0221,0267,021,035,010,'}',0346,0226,'U','Y','P','w','p',0354,032,'*',0302,05,023,0230,0354,0352,'y','2',0255,0373,'g','6','}',0324,'o',0224,0255,':',0260,'m',0246,'y','f','-',0342,0323,'*','7','J',012,0213,0277,0356,0376,' ',0365,032,011,0357,033,0242,0374,0215,'H',0277,0220,0271,0234,0322,035,0236,'u',0210,'S',0210,0204,0351,'8','r','=','2',037,'y','8','V',0243,'N',0246,0344,'[',0267,0366,0215,0252,0304,0364,'c',017,'!',047,'R','7',0243,0242,0322,'A',0266,'N',034,024,'}',0223,0373,'@',0311,01,'u','i',0247,0241,'l',0371,'y','r',0232,'V',0240,027,0361,'j','(',0215,0367,033,032,033,0264,06,0307,03,'#',0322,0271,'0',022,0246,02,0262,0212,0326,0257,0370,0315,'M','-',0252,030,0321,'c',0324,0306,0336,0262,'S','&',016,0366,0373,0205,034,'L',0263,037,0221,0371,'j',0331,'|',0305,0263,0243,0263,0341,'c',0203,0344,0356,0244,0270,0331,030,0255,'I','[','0',0263,'i',0345,0241,01,'+','`','(','#',0244,0177,0341,'R','|','8',0344,'5','h','k',0265,0331,0271,0313,0351,'A',0303,'D','8','}',0237,'s',0265,'+',047,'n',0330,0375,0335,'~',0337,0340,07,'!',0246,'6',014,06,0315,'6',02,0233,'z','s','|',047,0221,'S','{',0375,015,0212,0226,0260,'n','2','y',0221,'K',0256,'&','[',0322,06,'t',03,025,0250,0330,012,'~',03,0322,'5',0301,047,0272,0351,0214,0265,0220,0226,'p',0255,0214,033,'w','>','k',0264,'x',034,0253,0312,0243,0201,0377,0,0344,01,'P',0335,037,'G','d',0276,0325,0371,']','m','D',0215,'l',0177,':',0210,0357,'-',02,'c',0271,0207,0335,'s','p',';','R',0177,'4',0365,0271,0225,026,0254,'x',0371,025,0354,0332,'/',0205,0244,0217,'s',0313,'e',0321,03,0355,0332,0242,'7',010,0357,0374,'4','+','1',',','}',0325,'8','e','*',0371,'y',0324,0254,'9',0377,0,0236,030,033,0367,0362,'}',0275,'%',0217,0261,'H',0316,0352,'b',0331,0325,'+','?','@',026,032,030,015,0217,')',0210,0244,':',0215,0202,0243,0226,021,0375,0335,'S',0365,'2','`',0367,']',0212,'F',0316,'C','5',0352,'|','(','"',0224,06,03,0220,'y','L','^','r','d',0241,0365,0360,'g','m',0251,0365,'7','!',0334,0232,'!',0252,'a','-',0366,'i','v','O',04,0276,0300,'z','q','?',0324,0200,'J',0272,'T',07,'j',024,'4',0375,0323,';',036,0346,0325,0212,0203,0201,0241,0247,0227,033,0271,'s',0352,0243,014,'(',02,0263,'{',0331,'v','9','z','4',0270,0350,0341,0325,'j','Z',0204,0336,'l','?',0365,';','y','X','x',035,'d','5','&',0343,0221,0256,'p','N','-',0227,027,'9',0244,0241,0274,'a',0346,034,'^',0276,0210,0306,0274,02,0234,0306,0206,'B',0347,0367,'W',0302,'*',0303,'N','v',06,0351,0362,'i',0303,0324,'2',0355,0307,017,034,01,'y','7','[',0273,0225,032,'$',027,'V',0222,033,'0','z',0357,0345,0223,0263,0354,'o','X',010,030,0272,0272,0324,0331,'~',010,0316,0217,0230,0240,'z','#',027,0270,0270,0271,06,'n',0325,0211,'A',0364,0303,'g',0354,'=',0337,'#','2','m','8',016,0363,0326,015,0350,',',0365,0200,0213,0257,0322,'N',0364,020,'A','c',0323,0311,0310,020,0347,'F',0331,'Z',0364,'R',0201,0371,'/',035,'j','{',0254,026,0221,017,0376,0253,012,0232,0260,0342,'3','t',0362,0334,02,'K',01,'A',0232,0305,0351,0265,'L','6','@',0255,'q',0337,'D','^',0310,'X',0354,0365,0370,0316,0256,0347,0307,0375,04,0353,0233,0333,0310,0305,'X',0211,'|',0203,0233,'c',0332,0263,0214,020,0344,0354,0363,']',0337,0325,0275,0203,0233,'O',0345,'=','5','H',0253,0377,0,'b',0333,'5',0254,'|',0270,'M','P','d','k','A',',',027,'k','!','E',0307,0261,0375,0364,'^',0326,0240,0210,0265,0370,0306,0243,0254,'%','?',0304,0305,0317,0216,'R','X',02,0364,02,0262,0362,'{','`',0261,0344,0266,0355,'F','m',0322,016,0200,'z',0270,0274,'i',0177,0225,'j',0303,014,0275,'*',0274,0216,'Z',0215,013,0366,'E',0335,'*','f','X',0134,'i','X',020,0236,'N','z',';',016,'o',014,'}',0315,0363,0350,0206,0366,'i','s','Y',06,'k','L',0272,'a',036,'[',0233,0233,0237,'C',0215,'r',0270,022,'5','Z',0233,'b',0324,'Q',0303,'s',0300,0370,07,'Y',0365,0213,04,0266,'+','?',016,017,0303,'j','@',0341,'J',0264,0257,05,0252,0231,034,0202,0326,'4',0201,0215,0245,'F','6',0234,'a',0306,0327,'4',0212,0202,0202,0200,'*','v',021,0247,0342,'(',023,032,01,0200,'z',027,0347,0307,' ','W','V',0245,0313,02,'[','y','j',0366,':',0361,0215,'h',0204,'|',0331,'q','w',0300,0366,0240,0345,0320,'c',0255,'L',0327,'W',0326,0340,016,0333,0311,0375,'x','O','%',0333,0314,0335,'<','l',0224,']',017,05,0244,'M','j','r',0200,0212,0204,0207,0200,0225,0232,0256,0243,0236,0242,0242,0230,026,'q',0312,024,')','a','-',0376,0246,'C',0323,'/','E',02,0355,0312,0303,'}',0347,'w',0227,034,0203,0320,030,0243,0261,0253,0240,0324,011,0347,0205,026,0,'z',0331,0374,',','~',0367,'*',0177,017,0221,0212,0325,0302,0261,'m',0232,0370,0251,'v',0226,'4','Q',0300,'S','g','#',0200,0326,0273,'6',0271,0370,'L',06,0351,0235,'>','0','Q','o',0,0226,013,0265,'l',',','_','f',0224,'h',0324,'J',0,'g','W',0225,'S',0250,0325,0356,0324,032,0246,'{',0273,'[',0350,0344,0321,0317,0205,0330,0232,0255,021,0262,'z',013,035,026,'J',0321,'<',0316,07,'W','.',';',023,0222,0254,0216,'M',0231,0275,'5',0203,0334,'c','`',014,0,0365,0260,0210,'R',0357,0265,'-',0367,0263,'#','(',0254,0205,'0','5','t',0246,'V','[',0305,0227,0264,'h','A','(',']',0262,0310,0353,'C',030,'S','B',0256,'S',0245,'7','B',0310,0332,0221,0223,'+',0341,'0',0264,'1','>','|','1',034,'o','?',023,'=',0371,'x',0250,0305,'4',0340,0300,0331,034,036,0216,'U',0217,0235,0204,0322,013,'|',0227,'u',0265,'_','K',0255,016,'C','`',0267,023,0242,'%',035,0245,0214,0332,0271,0350,'s','(',0373,0364,'X',026,0,'z',0354,'r',0276,047,'z',0201,0223,01,'u','i','y',014,0353,0277,02,0233,0232,0227,0231,032,0202,0241,'W',0361,'Q','#',0220,0255,'(','_',06,0224,0352,0265,0332,0200,0,0200,0260,024,033,'F',0177,'}',0277,0345,'"','U',0225,0305,'x',031,' ',0346,0334,07,'v',0301,0351,0347,'c','$',0214,06,07,'"',0341,0304,0220,'a','z','n',0327,0321,0233,'B',0336,034,0315,'g',0250,0255,0327,0327,'`',0356,0312,0375,0313,0341,0212,0334,0347,0307,0210,0220,0227,'Z',011,'&',0306,024,0231,'1',0242,0221,'u',0336,0301,0241,'I',0332,'R','X',0231,0316,0235,'4',0257,0,0204,0273,0213,0241,0255,023,0261,0356,'o','S',014,022,'3','u',0211,'9',022,0227,0253,0241,0,0344,'h',033,034,'0',0321,0250,0345,0234,0354,0222,'U',0200,'e',026,0371,0316,0343,047,'O','0',0237,'w',0220,0344,'{',017,'t',0342,0306,'&',0207,0263,0,'S',030,0361,'|',0331,0275,03,0253,0237,0256,0271,0306,016,0314,'U',0353,0370,'Q','K',0243,'?','t',0214,0222,0267,'W',0301,010,']','h',03,'5',0325,'v',0307,0224,0240,':',0324,'A','d',0334,0245,',',0332,'T',0272,0370,'8',04,0253,01,'G',0234,0276,0376,0251,'M',0256,06,'m','*',0364,033,' ',0221,0321,'K',0233,0202,0214,0314,']',0207,0243,0237,025,0252,0326,'U','`','>',03,0270,0327,0313,06,'H',027,'V',0220,0332,0356,'-','%',0372,0322,0362,0215,'8',0203,'9',0330,'(',0303,0344,035,'u','=','p',02,024,0253,0,0244,'l',0235,021,0257,'Z','Y',0241,0312,0325,0257,'L','4',017,020,0235,0201,'z','l',034,0262,0220,0134,015,0247,'6',0205,015,035,021,033,0342,'5',0202,0246,0252,'Y','|','"','/',030,0231,032,0370,'@','Z',0227,0273,0334,0337,014,'E',01,'d',0341,0321,0301,0347,'X',0325,010,0362,'a','8','H','!','Q',0204,031,022,0202,0357,0344,021,0216,015,0207,0225,0237,0217,'o',0374,'k',0304,'`','@',0273,')',0277,'W',03,0235,025,'J',':',04,'@',036,0273,'y',03,'2','2',0360,0134,0233,0227,034,01,'%',0262,0263,'H',010,'n','5',026,0213,' ',0246,'A','^',011,0300,'*',030,0273,0240,0330,0270,'_',0301,'n',0256,'_','v',0225,0205,'g','~',0260,0366,'9','p',014,'z',0202,0263,0303,0343,'_',0365,0305,0202,0231,0221,'b',0307,0356,0334,0371,'E',0313,']','6','E',0356,'b','i',034,'Y','j',0340,0377,0,0204,031,'w','^',0271,0313,013,0246,')','&',0310,0315,0253,0311,'k',0263,'_',027,020,0225,0312,0246,'(','<','#','*',016,'a',0310,'Q',0224,0207,05,0235,014,0370,'3','A',0264,0312,0235,'R','V',0263,0300,0305,0320,0326,0211,'X','/',011,0344,'P',0275,0220,'p',037,'T',0313,'4','C','R','b',0267,0263,036,0347,0314,0367,0236,023,0364,0367,'.',014,0211,'Q',0336,'C','=',0241,0356,'=','<',0206,0277,'(',0315,0360,'d',0342,06,'T','|',0230,'A',0367,0375,0203,0353,0215,0260,0245,0134,0212,'R',036,0314,0327,0255,'`',0215,0223,'W','J','S','%',';','x',0332,0373,0260,'T',0345,0272,0242,'`',0301,0233,0201,'A',0230,0274,0225,'*',0244,'d',0334,0250,0223,023,'k','R',0252,'[',0264,0340,022,'0',05,024,'M',0357,0277,0252,'C','k',0201,0233,'V',0333,'b','3','t',0344,'q','h',0220,0245,0331,0216,031,0235,0270,0205,0300,030,'<',0203,0326,017,0221,0272,'t',034,0350,023,'h',0367,0361,'a',0256,'&',' ',011,0354,'B',0332,0257,0256,0336,0216,0314,0214,0274,027,0353,'+','s',0257,0217,047,'J',022,'0',012,013,'Q','b',']','(','<','C',0302,'&',0334,0307,0306,031,'x',0300,0374,0370,'C','X',0327,';',0334,0336,'1','R',0243,0310,'H','J',0233,0226,'l',0312,0277,'Y',034,'3','a','!',0374,03,'#',0255,024,0314,'8',0200,0221,0342,'$','(','.',0,0225,0251,0214,0265,0324,0301,'m',0320,'A',0323,0206,0327,0216,0327,025,036,'v',0240,0,010,013,01,0353,'a','b',0206,'2',011,0301,0233,'W',0326,0325,0306,0134,01,0177,02,0206,')','#',0341,'8',0345,'{','T',0312,'%','%',07,'0','2',0370,'Z',0357,0220,0322,0202,010,',','V','v','[',0217,'c',0373,0344,0235,0326,0254,0231,0245,'N','L',':','8',0231,'N',0336,027,0370,0234,'@',0235,'T','F',0277,0274,':',0361,'Y','z','r',0306,0370,0347,'g',0255,0254,022,0330,0251,'O',0223,0365,0352,0254,0273,0360,'5','t',0246,0372,'^','_',034,'w',032,0321,'r',0241,0255,'t',024,034,0250,'x',0355,0323,032,05,'<','c',',',0351,0315,0245,']',024,0212,0307,021,0305,0320,0326,0204,0330,0367,'*',0332,'S','|',0274,0346,0242,'1',0240,'>',0374,0234,':',0300,'&','+',0364,0200,0322,'s',0177,026,06,023,0206,025,'U',06,0263,'q',05,0262,0224,'M',0246,0202,0332,0207,014,0211,'@',0307,0223,0201,'Q',0251,'~',0204,'Y',0367,0244,0365,0365,0270,032,013,0317,0315,0377,0,0265,0205,'Z',0336,'V',0355,0374,'S',010,0304,0336,0213,0230,'4',0241,'0','$',0242,0300,0332,0226,0206,0340,0254,0235,014,'5',0250,0301,'c',012,'"',0,0225,0300,'(',0344,031,0357,'M',0251,'|','6',06,'+','G',0242,0263,':',0351,0310,0362,0355,0351,0210,0310,047,0370,0327,'^',035,'H','F',011,'l',0353,'#',0255,';',0260,0344,'3','8','W','3',0337,'g','8',0354,'8','a',0211,')',0362,'.',0365,0255,033,0211,0134,0364,')',023,0231,0134,0264,'*',0353,0331,0274,0310,0361,0307,'q','U',0252,0,0246,'J',011,0206,0223,0177,010,0272,0241,04,0214,0225,'k',0343,'(',0244,'p','C','3',0302,'f','+','d','~','|',' ',',','k',0331,036,'b','v',021,'&','A',0213,0320,'q','.','(','Q',0312,'V','O',';',0272,0360,'L',0323,04,030,0252,'!',0336,0221,0221,'K',0253,0237,014,0345,'%',035,'s',0365,0331,0353,'q','$',0227,0237,0233,0177,'j',0364,0211,'m','F',0237,'i','R',0276,'-',0200,'G',025,']','L',0222,0200,0306,0233,'S','1',027,'5','1','H',0315,'L','@','E',0215,0324,0341,0312,'J',0322,'B',0332,0353,0372,0243,0,0200,'@',024,0330,0263,0225,0252,0200,0,'@','`',036,'d','|','U',0317,0337,07,024,0201,'"',0231,'e',':',0367,'8',037,0235,0224,'$',0207,020,'n','{','N',0306,0366,0371,0337,'b',0274,0245,'/',0373,0267,0241,0332,0315,0362,'a',047,0227,'9',0360,'~',0275,024,0270,0253,0325,0253,'c',0312,0335,0277,0215,0303,'a','u','S',0357,05,0367,')','d',0214,023,'@','!',0322,0225,0250,0264,0242,026,'E','A',0270,'4',013,0245,0300,'U',0356,0134,'u',032,0223,0342,'c',0220,'f',0264,'_',0333,0276,'e',0232,0371,0250,04,'n','4',0267,0327,0306,0347,0325,0303,'-',0313,'#','5',0343,0333,0201,'+',0374,0366,047,0205,'B',02,'V',0300,'Q','#',07,'6',023,0353,0313,0320,'s',0312,0357,'#',':',022,'f','~',0260,0177,'i',0306,0345,0237,0355,024,0263,0342,'0',0263,05,0206,0225,020,0200,0234,'/',0375,0371,'#',0340,0262,'2',')',0357,'&','5',0225,'.','e',0254,031,'x',0215,034,0230,05,01,0334,'s',0253,0335,0340,':',012,0350,0250,'a',0265,'M',026,'`','Y',0321,0341,0225,0242,0303,0222,0202,'X','.',0326,'J',0231,0333,0363,0300,024,0304,'&',0230,0234,'!',0224,025,0336,'&',024,0356,'8',0,0351,0276,0334,0256,034,' ','d',']',' ','>',0301,0362,0203,'#','J',0340,'+',0260,0221,'|','?',0264,0335,'7',025,0226,0224,012,0260,031,0265,'4','6',0216,016,0365,0201,'g','u','c',0275,030,'*','E','c','B','h','[','c',0317,0237,'*','d',0304,03,'&',0260,'"','p',0321,0314,0362,'5','6',0203,',',0207,'Z',0301,0277,01,0253,0245,'7',0222,0362,0370,0226,'3',0200,0320,0250,0212,'(',047,'L',0236,'2',0353,0341,'c','o','X','s','x','E','^',0227,'?',010,0371,0345,0350,012,'X','n',0342,'4','=',0334,'2',0324,0214,0310,07,0353,0200,'G','%','m',0270,0134,'w','s','z',04,'~',0351,0344,0336,0231,0326,'d',0321,0317,0356,']',0360,0225,0345,'F',07,'6',0234,0370,'7',011,03,0225,'"','Q','`',0202,0307,'z',0221,0336,0341,'c',0357,0363,0307,0201,0241,'1',0253,'"',0257,'B','b',0256,'&','Y',0273,0255,0,'V',0315,0320,0240,05,02,0302,0257,0321,'$','I','N','D',0357,0252,'G','s',':',014,'B','J','9','Y','s',0253,0225,06,0276,026,')',0317,'@',0242,0372,024,024,014,0236,'D',0377,0,'M',011,'3',0300,0310,0364,05,05,'{',0356,'o',0213,'p',0346,0377,0,0314,'>',';',031,0333,07,0327,017,0356,'4',0276,0374,0213,0345,0270,0212,0275,0206,'8','4','(','h','c',0226,0256,'k','_',0310,047,027,'3',0317,'*',0221,030,'N',0213,0377,0,'c',0216,'*','B',0341,0374,0333,016,0365,'q',',',0336,0204,'!','u',0243,0277,0256,0252,0375,0375,'e','=',0224,014,'i','g','4',015,022,'`','i','Q',0177,06,'h','A','%',0312,':','i',0250,0204,011,'[',01,'D','#',0256,0376,0251,':',0302,015,'j',033,0323,0320,0240,0227,'&',';',0210,'s','F',0134,'o','x',0310,'7',035,0200,0363,'J','T','i',022,0331,0266,0275,'i',03,0201,'*',0324,'X',0270,0377,0,0243,0344,0245,011,'d',0250,'g','S',0256,'u','x',0246,'/','*',0317,010,'%',0313,0267,0327,0245,'#',026,0271,0325,'k',026,0203,0265,'e',0311,0236,0260,07,0244,0265,0273,0261,'@',0213,0342,'[',0321,0343,'=',0205,04,0265,021,'$',0365,0251,0260,011,04,0345,'J',0245,'n',0265,'2',';',030,037,0237,010,0313,'v',0367,0177,0257,0242,0220,'h',0274,'D',0323,'o',0304,'b',0310,0270,0320,'a',0361,0302,032,0340,'8',0354,015,0320,0321,0217,04,'A',0371,0301,0345,0311,0255,0337,'A',0375,0357,'R',026,0335,0306,'O',0216,0334,0,0311,0200,0272,0264,0215,0262,0255,0265,0353,'W',0372,0375,0367,0246,'n',0276,0361,'P',0374,0202,010,0244,'A','&','M',020,'7','p','4',0245,'F',0301,0253,'H','L',0244,0325,0234,0321,0335,0247,0271,022,'S',027,010,0225,0212,01,0206,0366,0325,031,'P',' ',012,0316,'7','2',0351,'S',017,'D','+',0,0225,0270,0234,'I','+',0361,'>','?',0266,0315,0370,'J','N',0347,036,0134,'c','<','/','v',047,0301,'v',0220,'{',036,0134,0222,0302,'t',0232,0303,0265,0303,'S','3',0265,'?',020,011,0250,0370,0301,0332,'X',0177,'7',0376,0325,0221,0345,'l',0337,0306,0332,'Z',0231,'4',011,0310,0134,0374,'!','R',06,015,'F',0213,0235,'1','C',014,05,'s','p',0306,'m',033,'2',0240,'(',0203,'|','z',0315,022,0362,0330,0344,'3','Z',04,0340,0261,0315,'f',0276,0216,015,0324,0361,'q',0355,0317,'o',035,0354,0356,027,0357,0207,0366,'@',0277,0134,'a',0202,0231,'u',0243,'v',035,'F',0365,'"',0320,'{','<',0315,0235,'G','5','k',0340,0357,0177,0317,0347,0202,'7','f',0331,0226,'E','f','W','-',015,'*',0300,0206,032,07,0,0252,'K','5',033,0362,0342,0202,0221,'3','<','.',017,0327,'X',0325,0221,0267,'c',0221,0255,'c','Y','F',0347,0331,'z','G',0347,0245,0276,0270,0277,'i',020,0370,0263,0217,0340,'&',0353,0204,0327,034,'[','K',0274,0216,014,'"',07,0335,0363,'5',06,0264,'}','?',0367,0314,0220,026,0356,017,0336,0325,0210,0203,'}',014,0312,0261,0235,0233,047,'k','W','^',0217,0350,'U',0372,0261,'u',0276,0234,'x','`','f',0260,'i','0',0215,0216,0232,'<','r',0322,0255,0316,'v',0355,0250,' ',0202,0243,'/',0373,0335,0336,0237,'>',0225,016,0366,0201,'>','x',0220,'2',0324,'d',011,0343,0205,0367,0232,0201,'}',0236,020,'+','0',0266,'<',0201,02,0230,0355,0312,0347,0313,'Z',0300,037,'O',0277,'3',034,026,'J',0301,0245,0237,03,0315,026,';',0323,0242,'J','e','_','$','D',0227,0212,0300,'X',0305,0325,0326,0247,0234,')',01,0326,'0','d','z','P',0254,'u',0344,'8','`',0363,'H',0355,020,'>',0350,0260,0221,07,0212,0313,'c','f','o',0370,'p',0243,0316,0343,0352,'I',0361,'W','X','?',')',047,0216,':','%',0201,0323,037,'e',0243,'K',':',0210,0312,0311,'g',0314,'a',031,'%','3','T',0223,034,0222,'V',0224,0333,'0',0321,'y','V','B',0315,0250,0177,015,0201,0212,0323,0346,033,'G','7','N','G',0246,'q','9',0220,0241,0355,0303,0266,0205,0365,0265,037,'e',0301,0244,0271,0254,0224,'=',0275,0270,0211,'M',0247,0213,025,0306,'&','e',0201,0263,'X',0302,0367,0321,0250,0206,0317,0257,0375,0237,'O','l',0265,0367,'i',0341,03,036,0331,'=',0375,';',0335,036,'@',03,0303,0236,0227,'%',',','}',0234,013,0277,0274,0303,0361,0234,'^',0305,0351,0303,0361,0307,0310,0217,013,'~',0254,037,0217,'z',0230,031,'3',0372,0373,0364,0301,0344,0247,'j',0346,'l','j',0353,'@',0211,':','#','N',0264,011,0205,0,0300,'=','3',0245,010,':',0215,'?',0204,0250,'J',0272,0360,0214,'"','D','g','$',0374,0274,023,'P',0224,'B','S',0307,0222,0330,0240,0245,0332,'8','c',035,0242,0354,'K',0330,0362,021,0333,'7',0330,0371,0212,0207,0257,0334,017,'M','o',0254,0320,0324,'c',05,'C',0217,0247,'I',0212,0220,'T','[',0354,'Z','q','"',0263,'q','v',027,014,0234,'s','K','0','/','s',0205,'*',020,'A',014,'D','u',0302,0217,016,036,'`','H',0373,0361,0343,032,047,0245,033,0262,0324,'h',0325,0233,0213,0234,0217,0244,0263,0227,0373,0264,0360,0310,0237,'c',0335,0353,0361,0352,0,0257,02,0355,'/','g',013,'p',0225,0202,'A',0356,0321,0357,04,'v',03,0353,0206,'2',035,0244,0266,0373,'x',0227,0337,')',0242,'s',034,0241,0362,030,'a',0,016,'n','>',0363,0336,0243,031,0217,0374,'>',0375,031,0331,'s',0265,031,0226,';',0326,'|','9',0334,0312,0201,04,026,'=','8','>',0300,'H',011,'Z',0222,'I',0270,0263,05,0267,'A',07,'N',030,'K','$','$',0351,0367,0336,0234,'V',0243,015,0250,'=',0206,036,0224,0220,0335,0304,0201,0204,0341,0302,0257,'&','#',';',0357,'>','C',013,'0','?',0206,0261,'P',0331,' ',0352,036,0216,0316,'[',0354,0322,0244,'@',0240,0333,'z','1',0354,0337,'6','c',0352,'2',0362,'@',0334,0270,0270,0205,0212,0344,0317,'x',0346,0207,031,' ','m','f',06,'C',0234,027,0243,0205,0242,033,0210,03,'#','Q','L','A','=',0261,0354,'x',0360,0307,'Y','i',0243,'B',0364,0264,'4','j',0302,0304,0247,0221,0364,'6',02,0377,0,'v',0236,030,'e','+',016,':','}','}','N',0236,032,'l',0213,0334,0225,0364,0216,026,025,0215,0241,030,012,0203,0204,' ','#','7',0255,047,0257,036,'-','B','I','-',0201,0370,0305,0305,'k',0214,'o','%','g','C',0365,0362,'!','d',024,0177,035,'z',0324,0300,'0',0377,0,0227,0337,0240,')','%',0342,0260,'6',030,0352,0353,'X',0242,'I','G',0244,'z',0235,034,'9',0210,'>','Q',0347,034,'X',0224,0344,0204,'e','=',0347,0310,'H','x',0211,' ','!','*','|','U',023,0235,'~',0262,'8','^',0310,0245,0230,0221,0250,0222,0263,0321,0355,0336,0366,0216,';','a','=',0305,'g',0351,0351,'Z',0351,'c',0236,'T',0220,0303,0347,0332,0253,'v',0332,0202,034,0244,'N',0206,'m',012,'p',021,'9',0256,'o',0250,0302,0202,0343,'t',033,'9',0367,037,'a',0305,0255,'b',0313,0210,0366,'!','m','W',0311,'i',0312,010,'u',0276,0312,'z',032,'q',014,07,012,'[',011,'q',0202,0332,'@',0266,'k',036,0316,0367,0251,'8','e',0376,0374,0353,013,0235,0273,'o',014,021,0204,0337,034,0243,0324,0205,0372,0244,0372,'e',0254,'n',0355,0305,')',0261,'.','R',0134,0352,0216,0252,02,010,026,03,0311,0213,'y','-',0223,'-',0350,'l',0274,'n',0234,0215,0222,036,0274,'"',0241,030,'L',0350,0241,0267,'!',0271,0373,'s',0334,'x',0360,0276,':',014,'=',0276,'*','t',047,0330,'g',0373,'o','4','v',0227,01,'Y','7',0342,0352,0326,'9',032,0303,0333,0351,0352,'L',02,0316,0310,'*','c','v',010,'~',0335,0327,0212,'>',0317,'D',0277,0375,0207,0376,036,']',0372,0300,01,'y','=',0323,0331,0333,0213,020,'C','L',0277,0236,'a','A',0250,0357,0240,0350,0230,047,023,0227,012,'^',0203,012,'x','`','2',')',0241,'o',0354,0371,0226,0222,0335,0272,032,0321,'7','v',0371,06,'m','F',021,'A',0276,0376,0246,'r',0364,'-',0203,0361,0375,'[',0211,0213,'C',0312,'%',0267,'Z',0334,0266,'Q',0220,01,0,'e',0345,0221,0300,'>','T','B','4','_',0241,0326,0254,':',0230,';',0234,'M','f','c','{','?',0265,'g',0337,'Z',0,'F','F',0342,'q',')','m','&','Y',0373,0343,0336,0257,'f','o',0356,0324,0362,0324,'!',0236,0365,0332,0202,0,013,'i','X','>',')',0227,'K',0324,0202,033,0207,030,0255,0236,'f',035,0331,'R','2','*',0272,0274,'/',0374,'@',0345,'Z',024,'R',0341,0213,0222,0303,0245,'n','r',0347,0346,0300,012,'#',027,0377,0,0225,0334,')',033,':',020,0204,'x',0260,'K','&','0',0374,0232,0272,'i','<','6','e',017,0300,'5','2',0217,'"',0201,026,'7',034,0267,0362,01,'P',']',0247,'A','w','q','t',0250,'"','u',02,0366,0223,'u','9',036,0246,0377,0,013,'>',0372,0356,0203,'7',0355,0247,0217,'4',0265,0323,0220,'@','r',0342,0221,0225,0205,'7',0327,0371,0362,0363,0311,'O',02,'k','7',0303,'?',020,0220,031,'!',010,0353,'P',0224,'-',0220,'G',0303,'3','<','L',0343,0204,0241,0301,0333,'f',0224,0355,0302,05,011,'H',0244,033,037,025,0206,'<',0277,0370,0247,047,0274,0177,')',0313,0366,0353,0377,0,014,0254,0323,0362,0212,'j',0377,0,0211,0225,'w',0302,013,0327,'I','K',035,0315,0250,0307,0205,0362,0374,0365,0240,' ',0200,0260,036,0242,0321,'R',0351,0262,0375,'Z',0263,0243,0355,0266,0375,'_',0210,0220,'Y',0242,017,0374,015,0322,0211,'`','G',0320,0327,'W',0177,'=','c',0243,017,'J','R',0244,030,'t',036,0303,0321,0317,0210,014,0352,'(','+',0210,0353,'Q',0333,0226,011,0231,0232,'k',0315,0211,0230,'p',0344,'d',0214,0275,0305,'{','M',017,0311,'N','A','s','(',0345,037,'3','Y','1',0347,0377,0,'U',011,0323,'@',033,'f',0301,0367,'K',0316,0316,'!',0354,'R',0203,015,0223,'?','z',010,' ',0261,0352,'a',034,'|',013,'>',0362,0253,0226,'S',0374,0213,'}',0134,0373,034,'A','R','#','J',0254,05,032,'&',032,0206,'I',0271,0326,0357,0241,0204,']','@','r',0371,'5','3',')','o','C',031,014,0223,'1',0327,0210,0320,'1',0330,'C',04,'h','{',':',0261,'.','G',0376,'W',0237,0370,0326,0253,0227,'7','u',0371,037,0226,0276,0276,0241,0263,0364,0361,0256,03,'!',0272,030,0314,0203,0261,0317,0321,0337,0364,0225,0260,'>','7',047,047,0255,'!','~',0211,02,0304,'x',0340,0312,0205,0356,0313,0347,'b','o',0221,0252,'q',0205,0263,023,0374,';',':',010,'q',0246,0354,0335,0235,'b',0223,0320,'K',0375,0215,015,0216,'4',0303,0360,0270,'9',0215,'9','j',0354,'_',0322,'`',0373,0216,0,'g',0370,'=',035,0222,'}',0242,05,'d','L',0236,'1','d',0362,'z',0341,0362,0216,0243,'V',0231,0237,0134,0346,'n','[',0374,03,0254,'S',036,'P',06,'"',0317,0370,0275,0375,0224,0345,0221,'*',0305,0343,0311,'`','T',0206,0134,0271,0271,0341,0255,02,0367,07,0,'2','=','6',0225,0237,014,0311,0323,'O','C',0222,'"','~','E',0213,021,'8',0344,'W','"','+','f',0211,'!',033,'|',027,0371,0354,0241,'v','2',0177,'X','z',0322,'F',0266,0305,'=',0223,0221,'/','*',0232,0207,'O',0356,0215,'Y','w',0362,027,0267,0316,'*',0333,'M',0371,0345,0255,07,012,0305,05,0200,036,0243,033,'g',0344,021,0207,0325,0222,0233,'$',0300,0334,0376,0233,0236,'D','Q',0253,'?','H',0275,'q',0336,0257,0225,'Z',0363,'y',0343,0327,'m',0350,'R',0244,'0',032,0211,0352,'y','s',0200,0235,0330,'c',0315,0232,'/',07,'K',0343,0357,'<',0210,'6',0362,'m',0375,'6',035,'7',0342,0342,0354,'b','j',0134,016,01,0240,'z',0253,014,'|',0322,0321,0370,'>',')','!',0334,'_','j',0276,0214,'O','$',0304,0373,'2','u','.',0331,'c',0215,'`','k',0257,'z','U',0316,0215,'-',0306,0345,0356,0366,0375,'G',0244,034,0221,'&',0223,'u',0243,0246,'0','1','M',0326,'[',0212,'n',0253,0225,0247,'q',0354,'m',0344,0313,024,020,'N',0200,'T','e',0262,0315,031,':','M','0','7',0365,0242,'=',0357,0216,0265,'1',035,0312,'o',036,0300,0222,0330,0304,0331,0326,'<',0246,015,'R',0313,0266,'L','*',0326,0344,'`',0335,0204,'K','v','h',0306,0216,0204,0355,024,047,023,'`','L',0336,'!',0333,0316,'8',0233,022,036,0355,'4',0311,'G',0363,'!','9','M',012,0301,0220,0350,0211,'0',';','5','~',0337,01,0337,'h',0350,'<',0240,'J',032,0,0327,0350,0306,0213,021,0216,0230,0330,0311,0356,0347,0353,0322,'H','n','R',0376,0265,0345,0376,'F','-',0313,'m',0235,'e','b','"',0303,'U',0200,0334,0363,'!',0200,020,030,0234,0363,0354,0250,0342,032,'%',0335,'@','*','h',0236,0313,0357,'S','1','1',0215,'v',0237,0265,'~',0201,0373,'*',0374,0235,0372,0324,'9',':',0210,0372,0245,0313,'#',0360,0224,0374,'C','w','5',0312,0314,'?',0205,'A','_',0265,0211,0354,0243,'K','j',0321,0336,0260,'=',0251,0212,0206,'m',016,'V',015,' ',0303,0224,0271,0205,0362,0322,03,0240,'%',']','*','w',0246,011,'c',0370,'=',0366,'(',0202,0345,0234,'u','u','w',0177,0302,0304,'m',0241,0275,0325,'.','U',022,0257,'D',033,'e',0321,016,0324,0364,0,'$','!',0210,0237,0340,0310,'7','l','X','s',0324,0371,'w','*',0332,0362,0311,'+',0261,0363,0337,0374,'c',0241,'e',0310,'+',0374,'%','Y',0366,'N',01,0357,0375,034,0212,'C',0346,0207,012,'1',023,0326,0311,0134,0306,0325,0377,0,0206,0252,0225,'x',0353,0207,0267,0364,'a',0201,'A',0,0320,010,03,0374,0227,0344,'b','=',0232,0337,0243,'j','P',0223,'r',016,'Y','A',0246,0363,020,'s','0',0350,0365,017,0177,'.',0201,0240,'U',0254,0201,0302,'F',0306,0204,0340,0337,'B',')',0331,0357,'.',0364,'l',0306,0200,' ','?',0315,026,0321,0,'?','F',0223,0277,0224,0335,'~','-',0221,'K',0200,0260,'>',0221,0354,0253,05,'f','B',0255,0226,0317,'O','D',0326,'R',035,0344,02,0244,'`','3','m',')','.','O','e',0351,0205,'J',0351,'K',013,01,'K','-',0335,0335,'_',0364,0361,'{',0324,'C',0322,0234,0335,015,0366,'0','z',0225,'5',')','M',0272,0322,'l','{',0323,0340,'/',0254,'P',035,0352,030,0315,0237,0222,0206,0311,'s',037,0322,'<',0203,')','X','-',':',024,0274,0267,0370,0270,024,'$',0217,0226,'3',0245,'G',035,0213,0327,'c',024,035,015,0231,0320,0376,'K',0225,0317,0235,0223,0267,0373,'`','!',04,0321,0254,'@','(','M',0367,'+',03,037,0262,0305,'c',0235,'T',0373,0251,0302,'}',0235,'0','V',0344,0371,032,0304,'G',0354,0275,' ',0257,0271,0245,0354,'V',0320,'8',0217,0360,0277,0377,0332,0,014,03,01,0,02,0,03,0,0,0,020,0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I',' ',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,01,'%',037,0235,0344,0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0220,04,0367,0377,0,0377,0,0300,0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0200,0257,0377,0,0377,0,0377,0,0214,0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0221,0177,0377,0,0377,0,0376,'`',0222,'I','$',0222,'I','$',0222,'I','$',0222,01,0,0200,'H',04,0202,'I','$',0222,'I',' ',0323,0377,0,0377,0,0377,0,0365,0304,0222,'I','$',0222,'I','$',0222,'I','$',0200,'I','$',0222,'I','$',0220,'@','$',0222,'I',05,0277,0377,0,0377,0,0377,0,0314,'$',0222,'I','$',0222,'I','$',0222,'@',0,0222,'I','$',0222,'I','$',0222,'I','$',022,'I',',',0377,0,0377,0,0377,0,0376,0361,'$',0222,'I','$',0222,'I','$',0222,'A','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','?',0377,0,0377,0,0377,0,0360,01,'$',0222,'I','$',0222,'I','$',0202,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,011,'?',0377,0,0377,0,0377,0,0342,011,'$',0222,'I','$',0222,'I','$',022,'I','$',0222,'H',' ',0222,'H',' ',0222,'I','$',0222,'I',0377,0,0377,0,0377,0,0377,0,'p','I','$',0222,'I','$',0222,'I',04,0222,'I','$',0220,'@','$',0222,'I',' ',0200,'I','$',0202,']',0377,0,0377,0,0377,0,0376,020,'I','$',0222,'I','$',0222,'A','$',0222,'I',' ',0202,'I','$',0222,'I','$',0222,010,04,0201,0217,0377,0,0377,0,0377,0,0374,022,'I','$',0222,'I','$',0222,'I','$',0222,'I',' ',0222,'I','$',0222,'I','$',0222,'I',0,02,0177,0377,0,0377,0,0377,0,0260,022,'I','$',0222,'I','$',0220,'I','$',0222,01,'$',0222,'I','$',0222,'I','$',0222,'I','$',023,0177,0377,0,0377,0,0377,0,0,0222,'I','$',0222,'I','$',0220,'I','$',0220,'I','$',0222,'I','$',0222,'I','$',0222,'I',' ','H',0277,0377,0,0377,0,0365,04,0222,'I','$',0222,'I','$',0202,'I','$',02,'I','$',0222,'I','$',0222,'I','$',0222,'H',0,0356,'M',0377,0,0377,0,0241,'$',0222,'I','$',0222,'I','$',022,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I',' ',0366,'B',0307,0375,0240,'$',0222,'I','$',0222,'I','$',0222,'I',0,0222,'I','$',0222,'I','$',0222,'I','$',0222,'@',027,0355,023,030,0341,0200,04,0222,'I','$',0222,'I','$',0222,'H','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','W',0341,0262,0235,0362,011,' ',0222,'I','$',0222,'I',04,0222,'@','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,013,0277,0340,'-',0217,'v',011,' ',0222,'I','$',0222,'H','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'K',0377,0,0243,'5',0177,'p','I',' ',0222,'I','$',0222,'A','$',0222,011,'$',0222,'I','$',0222,'I','$',0222,'I','$',0220,'=',0374,'k',0261,0364,0,'I','$',0222,'I','$',0222,'A','$',0220,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',01,0277,0323,'.',0221,0314,020,'I','$',0222,'I','$',0222,'I','$',0200,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0220,0177,0337,0247,0316,'T',022,011,'$',0202,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',037,0364,016,0222,0245,04,0222,011,'$',0200,'I','$',0220,'I','$',0202,'I','$',0222,'I','$',0222,'I','$',0222,'I',' ',0223,0362,0271,0360,'c',04,0222,'I','$',0220,'I','$',0220,'I','$',022,'I','$',0222,'I','$',0222,'I','$',0222,'I',' ',0237,0377,0,0235,'?',0230,'$',0222,'I','$',0220,011,'$',0202,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'H',047,0377,0,0323,'t',0375,' ','$',0222,'H','$',0222,'I','$',0202,'I',' ',0222,'I','$',0222,'I','$',0222,'I','$',0222,'@',033,0374,030,0351,0376,0311,'$',0222,'H',04,0222,'I','$',0202,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'@',0257,0363,'>','*',034,011,'$',0222,'I','$',0222,01,'$',0202,'I',04,0222,'I','$',0222,'I','$',0222,'I','$',0222,01,0277,05,0364,07,'R','I','$',0222,'I',04,0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'D',0277,0347,' ',0200,'"','I','$',0222,'I',04,0222,'I','$',022,'I','$',0222,'I','$',0222,'I',04,0222,'I','$',0222,'k',0375,0275,'/','n',02,'I','$',0222,'I',' ',0222,'I','$',0222,'I','$',0222,'I','$',0202,'I',' ',0222,'I','$',0222,037,0304,'^',')','x',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0223,0331,0301,022,'I','$',0221,'?',0303,0377,0,0217,0244,0222,'I','$',0222,'I','$',0222,'H','$',022,'I','$',0222,'I',0,0211,0377,0,0377,0,'2','I','$',03,0302,0222,'8','~','d',0222,'I','$',0222,'I','$',0222,'H','$',022,'H','$',0222,'H',' ',0337,0377,0,0377,0,'2','I','$',0207,0220,'g','I',0303,'$',0222,'I','$',0222,'I',' ',0222,'I','$',022,'I','$',0222,'H',05,0377,0,0377,0,0377,0,020,'I',' ','c','W','9',0244,')','$',0222,'I','$',0222,'I','$',0222,'H','$',022,'H','$',0222,'A','7',0377,0,0377,0,0377,0,0352,'I','$','|','N','u',011,0370,'$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'A','/',0377,0,0377,0,0377,0,0364,'I',' ',0374,'i','k',07,'I','$',0222,'I','$',0222,'I',' ',0222,'I','$',022,'I','$',0222,'H',037,0377,0,0377,0,0377,0,0377,0,011,025,0377,0,0202,'n',0276,011,'$',0222,'I','$',0222,'I','$',0222,'I','$',022,'H','$',0222,'H',027,0377,0,0377,0,0377,0,0377,0,01,'?',0377,0,0377,0,023,0252,'I','$',0222,'I','$',0222,'I',' ',0222,'H','$',022,'H','$',0222,'I',027,0377,0,0377,0,0377,0,0375,0251,'w',0377,0,0377,0,0375,0200,'I','$',0222,'I','$',0222,'I',' ',0222,'H','$',022,'I','$',0222,'H',04,0377,0,0377,0,0377,0,0377,0,0315,'?',0377,0,0377,0,0377,0,02,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I',01,0377,0,0377,0,0377,0,0377,0,0377,0,0377,0,0377,0,0377,0,0364,022,'I','$',0222,'I','$',0222,'I',' ',0222,'A','$',022,'I',04,0222,'I','&',0377,0,0377,0,0377,0,0377,0,0377,0,0377,0,0377,0,0377,0,0234,0222,'I','$',0222,'I','$',0222,'I',' ',0222,'I','$',022,'I',04,0222,'I',04,0377,0,0377,0,0377,0,0377,0,0377,0,0377,0,0377,0,0376,0200,0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0202,'I',0,0222,'I',0,0177,0377,0,0377,0,0377,0,0377,0,0377,0,0377,0,0377,0,'$',0222,'I','$',0222,'I','$',0222,'I',04,0222,01,'$',0202,'I','$',0222,'I',' ','_',0377,0,0377,0,0377,0,0377,0,0377,0,0377,0,0252,04,0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0202,'I','$',0222,'I','$',0377,0,0377,0,0377,0,0377,0,0377,0,0377,0,0377,0,0230,'$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',022,'I',' ',017,0377,0,0377,0,0377,0,0377,0,0377,0,0377,0,0311,'$',0222,'I','$',0222,'I','$',0222,'H','$',0222,'I','$',0222,'I','$',022,'I','$',03,0377,0,0377,0,0377,0,0377,0,0377,0,0355,0311,'$',0222,'I','$',0222,'I','$',0222,'A','$',0220,'I','$',0220,011,'$',0222,'I','$',035,0177,0377,0,0377,0,0377,0,0377,0,0370,011,'$',0222,'I','$',0222,'I','$',0222,01,'$',0220,'I','$',0222,011,'$',0202,'I','$',02,0377,0,0377,0,0377,0,0377,0,0377,0,0200,'I','$',0222,'I','$',0222,'I','$',0222,011,'$',0222,'I','$',0222,011,'$',0200,'I','$',0222,0177,0377,0,0377,0,0377,0,0370,0342,'I','$',0222,'I','$',0222,'I','$',0222,011,'$',0202,'I','$',0222,'I','$',0222,'I','$',0223,'O',0377,0,0377,0,0377,0,0375,0222,'I','$',0222,'I','$',0222,'I','$',0220,'I','$',0202,'I','$',0222,'A','$',0222,011,'$',0222,'o',0377,0,0377,0,0377,0,0302,022,'I','$',0222,'I','$',0222,'I','$',0202,'I',' ',0222,'I','$',0222,'I',04,0222,'I','$',0222,03,0377,0,0377,0,0361,'@',0222,'I','$',0222,'I','$',0222,'I','$',0202,'I',' ',0222,'I','$',0222,'I',04,0222,'I','$',0222,01,'G','A',0310,04,0222,'I','$',0222,'I','$',0222,'I',' ',0222,'I',' ',0222,'I','$',0222,'I','$',0222,'I',04,0222,'A',' ',02,'@','$',0222,'I','$',0222,'I','$',0222,'I',04,0222,'I','$',0222,'I','$',0222,'I','$',022,'I',' ',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'H',04,0222,'I','$',0222,'I','$',0222,'I','$',0202,'I','$',0202,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'@','$',0222,'H','$',0222,'I','$',0222,'I','$',0222,'I','$',0202,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0220,01,'$',0222,'A','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,01,'$',0222,'I','$',0222,'I','$',0222,'I','$',0,'I','$',0222,011,'$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'H',04,0222,'I','$',0222,'I','$',0222,'I',' ',0222,'I','$',0220,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I',04,0222,'I','$',0222,'I','$',0222,'A','$',0222,'I','$',0202,'I','$',0222,'I','$',0222,'I','$',0222,'H','$',0222,'I','$',022,01,'$',0222,'I','$',0222,01,'$',0222,'I',0,0222,'I','$',0222,'I','$',0222,'I','$',0222,'I',0,022,'I','$',0222,'H',04,02,010,' ',022,'I','$',0222,'I',04,0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,01,'$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,01,'$',0222,'I','$',0222,'I','$',0222,'I','$',0222,011,'$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'A','$',0222,'I','$',0222,'I','$',0222,'I','$',022,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I',' ',022,'I','$',0222,'I','$',0222,011,'$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0202,'A','$',0222,'I',' ',022,011,'$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I',' ',02,'H','$',0222,'I','$',0222,'I','$',0222,'I','$',0222,'I','$',0377,0304,0,',',021,01,0,02,01,03,03,02,06,02,03,01,01,0,0,0,0,01,0,021,'1','!','A','Q',020,'P','a','0','q',' ',0241,0261,0301,0321,0360,'@',0201,'`',0221,0341,'p',0240,0377,0332,0,010,01,03,01,01,'?',020,0377,0,0301,0212,0267,'S',024,0334,017,0372,0305,0360,047,0267,'<',0277,'(',037,020,0377,0,'j',']',036,0375,0243,0352,'f','!',0243,0304,'U','m',0364,013,'f',0335,0362,0334,0315,047,07,036,0220,'+','D',0252,0335,0277,'{',0274,'9',0330,0226,047,'_','O',0354,0277,0236,0371,'1',026,'o','O',0202,020,'+','C',0275,'8',0300,'K','K',033,'z','t',0267,0367,04,'b',';',0322,0201,'l','J',0260,'z','b','3','2',0226,0357,'v','o',0323,0177,'L',025,0242,05,0331,'?','k',0275,0320,0335,'~','^',0245,'J',0365,0333,0275,0205,0273,0270,0210,0234,0236,0235,0235,0227,0317,0275,0271,0222,'?','N','J','v','3',0,030,';',0322,0201,'l','}',034,'1',0351,0231,0220,0236,0367,0134,0331,0337,0323,05,'h',0207,0253,0226,'{',0337,'8','>','^',0245,'M',0375,0273,0333,'9',047,0332,037,0265,027,025,0227,'F',032,'K',037,'G',0202,'_','>',0366,01,0134,'D',0267,'c',021,0323,'V','@',0314,'F',0222,0230,01,0374,020,017,0364,0267,0370,0333,0205,0274,02,0300,'w',0273,0373,'f','z',0247,'O',01,0274,0250,'*',0223,'+',0275,0307,'f',010,',',0307,0300,013,'3',011,0367,0273,0234,035,'v',035,'D',0217,'j',0204,'*',015,'_',0304,'a',0373,0211,'5','x','H',0301,0367,022,'c','E',0351,0231,0313,016,0366,0246,'H',0363,'=','J','o',011,'@','l',0334,'e',0366,0327,0303,020,'J','c',0201,034,0327,'0','j',0205,'K',0347,0210,04,024,035,'.',0371,'?',0236,0366,0240,'[',033,'O',06,'>',014,'K',0210,'=',0343,023,'@','O',033,'J',0275,'9','M',0324,'H',0315,0354,035,036,0367,010,0,'Q',0336,0376,0353,0370,0352,0222,0220,'K',0220,'W',0231,'y',0321,'r','K',010,'.',0311,0222,'"','}',0252,0134,'%',0373,'A','a',0342,'q',0320,'L',0214,0367,0270,0264,0362,0342,'*',0266,0364,06,0300,'G',0347,'`',0374,0312,012,'6',0134,'f','Y',032,0227,0373,'i','a',0343,0376,0300,'m',0256,',',0243,0240,'+','D','=',0134,0271,0357,'f',0230,010,0315,0365,0312,'5','T',0307,'*',0303,0215,0245,'Z','l','F',0363,'#',0345,0201,0346,0226,'Q','9',034,0270,024,'W','J',0233,0273,'w',0316,'0','>',0277,06,'W',0253,'H','q','(','B',':',0265,0227,'h','U','(','5',0254,0254,0272,'h',0300,'}',0342,0270,'p','{','t',0343,07,'|',0347,'g','W',017,'A',017,'M',0271,'y',0366,0227,'S',0334,0206,'e','c',0222,'R',012,'e',0362,'!',':',0316,0316,0360,0301,0320,'@','r',05,0210,0357,'n','d',0217,'3',0325,0306,0317,'w',0332,'c',0340,0313,'6',0247,0301,0226,'X',0231,0312,024,0222,035,0367,0225,0303,'~','u',0200,05,020,025,'D','*',0134,0271,0357,0234,'H',0353,'w',0354,',','f',0265,'R',0313,024,'`',0347,0314,'m','i','c',0220,'B','%',0324,'_',0367,0326,0263,0375,'?',0236,0371,0314,017,0323,0340,'A','w',030,'Q','R','1','Y','0',0300,0321,0301,0314,0255,0345,0347,'_','I','@',0264,0301,0323,'W',0301,0373,'S',035,0355,'L',0234,'f','z',0343,017,'t',0322,0311,0224,03,0310,'M',0246,0271,0304,'d',0303,0206,0230,' ',0275,'T',0325,0233,'c','}','`',0231,023,0304,'w',0315,014,0303,0255,010,0327,01,0367,0215,0257,',',013,'Y',0203,0270,'~',0265,0213,'[','|','L',0265,'+','[',0344,0224,0340,0267,0226,05,0264,'C',0327,0313,'=',0363,0226,0234,0373,'u',0305,010,'F','j',0355,0240,0361,020,0330,'U',0323,021,'r',0335,02,'%','*','I','L',0277,013,06,':','9','t',0267,0344,0376,'{',0341,';',0216,0331,036,0254,0202,0307,'5',0304,'l','[','_',0367,0,'W',0226,02,016,'4','%',0260,0207,0231,0232,0246,0253,0342,036,012,015,010,0224,0270,'3',0,012,';',0346,0204,'`',0353,'Z','o',0,0363,'*','u',0317,0364,0302,0316,'9','N',013,'b','T',0225,0264,0336,0223,0240,'D','o',' ','C',031,030,'L',0367,0316,'b','~',0235,'_',0245,06,0363,'Y','%',015,'W',0217,0323,0323,0204,'[','s',',','b',0375,0241,'1','U',0203,0363,0323,0226,037,0227,'l','Z',0325,0232,'`',0333,0342,'b',0201,025,0206,0367,'{',0315,';',017,0246,0246,'J','3','=','p',010,'2',0347,0304,'N','-',0204,'S',0351,025,'O','Z','3',0323,0206,017,0257,'l',0322,'5','3','+',0323,0217,0205,020,'3',014,0267,'o',0351,'q','!',023,0274,'c',0313,035,0353,0214,'M',035,0253,'.',0231,'o','5',0265,0217,0365,'9','A','4',0222,035,'?','W',0230,'u',0254,'z',023,'0',0306,'#',0265,03,'e',023,'i',017,0257,0241,0250,0260,0351,0370,0364,'y',0261,0372,'@','l',04,'K',0256,037,'>','b','R',0361,'y','f','Q','.',011,0356,0340,'7',0215,',',014,0264,0327,'8',0211,0316,0222,'[','g',03,0202,02,0264,'B',0327,0313,0236,0324,02,0334,'O',0,'z','#','M',0223,0311,'?',032,'<','D','g',0275,'=',0346,'^',011,0276,0326,'|',020,'V',0221,0307,033,':',017,023,'`',0215,0237,'x',0240,'[',',',')',0244,0320,0316,'o',0337,0336,0,'0','K','~','G',0347,0265,0320,07,0335,0364,0354,'K','m','~','>','$','>',0263,0330,'-','=',0342,0252,011,0316,0337,0324,031,'P','C',0372,'N',0357,'0',0241,'4','j',0276,'!','E','A',0244,0325,0217,0262,'K',0354,0263,0373,'p',0200,';','b','Q',0261,0230,0,0243,0265,0273,0375,'6',0364,0356,0233,':','|','Z',021,0227,0253,'[',0207,0322,']','}',0310,0211,'Z','1','p',027,0230,0233,'+',0230,0336,0346,0275,012,0201,0370,022,'D','f','F',011,014,0357,0333,'*','~','k',0324,0255,0335,0360,03,'`','#','3',0370,020,024,0313,'/','i',0331,0216,030,0211,'m','T',0322,017,0272,0300,0255,'%','-',0347,035,0266,0277,0350,0372,0226,0253,0177,0202,0347,0375,0237,033,'?','`',0356,'M','`',0206,'x','<',0302,0202,0202,'j','n',035,0272,0325,0343,0324,'p',';','A',034,'O','C','m',0333,'E','U',0276,0210,',',0314,023,0333,0253,'^','q',')',0247,0324,07,'n',0214,'>',0327,0246,0223,0232,037,0247,'p',0367,0226,0277,0307,0340,'C',0270,0351,0177,'G',0361,0201,0346,'a',035,0257,0360,'r','9','9',0372,0177,0203,0331,0301,0307,'t',0326,017,'o',0341,0223,0336,0204,0366,0277,0301,0310,0344,0347,0374,036,0316,016,';',0276,0230,'{',0177,0,036,'f',021,0332,0356,0376,'i',0376,07,'0','?','N',0363,'}','X','u',0365,0265,0234,016,0365,0244,0262,'~',0276,0253,0206,'F',031,0236,0367,'w',0263,'o','S',0222,0234,'w',0315,'c','#',0323,'m','|','3','1',0337,'Z',0322,0367,'=',014,0307,'/','I',0363,0200,0207,'N',0376,0205,0301,'n','g',0235,0371,'~',047,0231,'<',0350,'o',')',0222,0266,'m',027,0377,0,014,0237,0377,0304,0,',',021,01,0,01,02,03,06,07,01,01,01,01,01,0,0,0,0,01,021,0,'!','1','A','Q','P','a','q',0201,0301,0321,020,'0',0221,0241,0261,0341,0360,' ','@',0361,'p',0240,0377,0332,0,010,01,02,01,01,'?',020,0377,0,0301,0212,0203,0304,0326,'&','p',035,0342,0204,0342,'q','~',0237,0232,'~',023,0325,0352,'R',0237,'O',0272,'S',047,0241,'@',0306,0134,0276,0350,0215,0244,0335,047,0314,0324,0337,0223,'3','3','s',0267,'p',0244,'^','U',0207,0257,'i',0247,023,'q','Y',0357,0215,'#','$',0276,'D','7',0220,'9',0315,0272,0355,0311,'#',0300,0315,0341,'H','3',0334,0235,0134,0376,'7','y','@',0202,'V',0215,0234,'W','x',0375,'a',0266,0361,0370,0360,':',0273,0251,0214,0305,0375,'m',0336,0134,'P',034,035,'{','z',0351,0266,0333,0342,'d','j',0376,0306,0222,011,'_',0320,'n',0362,0336,'3','S','{',0247,'z',0,'A',0206,0332,'B','`','S',0233,'a',0201,0241,0337,'_',',',0315,0263,'7','C',0366,024,04,0300,0333,'@',0311,01,'K',0303,0215,0373,0336,0236,'Y','3','*',0200,0337,'s','u',0177,'a',0266,0345,0251,'f','-',']','8',036,0357,0226,0210,011,'Z',030,0355,'M',0306,0235,0366,0335,0305,0326,'t',';',0276,0307,'/','2','8','W','p','i',0277,0213,0226,0333,0314,0347,0203,0257,02,0222,0244,0255,0327,0313,0262,0366,'`','j',0366,'>',0265,0333,'k',0235,0217,'}',0305,'f',0327,0201,0241,0247,0227,047,'X',0342,0364,0342,0320,'P',0200,0260,'m',0247,'M',01,'P',0215,0300,0352,0361,0366,'<',0261,0316,0357,0266,0366,0262,'{',0305,0325,0327,'m',0312,'=',0214,'Z',0272,'r',0317,0177,017,',',0321,'J',0324,0243,'x','z',034,'=',0335,0267,'{','z',06,0275,0276,0253,033,0276,0134,'A',0335,0203,'C','^','.','[',0270,0355,0267,0134,0243,'W','J','a','%',0177,'A','D',0360,'h','A',047,0223,'j',0272,0217,'c','?','M','v',0332,'u',0200,0273,'O',0260,0233,015,0332,0361,'~',0250,'A',0270,'x','!',030,0225,' ','W',0376,0307,0213,014,'Z',035,0334,0250,'q','@',0261,0266,0345,0322,0317,'S',0247,03,0347,0207,0215,0211,'m',036,014,'I',024,'q',0266,0353,'B','7','?',0201,'N','U',033,0346,':',0272,0355,0273,0223,0320,'5',0355,0365,0342,'L',0362,0245,'x',0215,026,02,'V',0263,020,0360,0272,0256,'P',031,0317,0300,025,0202,0202,07,'S','q',0247,0177,0255,0267,0226,0326,06,0256,'E','(','r',0276,'2',0224,'P',0303,'4',')',0246,'4','K','*','&','0',024,0,0203,0302,'p','6',0311,0327,0267,0256,0233,'l',012,0220,024,0334,0221,0337,0237,0307,0360,'(',0363,0247,'9','U','4','j',0311,023,'D',0357,0232,0213,0360,'/',025,'7',0356,0357,0272,0200,0201,01,0266,0361,023,0213,0247,0177,'M','|','e',0315,'-',0266,012,0235,02,0325,'-','W',0244,'0',0222,0206,'`','U',020,0357,0247,0206,'3','o',0261,0255,015,'v','=',0367,0273,'l','d',0321,035,0371,'|',0322,'5','%','|',024,022,0323,0243,0221,0205,'E',0353,'R','C',024,0232,'f',0325,'h',0301,0250,'<',011,04,0255,'s','H','z','r',0371,0333,'n',0314,0134,'k',0224,0231,0241,0247,0354,0374,'V','&','t','L','a','G',0233,'t',0244,0231,0300,0240,'a',0235,0251,0300,0242,0336,020,0201,0273,0203,'C','^','y','n',0343,0267,'.',0246,0266,0367,0261,0363,0313,0370,'`',024,'*',0222,0231,'%','v',0207,05,'&','9','e','N','=',017,013,'W',0324,'t',0357,0367,'A',026,'6',0335,0361,0350,0232,0366,0372,0361,01,'-','!',01,'j',0236,'Q',030,'d',0254,'<','#','L','P',0,025,0234,'V','.',0206,0264,')',0300,'m',0274,'"',0317,'w','"',0230,';',0277,0243,0227,0214,0354,0223,032,0215,030,'T','$',0306,0204,'o','e',021,022,0234,0222,0201,026,'(',0321,'J',0324,'G','z','z','p','6',0345,0344,0321,0336,0346,0366,0335,0307,0307,'v',024,0210,0264,024,0262,0252,'D',0337,0301,07,0225,0361,0212,015,0334,033,0265,0347,0226,0353,0355,0313,0337,0255,0270,0373,0370,0376,034,'L',0351,022,'>',010,0353,'@',026,024,0353,'Y','x','4','V',0246,0367,'N',0373,0270,0320,01,06,0333,0311,013,03,'W','"',0224,0231,0361,'2',0252,0221,0212,0226,0310,0322,022,0317,0204,030,'K',026,0204,015,'c','6',0373,031,0264,'I',0330,0367,0337,0266,0326,'.',0325,0351,0310,'7',0352,0366,0335,0342,021,'b','S',0262,'P',0252,'J','r','=','j',021,'1',032,0316,'a',0251,'S','z','"',02,'V',0216,']',0363,'v',0356,'_',';','r',0344,0352,'n','4',0347,0361,0307,0304,0235,0345,'N',0263,0251,'@',0362,0250,0264,')',021,0206,0231,0272,0324,0,010,0360,0221,0203,0203,0257,'o',']','6',0347,'*','3','W','#',0366,'T',0321,'J',0273,0342,024,'M',0263,0250,'y',0300,0251,'P','T',0243,0312,0240,05,0350,0250,0226,0242,',',02,0247,015,'e',0323,0213,'D',0212,03,'m',0254,']',0253,'c',0312,'7',0352,0363,0370,0361,0263,'1',0312,0243,'m','4',0322,'1','4',0226,'J','!',04,0225,0233,0346,0245,015,'(','Y',0232,'9',0351,0272,0272,0355,0313,0317,0251,0270,0373,0370,0361,'t',0220,'P',0251,'$',0251,'9',06,036,010,0263,0251,'&',025,'5',0,'6',0323,0302,0327,'i','n',';',0271,0377,0,0335,0230,02,'T',025,'"',0263,0335,0207,0256,036,0223,'V','@',036,0257,0275,0275,0253,033,0134,0240,0370,')',0271,0261,0243,0177,0277,'z','2','|',015,'x','y','y',011,'{',0271,024,0204,0317,0210,0231,'A',0215,'M',034,0260,0236,'0','x','X','n',0263,0330,0317,0323,'f','K',0360,'s',03,0213,0320,0366,0245,0262,'t',',','z','w',0237,0344,':','B',0134,0240,034,0253,'&',0377,0,0274,'|',0253,0221,0243,0275,0315,0355,0367,'N',035,'K','I',032,0206,0204,'M',01,'8','5',030,'4','%','%','9',0363,'P',0262,'I',0341,0314,011,0320,0327,0366,'t','$',0300,0331,'H','c',014,0351,047,0252,0275,0215,0336,0276,'D','Z','d',0363,0313,0336,0334,0374,0233,0327,0323,'>',0360,'9',0322,0202,'Z','K','X',030,'V',0237,025,'1',0316,0211,0325,0245,'@',0231,'+','<',0312,'D',0263,'R','k',034,012,'F',04,0255,014,032,0256,0316,'[',')',032,0300,']',0250,'Y','a',0201,0325,0337,0361,0344,0242,06,'%',010,0271,'=',0363,0367,0376,0360,0313,0374,0216,'u',0214,'[',0372,'9','T',')','o',0235,011,0316,0250,0311,0201,'H',0317,014,0251,'2',03,012,0302,0261,034,0320,06,'P',01,05,'J',0202,0337,0223,0333,0376,'l',0267,'G',0215,0336,031,035,'}','<',0271,'y',0212,'N',016,'>',0377,0,'?',0335,0360,0327,0336,0375,'a',0353,'W',0231,0351,0324,'o','H',0345,'^',0202,'P',0204,'h',0306,01,'Z',0366,0323,0200,'w',0251,01,'8',0321,014,0273,0255,0332,'q','~',0350,0301,0300,'X',0331,'k','7','(','=',0217,'.','q','l',0331,0340,0366,'a',0376,0256,0256,'Q',0273,'W',0226,'[',0374,'b',015,'Y',027,'<','%',0230,032,0231,06,0372,'R','<','f',0255,0335,0332,':',022,0266,'(','&','&','+',0253,0333,'M',0231,'!',0324,037,'`',0351,0346,'j',024,'C',0304,0307,0277,'?',0341,0362,0200,'%',0254,016,0262,'4','2',0375,0257,0360,0203,'f',0262,0311,0322,0230,0202,037,015,034,'<',',',0265,0336,0203,'^',0177,034,'v','l',047,0314,'O','K',0365,0363,047,025,0256,'8',0230,0372,0237,037,0305,0331,0261,'~',',',0216,'X',0273,0370,0177,'p','#','@',0256,016,'t','N',0246,0211,0324,0337,0273,0276,0352,0,' ',0331,0262,0207,026,'=',':',0371,0230,0270,0251,0254,',',0204,0376,0341,0340,0347,'2',0303,0177,0326,'4',0341,0245,'|',0222,'N','Z','d','%',0213,0253,0233,0263,0205,0354,0211,0353,'H',0334,'C',0314,'D',0220,0360,'t','{','>',0315,'D','c',0240,']',0177,'z','R',0333,'F','F',0207,0354,'|',0253,'W',0247,0270,0373,0307,0323,'h','G',014,',','s',0307,0336,0177,0317,'i','z',0256,'G','W',0357,'h',0312,'|',0256,0347,'s',0257,0371,0204,';',0277,0247,0225,'`',0226,'{',0271,0273,'F',')',0306,'$',0342,'_',0353,0374,0326,0207,0246,'i',0317,027,0226,0323,0211,'p',0231,'8','7','?',0311,'c',0372,0356,0235,'_',0275,0251,020,'8',0276,'N',0277,0343,0306,'m',0366,0337,0312,0260,0233,'=',0367,0363,0332,0221,'F','!',047,022,0376,0370,0177,0216,0307,0364,0315,':',0277,'[','Z','8',0302,'d',0340,0334,0355,0376,033,035,0327,'r',':',0277,'{','^','p',0260,0273,0344,0353,0376,01,'N',0377,0,0236,0325,0206,'Q',0356,0346,0363,0332,0345,0236,0245,0270,0342,'{',0322,'#',017,0237,'`','t',0367,037,'x',0372,'m',0231,'`',0311,'s',0307,0337,0316,0262,'Y',0256,0367,'#',0276,0355,0265,'`','3','y','g',0337,0227,0232,0134,'J','A','Y','1',0342,0352,0346,0376,0313,'m',' ',0220,0323,'B',0335,'s',0203,0333,017,'2',0301,0374,03,'^',0177,034,'v',0345,0244,0317,'7',0352,'~',0317,0313,0231,015,'N',0334,0376,'(',01,06,0335,02,011,034,07,0311,0327,0327,0207,0366,012,0202,0232,':',0333,0313,'.','~',0215,037,0200,'6',0373,0310,'e',0314,0267,0305,'a','H',0340,0235,'F',0234,0257,'X',0355,'N','O',0261,0332,0234,0227,0350,'W',0321,024,0364,0207,0211,0355,025,0201,0334,0227,0365,0307,0377,0,0206,'O',0377,0304,0,'*',020,01,0,01,03,03,03,03,04,03,01,01,0,0,0,0,0,01,021,0,'!','1','A','Q','a',' ','q',0201,'0','@',0221,020,0241,0261,0301,'P',0321,0360,0341,0361,0377,0332,0,010,01,01,0,01,'?',020,0376,'q',0231,0366,02,'K','`',0312,0360,'^',0242,0246,'X','o','9',0225,0344,'*','F','>',0355,036,'W',0302,'T','s','2','}',0340,'A',0326,'*',021,0204,0302,'Q',0230,'q','2',0333,015,0224,'p','-',0307,0214,0177,'5',02,0311,' ',034,0255,'$',0302,0377,0,0351,0337,0317,0303,'Z',0316,0362,0236,0273,023,0203,0203,0351,0263,0352,0204,'^',0306,0265,'.',06,0321,0177,'0',0240,'(','&',0202,0373,01,0374,0327,0330,'^',0237,0222,0261,0217,'k',0373,0264,0266,0303,0301,'F',']',0377,0,'N',0215,013,'k',0265,0257,0357,'4',033,'G','-',0227,'d',0322,0236,'C','D','!',023,011,0374,0326,0360,'x',0332,'p',0325,0377,0,0326,012,0274,0351,0257,'0',037,'s','M','"',0224,011,'l','R',0256,0331,'I','k',0317,0364,0232,014,0230,0305,0327,0234,0251,0365,0351,')','+',0347,0320,'a','T','$','h',0246,'O',0200,0371,'R','c',04,'y','F',03,0347,0371,0230,'c','k',013,0350,013,'U',0377,0,'X',0253,0367,0232,0323,0265,'7',']',']','{',0,'"',0271,'o',0233,'`',0375,0325,0305,0374,'.','N','Z',0376,'8',0364,0222,0341,'@','J',0256,02,0205,'H','l',015,'^',0236,',','x',0251,'=','^',05,0226,'a',0334,'g',0301,0277,0363,012,017,0251,0330,024,0210,'N',0253,'p','k',0314,0373,026,0345,0261,'3','7','n',0267,'v',012,0324,0360,'.',03,'`',0320,0343,0323,'{',025,'K','o',0237,0353,0363,0265,01,0342,'e',023,0375,0232,']',0303,'l',0211,'r',0354,'X','8',017,'x','P',0214,0240,'i',0235,01,'&',015,'i',0301,0355,014,0230,0305,0245,0216,'f','6',0232,'X',0364,'d',0343,'H',0231,0327,'E',02,'#',014,0223,'.',05,'@',020,011,0226,0227,0237,0200,'i',020,033,0376,'c',0250,';','k','5',')','I','Y',0335,0257,0240,06,036,'t',0201,0370,0250,0312,0273,022,0244,0362,0227,'v','_',0304,0224,032,0300,'Z',026,0267,020,0363,036,')','t',020,032,015,'Y','c',0302,';',0225,027,0340,026,'D',0330,'~',0303,0250,0240,'J',034,0244,'@',031,0332,013,0351,'E',0274,0344,'0','p',0226,'}','@','l','e','D',01,0273,'H',0307,'a','!',0327,0303,'C',0316,0320,0273,'h','6',0372,07,0366,0324,0322,0244,0360,'4',015,0203,0323,0230,0302,0373,0216,017,0357,0376,0321,0220,04,0,0200,')','p',0202,0333,0276,0255,0203,0340,0266,0251,0356,'[',0365,0215,0311,'u',0134,012,0314,0260,0266,'"','e','|','a','9','^',0364,0261,0265,02,'[',0206,'$',0331,0376,0346,0225,021,0222,'s','>',0300,0215,036,'L',06,'Q','p','F',06,'f',0203,'&','(','c',0134,0226,']',0315,0373,0342,0237,0202,0242,0317,'H',02,0235,0277,'j',0226,0272,'!',0215,'-','m',0205,0260,'a',0364,'r',0267,'/','g',0253,'l','|',0261,0211,0222,'I',',','?',07,'-','N',036,'`',0267,0367,':',0276,0234,0212,036,05,0277,0273,'b',0211,04,'1',0373,'y','j',0361,026,'&',0302,0345,0335,'>','[','h',0236,0335,'@','U',0200,0326,0214,0324,0201,01,021,'f',0211,'|',033,0233,'5','$',0251,02,0224,0262,011,'S',' ',0304,0332,0311,0244,0322,0352,'P',0232,'R','d',035,0202,0305,0255,0355,0201,0230,'T',0230,0310,013,0210,0352,'R',047,'`','$',0227,'K','t',',','H','K',035,0221,'S',0220,012,'*',014,021,033,0302,0246,'&',0366,'M',033,0215,'<',0306,0252,'G',0246,0320,0361,0325,'w','0',':','3',0340,0276,0245,030,0223,'(','@',032,0253,'S',0341,0263,022,0337,0372,0343,0277,0246,0244,'!',0206,0333,0257,05,'F',0243,'a',0337,'s',0266,0305,'6','e',0302,'i',0243,0375,05,0335,'(','5',014,'8',0,0200,'=',0253,0250,'p',0240,031,'U',0301,'I',0335,0264,'G','d','Q',0354,'1',0205,'*','@',0222,'L',0202,'H',014,'h',0302,'5',0203,017,'x',0365,'-','2',0255,021,'`',0371,012,'M','k',0204,031,0273,0250,0264,'J',0236,015,'Y','"',0241,'B',0330,'.','Z',0200,0233,'}','%',0343,0341,0256,034,'!',0261,0241,0253,0346,022,0236,'S',0224,'2',0253,'Y',0356,0341,0330,'4','p','}',0337,0277,0244,0201,0234,01,'*',0354,'T',016,']',0231,0206,0337,0337,'=',0252,0316,'"',0227,'p',0332,01,0376,0232,0263,012,0224,0134,'}',0327,0330,03,'O','j','~',014,0204,'!',0330,0272,'3',022,05,0336,0352,'q','R',0206,'2',0261,'t',0230,0273,'I',0270,'a',0374,04,'u',0200,'R',03,'S','7',015,0224,0342,0213,0300,04,'F',0260,'e',0353,04,0260,0263,0,'E','_',0250,'N',0306,0201,0240,026,012,0261,025,0346,0273,0376,0337,0261,0343,0324,0304,0363,'"',0305,0327,0313,0247,037,'c',0234,'c','e',014,0,'j',0255,'G',021,07,'x','d','M',0215,'w','|','{','K',0276,0242,0221,'$',0305,0370,'D',031,'P',0275,'7',0,'1',027,0223,':',022,0336,'i',06,'=','Y','>','b',0373,'p','.',0275,0252,0306,'0','D','~',0302,0237,'4',0372,012,0221,0323,0212,':','*','-',0314,0324,0371,'i','R',')','`',0207,0205,'e',0361,'N',0260,0354,030,0254,0207,0370,0353,016,0225,0221,0273,'<','>',025,'1',0230,0356,0336,0242,'>',0364,015,0302,'~',010,'U',0360,'T',0247,0322,0325,0231,0200,0227,0323,0202,0313,'.',015,0323,'@','J',0272,05,035,0360,0214,0204,';',0274,014,'h',034,0252,0276,021,'5','z',0273,0270,')','3','1',0226,'W',0323,0203,0321,'m','`',0375,'5',0337,033,0326,'*',0327,'S',07,'w','2',0344,'c','b',0372,0333,0331,0341,06,'+',0253,']','H',0263,'w','D',0260,033,026,0263,0264,'a',0261,0320,03,'.','W',0322,0327,'I',012,':',04,0253,'m',012,024,033,06,0317,02,0,'{',0212,0217,016,'/',0347,'R','.',0305,'D',0243,'0',026,032,0310,017,'c','E','5','G',011,0200,020,036,0226,'r','8',026,0360,0322,'%',0361,'%',0333,0267,'{','f','j','w',0264,'^',0271,012,'b','7','6',0351,'A',024,0343,0314,0354,06,'3','d',0264,0336,']',0273,0314,'M',0303,016,0251,':',0237,025,'N','I',0200,06,'U','b','*',04,'4',0333,0325,0217,0305,0224,'e','5',02,0240,0212,0260,'e','h','9','i',0267,0320,'6',0332,'C',0323,016,0354,0343,0333,'g','-',07,'d','a',0200,0245,031,026,0366,0307,0367,015,'w',0306,0360,0,0,'@','{','!','O',0324,'E','%','E',0202,0220,035,0240,'F','l',0341,'T',0233,0353,'h',0221,0364,'c',0355,',',0220,022,'(',0231,'%',0301,'Z',0336,0267,021,026,'+',033,'6','d','-','M',0263,'J',0361,021,'&','K',01,'t',0276,0311,0202,0300,'#',0354,04,'N',0365,0216,0371,011,'R','`',0225,013,0351,'l','f',0202,025,0317,'A',025,'n',0230,0210,'[','%',0253,0206,'h',0264,'!',012,0341,'>',0266,0264,0327,0230,'1',':',036,03,'9','H',013,0341,'P',0200,014,0255,'K',0225,'(',0266,0344,0371,'~',0307,0237,'N','|',0212,0353,0201,0252,0340,0241,0337,'Q','K',0353,'*',0265,0313,0214,0264,0230,'9','`','<',0340,'h',0254,027,0,07,0345,0347,'_','e',0222,':',0271,0203,0271,0276,0203,0361,'-',015,0134,0341,0220,0301,'[','Z','8','I','g',0321,'1','3','r',0252,0275,0214,'d','2',0206,0312,0207,02,024,0362,'l',0220,0254,'8','D','#','*',02,0,010,03,07,0267,0222,0202,0304,'[',023,'g',0200,0260,0303,027,032,0260,'1','1','/','v','-','v','e',0345,'T',0177,0214,'a','B','2',030,010,0256,'$',04,0213,0,0,0,013,01,'Y',0226,'o',0260,'~','&',0274,0366,0364,0322,0372,'@','%','W',01,'R',0342,04,'F',0374,017,07,0335,0361,'K',0220,0262,0375,0327,'`','%',']',02,0240,0251,'q',0341,0324,'{',030,015,017,'>',0312,0345,'m',010,0307,0224,'[',0255,'[',02,'T',02,0324,0335,'r',0242,0367,06,047,0264,0204,0255,0,07,'X','A','A',026,0357,011,04,0234,'6',0324,0264,0301,0232,020,'H',023,0210,'$',0347,0270,'(',0221,0227,013,'{',0273,0266,'2',0337,'+',0303,'+',0330,0312,'S','y','n','M','_',0301,0306,0224,';',0266,017,'7','v',0337,0362,0225,'J',0262,0271,'_','O',07,04,0203,'6',0256,']','8',0357,'H','@',0243,0,'e',0240,0214,0307,0201,'t',0271,0336,0331,0360,'4',0277,0261,0310,0333,0370,0250,'W',0262,0241,'W',0,'R',02,0323,0331,0234,0313,'.','@','r',0266,'V',0352,0355,0200,':',0210,0211,037,0247,0,'*',0321,0256,'.','k',0363,'u','~','.',0340,0310,'0',034,0215,03,'i',0,0,06,0307,0273,'1',0362,0366,0256,0300,0325,'X',03,'v',0235,0325,'-',0331,03,'a',0371,']','U',0253,0246,0214,0213,0351,037,0355,')',0373,'s','t',07,0350,012,02,0327,0270,0227,'r',0233,'#',0241,022,'#',0321,0201,0221,'l',',','z','v','5',0370,0336,0261,'Z',0307,0243,'b',0326,'l','8',0346,0372,023,0354,'u',0347,0267,'>',0332,0342,0200,'7','j','w',0,0272,0246,'a',0274,'C',0235,06,0,':',0222,031,013,0134,020,0313,'C',':',04,012,016,'D',0225,0312,0304,';',0337,',',0327,0202,'J',0376,0360,031,0,'%',0134,024,0231,0255,022,0266,05,'q',0223,0204,0272,0330,0342,0241,0206,03,'-','_',0214,0365,'h','n',0362,0377,0,'[','Q','2','|','a',0246,0306,0324,' ',0221,0221,0250,0350,0206,0301,0236,0364,0221,0312,0300,'Y',0353,0231,0250,0303,0370,016,']','?',0345,021,0303,0264,'b',0242,0271,0201,0264,0264,'M',0335,'v',';',0224,033,0204,'l',0,0200,015,017,'b','~','=','Y','5',07,0,015,'I',0325,0226,0311,0215,0210,0300,0346,0202,0352,'z',0246,']','y','x','6','B','4',026,':',0222,0224,0213,05,0322,'u',010,0276,'r',0212,0373,0335,0341,'%',0312,'c',0310,0317,026,0326,0325,0273,014,0330,0377,0,03,0363,0333,0350,'0',0311,'f',0256,0347,']','}',0216,0277,'B',0240,0220,0202,0202,0237,0235,0326,'S',0225,06,'G',0242,'@','6',03,'C','u',0340,0242,'&','E',0301,'}','E',0253,'d','Q',027,0376,'J',0277,04,0255,0206,0205,0364,0311,0213,'%',0236,'`','/',03,'b',0203,0330,0240,'K',011,0323,035,'L',0212,0373,';',031,0352,0205,022,02,0254,0272,0346,'R',02,0371,0271,'4',037,0225,014,'"',0,0260,07,0275,'F',0340,0256,0274,':',0206,0322,0362,0301,0275,' ','B',0243,'(','e','W','y',0254,'?',0272,031,0302,0367,'h',0177,0312,'Y','e',0317,0321,0247,0324,'l','5',0241,0351,031,'N',032,0366,0207,'t',0321,0342,033,0205,0273,'*',0311,0271,'D',0211,06,'+','w','R',0134,'3',012,0372,'!',02,0214,01,0226,0212,047,021,0237,0200,0376,0371,0355,'P','2',0303,'B',0225,'.',0,'&',0222,0313,0240,0223,'h','x',',','h','A',0231,0226,0270,04,'q','i',0306,0325,0240,015,0311,020,017,0364,0256,0340,':','"',0214,'"','{',01,0205,0244,0277,'3','e','q',0207,035,0340,0322,0365,'F',0237,'r',032,0335,0363,'1',07,0254,0213,0312,0207,'_',07,0226,0200,',',0,0,036,0367,0275,'2',0305,0240,0335,'X',016,0365,01,06,0202,0310,'0',0366,0373,0252,0353,'N',0324,0206,047,0276,0220,0253,0247,'i',0330,'4',016,014,'}','B','Y','1','T',0251,0204,0212,'U','#','!',0330,'-','#',024,0211,0251,'L',0355,0312,0263,0331,'S',0373,0203,031,0226,0253,0332,0237,022,0245,']','_',0246,'b',0213,'L',0277,0355,0276,'v',0372,']',017,'g',0323,'!','v','W',0340,015,'S',0364,0272,0204,'G','.','$',0360,0276,'p',024,0210,04,'D','n',047,0255,016,0247,04,035,'i',0272,0240,'m','2',0330,'i','(','.','G',024,0372,030,06,0305,0345,0227,0251,0310,0345,'%','[',033,'*',024,'f',0234,0310,021,'s',0311,0250,023,0,036,0367,025,0230,06,011,'l','o','0',0270,'y','u',0240,04,'e',010,0,0312,0323,'9',0243,0277,'p',0134,0277,0216,0214,0324,0202,0362,'k','F','8','s','m','8',0244,0272,'P',0271,'(',0343,'9',0221,0244,'R','Z',0336,024,02,0210,034,0224,0371,0372,'H',' ',0243,0212,'Z',036,0372,0354,'x',0240,'L','`',010,0,0300,'T',' ',0314,022,0273,'Y',0134,0344,0345,'.',0305,033,'9','Q','*',0356,0364,'?',0253,0261,0201,027,027,'Z',0255,'E',0252,0257,0254,'A','7',0260,'%',022,':',0203,0310,0225,0304,0352,'q',017,025,'&','N','s','x',0324,02,0327,'A',037,' ',06,0261,0333,0246,0241,'}',0364,0306,'`','[',0265,0274,'`',0340,0227,'S',0351,0363,'*',0276,'?','w',0343,0177,0253,'S','L',0350,';',0323,0326,0247,'-',0242,0320,',',0134,032,0277,0350,0337,0344,012,0216,0207,0313,0305,0373,0322,'c',0215,'S','X','o',0201,0324,0330,0372,0134,0315,0312,'c','Y','p','T','y',05,0327,'+','U',0313,'G',0341,',',0344,02,01,030,0212,0261,':',0260,',',031,'-',021,0201,'h',020,06,0307,'J',0201,014,0234,'c','r','Z','p',0311,'p','j',',',0326,021,0306,0203,'`',0350,'o',0352,04,'h',0316,'%','l',0315,0226,'F','"','+',0236,0241,'#',0200,'I','a',0267,'T','*',0361,'(',0331,015,0241,'M',0347,04,0254,022,0275,0364,'7',0236,'l','+',0366,'5',']',01,0247,0304,0362,'u',']',015,0203,01,0240,'S','4',031,']',0243,0252,0340,0374,0305,'$',07,0250,'J',0256,'W',0350,'n',0312,0200,'5','i',0232,011,0330,0353,0265,04,'K','(','F','c',0275,010,0270,022,'M','*',0250,04,011,0205,0241,0332,0237,'I',0246,'x',0357,'K','Y','_','?','D',0272,0320,011,'U',0300,'Q',0310,03,03,0246,0303,0203,0356,0320,0252,030,'r',0243,0,'P',';',0321,'l',012,016,0221,'C',0313,'i',0242,'a','W',013,0371,'Z',0310,'n','`',0,'u','$',0332,0272,0230,02,0326,'#','g',0,'%',0372,'a',' ',0252,020,01,0225,'i','!',' ',011,'h',0200,0224,0233,0356,'d',0134,':',0235,0316,0344,0232,0272,0325,0360,'G','y','=',0363,0312,'S',0260,02,'U','v',0212,0226,0303,'6',0334,0231,0274,047,0202,015,'(',0353,0251,0376,032,0324,0225,'&','E',0217,07,0373,'_',0250,0311,0231,'c',0274,'P',0301,'*','a',0275,031,0327,'o',0271,'x',0245,'@',0202,015,0271,0251,'y',016,'F',0365,030,0350,'D','`',0265,'0',0356,0317,0323,015,']',0246,'O',0315,0323,0216,0377,0,'K','}',0333,0373,0271,0236,0301,0360,'w','~',0203,'x','H',0204,0231,0202,0307,035,0264,0262,010,'/',04,0350,'l',0256,'D','N',0230,'P',0222,06,0222,0321,020,'G',0212,'%','7','d',020,0200,0304,0260,'X',017,0244,'6',0203,0250,0200,'E',010,0226,02,'a','.',0235,'G',0332,020,'x',0250,0216,0216,0234,0322,0210,032,03,0212,'p',030,'c',0,01,0357,0264,0316,017,0362,'y','l',0274,'A',0253,0364,0314,0233,0201,'`',0375,037,0231,0343,0241,037,0221,'3',0315,'#',0,020,0233,0325,0205,0302,0207,'S',0262,'`',030,0245,'e',027,'{',0216,0335,0252,'R','C',0224,0337,'C',0350,0230,0234,'C',0215,016,0315,0377,0,0355,0,0,' ','0',025,01,'*',0315,0330,0356,'r',031,'w',0261,0275,'c',0353,013,06,0270,07,0337,0337,'u','|','2',0352,0331,'^','^','z',')','`',0202,0260,'J','m',0364,0234,'C',0243,'I',0264,'d',0243,':',04,'O',017,'U',0261,']',0231,02,0355,0204,0220,016,013,0241,0357,0263,022,0377,0,'T',0267,0203,'/',07,'%','(','7',0345,0321,0225,0254,03,'p','#','|','/','v',0207,0235,0276,0257,0221,0320,015,'i',0321,0306,'F',035,0365,025,012,'e',0231,0243,0263,'d',0213,017,0356,0200,010,0210,0352,'U',0216,'(','M',0204,']',047,'}','>',0305,0312,0272,0264,07,'C','t','v',0326,'U','`',015,033,0273,0257,'.','j','E',0364,025,0201,0273,0337,'@',0335,0240,'@','X','v',0335,0335,'r',0272,0257,'F',0243,0347,0262,0256,'h',0336,0316,0214,'4','D',0270,0220,015,' ','C',')','1','`',015,036,0230,0305,'+',0243,010,'h',0210,047,'j','p','K',0316,0302,'3',0364,0230,'n',0207,'_','@',030,034,' ',023,'n',0246,'P',0301,0324,0331,0326,')',0314,'}',0242,'M','i',0204,020,'c',0336,0271,04,'?',0,022,0253,0265,03,04,0210,0266,'W','f',0350,0236,0320,'i','L','-',0253,014,0351,016,'Z',0270,'9',0235,0206,0201,0300,'}','J',0375,0262,0306,015,0353,03,'/','s',0232,'z',0322,0233,035,0232,0341,'`',0260,'+','h',']','p','8',0244,0316,'l',0306,0352,'p',0212,']','Z','K',0341,01,'*',0270,012,032,'$',01,0350,0350,'8','?',0272,05,'C',036,'T','`','(',0323,'c',0330,'<',037,01,0345,0327,0253,031,'F',0347,'j','(','K','y',0220,026,0343,0252,025,0255,'`',0,0301,0336,'V',0330,'h',0375,04,'>',0277,'H','H','H',031,'&','3',0241,'n',0241,0312,0350,'W','9',020,'K',0245,'c',0337,'<',':',01,0373,0345,0356,'9','x',0215,0332,'X','%',0305,'%',0372,0354,030,0326,0375,'N',';',0375,'x',0314,0373,0250,'C',0201,01,'V',0276,0314,0350,'p','P',0354,06,012,021,0250,014,'"',0366,'?',0134,'8','6',0330,'?',0276,0234,'_','_',0245,0206,0255,0335,0324,0370,0206,'6',';',0275,'a',0345,0244,'*',',','r','"',0224,0212,0276,'q','F',0307,011,'E',0214,'*','i',0322,0247,0332,'q','&',0305,0373,'C','?',024,'i',0235,0,0204,022,'h',0210,0365,'3',0244,0266,020,')',0330,05,0245,0322,'j',0230,'M',0233,'h',016,07,'H','5','.','P','#','&','h',0374,0342,0201,'1',0200,' ',03,01,0357,'e','t','^','9','K',035,0213,0257,03,'K',0225,031,0304,'e','k',021,'o',0245,0366,0273,0277,0211,0343,0243,012,'I','h',0246,012,'D',0250,012,'r',02,0305,0253,'6',0314,02,'[','o','G','V','a','[',07,0321,0344,023,012,0370,017,0357,0216,0364,04,0,020,06,012,'{',0310,'(',',','7',';','K','>',06,0266,0,0,' ','4','=',07,01,01,0331,0234,0214,0200,0221,'b',0360,0315,0216,0246,'}','u','r',0313,021,0240,04,0336,0374,0345,0351,'V',0354,0,0233,',',0374,'3','{',0236,0254,'9','%',0250,'Y','f','A',0344,';','}',0350,'2',0,'J',0270,')',0244,0314,0300,'b',0377,0,0270,'-',0300,'U',0350,'c',023,0335,0360,'?',0330,0232,0226,'~',047,0350,0340,0307,0326,0347,0261,0205,0276,'k',0202,',','$',0243,'e',03,0376,'3','X','.','Y',0277,0302,0201,01,'(','e',0134,0323,':','`',0273,'i','V',0204,026,'6',015,'(',0323,0277,'w',032,0313,0265,'F',0221,0304,0271,'Z',0257,'-',07,0220,0272,0222,014,0237,0200,0325,'B',0203,'n',0357,0235,0326,0352,0312,0273,0276,0211,0134,030,037,'a','o',0370,'*',016,0362,0264,'"','G','"',047,0216,0231,'D',0212,014,0331,0267,01,',',0362,0247,0236,0225,0216,0335,'/',0303,035,0206,0213,0243,0214,0275,'!','/','c',0224,0200,015,'U','B',0213,0330,0212,0302,0372,03,'T',0367,0262,012,'k',0310,0276,'7',0315,0307,'c',0205,'(',025,'@','.',0255,'*','2','t','L','=','|',0364,0343,0352,0360,0324,0335,0273,'b',0210,0320,'@',012,'n','V',0310,0223,'L',0356,'w','K',0332,0247,0365,0263,025,'!','E',0311,027,';',')',02,'>','a',0313,'H',0331,0300,011,'W','j',020,'$',0317,0260,036,0337,0237,024,'-',0302,014,0241,0200,016,0364,'l',020,0134,'8',027,0223,0313,'.',0276,0231,035,023,0234,025,014,0341,'p',0332,036,0222,' ','D','b','&','2',0210,021,0315,0301,'!','"','x',0351,0314,0265,'2',06,0,'<',0272,'9',0351,'1','#',0325,0,'%',0346,0361,034,0373,0331,0216,0203,06,0357,01,0312,0300,'w',0251,0206,0203,014,034,07,01,01,0332,0261,'6','R','<','}',0337,0307,0177,0251,0247,'$','(','O','.','N',0356,0253,'E',0316,0324,0223,';',0370,0240,0200,0270,0201,0336,0215,0211,'.','&',0264,0240,022,0302,0271,'K','h',0370,0237,'H','k','T',0206,0313,0257,0352,'y','v',0372,'f',0243,07,0317,'g',0275,0301,0304,0272,0236,0244,0273,0225,0266,0267,0356,01,0234,0362,0352,'P','L',0310,0200,022,'Y',0201,0223,'x',023,'~',0200,'8',' ',0224,'$',047,'T',07,0232,0177,'"',0245,'*','r',0256,0257,'H',035,0227,0326,'`',024,0354,0315,0277,0323,0336,0301,0315,'y',026,0306,0370,0270,0356,0360,0250,0223,05,0306,'|',037,0355,06,0230,012,0316,0337,0352,04,0224,'4',0246,0240,0320,'_',0215,0265,0243,036,'s',0332,032,0264,0215,023,013,0265,031,0242,0200,0272,035,'i',0230,0276,0366,0210,0321,0204,'j',0321,'s','a',037,'m',07,'/',0367,'A','t',0240,' ',03,01,'S','(',023,020,0314,027,0234,0273,035,0312,04,0306,0,'@',032,07,0250,'*',0257,'D','H','e',0323,0250,0216,030,0213,'c',0301,'m','1','7',0345,0321,';','5','"',011,021,0356,'g',0211,0352,0221,0322,'~',0212,'H',0305,0337,0260,0215,'=','f','Q',0203,'$',017,0313,'I',011,'f','A',')',06,'&',0344,017,0315,032,'}',0376,0362,037,'L',' ',0304,0302,'7',0267,0354,037,0224,0247,'Y','e',0272,0237,'v',0225,0244,'*','f',026,0276,'z','q',0365,'r','D',0260,'3',033,'P','n','_',015,032,'>',0217,05,'b',0260,'D','f',0367,0242,'6','0',0272,0212,025,0340,0312,0272,0356,0264,'9',0244,0216,0305,0251,0263,0377,0,'h',';','T','v',06,05,0337,'/',0373,'J',0260,'b','%',0375,'$',027,0250,027,']',05,0337,0312,'_',0353,'O','T',0312,01,010,0341,'*','G',0322,'T',0330,0312,'v','S','8','z','T',04,0225,'h','#',0376,0236,':',05,0242,'h',0230,0,047,0226,'Q',0331,0351,'H',0316,0,0225,0134,01,'J',0225,'D','U',04,0312,0335,'w','z','j',0257,06,0241,0330,'.',0270,012,0276,'K',03,'A',0334,'O',0317,0205,'g',0236,'M',0231,0265,0221,0363,'H',0225,'U','n',0256,0277,0134,0343,']',0373,'R','i',0305,'5',',',0301,0,031,023,'B','^',0326,'I',0304,'z','*',0320,'(',0300,04,0253,0342,0233,'0',';','V',0333,0313,'u',0345,0254,'w',0272,0253,0355,'w',0177,023,0277,0325,0347,0256,01,'v',0221,011,0221,06,0224,0207,'h','K','4','6',0372,031,'t',0234,0324,016,0330,'L',015,'b',0214,0336,06,0261,'J',0252,0267,'k',011,0255,'%',0366,'{',0271,'|','s','H','@',0243,0,'e',0242,0216,0202,0223,'9',0301,0316,0257,'6',0320,0365,0325,'8','M','a','`','w','S',0335,'z','Z',0362,'B',0303,'d',0336,'m','7',0216,0202,'r','[',0360,0203,0356,05,0362,'t',0304,0370,0134,'U',036,06,'x',0364,0241,'<',' ',03,0225,0250,0357,'Y','_','z',0277,0237,0206,0265,0236,'Z',0277,'b',0370,'8',0243,'l','i','R',0,0250,'T',037,0372,025,0276,'&',0247,'M','0',0302,036,'[','}',0253,016,';',0177,0,'P',0244,0365,0300,037,'9',0373,0324,0201,05,'*','I',0311,'k',0333,'5',0225,'Y',020,014,0215,'C','1',010,'3',0375,03,047,0217,'B',0303,'&','9','y','>',0350,0227,0200,0321,0245,0326,'C','-',0364,0207,'z',0277,0356,017,0321,0301,0212,0217,0244,01,07,037,'x',0356,0320,'R',0200,'.','Z','t',0314,015,'#','o',0250,'D',010,'6',0237,0246,'y',0331,013,'m',0366,'2',0370,'7',0372,'^','"','9',0354,'m',0373,026,'=',0200,',',0302,0315,0310,'3',0302,0241,0313,0322,'o',023,04,015,'k',0330,0240,0311,'%',0317,0251,0246,'.',0216,'j',0217,0301,'|',0364,0243,0316,'y','+','>','/',0237,'E',0354,'R',0216,'$',0340,0320,0335,'l','|',025,0236,0346,'B','s','?',0221,0361,05,0276,0223,0334,';','O',0177,0374,'X',0373,'S',0231,'.',0222,0321,0343,'^',0354,0364,0276,'r','2',0302,'U',0270,0330,0375,06,0275,0234,0325,0360,0300,017,0204,'v',023,0327,0134,'1',0305,0205,'l','<',0254,025,'5',0,0322,023,0241,0300,'@',033,05,'f',0253,'*','6',0326,0375,017,0373,'I','b',0351,0322,0324,'h','e',0302,03,0363,'Y',0202,022,0254,0305,017,0232,'k',0266,';','Q',0362,'[','s','%','6','#','Y',022,0215,0217,0225,'0',0366,0245,0310,020,0203,0351,'k','s',0201,'m','w',0375,0255,'Y',0216,0307,0355,0345,0253,'o',01,'M','l',037,01,0345,0321,0241,'D',0261,0340,02,0,0366,027,0313,'!',0206,06,0277,017,0216,0241,026,016,']',0375,0207,0324,0313,'g',0244,'t',014,0223,05,0304,0372,06,'~',014,0207,07,'@',0340,'2',0272,036,')',0350,0267,0266,0210,'4',015,0277,'t','Y',0332,'W',01,'N','#',011,'6',036,']',016,'>','v',0364,036,06,0204,'4','5','~','m',0346,0274,'-',0373,'d',027,0202,'O',035,'w',023,'9','+','a','~',023,0334,0323,014,0321,'!',0276,05,0356,0340,0363,'F',012,0250,03,'Z','3',031,016,07,024,011,'a',0305,0274,0215,010,021,0247,'j',0256,0275,033,013,'=',0366,0254,0,021,'T','l',0134,'%',03,010,'%',0304,0247,04,0304,0345,'v',0244,0204,0360,011,'U',0301,'E',0310,0302,'}',035,07,07,0346,'h',0327,033,' ',0257,0366,'k',013,0204,013,0261,0177,06,03,'c','u',0366,' ',021,'$',0331,'o','|','/',0236,0244,0320,0215,0262,' ',0236,0350,0375,'n','l',0307,0373,0266,0335,'7','m',0210,0236,'W','?','w',0317,0240,0273,022,0310,0264,033,0367,0253,0366,0203,'J','(',0252,'a',0200,0313,'I',0321,'k',0237,0366,'~','=',025,'m','P',0211,0221,0242,'_',011,'(',0323,01,0341,032,047,0240,'_',0356,'r','/','d',0351,'5','$',0332,0301,027,0355,027,0357,06,0264,0276,'P','Y','e','Z','w','_',0315,'5','s',':','4',032,07,01,'j',0324,0303,0,'e',0336,0223,0253,'R',033,0272,024,0362,'h','p',0300,'h','U',0376,'^',0346,0223,0264,0270,'{','6',0244,0,'%','l',05,'M','"',0222,'"',0245,0233,0211,'z',0367,')',0333,'Q','*',0345,'j','"',0374,'C','e',0303,0372,0236,'x',0372,'X','Z',031,'n',0237,0227,0341,0335,0366,'B',0261,'1',0237,07,0357,0253,0214,0276,'0','?','_','U',0322,',','n','s',0310,037,'=','.','v',')',0331,'s',0370,':',0346,'j','}',0321,03,0362,0237,'H',0321,'@','C','R',0177,' ',0276,017,'O','A','!','=',0204,0360,0224,0305,0273,012,0350,0203,0310,'K',0313,0240,01,031,'B',0,'2',0264,0322,0356,0267,'k','x',0356,0257,0332,015,'+','8','o','#','m','o',0214,'|',0321,'S',04,'S',0312,')','@',0260,032,'8','h',013,0202,020,0221,0245,'=',04,'.','7',0305,'-','d',0271,'4','4',0206,022,0321,'_',')','k',0217,0370,'R','O','2',0311,0177,024,0326,0314,0244,0320,0247,'T',0300,'6',0233,016,0134,'|',0355,'F',0214,'@',0254,06,012,'{',0204,0302,'-',0233,0261,0253,0305,0265,0240,0,0,05,0200,0366,'D',0234,0231,'j',010,0362,0201,0347,0251,0206,0304,037,02,017,0307,0324,0204,'"','~',0311,0373,'t',0245,0200,'R','W','C',0257,0331,0363,0326,0203,022,'#',0367,'?','O',0244,0313,'0',0214,'t',017,'N',0361,'0',0206,017,0263,017,0212,0235,025,'@',0354,'y',022,'y',0242,0306,'B',0340,022,'?',017,0326,0310,'s',0232,0276,024,0362,0267,'b',0222,0261,'E',0217,047,'_',035,'9','i','e',0226,0357,0320,'K','o',0376,0320,0244,'H',0207,'5',0273,037,0240,027,0227,0215,0307,0212,022,0345,0346,'>','J',023,0221,04,'V','u','%',0370,0177,0302,0236,'X','G',0312,0265,014,')','i',0376,0340,0301,'V','w',0242,0217,0320,'A','z',0267,0235,025,0375,0204,0267,0366,'p','O',0375,0343,0365,0325,0315,0237,':','_',0277,0253,0263,0322,'z',026,030,0200,0346,':',0363,0354,'y',0320,'G',0363,0134,'E','$',0203,011,0362,'S',0350,0205,'|',017,0310,0372,'x',0243,'e',0237,0213,0331,0371,0317,0232,011,0253,'(',0335,0326,0371,'1',0331,0372,'4','Z','i',0205,0341,0345,'~','.',0351,'G','.','E','x',',',016,0,0,0340,0251,':','V','V',0236,017,0337,'u',0350,'0',0210,0134,'J',':','v',03,0363,'7',0241,0242,0222,047,0320,'V',0303,'B','4','j',0322,0251,'[',0255,'F','c',0300,'g',0374,0237,0307,'z',05,0,'*',0330,012,'8',010,'i','K',0344,0356,'j',0363,'m',017,'h','L',0260,0215,0314,'u','[',')',0227,0374,0213,0177,0251,'x','B',0242,' ',0237,0336,0353,0317,'K',0376,03,014,'(',013,0306,'=',0323,0320,0260,0252,0362,0200,'_','p',0361,'P','Q','d',035,0322,'~','_',036,0246,'<','(',035,0253,0236,'J','*','3','u','0','>','^',023,0342,0316,0224,030,032,'h',047,'Q',0233,'?',0235,'&',0203,0242,01,0254,0254,'!',0273,0241,0241,0312,0324,'-',0336,031,0316,'G',0206,'^','c','n',0262,0310,'^','U',027,025,0343,0221,'x',0336,0232,0322,0273,'h',032,024,0202,0251,0205,0337,0247,0236,0274,'P',0,0,020,06,0225,'q','H',0345,0261,'~','?',0342,0317,0265,01,'D','E',0313,'d',0370,'u',017,0260,0374,0200,030,016,03,0353,'h','m',0224,025,0361,0362,':','I',025,0265,01,'I',0354,'^',0360,'z',030,047,'(',0304,0277,0271,0366,0252,015,'$','O','i',0227,0355,0343,0324,0134,' ',0374,0320,0342,0230,'9',')',0343,'q',0344,'m',0343,0351,'4',06,0377,0,0327,'w','c','?',0373,'I','q',0244,'%','W','/',0243,'m',0134,015,015,0327,0200,0275,021,022,'3',015,0333,'*',0261,010,'@',0304,0335,0356,0340,'7','v',0232,'%',0316,0300,07,0373,'>',0325,027,0204,0232,0374,')',035,')',0233,'J','I',02,'X',0326,0364,017,0240,'e',0260,020,'}','f',0242,0373,0353,033,0311,0360,0364,0360,' ',0205,06,'o','"',0226,'A','|','3',' ',037,'g',0257,0340,0211,'M',0237,'x',0361,'X',0304,0234,0332,'H',032,0,'U',' ',0350,0236,0245,0326,'t',0226,0325,0235,0207,0354,0367,'i',0314,'%','#','v',0341,0371,'m','V',0271,0310,'L',0233,035,0367,'}',',',037,0260,'%',0362,07,'w','?',024,'i','B',014,0241,0200,'<',0321,021,015,'x',0263,'`',0370,017,'.',0276,0332,'o','f',030,0204,0276,0362,0277,0,0351,'F',0214,0226,'R',03,0362,'/','=',06,'a','l',021,0252,0327,0226,0360,0352,'h',0331,0372,0271,0235,'[',0252,0266,'u',0231,0353,0237,0326,'&',0240,0211,0360,0326,0335,'o',0304,0250,'/',0332,0240,'b',024,0373,0347,0333,0231,06,0302,022,0332,'=',0232,0277,0367,0351,021,'d',' ',0340,0307,0345,017,'.',0336,0334,'F','I','B',0314,022,'p',0201,'8','z','T',0344,0354,'L',010,0327,0306,'o',0327,'A',0253,'I',02,'b',02,'m','6','.',0361,0324,'0',0364,' ',0204,'d',033,0223,0377,0,0361,0350,'Z','h',023,0244,'m','}',0215,'"',025,0331,0370,'o',0370,'|',0275,0265,0235,0314,'l','5','^',02,0364,':',0300,0335,'g','Q','r',0265,04,0302,'-',0262,0261,'w',0204,'v',0227,'J',015,0312,'6',0,'@',06,0321,0355,0254,'h',030,'J',0302,01,0252,0240,035,0351,0363,0324,0304,0231,'R',0345,'V','g',0247,0205,0374,0230,'7',04,0177,0263,0240,'&','4',0214,'#','d',0246,'l',0317,'Y',035,'s','2','P',0352,'#',0322,0326,0252,'~',0360,0235,0262,02,0364,027,'%',0216,0373,'K',0354,0373,0352,07,'y',06,'@',0363,021,0347,0333,'`',0271,020,'K',0344,016,0356,'_',024,'&',0235,0250,'/',0353,0232,0321,'D',0262,0356,']',0355,0241,0300,'{','t',047,010,0316,025,'V',0362,0251,'&',0266,'6','z',0255,0356,0373,0223,'&',0356,0276,0224,0230,0377,0,022,'a',033,0220,'W','J','l',0316,'R',0320,0332,'h',05,'p',0271,0246,0333,'}',010,'S','&','D',017,']',0254,0271,020,0220,'>',030,'|','S','G','D',0362,014,047,0311,'Q','t',04,'^','c',0354,0307,0217,'i',0221,0340,'a','-',0224,0354,'2',0370,0337,0351,03,02,030,0356,0337,0237,0370,0273,0356,'$',014,'2',0233,'F',']','-',0363,036,0222,0360,0277,'1',012,'s',0310,'U',0363,'&',0313,'l',0335,0273,'l',0236,0234,0315,'(',0302,'C',0275,'e',0345,0352,'&','R',0233,0221,'Y',06,0206,023,0273,0277,0241,0211,0322,'%',0260,'y',0301,'x','V','!',0202,0234,0347,0374,'m',0354,0345,0375,032,'t',032,0257,01,'P',0276,0215,'.','V',0253,0312,0323,022,021,'2','[','0',0367,0313,0307,'r',0200,0200,02,0,',',036,0335,0363,0327,0200,0225,0313,0260,'4',0344,0337,0246,'&',0315,0264,'G',03,0246,'*',0377,0,0341,010,020,0322,'B',':',0221,0276,0215,03,')',04,0352,'!',0315,012,'<','%',010,0221,0310,0211,0343,0244,'d',014,'P',020,0315,'[',0315,0220,'k',0257,0320,0275,',',0373,0264,'~',037,'6',0274,0302,'-',0222,0335,0341,'<',0373,'<',017,02,0211,'|',0247,'s',0227,0306,0325,016,'C','-',06,0253,'`','%','{','V',']',0201,0210,'V','^','W',0340,0203,'O','q',034,0363,'h','q','Q','5',026,'l','Y',0355,0324,0311,'F',0240,'#','(',0354,'K',';','z',0343,'0',0357,022,0211,'>',031,0134,'`',0351,0227,07,'#',06,0200,0350,0210,'>','*',030,0347,'#',012,'o',0302,0304,015,0304,'u',0353,0207,02,'j',0324,0237,'a',0207,0305,'/',0225,'W',' ',0302,'|',0225,037,0301,0373,0350,0370,0230,0361,0354,'s','T','J','%',0262,0235,0206,'_',033,0375,'0',0260,0322,0335,0134,0340,0330,0276,015,037,'r',0251,0256,0215,'&',0321,0223,027,0330,'2','6',0364,0340,0311,0322,'8',0134,0252,036,'j',030,0257,0320,0212,0300,0325,0272,0342,0213,0342,0200,0262,'3','`','f','o','n',0367,'V','`','t',0333,'X','_','+',0270,'3',0350,'-','b','(',026,0320,';',0311,'N','V',0224,0306,0315,0377,0,0306,0347,0260,0261,0370,033,033,0257,01,'z',035,'5',0332,0265,027,'v',0260,015,0300,0266,'~','P',0262,0370,'5',0367,'9',0331,0262,'/',010,',',0350,013,'k',':',0365,'^',')',024,032,'f',0316,0266,011,0257,0241,01,0211,031,'q','C',0221,024,0247,0247,'9',034,'L','p',0270,0261,0205,'M',':','r',0242,'G','D',0360,0220,0265,'O',0331,035,'U',0271,'3',015,0203,016,0251,'a',0353,0301,0367,0220,'K','h','h',034,'X',0242,0375,015,0376,0340,0244,'`','D','a',035,'=','|','G',0264,0245,0364,0274,0271,0355,025,0227,'8',0205,0265,0216,02,'Z',0313,034,'C','t',0234,0254,0276,0341,'@',0253,01,0225,0250,0350,0311,0310,0266,026,0323,01,0224,'S',0257,'R','.',0250,'6',015,'K',' ',0227,'J',0307,0242,0341,020,'a',0365,'+','_','P','Y',']',0276,0244,031,0316,02,'<',0265,'l',010,0134,0351,07,0256,'J','f',032,0202,047,0303,'C','*','(','D','X',0300,0234,'$','>','j',0333,']',0256,'V',0317,0231,0371,'=','h',0231,0356,'M',0207,0247,0367,0343,0275,04,020,'b',0260,0231,0320,'7','|','+','e',0360,'i',0356,'P','K',0301,0210,021,' ',0212,0224,0331,0272,'L',0272,0232,0331,')','$',011,'^',0347,0315,0322,0212,'A','@',' ',03,0,'z','0',0311,033,'a',0234,'J',0320,'Y',035,020,'j',034,0201,0262,014,0242,0226,026,'w',0300,0351,'r',0310,0220,'0',0216,0364,'Y','D','e','k',06,032,021,',','4',0220,0223,0325,'d','i',0300,'i',0134,0367,0262,0253,' ',024,0306,']','?',0210,'}','T','-','S',0231,'P',030,0302,'c',0273,0345,0377,0,'i',025,0207,0246,0222,0305,'s',0221,'b',0370,'5','}',0316,'>',0361,0371,0245,0203,'U',0300,027,'T',014,0325,0312,0325,0260,0226,013,0267,05,'Q','e',026,0230,0352,'k','x',0364,05,036,0324,0210,0354,037,'L',0351,0355,0254,0256,0340,'5',0214,0274,0253,035,'T',0317,';',0260,0271,'7','p',021,0321,027,0210,0242,':',0365,0262,016,'K','P','(',0270,0211,0247,'T','k',022,0377,0,')',0366,0233,'<','-','$',06,0201,0270,0226,'D',0242,'"',032,'G','W',0217,0213,0236,'=','L','[',0214,0307,0376,0247,0361,0336,0265,0272,'`',0221,0345,0340,'>','l','k','P','P','=',0345,0252,0345,'e','{',0373,0230,'~',012,'@',0252,'a','e',0263,0231,0225,0255,'^',0225,0352,'K',033,0203,0204,0223,'!','l',0205,0,'y',07,0200,026,0,0320,0364,0302,0245,0236,'F',031,0310,0212,'T','y','q',0326,'K','9',0356,'b',0272,0304,'$',0364,0220,'y',0361,0205,'A','>',021,0244,0265,0221,' ','L','a',011,023,'D','z',0242,'D',0330,',','g',0340,0371,'Q','Z',0374,031,0221,0373,'?','!','H',0214,'6','}','&',0246,034,0373,01,0345,0374,'Q',0223,0,0200,030,0342,0261,'$',014,'_',047,03,0253,0315,0264,0367,'.',015,'X',0331,0222,'A',0244,010,':',0316,0302,0220,025,'B','U','r',0257,'K',0340,'i','*',0260,01,'u','T',0,0250,'j',0200,027,025,0266,'[','E','e',0230,'^',0251,',',0230,06,'9',0231,0213,01,027,0201,0255,0246,0223,0264,0234,04,0302,'#',0204,'t',0352,'K',015,0360,0320,0263,0272,'N',0340,'J',0347,'Q',0,030,'6','P',0270,'v','~','I','5',0247,'2',0256,0213,0204,0334,'H','G','f',0220,'Z',0340,0345,'Z',0223,'m',0376,'{','u',0230,05,030,0,0225,0254,0337,0215,0266,'8',0321,0335,0370,'h','Z',0216,0301,0225,0335,'u','i',0220,'h','h','1',0334,'|',0275,0237,'r',0302,012,'t',014,'_','R','X',0362,01,'`','T',0325,0254,'3',0,01,0240,01,0240,015,':',0226,'y',0247,'P',0341,0204,0331,06,0326,'o','!',026,'z',0344,0255,0260,0224,0355,02,0302,'1',0344,0273,'g','I',0271,0332,'`',0231,02,0134,'F',0362,'T',0264,'P','%',0316,'M',0,047,'@','8',07,'P',0344,0,0206,0301,0336,'n',0216,'a',0360,0356,'I',0355,0302,'F',0310,0342,0205,01,0245,'U',0256,0355,0223,0346,0223,0360,0307,0373,'T',0274,'>',0353,'E','a','w',027,0352,0231,0354,'=',0352,032,'F','p',037,0246,0217,'1',032,'G',0372,'Q',0320,0346,0316,']',0334,0321,'A',0303,015,0247,0274,'_','u',':','>',06,'K',0233,'~',0333,035,0312,010,06,0,0200,014,01,0356,'"',0331,'1','k',')',013,0256,0253,0201,'+',0212,'6','9','5',021,'b',':',023,'+',0224,0253,0236,0253,'}',0204,0256,0204,0274,0302,06,0240,0261,'(',0266,0307,')','@',0312,'n',0214,0252,0272,0252,0347,0327,017,0325,0364,0211,011,0377,0,'r','7','*',0331,'F',0252,0345,0364,0256,020,'o',0,03,0251,0317,017,06,0,'K',0201,04,'L','E','M','~',0312,013,0134,'p',' ',0270,':',0200,0205,'F','@',0204,0215,0206,030,0363,'R',012,0232,04,'}',0312,'o',0336,0207,0352,0235,0367,0345,0373,0244,'}',0260,0177,'Z','2',027,014,0252,'C',0213,'+',0367,'T','O',0374,'N',01,0373,0320,0247,0334,024,016,0342,0324,0361,'@','@',01,0,020,036,0346,034,0364,0254,'1',0344,'v',']',0325,0205,0326,0202,0374,0343,0231,'r',013,0225,'e','n',0266,0,0351,0234,013,'t',030,06,0352,0224,'(','a','Y','"',':',0205,0300,'n','`','&',017,'`',')','0','l',016,'!',0352,0335,022,022,'%',0254,0216,0301,0271,'>',' ','(','M','C',0243,047,'R','p',0340,'m',0244,013,0210,0202,'%',0304,0245,'e','e','&',')','X','L',015,0227,'d',017,0360,0267,0224,'@','S',012,'[','.','^',0301,0,0316,';',0224,0222,'d',0216,'l','n',0267,'W','^',0271,0270,'k',0215,0362,0314,0242,06,'V',0360,0240,0366,'@','H',03,0214,0222,0233,0327,0344,0260,0206,'B',0370,014,020,016,0375,'d',0303,'I',0306,',',05,0321,'#',0205,0242,0324,0,0361,'U',011,',',010,0377,0,06,0330,'N',0201,0230,0302,015,0234,0315,';',0202,'M',0363,'~','"',0354,04,01,0200,0,0320,0353,0217,0201,'3','Y','e','j',0226,';',0324,02,017,'i',0212,0220,0260,'j',0313,0200,0231,0373,0221,015,036,'2','V','P',0246,0204,021,021,0270,0235,'w',0232,'Z','/','2',0255,0334,0226,'!',0330,024,0202,'<','1',0222,0231,'~',034,0353,022,'"',036,0376,012,0210,0321,0373,0272,0270,02,0353,'b',0216,'$',06,0231,0263,034,0271,0334,0235,0267,'D',014,'u','Q','e','U',0313,0327,'2',0201,031,'M','q',0254,0204,'j',010,'j',014,'x',0235,025,0,026,0,' ',017,'m','$',0215,0330,035,02,0340,'@','0','$','X',0242,0376,0256,0342,0210,'B',0342,'=','z','!',0331,0265,'H',0134,0262,0216,0342,0215,0232,'i',032,'*',0266,0310,0254,0333,036,0267,'$',0265,013,0271,0354,0270,'D',0367,0213,05,'H',0303,0342,'[',0334,'3',02,'_','I',0222,'S','F','!','Y',037,'3','o','v',0342,'j',0275,0,0257,0326,'H','N',026,'d',0311,0276,032,0201,0300,0203,0304,' ','K',0,0,07,0270,'r',0341,0220,014,0301,'&','t',01,'u','I','b',0314,07,0231,'4','D',0262,'d','D','K',0212,'z',04,0206,0345,'#',0332,'F',0271,0200,'!',0,0300,0254,'0',0371,0226,01,'.',0251,0233,024,031,0251,0252,0372,0327,'a',04,'N','O','r','F',014,0217,'f','Z','b',0320,'`',0221,0201,0242,0264,'v','@','Q','k','x','[',014,'0',0254,0372,' ',0202,0301,035,'p',0363,0261,'D',03,0223,0272,'i',0,',',0,'@',036,0352,036,0227,0207,'s',0243,0264,0256,'Z','n','*',0364,0364,'I',01,013,'e',0223,'W',022,022,'B','/',0240,0331,0264,0376,'|',0233,0366,0260,',',0201,'0',0224,0361,0204,034,0255,0256,0244,'x',0346,'`',0243,'p',020,'m',0342,'B',03,'p',017,0264,0134,0274,'.','L',0252,0,0356,0321,0257,'P',021,0306,'H','p',0246,015,0303,'j','~','o',0241,'9','v','&',0341,'A','O','D','@','`',0205,0270,01,'W',0265,'M',0215,01,0313,'V','I',0246,022,0327,0270,020,' ',0202,0307,0274,0210,022,0201,032,0,0331,0350,0202,'P',0301,')',0272,'J',05,0223,'N','3',010,'_',0321,0261,030,'Z',024,'J',0205,'B',0334,'j',0346,021,'F',016,'d',0242,'5','w',0306,'d',024,'S',',',0210,0331,0221,'t',0320,'Q',0211,'s','G',0257,0340,0260,0177,0324,05,010,0222,0134,0365,'C',0273,0310,0357,'k',0225,'M','#',0325,'3',0211,0231,'y',0307,'B',0222,'n',0341,0233,'B','.','/',011,0316,0266,0273,0313,0221,'p',0304,'Y',0213,'Z',026,0273,0372,'H',0317,0206,'G',014,0320,'v',022,0257,03,014,0,020,'A',03,'T',0255,0374,0306,0204,0230,03,0337,03,0,0204,'"','H',0224,0254,0320,'A',017,0310,0261,'e','W',0231,'-','.',0242,023,0263,0336,'y',02,'i',0237,'Q',0335,0202,'/',0201,'=',0210,0266,0212,0234,0221,0271,0311,'8',0220,0361,'1',0336,'i','-',0353,' ',0213,'e','i',0276,0211,0357,'_',0266,0262,017,0370,024,'8','g',0264,'|','T',0236,0225,',',0237,0205,0312,0210,0301,0313,'|','2',0241,'H',0211,'D',0274,016,0264,':',0360,'c',0232,02,0325,'L','A',0216,'V',0317,0264,0267,0305,032,037,'7',0210,0267,0211,'}',0244,0242,'5',0220,010,'W',0274,0321,0245,0252,0263,0377,0,'(',0304,0352,'H',0341,'^','f',0245,0224,'&',0206,0350,'U',0357,0351,0233,0237,0246,'I',0200,05,0325,'m',024,')',0272,'V','Z',0360,022,0341,031,0233,0322,'H','j',047,'8',0217,'.',0251,0226,0312,0212,0271,0177,0202,'v','6','V',04,0211,02,' ','-',0321,0266,'*',0362,022,0214,0200,',',010,014,0366,0,0363,'N',0236,010,0266,0204,'.','"','"','8',0376,06,'#',02,'^',0220,'!',0213,011,0261,0211,'!',0241,'f',0200,0222,'D',0276,'@',0273,'r','`',0302,0277,0206,0301,06,':',020,0222,'$',02,'X',01,0305,'@',0316,0341,'m',0314,0330,01,'-','-',010,0345,'n',0272,0204,'.','"','"','8',0367,0252,'*','Y',0341,0275,0306,'-',0320,'B',0311,'$',013,0323,0270,0224,0313,'M','G',04,025,0205,010,0224,'h','|','X',',',0,'X','8',0376,'&',0333,0204,'.',014,'B',0203,0223,0205,04,'v',0134,010,034,0231,'f',0354,'n','w',0244,0222,02,'F',0214,'K',0204,0271,'t',0227,0317,0270,013,'U','L',0206,0,'*',0360,'S',0247,0372,0272,0211,0234,0301,'k','j','f',0226,0211,0354,'9','p','Z','I','%',0264,0320,0344,'@','@',014,0,'`',0376,'5',022,'0','~',0272,'&',0215,'2',0316,'o',022,'5',0234,'5','P',0307,0252,' ',020,0325,'X',0263,0221,0305,'3',0223,014,014,0314,'h','m',0224,0217,0262,0236,'d',010,0321,'0','%','X',034,024,0324,'}',03,0330,'K',033,0250,'2',0350,01,0355,'x',03,':',0311,0212,'<',0351,022,'+',027,0371,0134,0231,0177,0223,012,011,06,013,0224,'C','N',0231,0325,031,0313,0225,0362,021,'A','&',0236,0257,026,'I',0205,0256,0340,'y',0253,0207,034,013,'F',023,'4',0363,'9',0253,0215,0370,'X','^',0366,0207,'8',0243,0210,'P',' ',0256,0326,0257,0350,'^',0233,'=',0211,01,'Z',02,'+',047,0231,0210,0371,'i',0334,0347,012,'p',0336,0354,0362,015,'^',0224,0315,013,030,0206,'_','V',0355,0212,0276,02,031,'M',0231,0203,0204,0346,'k','8','E','L','-','V','$',0274,0277,0315,0247,'s',0310,'H',0321,0226,04,'2',034,0213,'4','Z','L',0177,0224,0252,'v','7',0371,'l','(',0330,0343,'@','~',02,'Q',0241,035,0376,0205,0245,0247,0374,0313,'c','W',0361,0322,0365,0335,025,0255,012,0364,'w',0340,'?',0202,0377,0331,};
diff --git a/src/embedded_images/tick_erased.jpg.h b/src/embedded_images/tick_erased.jpg.h
new file mode 100644
index 0000000..d1ef2cb
--- /dev/null
+++ b/src/embedded_images/tick_erased.jpg.h
@@ -0,0 +1,16 @@
+/* Autogenerated by hxtools bin2c */
+#ifndef TICK_ERASED_JPG_H
+#define TICK_ERASED_JPG_H 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern const unsigned char bin2c_te_jpg[54896];
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+
+#endif /* TICK_ERASED_JPG_H */
diff --git a/src/embedded_images/tick_erased.xcf b/src/embedded_images/tick_erased.xcf
new file mode 100644
index 0000000..61d0b59
--- /dev/null
+++ b/src/embedded_images/tick_erased.xcf
Binary files differ
diff --git a/src/gui.c b/src/gui.c
new file mode 100644
index 0000000..9c34910
--- /dev/null
+++ b/src/gui.c
@@ -0,0 +1,7133 @@
+/*
+ * gui.c: An ncurses GUI for nwipe.
+ *
+ * Copyright Darik Horn <dajhorn-dban@vanadac.com>.
+ *
+ * Modifications to original dwipe Copyright Andy Beverley <andy@andybev.com>
+ *
+ * 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, version 2.
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+/* RATIONALE:
+ *
+ * This entire GUI is a non-portable task-specific thunk.
+ *
+ * The alternatives are, however, no better. The CDK is large and clumsy,
+ * and things like ncurses libmenu are not worth the storage overhead.
+ *
+ */
+
+#ifndef _DEFAULT_SOURCE
+#define _DEFAULT_SOURCE
+#endif
+
+#include <time.h>
+
+#ifndef _POSIX_SOURCE
+#define _POSIX_SOURCE
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ncurses.h>
+#include <panel.h>
+#include <stdint.h>
+#include <libconfig.h>
+#include <string.h>
+#include <strings.h>
+#include <sys/stat.h>
+
+#include "nwipe.h"
+#include "context.h"
+#include "method.h"
+#include "prng.h"
+#include "options.h"
+#include "gui.h"
+#include "pass.h"
+#include "logging.h"
+#include "version.h"
+#include "temperature.h"
+#include "miscellaneous.h"
+#include "hpa_dco.h"
+#include "customers.h"
+#include "conf.h"
+
+#define NWIPE_GUI_PANE 8
+
+/* Header window: width, height, x coordinate, y coordinate. */
+#define NWIPE_GUI_HEADER_W COLS
+#define NWIPE_GUI_HEADER_H 1
+#define NWIPE_GUI_HEADER_X 0
+#define NWIPE_GUI_HEADER_Y 0
+
+/* Footer window: width, height, x coordinate, y coordinate. */
+#define NWIPE_GUI_FOOTER_W COLS
+#define NWIPE_GUI_FOOTER_H 1
+#define NWIPE_GUI_FOOTER_X 0
+#define NWIPE_GUI_FOOTER_Y ( LINES - 1 )
+
+/* Options window: width, height, x coorindate, y coordinate. */
+#define NWIPE_GUI_OPTIONS_W 44
+#define NWIPE_GUI_OPTIONS_H 7
+#define NWIPE_GUI_OPTIONS_Y 1
+#define NWIPE_GUI_OPTIONS_X 0
+
+/* Options fields, relative to their window. */
+#define NWIPE_GUI_OPTIONS_TAB 10
+#define NWIPE_GUI_OPTIONS_ENTROPY_Y 1
+#define NWIPE_GUI_OPTIONS_ENTROPY_X 1
+#define NWIPE_GUI_OPTIONS_PRNG_Y 2
+#define NWIPE_GUI_OPTIONS_PRNG_X 1
+#define NWIPE_GUI_OPTIONS_METHOD_Y 3
+#define NWIPE_GUI_OPTIONS_METHOD_X 1
+#define NWIPE_GUI_OPTIONS_VERIFY_Y 4
+#define NWIPE_GUI_OPTIONS_VERIFY_X 1
+#define NWIPE_GUI_OPTIONS_ROUNDS_Y 5
+#define NWIPE_GUI_OPTIONS_ROUNDS_X 1
+
+/* Stats window: width, height, x coordinate, y coordinate. */
+#define NWIPE_GUI_STATS_W ( COLS - 44 )
+#define NWIPE_GUI_STATS_H 7
+#define NWIPE_GUI_STATS_Y 1
+#define NWIPE_GUI_STATS_X 44
+
+/* Stats fields, relative to their window. */
+#define NWIPE_GUI_STATS_RUNTIME_Y 1
+#define NWIPE_GUI_STATS_RUNTIME_X 1
+#define NWIPE_GUI_STATS_ETA_Y 2
+#define NWIPE_GUI_STATS_ETA_X 1
+#define NWIPE_GUI_STATS_LOAD_Y 3
+#define NWIPE_GUI_STATS_LOAD_X 1
+#define NWIPE_GUI_STATS_THROUGHPUT_Y 4
+#define NWIPE_GUI_STATS_THROUGHPUT_X 1
+#define NWIPE_GUI_STATS_ERRORS_Y 5
+#define NWIPE_GUI_STATS_ERRORS_X 1
+#define NWIPE_GUI_STATS_TAB 16
+
+/* Select window: width, height, x coordinate, y coordinate. */
+#define NWIPE_GUI_MAIN_W COLS
+#define NWIPE_GUI_MAIN_H ( LINES - NWIPE_GUI_MAIN_Y - 1 )
+#define NWIPE_GUI_MAIN_Y 8
+#define NWIPE_GUI_MAIN_X 0
+
+#define SKIP_DEV_PREFIX 5
+
+/* Window pointers. */
+WINDOW* footer_window;
+WINDOW* header_window;
+WINDOW* main_window;
+WINDOW* options_window;
+WINDOW* stats_window;
+
+PANEL* footer_panel;
+PANEL* header_panel;
+PANEL* main_panel;
+PANEL* options_panel;
+PANEL* stats_panel;
+
+/* Options window title. */
+const char* options_title = " Options ";
+
+/* Statistics window title. */
+const char* stats_title = " Statistics ";
+
+/* Footer labels. */
+const char* main_window_footer =
+ "S=Start m=Method p=PRNG v=Verify r=Rounds b=Blanking Space=Select c=Config CTRL+C=Quit";
+const char* main_window_footer_warning_lower_case_s = " WARNING: To start the wipe press SHIFT+S (uppercase S) ";
+
+const char* main_window_footer_warning_no_blanking_with_ops2 =
+ " WARNING: Zero blanking is not allowed with ops2 method ";
+
+const char* main_window_footer_warning_no_blanking_with_verify_only =
+ " WARNING: Zero blanking is not allowed with verify method ";
+
+const char* main_window_footer_warning_no_drive_selected =
+ " No drives selected, use spacebar to select a drive, then press S to start ";
+
+/* Oddly enough, placing extra quotes around the footer strings fixes corruption to the right
+ * of the footer message when the terminal is resized, a quirk in ncurses? - DO NOT REMOVE THE \" */
+const char* selection_footer = "J=Down K=Up Space=Select Backspace=Cancel Ctrl+C=Quit";
+const char* selection_footer_config = "J=Down K=Up Return=Select ESC|Backspace=back Ctrl+C=Quit";
+const char* selection_footer_preview_prior_to_drive_selection =
+ "A=Accept & display drives J=Down K=Up Space=Select Backspace=Cancel Ctrl+C=Quit";
+const char* selection_footer_add_customer = "S=Save J=Down K=Up Space=Select Backspace=Cancel Ctrl+C=Quit";
+const char* selection_footer_add_customer_yes_no = "Save Customer Details Y/N";
+const char* end_wipe_footer = "B=[Toggle between dark\\blank\\blue screen] Ctrl+C=Quit";
+const char* rounds_footer = "Left=Erase Esc=Cancel Ctrl+C=Quit";
+const char* selection_footer_text_entry = "Esc=Cancel Return=Submit Ctrl+C=Quit";
+
+/* The number of lines available in the terminal */
+int stdscr_lines;
+
+/* The number of columns available in the terminal */
+int stdscr_cols;
+
+/* The size of the terminal lines when previously checked */
+int stdscr_lines_previous;
+
+/* The size of the terminal columns when previously checked */
+int stdscr_cols_previous;
+
+int tft_saver = 0;
+
+void nwipe_gui_title( WINDOW* w, const char* s )
+{
+ /**
+ * Prints the string 's' centered on the first line of the window 'w'.
+ */
+
+ /* The number of lines in the window. (Not used.) */
+ int wy;
+ (void) wy; /* flag wy not used to the compiler, to silence warning */
+
+ /* The number of columns in the window. */
+ int wx;
+
+ /* Get the window dimensions. */
+ getmaxyx( w, wy, wx );
+
+ /*Calculate available total margin */
+ int margin = ( wx - strlen( s ) );
+ if( margin < 0 )
+ {
+ margin = 0;
+ }
+
+ /* tft_saver = grey text on black mode */
+ if( tft_saver )
+ {
+ wattron( w, A_BOLD );
+ }
+
+ /* Print the title. */
+ mvwprintw( w, 0, margin / 2, "%s", s );
+
+} /* nwipe_gui_title */
+
+void nwipe_init_pairs( void )
+{
+ if( has_colors() )
+ {
+ /* Initialize color capabilities. */
+ start_color();
+
+ if( can_change_color() )
+ {
+ /* Redefine cyan to gray. */
+ init_color( COLOR_CYAN, 128, 128, 128 );
+ }
+
+ /* If we are in tft saver mode set grey text on black background else
+ * Set white on blue as the emphasis color */
+ if( tft_saver )
+ {
+ init_pair( 1, COLOR_BLACK, COLOR_BLACK );
+ }
+ else
+ {
+ init_pair( 1, COLOR_WHITE, COLOR_BLUE );
+ }
+
+ /* Set gray (or cyan) on blue as the normal color. */
+ init_pair( 2, COLOR_CYAN, COLOR_BLUE );
+
+ /* Set red on blue as the hilite color. */
+ init_pair( 3, COLOR_RED, COLOR_BLUE );
+
+ /* If we are in tft saver mode set grey text on black background else
+ * Set white on blue as the emphasis color */
+ if( tft_saver )
+ {
+ init_pair( 4, COLOR_BLACK, COLOR_BLACK );
+ }
+ else
+ {
+ init_pair( 4, COLOR_BLUE, COLOR_WHITE );
+ }
+
+ /* Set white on green for success messages. */
+ init_pair( 5, COLOR_WHITE, COLOR_GREEN );
+
+ /* Set white on red for failure messages. */
+ init_pair( 6, COLOR_WHITE, COLOR_RED );
+
+ /* Set black on black for when hiding the display. */
+ init_pair( 7, COLOR_BLACK, COLOR_BLACK );
+
+ /* Set green on blue for reverse bold messages */
+ init_pair( 8, COLOR_GREEN, COLOR_WHITE );
+
+ /* Set green on blue for reverse bold error messages */
+ init_pair( 9, COLOR_RED, COLOR_WHITE );
+
+ /* Set black on yellow for warning messages */
+ init_pair( 10, COLOR_BLACK, COLOR_YELLOW );
+
+ /* Set black on blue for minimum temperature reached */
+ init_pair( 11, COLOR_BLACK, COLOR_BLUE );
+
+ /* Set blue on blue to make temperature invisible */
+ init_pair( 12, COLOR_BLUE, COLOR_BLUE );
+
+ /* Set magenta on blue */
+ init_pair( 13, COLOR_MAGENTA, COLOR_BLUE );
+
+ /* Set white on black for low critical temperature */
+ init_pair( 14, COLOR_WHITE, COLOR_BLACK );
+
+ /* Set the background style. */
+ wbkgdset( stdscr, COLOR_PAIR( 1 ) | ' ' );
+ }
+}
+
+void nwipe_gui_init( void )
+{
+ /**
+ * Initializes the ncurses gui.
+ */
+
+ /* Initialize the screen. */
+ initscr();
+
+ /* Disable TTY line buffering. */
+ cbreak();
+
+ /* Disable TTY echo. */
+ noecho();
+
+ /* Enable most special keys. */
+ keypad( stdscr, TRUE );
+
+ /* Create the text/background color pairs */
+ nwipe_init_pairs();
+
+ /* Clear the screen. */
+ wclear( stdscr );
+
+ /* Create the header window. */
+ nwipe_gui_create_header_window();
+
+ /* Create the footer window and panel */
+ nwipe_gui_create_footer_window( main_window_footer );
+
+ /* Create the options window and panel */
+ nwipe_gui_create_options_window();
+
+ /* Create the stats window. */
+ nwipe_gui_create_stats_window();
+
+ /* Create a new main window and panel */
+ nwipe_gui_create_main_window();
+
+ update_panels();
+ doupdate();
+
+ /* Hide the cursor. */
+ curs_set( 0 );
+
+} /* nwipe_gui_init */
+
+void nwipe_gui_free( void )
+{
+ /**
+ * Releases the ncurses gui.
+ *
+ */
+ /* Free ncurses resources. */
+ if( del_panel( footer_panel ) != OK )
+ {
+ nwipe_log( NWIPE_LOG_ERROR, "Deleting footer panel failed!." );
+ }
+ if( del_panel( header_panel ) != OK )
+ {
+ nwipe_log( NWIPE_LOG_ERROR, "Deleting header panel failed!." );
+ }
+ if( del_panel( main_panel ) != OK )
+ {
+ nwipe_log( NWIPE_LOG_ERROR, "Deleting main panel failed!." );
+ }
+ if( del_panel( options_panel ) != OK )
+ {
+ nwipe_log( NWIPE_LOG_ERROR, "Deleting options panel failed!." );
+ }
+ if( del_panel( stats_panel ) != OK )
+ {
+ nwipe_log( NWIPE_LOG_ERROR, "Deleting stats panel failed!." );
+ }
+ if( delwin( footer_window ) != OK )
+ {
+ nwipe_log( NWIPE_LOG_ERROR, "Deleting footer window failed!." );
+ }
+ if( delwin( header_window ) != OK )
+ {
+ nwipe_log( NWIPE_LOG_ERROR, "Deleting header window failed!." );
+ }
+ if( delwin( main_window ) != OK )
+ {
+ nwipe_log( NWIPE_LOG_ERROR, "Deleting main window failed!." );
+ }
+ if( delwin( options_window ) != OK )
+ {
+ nwipe_log( NWIPE_LOG_ERROR, "Deleting options window failed!." );
+ }
+ if( delwin( stats_window ) != OK )
+ {
+ nwipe_log( NWIPE_LOG_ERROR, "Deleting stats window failed!." );
+ }
+ if( endwin() != OK )
+ {
+ nwipe_log( NWIPE_LOG_ERROR, "Curses endwin() failed !" );
+ }
+
+} /* nwipe_gui_free */
+
+void nwipe_gui_create_main_window()
+{
+ /* Create the main window. */
+ main_window = newwin( NWIPE_GUI_MAIN_H, NWIPE_GUI_MAIN_W, NWIPE_GUI_MAIN_Y, NWIPE_GUI_MAIN_X );
+ main_panel = new_panel( main_window );
+
+ if( has_colors() )
+ {
+ /* Set the background style. */
+ wbkgdset( main_window, COLOR_PAIR( 1 ) | ' ' );
+
+ /* Apply the color change. */
+ wattron( main_window, COLOR_PAIR( 1 ) );
+
+ /* In tft saver mode we toggle the intensity bit which gives us grey text */
+ if( tft_saver )
+ {
+ wattron( main_window, A_BOLD );
+ }
+ else
+ {
+ wattroff( main_window, A_BOLD );
+ }
+ }
+
+ /* Clear the main window. */
+ werase( main_window );
+
+ /* Add a border. */
+ box( main_window, 0, 0 );
+
+ /* refresh main window */
+ wnoutrefresh( main_window );
+
+} /* nwipe_gui_create_main_window */
+
+void nwipe_gui_create_header_window()
+{
+ char anon_label[] = " (ANONYMIZED)";
+ char bannerplus[80];
+
+ /* Create the header window. */
+ header_window = newwin( NWIPE_GUI_HEADER_H, NWIPE_GUI_HEADER_W, NWIPE_GUI_HEADER_Y, NWIPE_GUI_HEADER_X );
+ header_panel = new_panel( header_window );
+
+ if( has_colors() )
+ {
+ /* Set the background style of the header window. */
+ wbkgdset( header_window, COLOR_PAIR( 4 ) | ' ' );
+
+ if( tft_saver )
+ {
+ wattron( main_window, A_BOLD );
+ }
+ }
+
+ /* Clear the header window. */
+ werase( header_window );
+
+ /* If in anonymized mode modify the title banner to reflect this */
+ strcpy( bannerplus, banner );
+
+ if( nwipe_options.quiet )
+ {
+ strcat( bannerplus, anon_label );
+ }
+
+ /* Print the product banner. */
+ nwipe_gui_title( header_window, bannerplus );
+
+ /* Refresh the header window */
+ wnoutrefresh( header_window );
+
+} /* nwipe_gui_create_header_window */
+
+void nwipe_gui_create_footer_window( const char* footer_text )
+{
+ /* Create the footer window. */
+ footer_window = newwin( NWIPE_GUI_FOOTER_H, NWIPE_GUI_FOOTER_W, NWIPE_GUI_FOOTER_Y, NWIPE_GUI_FOOTER_X );
+ footer_panel = new_panel( footer_window );
+
+ if( has_colors() )
+ {
+ /* Set the background style of the footer window. */
+ wbkgdset( footer_window, COLOR_PAIR( 4 ) | ' ' );
+ }
+
+ /* Erase the footer window. */
+ werase( footer_window );
+
+ /* Add help text to the footer */
+ nwipe_gui_title( footer_window, footer_text );
+
+ /* Refresh the footer window */
+ wnoutrefresh( footer_window );
+
+} /* nwipe_gui_create_footer_window */
+
+void nwipe_gui_amend_footer_window( const char* footer_text )
+{
+ /* Clear the footer window. */
+ werase( footer_window );
+
+ /* Add help text to the footer */
+ nwipe_gui_title( footer_window, footer_text );
+
+ /* Refresh the footer window */
+ wnoutrefresh( footer_window );
+
+} /* nwipe_gui_amend_footer_window */
+
+void nwipe_gui_create_options_window()
+{
+ /* Create the options window. */
+ options_window = newwin( NWIPE_GUI_OPTIONS_H, NWIPE_GUI_OPTIONS_W, NWIPE_GUI_OPTIONS_Y, NWIPE_GUI_OPTIONS_X );
+ options_panel = new_panel( options_window );
+
+ if( has_colors() )
+ {
+ /* Set the background style of the options window. */
+ wbkgdset( options_window, COLOR_PAIR( 1 ) | ' ' );
+
+ /* Apply the color change to the options window. */
+ wattron( options_window, COLOR_PAIR( 1 ) );
+
+ if( tft_saver )
+ {
+ wattron( options_window, A_BOLD );
+ }
+ }
+
+ /* Clear the options window. */
+ werase( options_window );
+
+ /* Add a border. */
+ box( options_window, 0, 0 );
+
+} /* nwipe_gui_create_options_window */
+
+void nwipe_gui_create_stats_window()
+{
+ /* Create the stats window. */
+ stats_window = newwin( NWIPE_GUI_STATS_H, NWIPE_GUI_STATS_W, NWIPE_GUI_STATS_Y, NWIPE_GUI_STATS_X );
+ stats_panel = new_panel( stats_window );
+
+ if( has_colors() )
+ {
+ /* Set the background style of the stats window. */
+ wbkgdset( stats_window, COLOR_PAIR( 1 ) | ' ' );
+
+ /* Apply the color change to the stats window. */
+ wattron( stats_window, COLOR_PAIR( 1 ) );
+
+ if( tft_saver )
+ {
+ wattron( stats_window, A_BOLD );
+ }
+ }
+
+ /* Clear the new window. */
+ werase( stats_window );
+
+ /* Add a border. */
+ box( stats_window, 0, 0 );
+
+ /* Add a title. */
+ nwipe_gui_title( stats_window, stats_title );
+
+ /* Print field labels. */
+ mvwprintw( stats_window, NWIPE_GUI_STATS_RUNTIME_Y, NWIPE_GUI_STATS_RUNTIME_X, "Runtime: " );
+ mvwprintw( stats_window, NWIPE_GUI_STATS_ETA_Y, NWIPE_GUI_STATS_ETA_X, "Remaining: " );
+ mvwprintw( stats_window, NWIPE_GUI_STATS_LOAD_Y, NWIPE_GUI_STATS_LOAD_X, "Load Averages: " );
+ mvwprintw( stats_window, NWIPE_GUI_STATS_THROUGHPUT_Y, NWIPE_GUI_STATS_THROUGHPUT_X, "Throughput: " );
+ mvwprintw( stats_window, NWIPE_GUI_STATS_ERRORS_Y, NWIPE_GUI_STATS_ERRORS_X, "Errors: " );
+
+} /* nwipe_gui_create_stats_window */
+
+void nwipe_gui_create_all_windows_on_terminal_resize( int force_creation, const char* footer_text )
+{
+ /* Get the terminal size */
+ getmaxyx( stdscr, stdscr_lines, stdscr_cols );
+
+ /* If the user has resized the terminal then recreate the windows and panels */
+ if( stdscr_cols_previous != stdscr_cols || stdscr_lines_previous != stdscr_lines || force_creation == 1 )
+ {
+ /* Save the revised terminal size so we check whether the user has resized next time */
+ stdscr_lines_previous = stdscr_lines;
+ stdscr_cols_previous = stdscr_cols;
+
+ /* Clear the screen. */
+ wclear( stdscr );
+
+ /* Create a new header window and panel due to terminal size having changed */
+ nwipe_gui_create_header_window();
+
+ /* Create a new main window and panel due to terminal size having changed */
+ nwipe_gui_create_main_window();
+
+ /* Create a new footer window and panel due to terminal size having changed */
+ nwipe_gui_create_footer_window( footer_text );
+
+ /* Create a new options window and panel due to terminal size having changed */
+ nwipe_gui_create_options_window();
+
+ /* Create a new stats window and panel due to terminal size having changed */
+ nwipe_gui_create_stats_window();
+
+ /* Update the options window. */
+ nwipe_gui_options();
+
+ update_panels();
+ doupdate();
+ }
+}
+
+void nwipe_gui_select( int count, nwipe_context_t** c )
+{
+ extern int terminate_signal;
+
+ /* Widget labels. */
+ const char* select_title = " Disks and Partitions ";
+
+ /* The number of lines available in the window. */
+ int wlines;
+
+ /* The number of columns available in the window. */
+ int wcols;
+
+ /* The number of selection elements that we can show in the window. */
+ int slots;
+
+ /* The index of the element that is visible in the first slot. */
+ int offset = 0;
+
+ /* The selection focus. */
+ int focus = 0;
+
+ /* A generic loop variable. */
+ int i = 0;
+
+ /* User input buffer. */
+ int keystroke;
+
+ /* The current working line. */
+ int yy;
+
+ /* Flag, Valid key hit = 1, anything else = 0 */
+ int validkeyhit;
+
+ /* Counts number of drives and partitions that have been selected */
+ int number_of_selected_contexts = 0;
+
+ /* Control A toggle status -1=indefined, 0=all drives delected, 1=all drives selected */
+ int select_all_toggle_status = -1;
+
+ /* Get the terminal size */
+ getmaxyx( stdscr, stdscr_lines, stdscr_cols );
+
+ /* Save the terminal size so we check whether the user has resized */
+ stdscr_lines_previous = stdscr_lines;
+ stdscr_cols_previous = stdscr_cols;
+
+ time_t temperature_check_time = time( NULL );
+
+ /* Used in the selection loop to trap a failure of the timeout(), getch() mechanism to block for the designated
+ * period */
+ int iteration_counter;
+
+ /* Used in the selection loop to trap a failure of the timeout(), getch() mechanism to block for the designated
+ * period */
+ int expected_iterations;
+
+ time_t previous_iteration_timestamp;
+
+ do
+ {
+
+ nwipe_gui_create_all_windows_on_terminal_resize( 0, main_window_footer );
+
+ /* There is one slot per line. */
+ getmaxyx( main_window, wlines, wcols );
+
+ /* Less two lines for the box and two lines for padding. */
+ slots = wlines - 4;
+ if( slots < 0 )
+ {
+ slots = 0;
+ }
+
+ /* The code here adjusts the offset value, required when the terminal is resized vertically */
+ if( slots > count )
+ {
+ offset = 0;
+ }
+ else
+ {
+ if( focus >= count )
+ {
+ /* The focus is already at the last element. */
+ focus = count - 1;
+ }
+ if( focus < 0 )
+ {
+ /* The focus is already at the last element. */
+ focus = 0;
+ }
+ }
+
+ if( count >= slots && slots > 0 )
+ {
+ offset = focus + 1 - slots;
+ if( offset < 0 )
+ {
+ offset = 0;
+ }
+ }
+
+ /* Clear the main window, necessary when switching selections such as method etc */
+ werase( main_window );
+
+ /* Refresh main window */
+ wnoutrefresh( main_window );
+
+ /* If the user selected an option the footer text would have changed.
+ * Here we set it back to the main key help text */
+ nwipe_gui_create_footer_window( main_window_footer );
+
+ /* Refresh the stats window */
+ wnoutrefresh( stats_window );
+
+ /* Refresh the options window */
+ wnoutrefresh( options_window );
+
+ /* Update the options window. */
+ nwipe_gui_options();
+
+ /* Initialize the line offset. */
+ yy = 2;
+
+ for( i = 0; i < slots && i < count; i++ )
+ {
+
+ /* Move to the next line. */
+ mvwprintw( main_window, yy++, 1, " " );
+
+ if( i + offset == focus )
+ {
+ if( c[focus]->select == NWIPE_SELECT_TRUE || c[focus]->select == NWIPE_SELECT_FALSE )
+ {
+ /* Print the 'enabled' cursor. */
+ waddch( main_window, ACS_RARROW );
+ }
+
+ else
+ {
+ /* Print the 'disabled' cursor. */
+ waddch( main_window, ACS_DIAMOND );
+ }
+ }
+
+ else
+ {
+ /* Print whitespace. */
+ waddch( main_window, ' ' );
+ }
+
+ /* In the event for the offset value somehow becoming invalid, this if statement will prevent a segfault
+ * and the else part will log the out of bounds values for debugging */
+ if( i + offset >= 0 && i + offset < count )
+ {
+
+ switch( c[i + offset]->select )
+ {
+ case NWIPE_SELECT_TRUE:
+
+ if( nwipe_options.method == &nwipe_verify_zero || nwipe_options.method == &nwipe_verify_one )
+ {
+ wprintw( main_window,
+ "[vrfy] %s %s ",
+ c[i + offset]->gui_device_name,
+ c[i + offset]->device_type_str );
+ }
+ else
+ {
+ wprintw( main_window,
+ "[wipe] %s %s ",
+ c[i + offset]->gui_device_name,
+ c[i + offset]->device_type_str );
+ }
+ break;
+
+ case NWIPE_SELECT_FALSE:
+ /* Print an element that is not selected. */
+ wprintw( main_window,
+ "[ ] %s %s ",
+ c[i + offset]->gui_device_name,
+ c[i + offset]->device_type_str );
+ break;
+
+ case NWIPE_SELECT_TRUE_PARENT:
+
+ /* This element will be wiped when its parent is wiped. */
+ wprintw( main_window,
+ "[****] %s %s ",
+ c[i + offset]->gui_device_name,
+ c[i + offset]->device_type_str );
+ break;
+
+ case NWIPE_SELECT_FALSE_CHILD:
+
+ /* We can't wipe this element because it has a child that is being wiped. */
+ wprintw( main_window,
+ "[----] %s %s ",
+ c[i + offset]->gui_device_name,
+ c[i + offset]->device_type_str );
+ break;
+
+ case NWIPE_SELECT_DISABLED:
+
+ /* We don't know how to wipe this device. (Iomega Zip drives.) */
+ wprintw( main_window, "[????] %s ", "Unrecognized Device" );
+ break;
+
+ default:
+
+ /* TODO: Handle the sanity error. */
+ break;
+
+ } /* switch select */
+
+ wprintw( main_window, "[%s] ", c[i + offset]->device_size_text );
+
+ /* Read the drive temperature values */
+ // nwipe_update_temperature( c[i + offset] );
+
+ /* print the temperature */
+ wprintw_temperature( c[i + offset] );
+
+ switch( c[i + offset]->HPA_status )
+ {
+ case HPA_ENABLED:
+ wprintw( main_window, " " );
+ wattron( main_window, COLOR_PAIR( 9 ) );
+ wprintw( main_window, "[HS? YES]" );
+ wattroff( main_window, COLOR_PAIR( 9 ) );
+ break;
+
+ case HPA_DISABLED:
+ wprintw( main_window, " " );
+ wprintw( main_window, "[HS? NO ]" );
+ break;
+
+ case HPA_UNKNOWN:
+ wprintw( main_window, " " );
+ wprintw( main_window, "[HS? ???]" );
+ break;
+
+ default:
+
+ wprintw( main_window, " " );
+ wprintw( main_window, "[HS? N/A]" );
+ break;
+ }
+
+ /* print the drive model and serial number */
+ wprintw( main_window, " %s/%s", c[i + offset]->device_model, c[i + offset]->device_serial_no );
+
+ if( c[i + offset]->HPA_toggle_time + 1 < time( NULL ) )
+ {
+ switch( c[i + offset]->HPA_display_toggle_state )
+ {
+ case 0:
+ c[i + offset]->HPA_display_toggle_state = 1;
+ break;
+
+ case 1:
+ c[i + offset]->HPA_display_toggle_state = 0;
+ break;
+ }
+ c[i + offset]->HPA_toggle_time = time( NULL );
+ }
+ }
+ else
+ {
+ nwipe_log( NWIPE_LOG_DEBUG,
+ "GUI.c,nwipe_gui_select(), scroll, array index out of bounds, i=%u, count=%u, slots=%u, "
+ "focus=%u, offset=%u",
+ i,
+ count,
+ slots,
+ focus,
+ offset );
+ }
+
+ } /* for */
+
+ if( offset > 0 )
+ {
+ mvwprintw( main_window, 1, wcols - 8, " More " );
+ waddch( main_window, ACS_UARROW );
+ }
+
+ if( count - offset > slots )
+ {
+ mvwprintw( main_window, wlines - 2, wcols - 8, " More " );
+ waddch( main_window, ACS_DARROW );
+ }
+
+ /* Draw a border around the menu window. */
+ box( main_window, 0, 0 );
+
+ /* Print a title. */
+ nwipe_gui_title( main_window, select_title );
+
+ /* Refresh the window. */
+ wnoutrefresh( main_window );
+
+ /* Output to physical screen */
+ doupdate();
+
+ /* Initialise the iteration counter */
+ iteration_counter = 0;
+
+ previous_iteration_timestamp = time( NULL );
+
+ /* Calculate Maximum allowed iterations per second */
+ expected_iterations = ( 1000 / GETCH_BLOCK_MS ) * 8;
+
+ do
+ {
+ /* Wait 250ms for input from getch, if nothing getch will then continue,
+ * This is necessary so that the while loop can be exited by the
+ * terminate_signal e.g.. the user pressing control-c to exit.
+ * Do not change this value, a higher value means the keys become
+ * sluggish, any slower and more time is spent unnecessarily looping
+ * which wastes CPU cycles.
+ */
+
+ validkeyhit = 0;
+ timeout( GETCH_BLOCK_MS ); // block getch() for ideally about 250ms.
+ keystroke = getch(); // Get user input.
+ timeout( -1 ); // Switch back to blocking mode.
+
+ /* To avoid 100% CPU usage, check for a runaway condition caused by the "keystroke = getch(); (above), from
+ * immediately returning an error condition. We check for an error condition because getch() returns a ERR
+ * value when the timeout value "timeout( 250 );" expires as well as when a real error occurs. We can't
+ * differentiate from normal operation and a failure of the getch function to block for the specified period
+ * of timeout. So here we check the while loop hasn't exceeded the number of expected iterations per second
+ * ie. a timeout(250) block value of 250ms means we should not see any more than (1000/250) = 4 iterations.
+ * We increase this to 32 iterations to allow a little tolerance. Why is this necessary? It's been found
+ * that in KDE konsole and other terminals based on the QT terminal engine exiting the terminal without
+ * first exiting nwipe results in nwipe remaining running but detached from any interface which causes
+ * getch to fail and its associated timeout. So the CPU or CPU core rises to 100%. Here we detect that
+ * failure and exit nwipe gracefully with the appropriate error. This does not affect use of tmux for
+ * attaching or detaching from a running nwipe session when sitting at the selection screen. All other
+ * terminals correctly terminate nwipe when the terminal itself is exited.
+ */
+
+ iteration_counter++;
+
+ if( previous_iteration_timestamp == time( NULL ) )
+ {
+ if( iteration_counter > expected_iterations )
+ {
+ nwipe_log( NWIPE_LOG_ERROR,
+ "GUI.c,nwipe_gui_select(), loop runaway, did you close the terminal without exiting "
+ "nwipe? Exiting nwipe now." );
+ /* Issue signal to nwipe to exit immediately but gracefully */
+ terminate_signal = 1;
+ }
+ }
+ else
+ {
+ /* new second, so reset counter */
+ iteration_counter = 0;
+ previous_iteration_timestamp = time( NULL );
+ }
+
+ /* We don't necessarily use all of these. For future reference these are some CTRL+key values
+ * ^A - 1, ^B - 2, ^D - 4, ^E - 5, ^F - 6, ^G - 7, ^H - 8, ^I - 9, ^K - 11, ^L - 12, ^N - 14,
+ * ^O - 15, ^P - 16, ^R - 18, ^T - 20, ^U - 21, ^V - 22, ^W - 23, ^X - 24, ^Y - 25
+ * Use nwipe_log( NWIPE_LOG_DEBUG, "Key Name: %s - %u", keyname(keystroke),keystroke) to
+ * figure out what code is returned by what ever key combination */
+
+ switch( keystroke )
+ {
+ case KEY_DOWN:
+ case 'j':
+ case 'J':
+
+ validkeyhit = 1;
+
+ /* Increment the focus. */
+ focus += 1;
+
+ if( focus >= count )
+ {
+ /* The focus is already at the last element. */
+ focus = count - 1;
+ break;
+ }
+
+ if( focus - offset >= slots )
+ {
+ /* The next element is offscreen. Scroll down. */
+ offset += 1;
+ break;
+ }
+
+ break;
+
+ case KEY_UP:
+ case 'k':
+ case 'K':
+
+ validkeyhit = 1;
+
+ /* Decrement the focus. */
+ focus -= 1;
+
+ if( focus < 0 )
+ {
+ /* The focus is already at the last element. */
+ focus = 0;
+ break;
+ }
+
+ if( focus < offset )
+ {
+ /* The next element is offscreen. Scroll up. */
+ offset -= 1;
+ break;
+ }
+
+ break;
+
+ case KEY_ENTER:
+ case 10:
+ case ' ':
+
+ validkeyhit = 1;
+
+ /* TODO: This block should be made into a function. */
+
+ if( c[focus]->select == NWIPE_SELECT_TRUE )
+ {
+ /* Reverse the selection of this element. */
+ c[focus]->select = NWIPE_SELECT_FALSE;
+
+ if( c[focus]->device_part == 0 )
+ {
+ /* Sub-deselect all partitions and slices within this disk. */
+ for( i = 0; i < count; i++ )
+ {
+ if( c[i]->device_type == c[focus]->device_type
+ && c[i]->device_host == c[focus]->device_host
+ && c[i]->device_bus == c[focus]->device_bus
+ && c[i]->device_target == c[focus]->device_target
+ && c[i]->device_lun == c[focus]->device_lun && c[i]->device_part > 0 )
+ {
+ c[i]->select = NWIPE_SELECT_FALSE;
+ }
+
+ } /* for all contexts */
+
+ } /* if sub-deselect */
+
+ else
+ {
+ /* The number of selected partitions or slices within this disk. */
+ int j = 0;
+
+ for( i = 0; i < count; i++ )
+ {
+ if( c[i]->device_type == c[focus]->device_type
+ && c[i]->device_host == c[focus]->device_host
+ && c[i]->device_bus == c[focus]->device_bus
+ && c[i]->device_target == c[focus]->device_target
+ && c[i]->device_lun == c[focus]->device_lun && c[i]->device_part > 0
+ && c[i]->select == NWIPE_SELECT_TRUE )
+ {
+ /* Increment the counter. */
+ j += 1;
+ }
+
+ } /* for all contexts */
+
+ if( j == 0 )
+ {
+ /* Find the parent disk of this partition or slice. */
+ for( i = 0; i < count; i++ )
+ {
+ if( c[i]->device_type == c[focus]->device_type
+ && c[i]->device_host == c[focus]->device_host
+ && c[i]->device_bus == c[focus]->device_bus
+ && c[i]->device_target == c[focus]->device_target
+ && c[i]->device_lun == c[focus]->device_lun && c[i]->device_part == 0 )
+ {
+ /* Enable the disk element. */
+ c[i]->select = NWIPE_SELECT_FALSE;
+ }
+
+ } /* for all contexts */
+
+ } /* if */
+
+ } /* else super-enable */
+
+ break;
+
+ } /* if NWIPE_SELECT_TRUE */
+
+ if( c[focus]->select == NWIPE_SELECT_FALSE )
+ {
+ /* Reverse the selection. */
+ c[focus]->select = NWIPE_SELECT_TRUE;
+
+ if( c[focus]->device_part == 0 )
+ {
+ /* Sub-select all partitions and slices within this disk. */
+ for( i = 0; i < count; i++ )
+ {
+ if( c[i]->device_type == c[focus]->device_type
+ && c[i]->device_host == c[focus]->device_host
+ && c[i]->device_bus == c[focus]->device_bus
+ && c[i]->device_target == c[focus]->device_target
+ && c[i]->device_lun == c[focus]->device_lun && c[i]->device_part > 0 )
+ {
+ c[i]->select = NWIPE_SELECT_TRUE_PARENT;
+ }
+
+ } /* for */
+
+ } /* if sub-select */
+
+ else
+ {
+ /* ASSERT: ( c[focus]->device_part > 0 ) */
+
+ /* Super-deselect the disk that contains this device. */
+ for( i = 0; i < count; i++ )
+ {
+ if( c[i]->device_type == c[focus]->device_type
+ && c[i]->device_host == c[focus]->device_host
+ && c[i]->device_bus == c[focus]->device_bus
+ && c[i]->device_target == c[focus]->device_target
+ && c[i]->device_lun == c[focus]->device_lun && c[i]->device_part == 0 )
+ {
+ c[i]->select = NWIPE_SELECT_FALSE_CHILD;
+ }
+ }
+
+ } /* else super-deselect */
+
+ break;
+
+ } /* if NWIPE_SELECT_FALSE */
+
+ /* TODO: Explain to the user why they can't change this. */
+ break;
+
+ case 'm':
+ case 'M':
+
+ validkeyhit = 1;
+
+ /* Run the method dialog. */
+ nwipe_gui_method();
+ break;
+
+ case 'p':
+ case 'P':
+
+ validkeyhit = 1;
+
+ /* Run the PRNG dialog. */
+ nwipe_gui_prng();
+
+ break;
+
+ case 'r':
+ case 'R':
+
+ validkeyhit = 1;
+
+ /* Run the rounds dialog. */
+ nwipe_gui_rounds();
+
+ break;
+
+ case 'v':
+ case 'V':
+
+ validkeyhit = 1;
+
+ /* Run the option dialog. */
+ nwipe_gui_verify();
+ break;
+
+ case 'b':
+ case 'B':
+
+ validkeyhit = 1;
+
+ if( nwipe_options.method == &nwipe_ops2 )
+ {
+ /* Warn the user about that zero blanking with the ops2 method is not allowed */
+ wattron( footer_window, COLOR_PAIR( 10 ) );
+ nwipe_gui_amend_footer_window( main_window_footer_warning_no_blanking_with_ops2 );
+ doupdate();
+ sleep( 3 );
+ wattroff( footer_window, COLOR_PAIR( 10 ) );
+
+ /* After the delay return footer text back to key help */
+ nwipe_gui_amend_footer_window( main_window_footer );
+ doupdate();
+
+ break;
+ }
+
+ if( nwipe_options.method == &nwipe_verify_zero || nwipe_options.method == &nwipe_verify_one )
+ {
+ /* Warn the user about that zero blanking with the ops2 method is not allowed */
+ wattron( footer_window, COLOR_PAIR( 10 ) );
+ nwipe_gui_amend_footer_window( main_window_footer_warning_no_blanking_with_verify_only );
+ doupdate();
+ sleep( 3 );
+ wattroff( footer_window, COLOR_PAIR( 10 ) );
+
+ /* After the delay return footer text back to key help */
+ nwipe_gui_amend_footer_window( main_window_footer );
+ doupdate();
+
+ break;
+ }
+
+ /* Run the noblank dialog. */
+ nwipe_gui_noblank();
+ break;
+
+ case 'c':
+ case 'C':
+ /* main configuration menu */
+ validkeyhit = 1;
+
+ /* Run the configuration dialog */
+ nwipe_gui_config();
+ break;
+
+ case 'S':
+
+ /* User wants to start the wipe */
+ validkeyhit = 1;
+
+ /* Have any drives have been selected ? */
+ number_of_selected_contexts = 0;
+ for( i = 0; i < count; i++ )
+ {
+ if( c[i]->select == NWIPE_SELECT_TRUE )
+ {
+ number_of_selected_contexts += 1;
+ }
+ }
+
+ /* if no drives have been selected, print a warning on the footer */
+ if( number_of_selected_contexts == 0 )
+ {
+ wattron( footer_window, COLOR_PAIR( 10 ) );
+ nwipe_gui_amend_footer_window( main_window_footer_warning_no_drive_selected );
+ doupdate();
+ sleep( 3 );
+ wattroff( footer_window, COLOR_PAIR( 10 ) );
+
+ /* After the delay return footer text back to key help */
+ nwipe_gui_amend_footer_window( main_window_footer );
+ doupdate();
+
+ /* Remove any repeated S key strokes, without this the gui would hang
+ * for a period of time, i.e sleep above x number of repeated 's' keystrokes
+ * which could run into minutes */
+ do
+ {
+ timeout( 250 ); // block getch() for 250ms.
+ keystroke = getch(); // Get user input.
+ timeout( -1 ); // Switch back to blocking mode.
+ } while( keystroke == 'S' );
+
+ /* Remove the S from keystroke, which allows us to stay within the selection menu loop */
+ keystroke = 0;
+ }
+
+ break;
+
+ case 's':
+
+ /* user has mistakenly hit the lower case 's' instead of capital 'S' */
+ validkeyhit = 1;
+
+ /* Warn the user about their mistake */
+ wattron( footer_window, COLOR_PAIR( 10 ) );
+ nwipe_gui_amend_footer_window( main_window_footer_warning_lower_case_s );
+ doupdate();
+ sleep( 3 );
+ wattroff( footer_window, COLOR_PAIR( 10 ) );
+
+ /* After the delay return footer text back to key help */
+ nwipe_gui_amend_footer_window( main_window_footer );
+ doupdate();
+
+ /* Remove any repeated s key strokes, without this the gui would hang
+ * for a period of time, i.e sleep above x number of repeated 's' keystrokes
+ * which could run into minutes */
+ do
+ {
+ timeout( 250 ); // block getch() for 250ms.
+ keystroke = getch(); // Get user input.
+ timeout( -1 ); // Switch back to blocking mode.
+ } while( keystroke == 's' );
+
+ break;
+
+ case 1:
+
+ /* Ctrl A - Toggle select/deselect all drives */
+ validkeyhit = 1;
+
+ if( select_all_toggle_status == -1 || select_all_toggle_status == 0 )
+ {
+ for( i = 0; i < count; i++ )
+ {
+ c[i]->select = NWIPE_SELECT_TRUE;
+ }
+ select_all_toggle_status = 1;
+ }
+ else
+ {
+ if( select_all_toggle_status == 1 )
+ {
+ for( i = 0; i < count; i++ )
+ {
+ c[i]->select = NWIPE_SELECT_FALSE;
+ }
+ select_all_toggle_status = 0;
+ }
+ else
+ {
+ nwipe_log(
+ NWIPE_LOG_ERROR,
+ "gui.c:nwipe_gui_select(), Invalid value in variable select_all_toggle_status = %d",
+ select_all_toggle_status );
+ }
+ }
+
+ break;
+
+ } /* keystroke switch */
+
+ /* Check the terminal size, if the user has changed it the while loop checks for
+ * this change and exits the valid key hit loop so the windows can be updated */
+ getmaxyx( stdscr, stdscr_lines, stdscr_cols );
+
+ /* Update the selection window every 1 second specifically
+ * so that the drive temperatures are updated and also the line toggle that
+ * occurs with the HPA status and the drive size & temperature.
+ */
+ if( time( NULL ) > ( temperature_check_time + 1 ) )
+ {
+ temperature_check_time = time( NULL );
+ validkeyhit = 1;
+ }
+
+ } /* key hit loop */
+ while( validkeyhit == 0 && terminate_signal != 1 && stdscr_cols_previous == stdscr_cols
+ && stdscr_lines_previous == stdscr_lines );
+
+ } while( keystroke != 'S' && terminate_signal != 1 );
+
+ if( keystroke == 'S' )
+ {
+ /* If user has pressed S to start wipe change status line */
+ werase( footer_window );
+ nwipe_gui_title( footer_window, end_wipe_footer );
+ wnoutrefresh( footer_window );
+ }
+
+} /* nwipe_gui_select */
+
+void nwipe_gui_options( void )
+{
+ /**
+ * Updates the options window.
+ *
+ * @modifies options_window
+ *
+ */
+
+ /* Erase the window. */
+ werase( options_window );
+
+ mvwprintw(
+ options_window, NWIPE_GUI_OPTIONS_ENTROPY_Y, NWIPE_GUI_OPTIONS_ENTROPY_X, "Entropy: Linux Kernel (urandom)" );
+
+ mvwprintw(
+ options_window, NWIPE_GUI_OPTIONS_PRNG_Y, NWIPE_GUI_OPTIONS_PRNG_X, "PRNG: %s", nwipe_options.prng->label );
+
+ mvwprintw( options_window,
+ NWIPE_GUI_OPTIONS_METHOD_Y,
+ NWIPE_GUI_OPTIONS_METHOD_X,
+ "Method: %s",
+ nwipe_method_label( nwipe_options.method ) );
+
+ mvwprintw( options_window, NWIPE_GUI_OPTIONS_VERIFY_Y, NWIPE_GUI_OPTIONS_VERIFY_X, "Verify: " );
+
+ switch( nwipe_options.verify )
+ {
+ case NWIPE_VERIFY_NONE:
+ wprintw( options_window, "Off" );
+ break;
+
+ case NWIPE_VERIFY_LAST:
+ wprintw( options_window, "Last Pass" );
+ break;
+
+ case NWIPE_VERIFY_ALL:
+ wprintw( options_window, "All Passes" );
+ break;
+
+ default:
+ wprintw( options_window, "Unknown %i", nwipe_options.verify );
+
+ } /* switch verify */
+
+ mvwprintw( options_window, NWIPE_GUI_OPTIONS_ROUNDS_Y, NWIPE_GUI_OPTIONS_ROUNDS_X, "Rounds: " );
+
+ /* Disable blanking for ops2 and verify methods */
+ if( nwipe_options.method == &nwipe_ops2 || nwipe_options.method == &nwipe_verify_zero
+ || nwipe_options.method == &nwipe_verify_one )
+ {
+ nwipe_options.noblank = 1;
+ }
+
+ if( nwipe_options.noblank )
+ {
+ wprintw( options_window, "%i (no final blanking pass)", nwipe_options.rounds );
+ }
+ else
+ {
+ wprintw( options_window, "%i (plus blanking pass)", nwipe_options.rounds );
+ }
+
+ /* Add a border. */
+ box( options_window, 0, 0 );
+
+ /* Add a title. */
+ nwipe_gui_title( options_window, options_title );
+
+ /* Refresh the window. */
+ // wrefresh( options_window );
+ wnoutrefresh( options_window );
+
+} /* nwipe_gui_options */
+
+void nwipe_gui_rounds( void )
+{
+ /**
+ * Allows the user to change the rounds option.
+ *
+ * @modifies nwipe_options.rounds
+ * @modifies main_window
+ *
+ */
+
+ /* Set the initial focus. */
+ int focus = nwipe_options.rounds;
+
+ /* The first tabstop. */
+ const int tab1 = 2;
+
+ /* The current working row. */
+ int yy;
+
+ /* Input buffer. */
+ int keystroke;
+
+ extern int terminate_signal;
+
+ /* Update the footer window. */
+ werase( footer_window );
+ nwipe_gui_title( footer_window, rounds_footer );
+ wrefresh( footer_window );
+
+ do
+ {
+ /* Erase the main window. */
+ werase( main_window );
+
+ nwipe_gui_create_all_windows_on_terminal_resize( 0, selection_footer );
+
+ /* Add a border. */
+ box( main_window, 0, 0 );
+
+ /* Add a title. */
+ nwipe_gui_title( main_window, " Rounds " );
+
+ /* Initialize the working row. */
+ yy = 4;
+
+ mvwprintw( main_window, yy++, tab1, "This is the number of times to run the wipe method on each device." );
+ yy++;
+
+ if( focus > 0 )
+ {
+ /* Print this line last so that the cursor is in the right place. */
+ mvwprintw( main_window, 2, tab1, "> %i", focus );
+ }
+ else
+ {
+ mvwprintw( main_window, yy++, tab1, "The number of rounds must be a non-negative integer." );
+
+ /* Print this line last so that the cursor is in the right place. */
+ mvwprintw( main_window, 2, tab1, "> " );
+ }
+
+ /* Reveal the cursor. */
+ curs_set( 1 );
+
+ /* Refresh the window. */
+ wrefresh( main_window );
+
+ /* Wait 250ms for input from getch, if nothing getch will then continue,
+ * This is necessary so that the while loop can be exited by the
+ * terminate_signal e.g.. the user pressing control-c to exit.
+ * Do not change this value, a higher value means the keys become
+ * sluggish, any slower and more time is spent unnecessarily looping
+ * which wastes CPU cycles.
+ */
+ timeout( 250 ); // block getch() for 250ms.
+ keystroke = getch(); // Get a keystroke.
+ timeout( -1 ); // Switch back to blocking mode.
+
+ switch( keystroke )
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+
+ if( focus < 100000000 )
+ {
+ /* Left shift, base ten. */
+ focus *= 10;
+
+ /* This assumes ASCII input, where the zero character is 0x30. */
+ focus += keystroke - 48;
+ }
+
+ break;
+
+ /* Escape key. */
+ case 27:
+ return;
+
+ case KEY_BACKSPACE:
+ case KEY_LEFT:
+ case 127:
+
+ /* Right shift, base ten. */
+ focus /= 10;
+
+ break;
+
+ } /* switch keystroke */
+
+ /* Hide the cursor. */
+ curs_set( 0 );
+
+ } while( keystroke != 10 && terminate_signal != 1 );
+
+ if( focus > 0 )
+ {
+ /* Set the number of rounds. */
+ nwipe_options.rounds = focus;
+ }
+
+} /* nwipe_guid_rounds */
+
+void nwipe_gui_prng( void )
+{
+ /**
+ * Allows the user to change the PRNG.
+ *
+ * @modifies nwipe_options.prng
+ * @modifies main_window
+ *
+ */
+
+ extern nwipe_prng_t nwipe_twister;
+ extern nwipe_prng_t nwipe_isaac;
+ extern nwipe_prng_t nwipe_isaac64;
+ extern int terminate_signal;
+
+ /* The number of implemented PRNGs. */
+ const int count = 3;
+
+ /* The first tabstop. */
+ const int tab1 = 2;
+
+ /* The second tabstop. */
+ const int tab2 = 30;
+
+ /* Set the initial focus. */
+ int focus = 0;
+
+ /* The current working row. */
+ int yy;
+
+ /* Input buffer. */
+ int keystroke;
+
+ /* Update the footer window. */
+ werase( footer_window );
+ nwipe_gui_title( footer_window, selection_footer );
+ wrefresh( footer_window );
+
+ if( nwipe_options.prng == &nwipe_twister )
+ {
+ focus = 0;
+ }
+ if( nwipe_options.prng == &nwipe_isaac )
+ {
+ focus = 1;
+ }
+ if( nwipe_options.prng == &nwipe_isaac64 )
+ {
+ focus = 2;
+ }
+
+ do
+ {
+ /* Clear the main window. */
+ werase( main_window );
+
+ nwipe_gui_create_all_windows_on_terminal_resize( 0, selection_footer );
+
+ /* Initialize the working row. */
+ yy = 3;
+
+ /* Print the options. */
+ mvwprintw( main_window, yy++, tab1, " %s", nwipe_twister.label );
+ mvwprintw( main_window, yy++, tab1, " %s", nwipe_isaac.label );
+ mvwprintw( main_window, yy++, tab1, " %s", nwipe_isaac64.label );
+ yy++;
+
+ /* Print the cursor. */
+ mvwaddch( main_window, 3 + focus, tab1, ACS_RARROW );
+
+ switch( focus )
+ {
+ case 0:
+
+ mvwprintw( main_window,
+ yy++,
+ tab1,
+ "The Mersenne Twister, by Makoto Matsumoto and Takuji Nishimura, is a " );
+ mvwprintw( main_window,
+ yy++,
+ tab1,
+ "generalized feedback shift register PRNG that is uniform and " );
+ mvwprintw( main_window,
+ yy++,
+ tab1,
+ "equidistributed in 623-dimensions with a proven period of 2^19937-1. " );
+ mvwprintw( main_window,
+ yy++,
+ tab1,
+ " " );
+ mvwprintw( main_window,
+ yy++,
+ tab1,
+ "This implementation passes the Marsaglia Diehard test suite. " );
+ mvwprintw( main_window,
+ yy++,
+ tab1,
+ " " );
+ break;
+
+ case 1:
+
+ mvwprintw( main_window,
+ yy++,
+ tab1,
+ "ISAAC, by Bob Jenkins, is a PRNG derived from RC4 with a minimum period of " );
+ mvwprintw( main_window,
+ yy++,
+ tab1,
+ "2^40 and an expected period of 2^8295. It is difficult to recover the " );
+ mvwprintw( main_window,
+ yy++,
+ tab1,
+ "initial PRNG state by cryptanalysis of the ISAAC stream. " );
+ mvwprintw( main_window,
+ yy++,
+ tab1,
+ " " );
+ mvwprintw( main_window,
+ yy++,
+ tab1,
+ "Performs best on a 32-bit CPU. Use ISAAC-64 if this system has a 64-bit CPU." );
+ break;
+
+ case 2:
+
+ mvwprintw( main_window,
+ yy++,
+ tab1,
+ "ISAAC-64, by Bob Jenkins, is like 32-bit ISAAC, but with a minimum period of" );
+ mvwprintw( main_window,
+ yy++,
+ tab1,
+ "2^77 and an expected period of 2^16583. It is difficult to recover the " );
+ mvwprintw( main_window,
+ yy++,
+ tab1,
+ "initial PRNG state by cryptanalysis of the ISAAC-64 stream. " );
+ mvwprintw( main_window,
+ yy++,
+ tab1,
+ " " );
+ mvwprintw( main_window,
+ yy++,
+ tab1,
+ "Performs best on a 64-bit CPU. Use ISAAC if this system has a 32-bit CPU. " );
+ break;
+
+ } /* switch */
+
+ /* Add a border. */
+ box( main_window, 0, 0 );
+
+ /* Add a title. */
+ nwipe_gui_title( main_window, " Pseudo Random Number Generator " );
+
+ /* Refresh the window. */
+ wrefresh( main_window );
+
+ /* Wait 250ms for input from getch, if nothing getch will then continue,
+ * This is necessary so that the while loop can be exited by the
+ * terminate_signal e.g.. the user pressing control-c to exit.
+ * Do not change this value, a higher value means the keys become
+ * sluggish, any slower and more time is spent unnecessarily looping
+ * which wastes CPU cycles.
+ */
+ timeout( 250 ); // block getch() for 250ms.
+ keystroke = getch(); // Get a keystroke.
+ timeout( -1 ); // Switch back to blocking mode.
+
+ switch( keystroke )
+ {
+ case KEY_DOWN:
+ case 'j':
+ case 'J':
+
+ if( focus < count - 1 )
+ {
+ focus += 1;
+ }
+ break;
+
+ case KEY_UP:
+ case 'k':
+ case 'K':
+
+ if( focus > 0 )
+ {
+ focus -= 1;
+ }
+ break;
+
+ case KEY_ENTER:
+ case ' ':
+ case 10:
+
+ if( focus == 0 )
+ {
+ nwipe_options.prng = &nwipe_twister;
+ }
+ if( focus == 1 )
+ {
+ nwipe_options.prng = &nwipe_isaac;
+ }
+ if( focus == 2 )
+ {
+ nwipe_options.prng = &nwipe_isaac64;
+ }
+ return;
+
+ case KEY_BACKSPACE:
+ case KEY_BREAK:
+
+ return;
+
+ } /* switch */
+
+ } while( terminate_signal != 1 );
+
+} /* nwipe_gui_prng */
+
+void nwipe_gui_verify( void )
+{
+ /**
+ * Allows the user to change the verification option.
+ *
+ * @modifies nwipe_options.verify
+ * @modifies main_window
+ *
+ */
+
+ extern int terminate_signal;
+
+ /* The number of definitions in the nwipe_verify_t enumeration. */
+ const int count = 3;
+
+ /* The first tabstop. */
+ const int tab1 = 2;
+
+ /* The second tabstop. */
+ const int tab2 = 30;
+
+ /* Set the initial focus. */
+ int focus = nwipe_options.verify;
+
+ /* The current working row. */
+ int yy;
+
+ /* Input buffer. */
+ int keystroke;
+
+ /* Update the footer window. */
+ werase( footer_window );
+ nwipe_gui_title( footer_window, selection_footer );
+ wrefresh( footer_window );
+
+ do
+ {
+ nwipe_gui_create_all_windows_on_terminal_resize( 0, selection_footer );
+
+ /* Clear the main window. */
+ werase( main_window );
+
+ /* Initialize the working row. */
+ yy = 2;
+
+ /* Print the options. */
+ mvwprintw( main_window, yy++, tab1, " Verification Off " );
+ mvwprintw( main_window, yy++, tab1, " Verify Last Pass " );
+ mvwprintw( main_window, yy++, tab1, " Verify All Passes " );
+ mvwprintw( main_window, yy++, tab1, " " );
+
+ /* Print the cursor. */
+ mvwaddch( main_window, 2 + focus, tab1, ACS_RARROW );
+
+ switch( focus )
+ {
+ case 0:
+
+ mvwprintw( main_window,
+ yy++,
+ tab1,
+ "Do not verify passes. The wipe will be a write-only operation. " );
+ mvwprintw( main_window,
+ yy++,
+ tab1,
+ " " );
+ break;
+
+ case 1:
+
+ mvwprintw( main_window,
+ yy++,
+ tab1,
+ "Check whether the device is actually empty after the last pass fills the " );
+ mvwprintw( main_window,
+ yy++,
+ tab1,
+ "device with zeros. " );
+ break;
+
+ case 2:
+
+ mvwprintw( main_window,
+ yy++,
+ tab1,
+ "After every pass, read back the pattern and check whether it is correct. " );
+ mvwprintw( main_window,
+ yy++,
+ tab1,
+ " " );
+ mvwprintw( main_window,
+ yy++,
+ tab1,
+ "This program writes the entire length of the device before it reads back " );
+ mvwprintw( main_window,
+ yy++,
+ tab1,
+ "for verification, even for random pattern passes, to better ensure that " );
+ mvwprintw( main_window,
+ yy++,
+ tab1,
+ "hardware caches are actually flushed. " );
+ break;
+
+ } /* switch */
+
+ /* Add a border. */
+ box( main_window, 0, 0 );
+
+ /* Add a title. */
+ nwipe_gui_title( main_window, " Verification Mode " );
+
+ /* Refresh the window. */
+ wrefresh( main_window );
+
+ /* Wait 250ms for input from getch, if nothing getch will then continue,
+ * This is necessary so that the while loop can be exited by the
+ * terminate_signal e.g.. the user pressing control-c to exit.
+ * Do not change this value, a higher value means the keys become
+ * sluggish, any slower and more time is spent unnecessarily looping
+ * which wastes CPU cycles.
+ */
+ timeout( 250 ); // block getch() for 250ms.
+ keystroke = getch(); // Get a keystroke.
+ timeout( -1 ); // Switch back to blocking mode.
+
+ switch( keystroke )
+ {
+ case KEY_DOWN:
+ case 'j':
+ case 'J':
+
+ if( focus < count - 1 )
+ {
+ focus += 1;
+ }
+ break;
+
+ case KEY_UP:
+ case 'k':
+ case 'K':
+
+ if( focus > 0 )
+ {
+ focus -= 1;
+ }
+ break;
+
+ case KEY_ENTER:
+ case ' ':
+ case 10:
+
+ if( focus >= 0 && focus < count )
+ {
+ nwipe_options.verify = focus;
+ }
+ return;
+
+ case KEY_BACKSPACE:
+ case KEY_BREAK:
+
+ return;
+
+ } /* switch */
+
+ } while( terminate_signal != 1 );
+
+} /* nwipe_gui_verify */
+
+void nwipe_gui_noblank( void )
+{
+ /**
+ * Allows the user to change the verification option.
+ *
+ * @modifies nwipe_options.noblank
+ * @modifies main_window
+ *
+ */
+
+ extern int terminate_signal;
+
+ /* The number of options available. */
+ const int count = 2;
+
+ /* The first tabstop. */
+ const int tab1 = 2;
+
+ /* The second tabstop. */
+ const int tab2 = 40;
+
+ /* Set the initial focus. */
+ int focus = nwipe_options.noblank;
+
+ /* The current working row. */
+ int yy;
+
+ /* Input buffer. */
+ int keystroke;
+
+ /* Update the footer window. */
+ werase( footer_window );
+ nwipe_gui_title( footer_window, selection_footer );
+ wrefresh( footer_window );
+
+ do
+ {
+ nwipe_gui_create_all_windows_on_terminal_resize( 0, selection_footer );
+
+ /* Clear the main window. */
+ werase( main_window );
+
+ /* Initialize the working row. */
+ yy = 2;
+
+ /* Print the options. */
+ mvwprintw( main_window, yy++, tab1, " Perform a final blanking pass " );
+ mvwprintw( main_window, yy++, tab1, " Do not perform final blanking pass " );
+ mvwprintw( main_window, yy++, tab1, " " );
+
+ /* Print the cursor. */
+ mvwaddch( main_window, 2 + focus, tab1, ACS_RARROW );
+
+ switch( focus )
+ {
+ case 0:
+
+ mvwprintw( main_window,
+ yy++,
+ tab1,
+ "Perform a final blanking pass after the wipe, leaving disk with only zeros. " );
+ mvwprintw( main_window,
+ yy++,
+ tab1,
+ "Note that the RCMP TSSIT OPS-II method never blanks the device regardless " );
+ mvwprintw( main_window,
+ yy++,
+ tab1,
+ "of this setting. " );
+ mvwprintw( main_window,
+ yy++,
+ tab1,
+ " " );
+ break;
+
+ case 1:
+
+ mvwprintw( main_window,
+ yy++,
+ tab1,
+ "Do not perform a final blanking pass. Leave data as per final wiping pass. " );
+ mvwprintw( main_window,
+ yy++,
+ tab1,
+ "Note that the RCMP TSSIT OPS-II method never blanks the device regardless " );
+ mvwprintw( main_window,
+ yy++,
+ tab1,
+ "of this setting. " );
+ mvwprintw( main_window,
+ yy++,
+ tab1,
+ " " );
+ break;
+
+ } /* switch */
+
+ /* Add a border. */
+ box( main_window, 0, 0 );
+
+ /* Add a title. */
+ nwipe_gui_title( main_window, " Final Blanking Pass " );
+
+ /* Refresh the window. */
+ wrefresh( main_window );
+
+ /* Wait 250ms for input from getch, if nothing getch will then continue,
+ * This is necessary so that the while loop can be exited by the
+ * terminate_signal e.g.. the user pressing control-c to exit.
+ * Do not change this value, a higher value means the keys become
+ * sluggish, any slower and more time is spent unnecessarily looping
+ * which wastes CPU cycles.
+ */
+ timeout( 250 ); // block getch() for 250ms.
+ keystroke = getch(); // Get a keystroke.
+ timeout( -1 ); // Switch back to blocking mode.
+
+ switch( keystroke )
+ {
+ case KEY_DOWN:
+ case 'j':
+ case 'J':
+
+ if( focus < count - 1 )
+ {
+ focus += 1;
+ }
+ break;
+
+ case KEY_UP:
+ case 'k':
+ case 'K':
+
+ if( focus > 0 )
+ {
+ focus -= 1;
+ }
+ break;
+
+ case KEY_ENTER:
+ case ' ':
+ case 10:
+
+ if( focus >= 0 && focus < count )
+ {
+ nwipe_options.noblank = focus;
+ }
+ return;
+
+ case KEY_BACKSPACE:
+ case KEY_BREAK:
+
+ return;
+
+ } /* switch */
+
+ }
+
+ while( terminate_signal != 1 );
+} /* nwipe_gui_noblank */
+
+void nwipe_gui_method( void )
+{
+ /**
+ * Allows the user to change the wipe method.
+ *
+ * @modifies nwipe_options.method
+ * @modifies main_window
+ *
+ */
+
+ extern int terminate_signal;
+
+ /* The number of implemented methods. */
+ const int count = 10;
+
+ /* The first tabstop. */
+ const int tab1 = 2;
+
+ /* The second tabstop. */
+ const int tab2 = 30;
+
+ /* The currently selected method. */
+ int focus = 0;
+
+ /* The current working row. */
+ int yy;
+
+ /* Input buffer. */
+ int keystroke;
+
+ /* Update the footer window. */
+ werase( footer_window );
+ nwipe_gui_title( footer_window, selection_footer );
+ wrefresh( footer_window );
+
+ if( nwipe_options.method == &nwipe_zero )
+ {
+ focus = 0;
+ }
+ if( nwipe_options.method == &nwipe_one )
+ {
+ focus = 1;
+ }
+ if( nwipe_options.method == &nwipe_ops2 )
+ {
+ focus = 2;
+ }
+ if( nwipe_options.method == &nwipe_dodshort )
+ {
+ focus = 3;
+ }
+ if( nwipe_options.method == &nwipe_dod522022m )
+ {
+ focus = 4;
+ }
+ if( nwipe_options.method == &nwipe_gutmann )
+ {
+ focus = 5;
+ }
+ if( nwipe_options.method == &nwipe_random )
+ {
+ focus = 6;
+ }
+ if( nwipe_options.method == &nwipe_verify_zero )
+ {
+ focus = 7;
+ }
+ if( nwipe_options.method == &nwipe_verify_one )
+ {
+ focus = 8;
+ }
+ if( nwipe_options.method == &nwipe_is5enh )
+ {
+ focus = 9;
+ }
+
+ do
+ {
+ /* Clear the main window. */
+ werase( main_window );
+
+ nwipe_gui_create_all_windows_on_terminal_resize( 0, selection_footer );
+
+ /* Initialize the working row. */
+ yy = 2;
+
+ /* Print the options. */
+ mvwprintw( main_window, yy++, tab1, " %s", nwipe_method_label( &nwipe_zero ) );
+ mvwprintw( main_window, yy++, tab1, " %s", nwipe_method_label( &nwipe_one ) );
+ mvwprintw( main_window, yy++, tab1, " %s", nwipe_method_label( &nwipe_ops2 ) );
+ mvwprintw( main_window, yy++, tab1, " %s", nwipe_method_label( &nwipe_dodshort ) );
+ mvwprintw( main_window, yy++, tab1, " %s", nwipe_method_label( &nwipe_dod522022m ) );
+ mvwprintw( main_window, yy++, tab1, " %s", nwipe_method_label( &nwipe_gutmann ) );
+ mvwprintw( main_window, yy++, tab1, " %s", nwipe_method_label( &nwipe_random ) );
+ mvwprintw( main_window, yy++, tab1, " %s", nwipe_method_label( &nwipe_verify_zero ) );
+ mvwprintw( main_window, yy++, tab1, " %s", nwipe_method_label( &nwipe_verify_one ) );
+ mvwprintw( main_window, yy++, tab1, " %s", nwipe_method_label( &nwipe_is5enh ) );
+ mvwprintw( main_window, yy++, tab1, " " );
+
+ /* Print the cursor. */
+ mvwaddch( main_window, 2 + focus, tab1, ACS_RARROW );
+
+ switch( focus )
+ {
+ case 0:
+
+ mvwprintw( main_window, 2, tab2, "Security Level: high (1 pass)" );
+
+ mvwprintw( main_window, 4, tab2, "This method fills the device with zeros. Note " );
+ mvwprintw( main_window, 5, tab2, "that the rounds option does not apply to this " );
+ mvwprintw( main_window, 6, tab2, "method. This method always runs one round. " );
+ mvwprintw( main_window, 7, tab2, " " );
+ mvwprintw( main_window, 8, tab2, "There is no publicly available evidence that " );
+ mvwprintw( main_window, 9, tab2, "data can be recovered from a modern traditional " );
+ mvwprintw( main_window, 10, tab2, "hard drive (HDD) that has been zero wiped, " );
+ mvwprintw( main_window, 11, tab2, "however a wipe that includes a prng may be " );
+ mvwprintw( main_window, 12, tab2, "preferable. " );
+ break;
+
+ case 1:
+
+ mvwprintw( main_window, 2, tab2, "Security Level: high (1 pass)" );
+
+ mvwprintw( main_window, 4, tab2, "This method fills the device with ones. Note that" );
+ mvwprintw( main_window, 5, tab2, "the rounds option does not apply to this method. " );
+ mvwprintw( main_window, 6, tab2, "This method always runs one round. " );
+ mvwprintw( main_window, 7, tab2, " " );
+ mvwprintw( main_window, 8, tab2, "This method might be used when wiping a solid " );
+ mvwprintw( main_window, 9, tab2, "state drive if an additional level of security is" );
+ mvwprintw( main_window, 10, tab2, "required beyond using the drives internal secure " );
+ mvwprintw( main_window, 11, tab2, "erase features. Alternatively PRNG may be " );
+ mvwprintw( main_window, 12, tab2, "preferable. " );
+ break;
+
+ case 2:
+
+ mvwprintw( main_window, 2, tab2, "Security Level: higher (8 passes)" );
+
+ mvwprintw( main_window, 4, tab2, "The Royal Canadian Mounted Police Technical " );
+ mvwprintw( main_window, 5, tab2, "Security Standard for Information Technology. " );
+ mvwprintw( main_window, 6, tab2, "Appendix OPS-II: Media Sanitization. " );
+ mvwprintw( main_window, 7, tab2, " " );
+ mvwprintw( main_window, 8, tab2, "This implementation, with regards to paragraph 2 " );
+ mvwprintw( main_window, 9, tab2, "section A of the standard, uses a pattern that is" );
+ mvwprintw( main_window, 10, tab2, "one random byte and that is changed each round. " );
+ break;
+
+ case 3:
+
+ mvwprintw( main_window, 2, tab2, "Security Level: higher (3 passes)" );
+
+ mvwprintw( main_window, 4, tab2, "The US Department of Defense 5220.22-M short wipe" );
+ mvwprintw( main_window, 5, tab2, "This method is composed of passes 1, 2 & 7 from " );
+ mvwprintw( main_window, 6, tab2, "the standard DoD 5220.22-M wipe. " );
+ mvwprintw( main_window, 7, tab2, " " );
+ mvwprintw( main_window, 8, tab2, "Pass 1: A random character " );
+ mvwprintw( main_window, 9, tab2, "Pass 2: The bitwise complement of pass 1. " );
+ mvwprintw( main_window, 10, tab2, "Pass 3: A random number generated data stream " );
+ break;
+
+ case 4:
+
+ mvwprintw( main_window, 2, tab2, "Security Level: higher (7 passes)" );
+
+ mvwprintw( main_window, 3, tab2, "The American Department of Defense 5220.22-M " );
+ mvwprintw( main_window, 4, tab2, "standard wipe. " );
+ mvwprintw( main_window, 5, tab2, " " );
+ mvwprintw( main_window, 6, tab2, "Pass 1: A Random character " );
+ mvwprintw( main_window, 7, tab2, "Pass 2: The bitwise complement of pass 1 " );
+ mvwprintw( main_window, 8, tab2, "Pass 3: A random number generated data stream " );
+ mvwprintw( main_window, 9, tab2, "Pass 4: A Random character " );
+ mvwprintw( main_window, 10, tab2, "Pass 5: A Random character " );
+ mvwprintw( main_window, 11, tab2, "Pass 6: The bitwise complement of pass 5 " );
+ mvwprintw( main_window, 12, tab2, "Pass 7: A random number generated data stream " );
+ break;
+
+ case 5:
+
+ mvwprintw( main_window, 2, tab2, "Security Level: Paranoid ! (35 passes) " );
+ mvwprintw( main_window, 3, tab2, "Don't waste your time with this on a modern drive" );
+
+ mvwprintw( main_window, 5, tab2, "This is the method described by Peter Gutmann in " );
+ mvwprintw( main_window, 6, tab2, "the paper entitled \"Secure Deletion of Data from" );
+ mvwprintw( main_window, 7, tab2, "Magnetic and Solid-State Memory\", however not " );
+ mvwprintw( main_window, 8, tab2, "relevant in regards to modern hard disk drives. " );
+ break;
+
+ case 6:
+
+ mvwprintw( main_window, 2, tab2, "Security Level: Depends on Rounds" );
+
+ mvwprintw( main_window, 4, tab2, "This method fills the device with a stream from " );
+ mvwprintw( main_window, 5, tab2, "the PRNG. It is probably the best method to use " );
+ mvwprintw( main_window, 6, tab2, "on modern hard disk drives due to variation in " );
+ mvwprintw( main_window, 7, tab2, "encoding methods. " );
+ mvwprintw( main_window, 8, tab2, " " );
+ mvwprintw( main_window, 9, tab2, "This method has a high security level with 1 " );
+ mvwprintw( main_window, 10, tab2, "round and an increasingly higher security level " );
+ mvwprintw( main_window, 11, tab2, "as rounds are increased." );
+ break;
+
+ case 7:
+
+ mvwprintw( main_window, 2, tab2, "Security Level: Not applicable" );
+
+ mvwprintw( main_window, 4, tab2, "This method only reads the device and checks " );
+ mvwprintw( main_window, 5, tab2, "that it is all zero. " );
+
+ break;
+
+ case 8:
+
+ mvwprintw( main_window, 2, tab2, "Security Level: Not applicable" );
+
+ mvwprintw( main_window, 4, tab2, "This method only reads the device and checks " );
+ mvwprintw( main_window, 5, tab2, "that it is all ones (0xFF)." );
+
+ break;
+
+ case 9:
+
+ mvwprintw( main_window, 2, tab2, "Security Level: higher (3 passes)" );
+
+ mvwprintw( main_window, 4, tab2, "HMG IA/IS 5 (Infosec Standard 5): Secure " );
+ mvwprintw( main_window, 5, tab2, "Sanitisation of Protectively Marked Information " );
+ mvwprintw( main_window, 6, tab2, "or Sensitive Information " );
+ mvwprintw( main_window, 7, tab2, " " );
+ mvwprintw( main_window, 8, tab2, "This method fills the device with 0s, then with " );
+ mvwprintw( main_window, 9, tab2, "1s, then with a PRNG stream, then reads the " );
+ mvwprintw( main_window, 10, tab2, "device to verify the PRNG stream was " );
+ mvwprintw( main_window, 11, tab2, "successfully written. " );
+ break;
+
+ } /* switch */
+
+ /* Add a border. */
+ box( main_window, 0, 0 );
+
+ /* Add a title. */
+ nwipe_gui_title( main_window, " Wipe Method " );
+
+ /* Refresh the window. */
+ wrefresh( main_window );
+
+ /* Wait 250ms for input from getch, if nothing getch will then continue,
+ * This is necessary so that the while loop can be exited by the
+ * terminate_signal e.g.. the user pressing control-c to exit.
+ * Do not change this value, a higher value means the keys become
+ * sluggish, any slower and more time is spent unnecessarily looping
+ * which wastes CPU cycles.
+ */
+ timeout( 250 ); /* block getch() for 250ms */
+ keystroke = getch(); /* Get a keystroke. */
+ timeout( -1 ); /* Switch back to blocking mode */
+
+ switch( keystroke )
+ {
+ case KEY_DOWN:
+ case 'j':
+ case 'J':
+
+ if( focus < count - 1 )
+ {
+ focus += 1;
+ }
+ break;
+
+ case KEY_UP:
+ case 'k':
+ case 'K':
+
+ if( focus > 0 )
+ {
+ focus -= 1;
+ }
+ break;
+
+ case KEY_BACKSPACE:
+ case KEY_BREAK:
+
+ return;
+
+ } /* switch */
+
+ } while( keystroke != KEY_ENTER && keystroke != ' ' && keystroke != 10 && terminate_signal != 1 );
+
+ switch( focus )
+ {
+ case 0:
+ nwipe_options.method = &nwipe_zero;
+ break;
+
+ case 1:
+ nwipe_options.method = &nwipe_one;
+ break;
+
+ case 2:
+ nwipe_options.method = &nwipe_ops2;
+ break;
+
+ case 3:
+ nwipe_options.method = &nwipe_dodshort;
+ break;
+
+ case 4:
+ nwipe_options.method = &nwipe_dod522022m;
+ break;
+
+ case 5:
+ nwipe_options.method = &nwipe_gutmann;
+ break;
+
+ case 6:
+ nwipe_options.method = &nwipe_random;
+ break;
+
+ case 7:
+ nwipe_options.method = &nwipe_verify_zero;
+ break;
+
+ case 8:
+ nwipe_options.method = &nwipe_verify_one;
+ break;
+
+ case 9:
+ nwipe_options.method = &nwipe_is5enh;
+ break;
+ }
+
+} /* nwipe_gui_method */
+
+void nwipe_gui_config( void )
+{
+ /**
+ * Display the configuration Main Menu selection window
+ *
+ */
+
+ extern int terminate_signal;
+
+ /* Number of entries in the configuration menu. */
+ const int count = 8;
+
+ /* The first tabstop. */
+ const int tab1 = 2;
+
+ /* The second tabstop. */
+ const int tab2 = 40;
+
+ /* The currently selected method. */
+ int focus = 0;
+
+ /* The current working row. */
+ int yy;
+
+ /* Input buffer. */
+ int keystroke;
+
+ do
+ {
+ /* Clear the main window. */
+ werase( main_window );
+
+ /* Update the footer window. */
+ werase( footer_window );
+ nwipe_gui_title( footer_window, selection_footer_config );
+ wrefresh( footer_window );
+
+ nwipe_gui_create_all_windows_on_terminal_resize( 0, selection_footer_config );
+
+ /* Initialize the working row. */
+ yy = 2;
+
+ /* Print the options. */
+ mvwprintw( main_window, yy++, tab1, " %s", "PDF Report - Enable/Disable " );
+ mvwprintw( main_window, yy++, tab1, " %s", "PDF Report - Edit Organisation" );
+ mvwprintw( main_window, yy++, tab1, " %s", "PDF Report - Select Customer " );
+ mvwprintw( main_window, yy++, tab1, " %s", "PDF Report - Add Customer " );
+ mvwprintw( main_window, yy++, tab1, " %s", "PDF Report - Delete Customer " );
+ mvwprintw( main_window, yy++, tab1, " %s", "PDF Report - Preview Details " );
+ mvwprintw( main_window, yy++, tab1, " %s", "PDF Report - Preview at Start " );
+ yy++;
+ mvwprintw( main_window, yy++, tab1, " %s", "Set System Date & Time " );
+ mvwprintw( main_window, yy++, tab1, " " );
+
+ /* Print the cursor. */
+ mvwaddch( main_window, 2 + focus, tab1, ACS_RARROW );
+
+ switch( focus )
+ {
+ case 0:
+
+ if( nwipe_options.PDF_enable )
+ {
+ mvwprintw( main_window, 2, tab2, "PDF Report = ENABLED" );
+ }
+ else
+ {
+ mvwprintw( main_window, 2, tab2, "PDF Report = DISABLED" );
+ }
+
+ mvwprintw( main_window, 4, tab2, "Enable or Disable creation of the PDF " );
+ mvwprintw( main_window, 5, tab2, "report/certificate " );
+ break;
+
+ case 1:
+
+ mvwprintw( main_window, 2, tab2, "PDF Report - Edit Organisation" );
+
+ mvwprintw( main_window, 4, tab2, "This option allows you to edit details" );
+ mvwprintw( main_window, 5, tab2, "of the organisation that is performing" );
+ mvwprintw( main_window, 6, tab2, "the erasure. This includes: business " );
+ mvwprintw( main_window, 7, tab2, "name, business address, contact name " );
+ mvwprintw( main_window, 8, tab2, "and contact phone. " );
+ break;
+
+ case 2:
+ mvwprintw( main_window, 2, tab2, "PDF Report - Select Customer" );
+
+ mvwprintw( main_window, 4, tab2, "Allows selection of a customer as " );
+ mvwprintw( main_window, 5, tab2, "displayed on the PDF report. Customer " );
+ mvwprintw( main_window, 6, tab2, "information includes Name (This can be" );
+ mvwprintw( main_window, 7, tab2, "a personal or business name), address " );
+ mvwprintw( main_window, 8, tab2, "contact name and contact phone. " );
+ mvwprintw( main_window, 9, tab2, " " );
+ mvwprintw( main_window, 10, tab2, "Customer data is located in: " );
+ mvwprintw( main_window, 11, tab2, "/etc/nwipe/nwipe_customers.csv " );
+ break;
+
+ case 3:
+
+ mvwprintw( main_window, 2, tab2, "PDF Report - Add Customer " );
+
+ mvwprintw( main_window, 4, tab2, "This option allows you to add a new " );
+ mvwprintw( main_window, 5, tab2, "customer. A customer can be optionally" );
+ mvwprintw( main_window, 6, tab2, "displayed on the PDF report. Customer " );
+ mvwprintw( main_window, 7, tab2, "information includes Name (This can be" );
+ mvwprintw( main_window, 8, tab2, "a personal or business name), address " );
+ mvwprintw( main_window, 9, tab2, "contact name and contact phone. " );
+ mvwprintw( main_window, 10, tab2, " " );
+ mvwprintw( main_window, 11, tab2, "Customer data is saved in: " );
+ mvwprintw( main_window, 12, tab2, "/etc/nwipe/nwipe_customers.csv " );
+ break;
+
+ case 4:
+
+ mvwprintw( main_window, 2, tab2, "PDF Report - Delete Customer " );
+
+ mvwprintw( main_window, 4, tab2, "This option allows you to delete a " );
+ mvwprintw( main_window, 5, tab2, "customer. A customer can be optionally" );
+ mvwprintw( main_window, 6, tab2, "displayed on the PDF report. Customer " );
+ mvwprintw( main_window, 7, tab2, "information includes Name (This can be" );
+ mvwprintw( main_window, 8, tab2, "a personal or business name), address " );
+ mvwprintw( main_window, 9, tab2, "contact name and contact phone. " );
+ mvwprintw( main_window, 10, tab2, " " );
+ mvwprintw( main_window, 11, tab2, "Customer data is saved in: " );
+ mvwprintw( main_window, 12, tab2, "/etc/nwipe/nwipe_customers.csv " );
+ break;
+
+ case 5:
+
+ mvwprintw( main_window, 2, tab2, "PDF Report - Preview Organisation, " );
+ mvwprintw( main_window, 3, tab2, "Customer and Date/Time details " );
+
+ mvwprintw( main_window, 5, tab2, "This allows the above information to " );
+ mvwprintw( main_window, 6, tab2, "be checked prior to starting the wipe " );
+ mvwprintw( main_window, 7, tab2, "so that the information is correct on " );
+ mvwprintw( main_window, 8, tab2, "the pdf report. " );
+ break;
+
+ case 6:
+
+ if( nwipe_options.PDF_preview_details )
+ {
+ mvwprintw( main_window, 2, tab2, "Preview Org. & Customer at start = ENABLED" );
+ }
+ else
+ {
+ mvwprintw( main_window, 2, tab2, "Preview Org. & Customer at start = DISABLED" );
+ }
+ mvwprintw( main_window, 4, tab2, "A preview prior to the drive selection" );
+ mvwprintw( main_window, 5, tab2, "of the organisation that is performing" );
+ mvwprintw( main_window, 6, tab2, "the wipe, the customer details and the" );
+ mvwprintw( main_window, 7, tab2, "current date and time in order to " );
+ mvwprintw( main_window, 8, tab2, "confirm that the information is " );
+ mvwprintw( main_window, 9, tab2, "correct on the pdf report prior to " );
+ mvwprintw( main_window, 10, tab2, "drive selection and starting the erase" );
+ break;
+
+ case 8:
+
+ mvwprintw( main_window, 2, tab2, "Set System Date & Time " );
+
+ mvwprintw( main_window, 4, tab2, "Useful when host is not connected to " );
+ mvwprintw( main_window, 5, tab2, "the internet and not running the 'ntp'" );
+ mvwprintw( main_window, 6, tab2, "(network time protocol). " );
+ break;
+ } /* switch */
+
+ /* Add a border. */
+ box( main_window, 0, 0 );
+
+ /* Add a title. */
+ nwipe_gui_title( main_window, " Configuration " );
+
+ /* Refresh the window. */
+ wrefresh( main_window );
+
+ /* Wait 250ms for input from getch, if nothing getch will then continue,
+ * This is necessary so that the while loop can be exited by the
+ * terminate_signal e.g.. the user pressing control-c to exit.
+ * Do not change this value, a higher value means the keys become
+ * sluggish, any slower and more time is spent unnecessarily looping
+ * which wastes CPU cycles.
+ */
+ timeout( 250 ); /* block getch() for 250ms */
+ keystroke = getch(); /* Get a keystroke. */
+ timeout( -1 ); /* Switch back to blocking mode */
+
+ switch( keystroke )
+ {
+ case KEY_DOWN:
+ case 'j':
+ case 'J':
+
+ if( focus < count - 1 )
+ {
+ if( focus == 6 )
+ {
+ focus += 2; /* mind the gaps */
+ }
+ else
+ {
+ focus += 1;
+ }
+ }
+ break;
+
+ case KEY_UP:
+ case 'k':
+ case 'K':
+
+ if( focus > 0 )
+ {
+ if( focus == 8 )
+ {
+ focus -= 2; /* mind the gaps */
+ }
+ else
+ {
+ focus -= 1;
+ }
+ }
+ break;
+
+ case KEY_BACKSPACE:
+ case KEY_BREAK:
+ case 27: /* ESC */
+
+ return;
+
+ } /* switch */
+
+ if( keystroke == 0x0A )
+ {
+ switch( focus )
+ {
+ case 0:
+ /* Toggle on pressing ENTER key */
+ if( nwipe_options.PDF_enable == 0 )
+ {
+ nwipe_options.PDF_enable = 1;
+
+ /* write the setting to nwipe.conf */
+ nwipe_conf_update_setting( "PDF_Certificate.PDF_Enable", "ENABLED" );
+ }
+ else
+ {
+ nwipe_options.PDF_enable = 0;
+
+ /* write the setting to nwipe.conf */
+ nwipe_conf_update_setting( "PDF_Certificate.PDF_Enable", "DISABLED" );
+ }
+ break;
+
+ case 1:
+ nwipe_gui_edit_organisation();
+ break;
+
+ case 2:
+ customer_processes( SELECT_CUSTOMER );
+
+ break;
+
+ case 3:
+ nwipe_gui_add_customer();
+ break;
+
+ case 4:
+ customer_processes( DELETE_CUSTOMER );
+ break;
+
+ case 5:
+ nwipe_gui_preview_org_customer( SHOWING_IN_CONFIG_MENUS );
+ break;
+
+ case 6:
+ /* Toggle on pressing ENTER key */
+ if( nwipe_options.PDF_preview_details == 0 )
+ {
+ nwipe_options.PDF_preview_details = 1;
+
+ /* write the setting to nwipe.conf */
+ nwipe_conf_update_setting( "PDF_Certificate.PDF_Preview", "ENABLED" );
+ }
+ else
+ {
+ nwipe_options.PDF_preview_details = 0;
+
+ /* write the setting to nwipe.conf */
+ nwipe_conf_update_setting( "PDF_Certificate.PDF_Preview", "DISABLED" );
+ }
+ break;
+
+ case 8:
+ nwipe_gui_set_date_time();
+ break;
+ }
+ keystroke = -1;
+ }
+
+ } while( keystroke != KEY_ENTER && /* keystroke != ' ' && */ keystroke != 10 && terminate_signal != 1 );
+
+} /* end of nwipe_config() */
+
+void nwipe_gui_edit_organisation( void )
+{
+ /**
+ * Display the list of organisation details available for editing
+ *
+ */
+
+ extern int terminate_signal;
+
+ /* Number of entries in the configuration menu. */
+ const int count = 5;
+
+ /* The first tabstop. */
+ const int tab1 = 2;
+
+ /* The second tabstop. */
+ const int tab2 = 27;
+
+ /* The currently selected method. */
+ int focus = 0;
+
+ /* The current working row. */
+ int yy;
+
+ /* Input buffer. */
+ int keystroke;
+
+ /* variables used by libconfig for extracting data from nwipe.conf */
+ config_setting_t* setting;
+ const char *business_name, *business_address, *contact_name, *contact_phone, *op_tech_name;
+ extern config_t nwipe_cfg;
+
+ do
+ {
+ do
+ {
+ /* Clear the main window. */
+ werase( main_window );
+
+ /* Update the footer window. */
+ werase( footer_window );
+ nwipe_gui_title( footer_window, selection_footer_config );
+ wrefresh( footer_window );
+
+ nwipe_gui_create_all_windows_on_terminal_resize( 0, selection_footer_config );
+
+ /* Initialize the working row. */
+ yy = 2;
+
+ /* Print the options. */
+ mvwprintw( main_window, yy++, tab1, " %s", "Edit Business Name" );
+ mvwprintw( main_window, yy++, tab1, " %s", "Edit Business Address" );
+ mvwprintw( main_window, yy++, tab1, " %s", "Edit Contact Name" );
+ mvwprintw( main_window, yy++, tab1, " %s", "Edit Contact Phone" );
+ mvwprintw( main_window, yy++, tab1, " %s", "Edit Tech/Operator" );
+ mvwprintw( main_window, yy++, tab1, " " );
+
+ /* Print the cursor. */
+ mvwaddch( main_window, 2 + focus, tab1, ACS_RARROW );
+
+ /* libconfig: Locate the Organisation Details section in nwipe.conf */
+ setting = config_lookup( &nwipe_cfg, "Organisation_Details" );
+
+ /* Retrieve data from nwipe.conf */
+ if( config_setting_lookup_string( setting, "Business_Name", &business_name ) )
+ {
+ mvwprintw( main_window, 2, tab2, ": %s", business_name );
+ }
+ else
+ {
+ mvwprintw( main_window, 2, tab2, ": Cannot retrieve business_name, nwipe.conf" );
+ }
+
+ /* Retrieve data from nwipe.conf */
+ if( config_setting_lookup_string( setting, "Business_Address", &business_address ) )
+ {
+ mvwprintw( main_window, 3, tab2, ": %s", business_address );
+ }
+ else
+ {
+ mvwprintw( main_window, 3, tab2, ": Cannot retrieve business address, nwipe.conf" );
+ }
+
+ /* Retrieve data from nwipe.conf */
+ if( config_setting_lookup_string( setting, "Contact_Name", &contact_name ) )
+ {
+ mvwprintw( main_window, 4, tab2, ": %s", contact_name );
+ }
+ else
+ {
+ mvwprintw( main_window, 4, tab2, ": Cannot retrieve contact name, nwipe.conf" );
+ }
+
+ /* Retrieve data from nwipe.conf */
+ if( config_setting_lookup_string( setting, "Contact_Phone", &contact_phone ) )
+ {
+ mvwprintw( main_window, 5, tab2, ": %s", contact_phone );
+ }
+ else
+ {
+ mvwprintw( main_window, 5, tab2, ": Cannot retrieve contact phone, nwipe.conf" );
+ }
+
+ /* Retrieve data from nwipe.conf */
+ if( config_setting_lookup_string( setting, "Op_Tech_Name", &op_tech_name ) )
+ {
+ mvwprintw( main_window, 6, tab2, ": %s", op_tech_name );
+ }
+ else
+ {
+ mvwprintw( main_window, 6, tab2, ": Cannot retrieve op_tech_name, nwipe.conf" );
+ }
+
+ /* Add a border. */
+ box( main_window, 0, 0 );
+
+ /* Add a title. */
+ nwipe_gui_title( main_window, " PDF Report - Edit Organisation " );
+
+ /* Refresh the window. */
+ wrefresh( main_window );
+
+ /* Wait 250ms for input from getch, if nothing getch will then continue,
+ * This is necessary so that the while loop can be exited by the
+ * terminate_signal e.g.. the user pressing control-c to exit.
+ * Do not change this value, a higher value means the keys become
+ * sluggish, any slower and more time is spent unnecessarily looping
+ * which wastes CPU cycles.
+ */
+ timeout( 250 ); /* block getch() for 250ms */
+ keystroke = getch(); /* Get a keystroke. */
+ timeout( -1 ); /* Switch back to blocking mode */
+
+ switch( keystroke )
+ {
+ case KEY_DOWN:
+ case 'j':
+ case 'J':
+
+ if( focus < count - 1 )
+ {
+ focus += 1;
+ }
+ break;
+
+ case KEY_UP:
+ case 'k':
+ case 'K':
+
+ if( focus > 0 )
+ {
+ focus -= 1;
+ }
+ break;
+
+ case KEY_BACKSPACE:
+ case KEY_BREAK:
+ case 27: /* ESC */
+
+ return;
+
+ } /* switch */
+
+ } while( keystroke != KEY_ENTER && keystroke != 10 && terminate_signal != 1 );
+
+ if( keystroke == KEY_ENTER || keystroke == 10 || keystroke == ' ' )
+ {
+ switch( focus )
+ {
+ case 0:
+ nwipe_gui_organisation_business_name( business_name );
+ keystroke = 0;
+ break;
+
+ case 1:
+ nwipe_gui_organisation_business_address( business_address );
+ keystroke = 0;
+ break;
+
+ case 2:
+ nwipe_gui_organisation_contact_name( contact_name );
+ keystroke = 0;
+ break;
+
+ case 3:
+ nwipe_gui_organisation_contact_phone( contact_phone );
+ keystroke = 0;
+ break;
+
+ case 4:
+ nwipe_gui_organisation_op_tech_name( op_tech_name );
+ keystroke = 0;
+ break;
+ }
+ }
+
+ } while( keystroke != KEY_ENTER && keystroke != 10 && terminate_signal != 1 );
+
+} /* end of nwipe_gui_edit_organisation( void ) */
+
+void nwipe_gui_organisation_business_name( const char* business_name )
+{
+ /**
+ * Allows the user to change the organisation business name as displayed on the PDF report.
+ *
+ * @modifies business_name in nwipe.conf
+ * @modifies main_window
+ *
+ */
+
+ /* The first tabstop. */
+ const int tab1 = 2;
+
+ /* The current working row. */
+ int yy;
+
+ /* Input buffer. */
+ int keystroke;
+
+ /* buffer */
+ char buffer[256] = "";
+
+ /* buffer index */
+ int idx = 0;
+
+ extern int terminate_signal;
+
+ /* variables used by libconfig for inserting data into nwipe.conf */
+ config_setting_t* setting;
+ // const char* business_name;
+ extern config_t nwipe_cfg;
+ extern char nwipe_config_file[];
+
+ /* Update the footer window. */
+ werase( footer_window );
+ nwipe_gui_title( footer_window, selection_footer_text_entry );
+ wrefresh( footer_window );
+
+ /* Copy the current business name to the buffer */
+ strcpy( buffer, business_name );
+
+ /* Set the buffer index to point to the end of the string, i.e the NULL */
+ idx = strlen( buffer );
+
+ do
+ {
+ /* Erase the main window. */
+ werase( main_window );
+
+ nwipe_gui_create_all_windows_on_terminal_resize( 0, selection_footer_text_entry );
+
+ /* Add a border. */
+ box( main_window, 0, 0 );
+
+ /* Add a title. */
+ nwipe_gui_title( main_window, " Edit Organisation Business Name " );
+
+ /* Initialize the working row. */
+ yy = 4;
+
+ mvwprintw( main_window, yy++, tab1, "Enter the business name of the organisation performing the erasure" );
+
+ /* Print this line last so that the cursor is in the right place. */
+ mvwprintw( main_window, 2, tab1, ">%s", buffer );
+
+ /* Reveal the cursor. */
+ curs_set( 1 );
+
+ /* Refresh the window. */
+ wrefresh( main_window );
+
+ /* Wait 250ms for input from getch, if nothing getch will then continue,
+ * This is necessary so that the while loop can be exited by the
+ * terminate_signal e.g.. the user pressing control-c to exit.
+ * Do not change this value, a higher value means the keys become
+ * sluggish, any slower and more time is spent unnecessarily looping
+ * which wastes CPU cycles.
+ */
+ timeout( 250 ); // block getch() for 250ms.
+ keystroke = getch(); // Get a keystroke.
+ timeout( -1 ); // Switch back to blocking mode.
+
+ switch( keystroke )
+ {
+ /* Escape key. */
+ case 27:
+ return;
+
+ case KEY_BACKSPACE:
+ case KEY_LEFT:
+ case 127:
+
+ if( idx > 0 )
+ {
+ buffer[--idx] = 0;
+ }
+
+ break;
+
+ } /* switch keystroke */
+
+ if( ( keystroke >= ' ' && keystroke <= '~' ) && keystroke != '\"' && idx < 255 )
+ {
+ buffer[idx++] = keystroke;
+ buffer[idx] = 0;
+ mvwprintw( main_window, 2, tab1, ">%s", buffer );
+ }
+
+ /* Hide the cursor. */
+ curs_set( 0 );
+
+ } while( keystroke != 10 && terminate_signal != 1 );
+
+ /* libconfig: Locate the Organisation Details section in nwipe.conf */
+ if( !( setting = config_lookup( &nwipe_cfg, "Organisation_Details.Business_Name" ) ) )
+ {
+ nwipe_log( NWIPE_LOG_ERROR, "Failed to locate [Organisation_Details.Business_Name] in %s", nwipe_config_file );
+ }
+
+ /* libconfig: Write the new business name */
+ if( config_setting_set_string( setting, buffer ) == CONFIG_FALSE )
+ {
+ nwipe_log( NWIPE_LOG_ERROR,
+ "Failed to write [%s] to [Organisation_Details.Business_Name] in %s",
+ buffer,
+ nwipe_config_file );
+ }
+
+ /* Write out the new configuration. */
+ if( config_write_file( &nwipe_cfg, nwipe_config_file ) == CONFIG_FALSE )
+ {
+ nwipe_log( NWIPE_LOG_ERROR, "Failed to write organisation business name to %s", nwipe_config_file );
+ }
+ else
+ {
+ nwipe_log( NWIPE_LOG_INFO, "[Success] Business name written to %s", nwipe_config_file );
+ }
+
+} /* End of nwipe_gui_organisation_business_name() */
+
+void nwipe_gui_organisation_business_address( const char* business_address )
+{
+ /**
+ * Allows the user to change the organisation business address as displayed on the PDF report.
+ *
+ * @modifies business_address in nwipe.conf
+ * @modifies main_window
+ *
+ */
+
+ /* The first tabstop. */
+ const int tab1 = 2;
+
+ /* The current working row. */
+ int yy;
+
+ /* Input buffer. */
+ int keystroke;
+
+ /* buffer */
+ char buffer[256] = "";
+
+ /* buffer index */
+ int idx = 0;
+
+ extern int terminate_signal;
+
+ /* variables used by libconfig for inserting data into nwipe.conf */
+ config_setting_t* setting;
+ // const char* business_name;
+ extern config_t nwipe_cfg;
+ extern char nwipe_config_file[];
+
+ /* Update the footer window. */
+ werase( footer_window );
+ nwipe_gui_title( footer_window, selection_footer_text_entry );
+ wrefresh( footer_window );
+
+ /* Copy the current business address to the buffer */
+ strcpy( buffer, business_address );
+
+ /* Set the buffer index to point to the end of the string, i.e the NULL */
+ idx = strlen( buffer );
+
+ do
+ {
+ /* Erase the main window. */
+ werase( main_window );
+
+ nwipe_gui_create_all_windows_on_terminal_resize( 0, selection_footer_text_entry );
+
+ /* Add a border. */
+ box( main_window, 0, 0 );
+
+ /* Add a title. */
+ nwipe_gui_title( main_window, " Edit Organisation Business Address " );
+
+ /* Initialize the working row. */
+ yy = 4;
+
+ mvwprintw( main_window, yy++, tab1, "Enter the business address of the organisation performing the erasure" );
+
+ /* Print this line last so that the cursor is in the right place. */
+ mvwprintw( main_window, 2, tab1, ">%s", buffer );
+
+ /* Reveal the cursor. */
+ curs_set( 1 );
+
+ /* Refresh the window. */
+ wrefresh( main_window );
+
+ /* Wait 250ms for input from getch, if nothing getch will then continue,
+ * This is necessary so that the while loop can be exited by the
+ * terminate_signal e.g.. the user pressing control-c to exit.
+ * Do not change this value, a higher value means the keys become
+ * sluggish, any slower and more time is spent unnecessarily looping
+ * which wastes CPU cycles.
+ */
+ timeout( 250 ); // block getch() for 250ms.
+ keystroke = getch(); // Get a keystroke.
+ timeout( -1 ); // Switch back to blocking mode.
+
+ switch( keystroke )
+ {
+ /* Escape key. */
+ case 27:
+ return;
+
+ case KEY_BACKSPACE:
+ case KEY_LEFT:
+ case 127:
+
+ if( idx > 0 )
+ {
+ buffer[--idx] = 0;
+ }
+
+ break;
+
+ } /* switch keystroke */
+
+ if( ( keystroke >= ' ' && keystroke <= '~' ) && keystroke != '\"' && idx < 255 )
+ {
+ buffer[idx++] = keystroke;
+ buffer[idx] = 0;
+ mvwprintw( main_window, 2, tab1, ">%s", buffer );
+ }
+
+ /* Hide the cursor. */
+ curs_set( 0 );
+
+ } while( keystroke != 10 && terminate_signal != 1 );
+
+ /* libconfig: Locate the Organisation Details section in nwipe.conf */
+ if( !( setting = config_lookup( &nwipe_cfg, "Organisation_Details.Business_Address" ) ) )
+ {
+ nwipe_log(
+ NWIPE_LOG_ERROR, "Failed to locate [Organisation_Details.Business_Address] in %s", nwipe_config_file );
+ }
+
+ /* libconfig: Write the new business name */
+ if( config_setting_set_string( setting, buffer ) == CONFIG_FALSE )
+ {
+ nwipe_log( NWIPE_LOG_ERROR,
+ "Failed to write [%s] to [Organisation_Details.Business_Address] in %s",
+ buffer,
+ nwipe_config_file );
+ }
+
+ /* Write out the new configuration. */
+ if( config_write_file( &nwipe_cfg, nwipe_config_file ) == CONFIG_FALSE )
+ {
+ nwipe_log( NWIPE_LOG_ERROR, "Failed to write organisation business address to %s", nwipe_config_file );
+ }
+ else
+ {
+ nwipe_log( NWIPE_LOG_INFO, "[Success] Business address written to %s", nwipe_config_file );
+ }
+
+} /* End of nwipe_gui_organisation_business_address() */
+
+void nwipe_gui_organisation_contact_name( const char* contact_name )
+{
+ /**
+ * Allows the user to change the organisation business address as displayed on the PDF report.
+ *
+ * @modifies business_address in nwipe.conf
+ * @modifies main_window
+ *
+ */
+
+ /* The first tabstop. */
+ const int tab1 = 2;
+
+ /* The current working row. */
+ int yy;
+
+ /* Input buffer. */
+ int keystroke;
+
+ /* buffer */
+ char buffer[256] = "";
+
+ /* buffer index */
+ int idx = 0;
+
+ extern int terminate_signal;
+
+ /* variables used by libconfig for inserting data into nwipe.conf */
+ config_setting_t* setting;
+ // const char* business_name;
+ extern config_t nwipe_cfg;
+ extern char nwipe_config_file[];
+
+ /* Update the footer window. */
+ werase( footer_window );
+ nwipe_gui_title( footer_window, selection_footer_text_entry );
+ wrefresh( footer_window );
+
+ /* Copy the current business address to the buffer */
+ strcpy( buffer, contact_name );
+
+ /* Set the buffer index to point to the end of the string, i.e the NULL */
+ idx = strlen( buffer );
+
+ do
+ {
+ /* Erase the main window. */
+ werase( main_window );
+
+ nwipe_gui_create_all_windows_on_terminal_resize( 0, selection_footer_text_entry );
+
+ /* Add a border. */
+ box( main_window, 0, 0 );
+
+ /* Add a title. */
+ nwipe_gui_title( main_window, " Edit Organisation Contact Name " );
+
+ /* Initialize the working row. */
+ yy = 4;
+
+ mvwprintw( main_window, yy++, tab1, "Enter the contact name for the organisation performing the erasure" );
+
+ /* Print this line last so that the cursor is in the right place. */
+ mvwprintw( main_window, 2, tab1, ">%s", buffer );
+
+ /* Reveal the cursor. */
+ curs_set( 1 );
+
+ /* Refresh the window. */
+ wrefresh( main_window );
+
+ /* Wait 250ms for input from getch, if nothing getch will then continue,
+ * This is necessary so that the while loop can be exited by the
+ * terminate_signal e.g.. the user pressing control-c to exit.
+ * Do not change this value, a higher value means the keys become
+ * sluggish, any slower and more time is spent unnecessarily looping
+ * which wastes CPU cycles.
+ */
+ timeout( 250 ); // block getch() for 250ms.
+ keystroke = getch(); // Get a keystroke.
+ timeout( -1 ); // Switch back to blocking mode.
+
+ switch( keystroke )
+ {
+ /* Escape key. */
+ case 27:
+ return;
+
+ case KEY_BACKSPACE:
+ case KEY_LEFT:
+ case 127:
+
+ if( idx > 0 )
+ {
+ buffer[--idx] = 0;
+ }
+
+ break;
+
+ } /* switch keystroke */
+
+ if( ( keystroke >= ' ' && keystroke <= '~' ) && keystroke != '\"' && idx < 255 )
+ {
+ buffer[idx++] = keystroke;
+ buffer[idx] = 0;
+ mvwprintw( main_window, 2, tab1, ">%s", buffer );
+ }
+
+ /* Hide the cursor. */
+ curs_set( 0 );
+
+ } while( keystroke != 10 && terminate_signal != 1 );
+
+ /* libconfig: Locate the Organisation Details section in nwipe.conf */
+ if( !( setting = config_lookup( &nwipe_cfg, "Organisation_Details.Contact_Name" ) ) )
+ {
+ nwipe_log( NWIPE_LOG_ERROR, "Failed to locate [Organisation_Details.Contact_Name] in %s", nwipe_config_file );
+ }
+
+ /* libconfig: Write the new organisation contact name */
+ if( config_setting_set_string( setting, buffer ) == CONFIG_FALSE )
+ {
+ nwipe_log( NWIPE_LOG_ERROR,
+ "Failed to write [%s] to [Organisation_Details.Contact_Name] in %s",
+ buffer,
+ nwipe_config_file );
+ }
+
+ /* Write out the new configuration. */
+ if( config_write_file( &nwipe_cfg, nwipe_config_file ) == CONFIG_FALSE )
+ {
+ nwipe_log( NWIPE_LOG_ERROR, "Failed to write organisation contact name to %s", nwipe_config_file );
+ }
+ else
+ {
+ nwipe_log( NWIPE_LOG_INFO, "[Success] Business contact name written to %s", nwipe_config_file );
+ }
+
+} /* End of nwipe_gui_organisation_contact_name() */
+
+void nwipe_gui_organisation_contact_phone( const char* contact_phone )
+{
+ /**
+ * Allows the user to change the organisation contact name as displayed on the PDF report.
+ *
+ * @modifies organisation contact name in nwipe.conf
+ * @modifies main_window
+ *
+ */
+
+ /* The first tabstop. */
+ const int tab1 = 2;
+
+ /* The current working row. */
+ int yy;
+
+ /* Input buffer. */
+ int keystroke;
+
+ /* buffer */
+ char buffer[256] = "";
+
+ /* buffer index */
+ int idx = 0;
+
+ extern int terminate_signal;
+
+ /* variables used by libconfig for inserting data into nwipe.conf */
+ config_setting_t* setting;
+ // const char* contact_name;
+ extern config_t nwipe_cfg;
+ extern char nwipe_config_file[];
+
+ /* Update the footer window. */
+ werase( footer_window );
+ nwipe_gui_title( footer_window, selection_footer_text_entry );
+ wrefresh( footer_window );
+
+ /* Copy the current business address to the buffer */
+ strcpy( buffer, contact_phone );
+
+ /* Set the buffer index to point to the end of the string, i.e the NULL */
+ idx = strlen( buffer );
+
+ do
+ {
+ /* Erase the main window. */
+ werase( main_window );
+
+ nwipe_gui_create_all_windows_on_terminal_resize( 0, selection_footer_text_entry );
+
+ /* Add a border. */
+ box( main_window, 0, 0 );
+
+ /* Add a title. */
+ nwipe_gui_title( main_window, " Edit Organisation Contact Phone " );
+
+ /* Initialize the working row. */
+ yy = 4;
+
+ mvwprintw( main_window, yy++, tab1, "Enter the contact phone for the organisation performing the erasure" );
+
+ /* Print this line last so that the cursor is in the right place. */
+ mvwprintw( main_window, 2, tab1, ">%s", buffer );
+
+ /* Reveal the cursor. */
+ curs_set( 1 );
+
+ /* Refresh the window. */
+ wrefresh( main_window );
+
+ /* Wait 250ms for input from getch, if nothing getch will then continue,
+ * This is necessary so that the while loop can be exited by the
+ * terminate_signal e.g.. the user pressing control-c to exit.
+ * Do not change this value, a higher value means the keys become
+ * sluggish, any slower and more time is spent unnecessarily looping
+ * which wastes CPU cycles.
+ */
+ timeout( 250 ); // block getch() for 250ms.
+ keystroke = getch(); // Get a keystroke.
+ timeout( -1 ); // Switch back to blocking mode.
+
+ switch( keystroke )
+ {
+ /* Escape key. */
+ case 27:
+ return;
+
+ case KEY_BACKSPACE:
+ case KEY_LEFT:
+ case 127:
+
+ if( idx > 0 )
+ {
+ buffer[--idx] = 0;
+ }
+
+ break;
+
+ } /* switch keystroke */
+
+ if( ( keystroke >= ' ' && keystroke <= '~' ) && keystroke != '\"' && idx < 255 )
+ {
+ buffer[idx++] = keystroke;
+ buffer[idx] = 0;
+ mvwprintw( main_window, 2, tab1, ">%s", buffer );
+ }
+
+ /* Hide the cursor. */
+ curs_set( 0 );
+
+ } while( keystroke != 10 && terminate_signal != 1 );
+
+ /* libconfig: Locate the Organisation Details section in nwipe.conf */
+ if( !( setting = config_lookup( &nwipe_cfg, "Organisation_Details.Contact_Phone" ) ) )
+ {
+ nwipe_log( NWIPE_LOG_ERROR, "Failed to locate [Organisation_Details.Contact_Phone] in %s", nwipe_config_file );
+ }
+
+ /* libconfig: Write the organistion contact phone */
+ if( config_setting_set_string( setting, buffer ) == CONFIG_FALSE )
+ {
+ nwipe_log( NWIPE_LOG_ERROR,
+ "Failed to write [%s] to [Organisation_Details.Contact_Phone] in %s",
+ buffer,
+ nwipe_config_file );
+ }
+
+ /* Write out the new configuration. */
+ if( config_write_file( &nwipe_cfg, nwipe_config_file ) == CONFIG_FALSE )
+ {
+ nwipe_log( NWIPE_LOG_ERROR, "Failed to write organisation contact phone to %s", nwipe_config_file );
+ }
+ else
+ {
+ nwipe_log( NWIPE_LOG_INFO, "[Success] Business contact phone written to %s", nwipe_config_file );
+ }
+
+} /* End of nwipe_gui_organisation_contact_phone() */
+
+void nwipe_gui_organisation_op_tech_name( const char* op_tech_name )
+{
+ /**
+ * Allows the user to change the organisation contact name as displayed on the PDF report.
+ *
+ * @modifies organisation contact name in nwipe.conf
+ * @modifies main_window
+ *
+ */
+
+ /* The first tabstop. */
+ const int tab1 = 2;
+
+ /* The current working row. */
+ int yy;
+
+ /* Input buffer. */
+ int keystroke;
+
+ /* buffer */
+ char buffer[256] = "";
+
+ /* buffer index */
+ int idx = 0;
+
+ extern int terminate_signal;
+
+ /* variables used by libconfig for inserting data into nwipe.conf */
+ config_setting_t* setting;
+ // const char* contact_name;
+ extern config_t nwipe_cfg;
+ extern char nwipe_config_file[];
+
+ /* Update the footer window. */
+ werase( footer_window );
+ nwipe_gui_title( footer_window, selection_footer_text_entry );
+ wrefresh( footer_window );
+
+ /* Copy the current op_tech_name to the buffer */
+ strcpy( buffer, op_tech_name );
+
+ /* Set the buffer index to point to the end of the string, i.e the NULL */
+ idx = strlen( buffer );
+
+ do
+ {
+ /* Erase the main window. */
+ werase( main_window );
+
+ nwipe_gui_create_all_windows_on_terminal_resize( 0, selection_footer_text_entry );
+
+ /* Add a border. */
+ box( main_window, 0, 0 );
+
+ /* Add a title. */
+ nwipe_gui_title( main_window, " Edit Operator/Technician Name " );
+
+ /* Initialize the working row. */
+ yy = 4;
+
+ mvwprintw( main_window, yy++, tab1, "Enter the operator/technician's name that is performing the erasure" );
+
+ /* Print this line last so that the cursor is in the right place. */
+ mvwprintw( main_window, 2, tab1, ">%s", buffer );
+
+ /* Reveal the cursor. */
+ curs_set( 1 );
+
+ /* Refresh the window. */
+ wrefresh( main_window );
+
+ /* Wait 250ms for input from getch, if nothing getch will then continue,
+ * This is necessary so that the while loop can be exited by the
+ * terminate_signal e.g.. the user pressing control-c to exit.
+ * Do not change this value, a higher value means the keys become
+ * sluggish, any slower and more time is spent unnecessarily looping
+ * which wastes CPU cycles.
+ */
+ timeout( 250 ); // block getch() for 250ms.
+ keystroke = getch(); // Get a keystroke.
+ timeout( -1 ); // Switch back to blocking mode.
+
+ switch( keystroke )
+ {
+ /* Escape key. */
+ case 27:
+ return;
+
+ case KEY_BACKSPACE:
+ case KEY_LEFT:
+ case 127:
+
+ if( idx > 0 )
+ {
+ buffer[--idx] = 0;
+ }
+
+ break;
+
+ } /* switch keystroke */
+
+ if( ( keystroke >= ' ' && keystroke <= '~' ) && keystroke != '\"' && idx < 255 )
+ {
+ buffer[idx++] = keystroke;
+ buffer[idx] = 0;
+ mvwprintw( main_window, 2, tab1, ">%s", buffer );
+ }
+
+ /* Hide the cursor. */
+ curs_set( 0 );
+
+ } while( keystroke != 10 && terminate_signal != 1 );
+
+ /* libconfig: Locate the Organisation Details section in nwipe.conf */
+ if( !( setting = config_lookup( &nwipe_cfg, "Organisation_Details.Op_Tech_Name" ) ) )
+ {
+ nwipe_log( NWIPE_LOG_ERROR, "Failed to locate [Organisation_Details.Op_Tech_Name] in %s", nwipe_config_file );
+ }
+
+ /* libconfig: Write the organistion operator/technician name */
+ if( config_setting_set_string( setting, buffer ) == CONFIG_FALSE )
+ {
+ nwipe_log( NWIPE_LOG_ERROR,
+ "Failed to write [%s] to [Organisation_Details.Op_Tech_Name] in %s",
+ buffer,
+ nwipe_config_file );
+ }
+
+ /* Write out the new configuration. */
+ if( config_write_file( &nwipe_cfg, nwipe_config_file ) == CONFIG_FALSE )
+ {
+ nwipe_log( NWIPE_LOG_ERROR, "Failed to write organisation operator/technician to %s", nwipe_config_file );
+ }
+ else
+ {
+ nwipe_log( NWIPE_LOG_INFO, "[Success] operator/technician name written to %s", nwipe_config_file );
+ }
+
+} /* End of nwipe_gui_organisation_op_tech_name() */
+
+void nwipe_gui_list( int count, char* window_title, char** list, int* selected_entry )
+{
+ /**
+ * Displays a selectable list in a window, return 1 -n in selected entry.
+ * If selected entry = 0, then user cancelled selection.
+ */
+
+ extern int terminate_signal;
+
+ /* The number of lines available in the window. */
+ int wlines;
+
+ /* The number of columns available in the window. */
+ int wcols;
+
+ /* The number of selection elements that we can show in the window. */
+ int slots;
+
+ /* The index of the element that is visible in the first slot. */
+ int offset = 0;
+
+ /* The selection focus. */
+ int focus = 0;
+
+ /* A generic loop variable. */
+ int i = 0;
+
+ /* User input buffer. */
+ int keystroke;
+
+ /* The current working line. */
+ int yy;
+
+ /* Flag, Valid key hit = 1, anything else = 0 */
+ int validkeyhit;
+
+ /* Processed customer entry as displayed in selection dialog */
+ char* display_line;
+
+ /* Get the terminal size */
+ getmaxyx( stdscr, stdscr_lines, stdscr_cols );
+
+ /* Save the terminal size so we check whether the user has resized */
+ stdscr_lines_previous = stdscr_lines;
+ stdscr_cols_previous = stdscr_cols;
+
+ /* Used to refresh the window every second */
+ time_t check_time = time( NULL );
+
+ /* Used in the selection loop to trap a failure of the timeout(), getch() mechanism to block for the designated
+ * period */
+ int iteration_counter;
+
+ /* Used in the selection loop to trap a failure of the timeout(), getch() mechanism to block for the designated
+ * period */
+ int expected_iterations;
+
+ /* General Indexes */
+ int idx, idx2;
+
+ time_t previous_iteration_timestamp;
+
+ do
+ {
+
+ nwipe_gui_create_all_windows_on_terminal_resize( 0, main_window_footer );
+
+ /* There is one slot per line. */
+ getmaxyx( main_window, wlines, wcols );
+
+ /* Less two lines for the box and two lines for padding. */
+ slots = wlines - 4;
+ if( slots < 0 )
+ {
+ slots = 0;
+ }
+
+ /* The code here adjusts the offset value, required when the terminal is resized vertically */
+ if( slots > count )
+ {
+ offset = 0;
+ }
+ else
+ {
+ if( focus >= count )
+ {
+ /* The focus is already at the last element. */
+ focus = count - 1;
+ }
+ if( focus < 0 )
+ {
+ /* The focus is already at the last element. */
+ focus = 0;
+ }
+ }
+
+ if( count >= slots && slots > 0 )
+ {
+ offset = focus + 1 - slots;
+ if( offset < 0 )
+ {
+ offset = 0;
+ }
+ }
+
+ /* Clear the main window, necessary when switching selections such as method etc */
+ werase( main_window );
+
+ /* Refresh main window */
+ wnoutrefresh( main_window );
+
+ /* Set footer help text */
+ /* Update the footer window. */
+ werase( footer_window );
+ nwipe_gui_title( footer_window, selection_footer );
+ wrefresh( footer_window );
+
+ /* Refresh the stats window */
+ wnoutrefresh( stats_window );
+
+ /* Refresh the options window */
+ wnoutrefresh( options_window );
+
+ /* Update the options window. */
+ nwipe_gui_options();
+
+ /* Initialize the line offset. */
+ yy = 2;
+
+ for( i = 0; i < slots && i < count; i++ )
+ {
+ /* Move to the next line. */
+ mvwprintw( main_window, yy++, 1, " " );
+
+ if( i + offset == focus )
+ {
+ /* Print the 'enabled' cursor. */
+ waddch( main_window, ACS_RARROW );
+ }
+
+ else
+ {
+ /* Print whitespace. */
+ waddch( main_window, ' ' );
+ }
+
+ /* In the event for the offset value somehow becoming invalid, this if statement will prevent a segfault
+ * and the else part will log the out of bounds values for debugging */
+ if( i + offset >= 0 && i + offset < count )
+ {
+ /* print a entry from the list, we need to process the string before display,
+ * removing the double quotes that are used in csv for identifying the start & end of a field.
+ */
+ if( ( display_line = calloc( sizeof( char ), strlen( list[i + offset] ) ) ) )
+ {
+ idx = 0;
+ idx2 = 0;
+ while( list[i + offset][idx] != 0 )
+ {
+ if( list[i + offset][idx] == '"' )
+ {
+ idx++;
+ }
+ else
+ {
+ display_line[idx2++] = list[i + offset][idx++];
+ }
+ }
+ display_line[idx2] = 0;
+ wprintw( main_window, "%s ", display_line );
+ free( display_line );
+ }
+ }
+ else
+ {
+ nwipe_log( NWIPE_LOG_DEBUG,
+ "GUI.c,nwipe_gui_select(), scroll, array index out of bounds, i=%u, count=%u, slots=%u, "
+ "focus=%u, offset=%u",
+ i,
+ count,
+ slots,
+ focus,
+ offset );
+ }
+
+ } /* for */
+
+ if( offset > 0 )
+ {
+ mvwprintw( main_window, 1, wcols - 8, " More " );
+ waddch( main_window, ACS_UARROW );
+ }
+
+ if( count - offset > slots )
+ {
+ mvwprintw( main_window, wlines - 2, wcols - 8, " More " );
+ waddch( main_window, ACS_DARROW );
+ }
+
+ /* Draw a border around the menu window. */
+ box( main_window, 0, 0 );
+
+ /* Print a title. */
+ nwipe_gui_title( main_window, window_title );
+
+ /* Refresh the window. */
+ wnoutrefresh( main_window );
+
+ /* Output to physical screen */
+ doupdate();
+
+ /* Initialise the iteration counter */
+ iteration_counter = 0;
+
+ previous_iteration_timestamp = time( NULL );
+
+ /* Calculate Maximum allowed iterations per second */
+ expected_iterations = ( 1000 / GETCH_BLOCK_MS ) * 8;
+
+ do
+ {
+ /* Wait 250ms for input from getch, if nothing getch will then continue,
+ * This is necessary so that the while loop can be exited by the
+ * terminate_signal e.g.. the user pressing control-c to exit.
+ * Do not change this value, a higher value means the keys become
+ * sluggish, any slower and more time is spent unnecessarily looping
+ * which wastes CPU cycles.
+ */
+
+ validkeyhit = 0;
+ timeout( GETCH_BLOCK_MS ); // block getch() for ideally about 250ms.
+ keystroke = getch(); // Get user input.
+ timeout( -1 ); // Switch back to blocking mode.
+
+ /* To avoid 100% CPU usage, check for a runaway condition caused by the "keystroke = getch(); (above), from
+ * immediately returning an error condition. We check for an error condition because getch() returns a ERR
+ * value when the timeout value "timeout( 250 );" expires as well as when a real error occurs. We can't
+ * differentiate from normal operation and a failure of the getch function to block for the specified period
+ * of timeout. So here we check the while loop hasn't exceeded the number of expected iterations per second
+ * ie. a timeout(250) block value of 250ms means we should not see any more than (1000/250) = 4 iterations.
+ * We increase this to 32 iterations to allow a little tolerance. Why is this necessary? It's been found
+ * that in KDE konsole and other terminals based on the QT terminal engine exiting the terminal without
+ * first exiting nwipe results in nwipe remaining running but detached from any interface which causes
+ * getch to fail and its associated timeout. So the CPU or CPU core rises to 100%. Here we detect that
+ * failure and exit nwipe gracefully with the appropriate error. This does not affect use of tmux for
+ * attaching or detaching from a running nwipe session when sitting at the selection screen. All other
+ * terminals correctly terminate nwipe when the terminal itself is exited.
+ */
+
+ iteration_counter++;
+
+ if( previous_iteration_timestamp == time( NULL ) )
+ {
+ if( iteration_counter > expected_iterations )
+ {
+ nwipe_log( NWIPE_LOG_ERROR,
+ "GUI.c,nwipe_gui_select(), loop runaway, did you close the terminal without exiting "
+ "nwipe? Exiting nwipe now." );
+ /* Issue signal to nwipe to exit immediately but gracefully */
+ terminate_signal = 1;
+ }
+ }
+ else
+ {
+ /* new second, so reset counter */
+ iteration_counter = 0;
+ previous_iteration_timestamp = time( NULL );
+ }
+
+ /* We don't necessarily use all of these. For future reference these are some CTRL+key values
+ * ^A - 1, ^B - 2, ^D - 4, ^E - 5, ^F - 6, ^G - 7, ^H - 8, ^I - 9, ^K - 11, ^L - 12, ^N - 14,
+ * ^O - 15, ^P - 16, ^R - 18, ^T - 20, ^U - 21, ^V - 22, ^W - 23, ^X - 24, ^Y - 25
+ * Use nwipe_log( NWIPE_LOG_DEBUG, "Key Name: %s - %u", keyname(keystroke),keystroke) to
+ * figure out what code is returned by what ever key combination */
+
+ switch( keystroke )
+ {
+ case KEY_DOWN:
+ case 'j':
+ case 'J':
+
+ validkeyhit = 1;
+
+ /* Increment the focus. */
+ focus += 1;
+
+ if( focus >= count )
+ {
+ /* The focus is already at the last element. */
+ focus = count - 1;
+ break;
+ }
+
+ if( focus - offset >= slots )
+ {
+ /* The next element is offscreen. Scroll down. */
+ offset += 1;
+ break;
+ }
+
+ break;
+
+ case KEY_UP:
+ case 'k':
+ case 'K':
+
+ validkeyhit = 1;
+
+ /* Decrement the focus. */
+ focus -= 1;
+
+ if( focus < 0 )
+ {
+ /* The focus is already at the last element. */
+ focus = 0;
+ break;
+ }
+
+ if( focus < offset )
+ {
+ /* The next element is offscreen. Scroll up. */
+ offset -= 1;
+ break;
+ }
+
+ break;
+
+ case KEY_BACKSPACE:
+ case KEY_LEFT:
+ case 127:
+ *selected_entry = 0;
+ return;
+ break;
+
+ case KEY_ENTER:
+ case 10:
+ case ' ':
+
+ validkeyhit = 1;
+
+ /* Return the index of the selected item in the list. Values start at 1 not zero */
+ *selected_entry = focus + 1;
+ return;
+
+ break;
+
+ } /* keystroke switch */
+
+ /* Check the terminal size, if the user has changed it the while loop checks for
+ * this change and exits the valid key hit loop so the windows can be updated */
+ getmaxyx( stdscr, stdscr_lines, stdscr_cols );
+
+ /* Update the selection window every 1 second specifically
+ * so that the drive temperatures are updated and also the line toggle that
+ * occurs with the HPA status and the drive size & temperature.
+ */
+ if( time( NULL ) > ( check_time + 1 ) )
+ {
+ check_time = time( NULL );
+ validkeyhit = 1;
+ }
+
+ } /* key hit loop */
+ while( validkeyhit == 0 && terminate_signal != 1 && stdscr_cols_previous == stdscr_cols
+ && stdscr_lines_previous == stdscr_lines );
+
+ } while( terminate_signal != 1 );
+
+} /* nwipe_gui_list */
+
+void nwipe_gui_add_customer( void )
+{
+ /**
+ * Add new customer top level menu
+ *
+ */
+
+ extern int terminate_signal;
+
+ /* Number of entries in the configuration menu. */
+ const int count = 4;
+
+ /* The first tabstop. */
+ const int tab1 = 2;
+
+ /* The second tabstop. */
+ const int tab2 = 27;
+
+ /* The currently selected method. */
+ int focus = 0;
+
+ /* The current working row. */
+ int yy;
+
+ /* Input buffer. */
+ int keystroke;
+
+ char customer_name[FIELD_LENGTH] = "";
+ char customer_address[FIELD_LENGTH] = "";
+ char customer_contact_name[FIELD_LENGTH] = "";
+ char customer_contact_phone[FIELD_LENGTH] = "";
+
+ /* 0 = NO = don't save, 1 = YES = save customer */
+ int save = NO;
+
+ /* 0 = Display standard dialog footer, 1 = YES = display "Save Y/N" footer */
+ int yes_no = NO;
+
+ /* variables used by libconfig for extracting data from nwipe.conf */
+ config_setting_t* setting;
+ // const char *business_name, *business_address, *contact_name, *contact_phone, *op_tech_name;
+ extern config_t nwipe_cfg;
+
+ /* Update the footer window. */
+ werase( footer_window );
+ nwipe_gui_title( footer_window, selection_footer_config );
+ wrefresh( footer_window );
+
+ do
+ {
+ do
+ {
+ /* Clear the main window. */
+ werase( main_window );
+
+ /* Change footer based on whether we are waiting for a Y/N response */
+ if( yes_no == YES )
+ {
+ /* Update the footer window. */
+ werase( footer_window );
+ nwipe_gui_title( footer_window, selection_footer_add_customer_yes_no );
+ wrefresh( footer_window );
+
+ nwipe_gui_create_all_windows_on_terminal_resize( 0, selection_footer_add_customer_yes_no );
+ }
+ else
+ {
+ /* Update the footer window. */
+ werase( footer_window );
+ nwipe_gui_title( footer_window, selection_footer_config );
+ wrefresh( footer_window );
+
+ nwipe_gui_create_all_windows_on_terminal_resize( 0, selection_footer_config );
+ }
+ /* Initialize the working row. */
+ yy = 2;
+
+ /* Print the options. */
+ mvwprintw( main_window, yy++, tab1, " %s : %s", "Add Customer Name ", customer_name );
+ mvwprintw( main_window, yy++, tab1, " %s : %s", "Add Customer Address ", customer_address );
+ mvwprintw( main_window, yy++, tab1, " %s : %s", "Add Customer Contact Name ", customer_contact_name );
+ mvwprintw( main_window, yy++, tab1, " %s : %s", "Add Customer Contact Phone", customer_contact_phone );
+ mvwprintw( main_window, yy++, tab1, " " );
+
+ /* Print the cursor. */
+ mvwaddch( main_window, 2 + focus, tab1, ACS_RARROW );
+
+ /* Add a border. */
+ box( main_window, 0, 0 );
+
+ /* Add a title. */
+ nwipe_gui_title( main_window, " PDF Report - Add New Customer " );
+
+ /* Refresh the window. */
+ wrefresh( main_window );
+
+ /* Wait 250ms for input from getch, if nothing getch will then continue,
+ * This is necessary so that the while loop can be exited by the
+ * terminate_signal e.g.. the user pressing control-c to exit.
+ * Do not change this value, a higher value means the keys become
+ * sluggish, any slower and more time is spent unnecessarily looping
+ * which wastes CPU cycles.
+ */
+ timeout( 250 ); /* block getch() for 250ms */
+ keystroke = getch(); /* Get a keystroke. */
+ timeout( -1 ); /* Switch back to blocking mode */
+
+ if( yes_no == NO )
+ {
+ switch( keystroke )
+ {
+ // Save customer
+ case 's':
+ case 'S':
+ break;
+
+ case KEY_DOWN:
+ case 'j':
+ case 'J':
+
+ if( focus < count - 1 )
+ {
+ focus += 1;
+ }
+ break;
+
+ case KEY_UP:
+ case 'k':
+ case 'K':
+
+ if( focus > 0 )
+ {
+ focus -= 1;
+ }
+ break;
+
+ case KEY_BACKSPACE:
+ case KEY_BREAK:
+ case 27: /* ESC */
+
+ /* If the user has entered any text then ask if the customer entries should be saved */
+ if( customer_name[0] != 0 || customer_address[0] != 0 || customer_contact_name[0] != 0
+ || customer_contact_phone[0] != 0 )
+ {
+ /* Set the footer yes/no flag */
+ yes_no = YES;
+ }
+ else
+ {
+ return;
+ }
+ break;
+
+ } /* switch */
+ }
+ else
+ {
+ /* Waiting for a Y/N response */
+ switch( keystroke )
+ {
+ case 'y':
+ case 'Y':
+ save = 1;
+ break;
+
+ case 'n':
+ case 'N':
+ return;
+ break;
+ }
+ }
+
+ } while( save != YES && keystroke != 's' && keystroke != KEY_ENTER && keystroke != 10
+ && terminate_signal != 1 );
+
+ if( keystroke == KEY_ENTER || keystroke == 10 )
+ {
+ switch( focus )
+ {
+ case 0:
+ nwipe_gui_add_customer_name( customer_name );
+ keystroke = 0;
+ break;
+
+ case 1:
+ nwipe_gui_add_customer_address( customer_address );
+ keystroke = 0;
+ break;
+
+ case 2:
+ nwipe_gui_add_customer_contact_name( customer_contact_name );
+ keystroke = 0;
+ break;
+
+ case 3:
+ nwipe_gui_add_customer_contact_phone( customer_contact_phone );
+ keystroke = 0;
+ break;
+ }
+ }
+
+ } while( save != YES && keystroke != 's' && keystroke != KEY_ENTER && keystroke != ' ' && keystroke != 10
+ && terminate_signal != 1 );
+
+ /* If save set, or user pressed s or S then save the customer details */
+ if( keystroke == 's' || keystroke == 'S' || save == 1 )
+ {
+ write_customer_csv_entry( customer_name, customer_address, customer_contact_name, customer_contact_phone );
+ }
+
+} /* end of nwipe_gui_add_customer( void ) */
+
+void nwipe_gui_add_customer_name( char* customer_name )
+{
+ /**
+ * Allows the user to change the customer's contact name as displayed on the PDF report.
+ *
+ * @modifies customer's contact name
+ * @modifies main_window
+ *
+ */
+
+ /* The first tabstop. */
+ const int tab1 = 2;
+
+ /* The current working row. */
+ int yy;
+
+ /* Input buffer. */
+ int keystroke;
+
+ /* buffer index */
+ int idx = 0;
+
+ extern int terminate_signal;
+
+ // const char* contact_name;
+ extern config_t nwipe_cfg;
+ extern char nwipe_config_file[];
+
+ /* Update the footer window. */
+ werase( footer_window );
+ nwipe_gui_title( footer_window, selection_footer_text_entry );
+ wrefresh( footer_window );
+
+ /* Set the buffer index to point to the end of the string, i.e the NULL */
+ idx = strlen( customer_name );
+
+ do
+ {
+ /* Erase the main window. */
+ werase( main_window );
+
+ nwipe_gui_create_all_windows_on_terminal_resize( 0, selection_footer_text_entry );
+
+ /* Add a border. */
+ box( main_window, 0, 0 );
+
+ /* Add a title. */
+ nwipe_gui_title( main_window, " Add New Customer Name " );
+
+ /* Initialize the working row. */
+ yy = 4;
+
+ mvwprintw( main_window, yy++, tab1, "Enter the customer's name (business or personal name)" );
+
+ /* Print this line last so that the cursor is in the right place. */
+ mvwprintw( main_window, 2, tab1, ">%s", customer_name );
+
+ /* Reveal the cursor. */
+ curs_set( 1 );
+
+ /* Refresh the window. */
+ wrefresh( main_window );
+
+ /* Wait 250ms for input from getch, if nothing getch will then continue,
+ * This is necessary so that the while loop can be exited by the
+ * terminate_signal e.g.. the user pressing control-c to exit.
+ * Do not change this value, a higher value means the keys become
+ * sluggish, any slower and more time is spent unnecessarily looping
+ * which wastes CPU cycles.
+ */
+ timeout( 250 ); // block getch() for 250ms.
+ keystroke = getch(); // Get a keystroke.
+ timeout( -1 ); // Switch back to blocking mode.
+
+ switch( keystroke )
+ {
+ /* Escape key. */
+ case 27:
+ return;
+
+ case KEY_BACKSPACE:
+ case KEY_LEFT:
+ case 127:
+
+ if( idx > 0 )
+ {
+ customer_name[--idx] = 0;
+ }
+
+ break;
+
+ } /* switch keystroke */
+
+ if( ( keystroke >= ' ' && keystroke <= '~' ) && keystroke != '\"' && idx < FIELD_LENGTH )
+ {
+ customer_name[idx++] = keystroke;
+ customer_name[idx] = 0;
+ mvwprintw( main_window, 2, tab1, ">%s", customer_name );
+ }
+
+ /* Hide the cursor. */
+ curs_set( 0 );
+
+ } while( keystroke != 10 && terminate_signal != 1 );
+
+} /* End of nwipe_gui_add_customer_name() */
+
+void nwipe_gui_add_customer_address( char* customer_address )
+{
+ /**
+ * Allows the user to change the customer's address as displayed on the PDF report.
+ *
+ * @modifies customer's address
+ * @modifies main_window
+ *
+ */
+
+ /* The first tabstop. */
+ const int tab1 = 2;
+
+ /* The current working row. */
+ int yy;
+
+ /* Input buffer. */
+ int keystroke;
+
+ /* buffer index */
+ int idx = 0;
+
+ extern int terminate_signal;
+
+ // const char* contact_name;
+ extern config_t nwipe_cfg;
+ extern char nwipe_config_file[];
+
+ /* Update the footer window. */
+ werase( footer_window );
+ nwipe_gui_title( footer_window, selection_footer_text_entry );
+ wrefresh( footer_window );
+
+ /* Set the buffer index to point to the end of the string, i.e the NULL */
+ idx = strlen( customer_address );
+
+ do
+ {
+ /* Erase the main window. */
+ werase( main_window );
+
+ nwipe_gui_create_all_windows_on_terminal_resize( 0, selection_footer_text_entry );
+
+ /* Add a border. */
+ box( main_window, 0, 0 );
+
+ /* Add a title. */
+ nwipe_gui_title( main_window, " Add New Customer Address " );
+
+ /* Initialize the working row. */
+ yy = 4;
+
+ mvwprintw( main_window, yy++, tab1, "Enter the customer's address" );
+
+ /* Print this line last so that the cursor is in the right place. */
+ mvwprintw( main_window, 2, tab1, ">%s", customer_address );
+
+ /* Reveal the cursor. */
+ curs_set( 1 );
+
+ /* Refresh the window. */
+ wrefresh( main_window );
+
+ /* Wait 250ms for input from getch, if nothing getch will then continue,
+ * This is necessary so that the while loop can be exited by the
+ * terminate_signal e.g.. the user pressing control-c to exit.
+ * Do not change this value, a higher value means the keys become
+ * sluggish, any slower and more time is spent unnecessarily looping
+ * which wastes CPU cycles.
+ */
+ timeout( 250 ); // block getch() for 250ms.
+ keystroke = getch(); // Get a keystroke.
+ timeout( -1 ); // Switch back to blocking mode.
+
+ switch( keystroke )
+ {
+ /* Escape key. */
+ case 27:
+ return;
+
+ case KEY_BACKSPACE:
+ case KEY_LEFT:
+ case 127:
+
+ if( idx > 0 )
+ {
+ customer_address[--idx] = 0;
+ }
+
+ break;
+
+ } /* switch keystroke */
+
+ if( ( keystroke >= ' ' && keystroke <= '~' ) && keystroke != '\"' && idx < FIELD_LENGTH )
+ {
+ customer_address[idx++] = keystroke;
+ customer_address[idx] = 0;
+ mvwprintw( main_window, 2, tab1, ">%s", customer_address );
+ }
+
+ /* Hide the cursor. */
+ curs_set( 0 );
+
+ } while( keystroke != 10 && terminate_signal != 1 );
+
+} /* End of nwipe_gui_add_customer_address() */
+
+void nwipe_gui_add_customer_contact_name( char* customer_contact_name )
+{
+ /**
+ * Allows the user to change the customer contact name as displayed on the PDF report.
+ *
+ * @modifies customer's contact name
+ * @modifies main_window
+ *
+ */
+
+ /* The first tabstop. */
+ const int tab1 = 2;
+
+ /* The current working row. */
+ int yy;
+
+ /* Input buffer. */
+ int keystroke;
+
+ /* buffer index */
+ int idx = 0;
+
+ extern int terminate_signal;
+
+ // const char* contact_name;
+ extern config_t nwipe_cfg;
+ extern char nwipe_config_file[];
+
+ /* Update the footer window. */
+ werase( footer_window );
+ nwipe_gui_title( footer_window, selection_footer_text_entry );
+ wrefresh( footer_window );
+
+ /* Set the buffer index to point to the end of the string, i.e the NULL */
+ idx = strlen( customer_contact_name );
+
+ do
+ {
+ /* Erase the main window. */
+ werase( main_window );
+
+ nwipe_gui_create_all_windows_on_terminal_resize( 0, selection_footer_text_entry );
+
+ /* Add a border. */
+ box( main_window, 0, 0 );
+
+ /* Add a title. */
+ nwipe_gui_title( main_window, " Add New Customer Contact Name " );
+
+ /* Initialize the working row. */
+ yy = 4;
+
+ mvwprintw( main_window, yy++, tab1, "Enter the customer's contact name" );
+
+ /* Print this line last so that the cursor is in the right place. */
+ mvwprintw( main_window, 2, tab1, ">%s", customer_contact_name );
+
+ /* Reveal the cursor. */
+ curs_set( 1 );
+
+ /* Refresh the window. */
+ wrefresh( main_window );
+
+ /* Wait 250ms for input from getch, if nothing getch will then continue,
+ * This is necessary so that the while loop can be exited by the
+ * terminate_signal e.g.. the user pressing control-c to exit.
+ * Do not change this value, a higher value means the keys become
+ * sluggish, any slower and more time is spent unnecessarily looping
+ * which wastes CPU cycles.
+ */
+ timeout( 250 ); // block getch() for 250ms.
+ keystroke = getch(); // Get a keystroke.
+ timeout( -1 ); // Switch back to blocking mode.
+
+ switch( keystroke )
+ {
+ /* Escape key. */
+ case 27:
+ return;
+
+ case KEY_BACKSPACE:
+ case KEY_LEFT:
+ case 127:
+
+ if( idx > 0 )
+ {
+ customer_contact_name[--idx] = 0;
+ }
+
+ break;
+
+ } /* switch keystroke */
+
+ if( ( keystroke >= ' ' && keystroke <= '~' ) && keystroke != '\"' && idx < FIELD_LENGTH )
+ {
+ customer_contact_name[idx++] = keystroke;
+ customer_contact_name[idx] = 0;
+ mvwprintw( main_window, 2, tab1, ">%s", customer_contact_name );
+ }
+
+ /* Hide the cursor. */
+ curs_set( 0 );
+
+ } while( keystroke != 10 && terminate_signal != 1 );
+
+} /* End of nwipe_gui_add_customer_contact_name() */
+
+void nwipe_gui_add_customer_contact_phone( char* customer_contact_phone )
+{
+ /**
+ * Allows the user to change the customer contact phone as displayed on the PDF report.
+ *
+ * @modifies customer's contact phone
+ * @modifies main_window
+ *
+ */
+
+ /* The first tabstop. */
+ const int tab1 = 2;
+
+ /* The current working row. */
+ int yy;
+
+ /* Input buffer. */
+ int keystroke;
+
+ /* buffer index */
+ int idx = 0;
+
+ extern int terminate_signal;
+
+ // const char* contact_name;
+ extern config_t nwipe_cfg;
+ extern char nwipe_config_file[];
+
+ /* Update the footer window. */
+ werase( footer_window );
+ nwipe_gui_title( footer_window, selection_footer_text_entry );
+ wrefresh( footer_window );
+
+ /* Set the buffer index to point to the end of the string, i.e the NULL */
+ idx = strlen( customer_contact_phone );
+
+ do
+ {
+ /* Erase the main window. */
+ werase( main_window );
+
+ nwipe_gui_create_all_windows_on_terminal_resize( 0, selection_footer_text_entry );
+
+ /* Add a border. */
+ box( main_window, 0, 0 );
+
+ /* Add a title. */
+ nwipe_gui_title( main_window, " Add New Customer Contact Phone " );
+
+ /* Initialize the working row. */
+ yy = 4;
+
+ mvwprintw( main_window, yy++, tab1, "Enter the customer's contact phone" );
+
+ /* Print this line last so that the cursor is in the right place. */
+ mvwprintw( main_window, 2, tab1, ">%s", customer_contact_phone );
+
+ /* Reveal the cursor. */
+ curs_set( 1 );
+
+ /* Refresh the window. */
+ wrefresh( main_window );
+
+ /* Wait 250ms for input from getch, if nothing getch will then continue,
+ * This is necessary so that the while loop can be exited by the
+ * terminate_signal e.g.. the user pressing control-c to exit.
+ * Do not change this value, a higher value means the keys become
+ * sluggish, any slower and more time is spent unnecessarily looping
+ * which wastes CPU cycles.
+ */
+ timeout( 250 ); // block getch() for 250ms.
+ keystroke = getch(); // Get a keystroke.
+ timeout( -1 ); // Switch back to blocking mode.
+
+ switch( keystroke )
+ {
+ /* Escape key. */
+ case 27:
+ return;
+
+ case KEY_BACKSPACE:
+ case KEY_LEFT:
+ case 127:
+
+ if( idx > 0 )
+ {
+ customer_contact_phone[--idx] = 0;
+ }
+
+ break;
+
+ } /* switch keystroke */
+
+ if( ( keystroke >= ' ' && keystroke <= '~' ) && keystroke != '\"' && idx < FIELD_LENGTH )
+ {
+ customer_contact_phone[idx++] = keystroke;
+ customer_contact_phone[idx] = 0;
+ mvwprintw( main_window, 2, tab1, ">%s", customer_contact_phone );
+ }
+
+ /* Hide the cursor. */
+ curs_set( 0 );
+
+ } while( keystroke != 10 && terminate_signal != 1 );
+
+} /* End of nwipe_gui_add_customer_contact_phone() */
+
+void nwipe_gui_preview_org_customer( int mode )
+{
+ /**
+ * Display the organisation and customers details and the current system date and time
+ *
+ */
+
+ extern int terminate_signal;
+
+ /* Number of entries in the configuration menu. */
+ const int count = 12;
+
+ /* The first tabstop. */
+ const int tab1 = 2;
+
+ /* The second tabstop. */
+ const int tab2 = 27;
+
+ /* The currently selected method. */
+ int focus = 0;
+
+ /* The current working row. */
+ int yy;
+
+ /* Input buffer. */
+ int keystroke;
+
+ time_t t;
+
+ char output_str[FIELD_LENGTH];
+
+ /* Window dimensions */
+ int wlines;
+ int wcols;
+
+ /* variables used by libconfig for extracting data from nwipe.conf */
+ config_setting_t* setting;
+ const char *business_name, *business_address, *contact_name, *contact_phone, *op_tech_name;
+ const char *customer_name, *customer_address, *customer_contact_name, *customer_contact_phone;
+ extern config_t nwipe_cfg;
+
+ do
+ {
+ do
+ {
+ /* Clear the main window. */
+ werase( main_window );
+
+ /* Update the footer window. */
+ werase( footer_window );
+ if( mode == SHOWING_IN_CONFIG_MENUS )
+ {
+ nwipe_gui_title( footer_window, selection_footer );
+ nwipe_gui_create_all_windows_on_terminal_resize( 0, selection_footer );
+ }
+ else
+ {
+ nwipe_gui_create_all_windows_on_terminal_resize( 0, selection_footer_preview_prior_to_drive_selection );
+ nwipe_gui_title( footer_window, selection_footer_preview_prior_to_drive_selection );
+ }
+ wrefresh( footer_window );
+
+ /* Determine size of window */
+ getmaxyx( main_window, wlines, wcols );
+
+ /* Initialize the working row. */
+ yy = 2;
+
+ /* Print the options. */
+ mvwprintw( main_window, yy++, tab1, " %s", "Business Name" );
+ mvwprintw( main_window, yy++, tab1, " %s", "Business Address" );
+ mvwprintw( main_window, yy++, tab1, " %s", "Contact Name" );
+ mvwprintw( main_window, yy++, tab1, " %s", "Contact Phone" );
+ mvwprintw( main_window, yy++, tab1, " %s", "Tech/Operator" );
+ yy++;
+ mvwprintw( main_window, yy++, tab1, " %s", "Customer Name" );
+ mvwprintw( main_window, yy++, tab1, " %s", "Customer Address" );
+ mvwprintw( main_window, yy++, tab1, " %s", "Customer Contact Name" );
+ mvwprintw( main_window, yy++, tab1, " %s", "Customer Contact Phone" );
+ yy++;
+ mvwprintw( main_window, yy++, tab1, " %s", "System Date/Time" );
+
+ /* Print the cursor. */
+ mvwaddch( main_window, 2 + focus, tab1, ACS_RARROW );
+
+ /******************************************************************
+ * libconfig: Locate the Organisation Details section in nwipe.conf
+ */
+
+ setting = config_lookup( &nwipe_cfg, "Organisation_Details" );
+
+ /* Retrieve data from nwipe.conf */
+ if( config_setting_lookup_string( setting, "Business_Name", &business_name ) )
+ {
+ str_truncate( wcols, tab2, business_name, output_str, FIELD_LENGTH );
+ mvwprintw( main_window, 2, tab2, ": %s", output_str );
+ }
+ else
+ {
+ str_truncate( wcols, tab2, "Cannot retrieve business_name, nwipe.conf", output_str, FIELD_LENGTH );
+ mvwprintw( main_window, 2, tab2, ": %s", output_str );
+ }
+
+ /* Retrieve data from nwipe.conf */
+ if( config_setting_lookup_string( setting, "Business_Address", &business_address ) )
+ {
+ str_truncate( wcols, tab2, business_address, output_str, FIELD_LENGTH );
+ mvwprintw( main_window, 3, tab2, ": %s", output_str );
+ }
+ else
+ {
+ str_truncate( wcols, tab2, "Cannot retrieve business address, nwipe.conf", output_str, FIELD_LENGTH );
+ mvwprintw( main_window, 3, tab2, ": %s", output_str );
+ }
+
+ /* Retrieve data from nwipe.conf */
+ if( config_setting_lookup_string( setting, "Contact_Name", &contact_name ) )
+ {
+ str_truncate( wcols, tab2, contact_name, output_str, FIELD_LENGTH );
+ mvwprintw( main_window, 4, tab2, ": %s", output_str );
+ }
+ else
+ {
+ str_truncate( wcols, tab2, "Cannot retrieve contact name, nwipe.conf", output_str, FIELD_LENGTH );
+ mvwprintw( main_window, 4, tab2, ": %s", output_str );
+ }
+
+ /* Retrieve data from nwipe.conf */
+ if( config_setting_lookup_string( setting, "Contact_Phone", &contact_phone ) )
+ {
+ str_truncate( wcols, tab2, contact_phone, output_str, FIELD_LENGTH );
+ mvwprintw( main_window, 5, tab2, ": %s", output_str );
+ }
+ else
+ {
+ str_truncate(
+ wcols, tab2, "Cannot retrieve customer contact phone, nwipe.conf", output_str, FIELD_LENGTH );
+ mvwprintw( main_window, 5, tab2, ": %s", output_str );
+ }
+
+ /* Retrieve data from nwipe.conf */
+ if( config_setting_lookup_string( setting, "Op_Tech_Name", &op_tech_name ) )
+ {
+ str_truncate( wcols, tab2, op_tech_name, output_str, FIELD_LENGTH );
+ mvwprintw( main_window, 6, tab2, ": %s", output_str );
+ }
+ else
+ {
+ str_truncate( wcols, tab2, "Cannot retrieve op_tech_name, nwipe.conf", output_str, FIELD_LENGTH );
+ mvwprintw( main_window, 6, tab2, ": %s", output_str );
+ }
+
+ /**********************************************************************
+ * libconfig: Locate the current customer details section in nwipe.conf
+ */
+ setting = config_lookup( &nwipe_cfg, "Selected_Customer" );
+
+ /* Retrieve data from nwipe.conf */
+ if( config_setting_lookup_string( setting, "Customer_Name", &customer_name ) )
+ {
+ str_truncate( wcols, tab2, customer_name, output_str, FIELD_LENGTH );
+ mvwprintw( main_window, 8, tab2, ": %s", output_str );
+ }
+ else
+ {
+ str_truncate( wcols, tab2, "Cannot retrieve Customer_Name, nwipe.conf", output_str, FIELD_LENGTH );
+ mvwprintw( main_window, 8, tab2, ": %s", output_str );
+ }
+
+ /* Retrieve data from nwipe.conf */
+ if( config_setting_lookup_string( setting, "Customer_Address", &customer_address ) )
+ {
+ str_truncate( wcols, tab2, customer_address, output_str, FIELD_LENGTH );
+ mvwprintw( main_window, 9, tab2, ": %s", output_str );
+ }
+ else
+ {
+ str_truncate( wcols, tab2, "Cannot retrieve customer address, nwipe.conf", output_str, FIELD_LENGTH );
+ mvwprintw( main_window, 9, tab2, ": %s", output_str );
+ }
+
+ /* Retrieve data from nwipe.conf */
+ if( config_setting_lookup_string( setting, "Contact_Name", &contact_name ) )
+ {
+ str_truncate( wcols, tab2, contact_name, output_str, FIELD_LENGTH );
+ mvwprintw( main_window, 10, tab2, ": %s", output_str );
+ }
+ else
+ {
+ str_truncate( wcols, tab2, "Cannot retrieve contact name, nwipe.conf", output_str, FIELD_LENGTH );
+ mvwprintw( main_window, 10, tab2, ": %s", output_str );
+ }
+
+ /* Retrieve data from nwipe.conf */
+ if( config_setting_lookup_string( setting, "Contact_Phone", &contact_phone ) )
+ {
+ str_truncate( wcols, tab2, contact_phone, output_str, FIELD_LENGTH );
+ mvwprintw( main_window, 11, tab2, ": %s", output_str );
+ }
+ else
+ {
+ str_truncate( wcols, tab2, "Cannot retrieve contact phone, nwipe.conf", output_str, FIELD_LENGTH );
+ mvwprintw( main_window, 11, tab2, ": %s", output_str );
+ }
+
+ /*******************************
+ * Retrieve system date and time
+ */
+ time( &t );
+ mvwprintw( main_window, 13, tab2, ": %s", ctime( &t ) );
+
+ /* ************
+ * Add a border
+ */
+ box( main_window, 0, 0 );
+
+ /*************
+ * Add a title
+ */
+ nwipe_gui_title( main_window, " PDF Report - Preview Organisation, customer and date/time " );
+
+ /********************
+ * Refresh the window
+ */
+ wrefresh( main_window );
+
+ /* Wait 250ms for input from getch, if nothing getch will then continue,
+ * This is necessary so that the while loop can be exited by the
+ * terminate_signal e.g.. the user pressing control-c to exit.
+ * Do not change this value, a higher value means the keys become
+ * sluggish, any slower and more time is spent unnecessarily looping
+ * which wastes CPU cycles.
+ */
+ timeout( 250 ); /* block getch() for 250ms */
+ keystroke = getch(); /* Get a keystroke. */
+ timeout( -1 ); /* Switch back to blocking mode */
+
+ switch( keystroke )
+ {
+ case KEY_DOWN:
+ case 'j':
+ case 'J':
+
+ if( focus < count - 1 )
+ {
+ if( focus == 4 || focus == 9 )
+ {
+ focus += 2; /* mind the gaps */
+ }
+ else
+ {
+ focus += 1;
+ }
+ }
+ break;
+
+ case KEY_UP:
+ case 'k':
+ case 'K':
+
+ if( focus > 0 )
+ {
+ if( focus == 6 || focus == 11 )
+ {
+ focus -= 2; /* mind the gaps */
+ }
+ else
+ {
+ focus -= 1;
+ }
+ }
+ break;
+
+ case KEY_BACKSPACE:
+ case KEY_BREAK:
+ case 27: /* ESC */
+
+ return;
+ break;
+
+ case 'A':
+ case 'a':
+ if( mode == SHOWING_PRIOR_TO_DRIVE_SELECTION )
+ {
+ return;
+ }
+
+ } /* switch */
+
+ } while( keystroke != KEY_ENTER && keystroke != ' ' && keystroke != 10 && terminate_signal != 1 );
+
+ if( keystroke == KEY_ENTER || keystroke == 10 || keystroke == ' ' )
+ {
+ switch( focus )
+ {
+ case 0:
+ nwipe_gui_organisation_business_name( business_name );
+ keystroke = 0;
+ break;
+
+ case 1:
+ nwipe_gui_organisation_business_address( business_address );
+ keystroke = 0;
+ break;
+
+ case 2:
+ nwipe_gui_organisation_contact_name( contact_name );
+ keystroke = 0;
+ break;
+
+ case 3:
+ nwipe_gui_organisation_contact_phone( contact_phone );
+ keystroke = 0;
+ break;
+
+ case 4:
+ nwipe_gui_organisation_op_tech_name( op_tech_name );
+ keystroke = 0;
+ break;
+
+ case 6:
+ case 7:
+ case 8:
+ case 9:
+ nwipe_gui_config();
+ break;
+
+ case 11:
+ nwipe_gui_set_date_time();
+ }
+ }
+
+ } while( keystroke != 'A' && keystroke != 'a' && keystroke != KEY_ENTER && keystroke != ' ' && keystroke != 10
+ && terminate_signal != 1 );
+
+} /* end of nwipe_gui_preview_org_customer( void ) */
+
+void nwipe_gui_set_date_time( void )
+{
+ /**
+ * Set system date and time
+ *
+ */
+
+ extern int terminate_signal;
+
+ /* Number of entries in the configuration menu. */
+ const int count = 6;
+
+ /* The first tabstop. */
+ const int tab1 = 2;
+
+ /* The second tabstop. */
+ const int tab2 = 27;
+
+ /* The currently selected method. */
+ int focus = 0;
+
+ /* The current working row. */
+ int yy;
+
+ /* Input buffer. */
+ int keystroke;
+
+ time_t t;
+
+ /* Window dimensions */
+ int wlines;
+ int wcols;
+
+ extern config_t nwipe_cfg;
+
+ /* Update the footer window. */
+ werase( footer_window );
+ nwipe_gui_title( footer_window, selection_footer_config );
+ wrefresh( footer_window );
+
+ do
+ {
+ do
+ {
+ /* Clear the main window. */
+ werase( main_window );
+
+ nwipe_gui_create_all_windows_on_terminal_resize( 0, selection_footer_config );
+
+ /* Determine size of window */
+ getmaxyx( main_window, wlines, wcols );
+
+ /* Initialize the working row. */
+ yy = 4;
+
+ /* Print the options. */
+ mvwprintw( main_window, yy++, tab1, " %s", "Year" );
+ mvwprintw( main_window, yy++, tab1, " %s", "Month" );
+ mvwprintw( main_window, yy++, tab1, " %s", "Day" );
+ yy++;
+ mvwprintw( main_window, yy++, tab1, " %s", "Hours" );
+ mvwprintw( main_window, yy++, tab1, " %s", "Minutes" );
+ yy++;
+ mvwprintw( main_window,
+ yy++,
+ tab1,
+ " %s",
+ "If a Network Time Protocol (NTP) daemon is running date/time may not change" );
+ mvwprintw( main_window,
+ yy++,
+ tab1,
+ " %s",
+ "or may revert back to NTP provided time. Setting time here is for use when" );
+ mvwprintw( main_window,
+ yy++,
+ tab1,
+ " %s",
+ "the host system is not running NTP or not connected to the internet." );
+
+ /* Print the cursor. */
+ mvwaddch( main_window, 4 + focus, tab1, ACS_RARROW );
+
+ /*******************************
+ * Retrieve system date and time
+ */
+ time( &t );
+ mvwprintw( main_window, 2, tab1, "%s", ctime( &t ) );
+
+ /* ************
+ * Add a border
+ */
+ box( main_window, 0, 0 );
+
+ /*************
+ * Add a title
+ */
+ nwipe_gui_title( main_window, " Set date/time " );
+
+ /********************
+ * Refresh the window
+ */
+ wrefresh( main_window );
+
+ /* Wait 250ms for input from getch, if nothing getch will then continue,
+ * This is necessary so that the while loop can be exited by the
+ * terminate_signal e.g.. the user pressing control-c to exit.
+ * Do not change this value, a higher value means the keys become
+ * sluggish, any slower and more time is spent unnecessarily looping
+ * which wastes CPU cycles.
+ */
+ timeout( 250 ); /* block getch() for 250ms */
+ keystroke = getch(); /* Get a keystroke. */
+ timeout( -1 ); /* Switch back to blocking mode */
+
+ switch( keystroke )
+ {
+ case KEY_DOWN:
+ case 'j':
+ case 'J':
+
+ if( focus < count - 1 )
+ {
+ if( focus == 2 )
+ {
+ focus += 2; /* mind the gaps */
+ }
+ else
+ {
+ focus += 1;
+ }
+ }
+ break;
+
+ case KEY_UP:
+ case 'k':
+ case 'K':
+
+ if( focus > 0 )
+ {
+ if( focus == 4 )
+ {
+ focus -= 2; /* mind the gaps */
+ }
+ else
+ {
+ focus -= 1;
+ }
+ }
+ break;
+
+ case KEY_BACKSPACE:
+ case KEY_BREAK:
+ case 27: /* ESC */
+
+ return;
+
+ } /* switch */
+
+ } while( keystroke != KEY_ENTER && keystroke != ' ' && keystroke != 10 && terminate_signal != 1 );
+
+ if( keystroke == KEY_ENTER || keystroke == 10 || keystroke == ' ' )
+ {
+ switch( focus )
+ {
+ case 0:
+ /* Set year */
+ nwipe_gui_set_system_year();
+ keystroke = 0;
+ break;
+
+ case 1:
+ /* Set month */
+ nwipe_gui_set_system_month();
+ keystroke = 0;
+ break;
+
+ case 2:
+ /* Set day */
+ nwipe_gui_set_system_day();
+ keystroke = 0;
+ break;
+
+ case 4:
+ /* Set hours */
+ nwipe_gui_set_system_hour();
+ keystroke = 0;
+ break;
+
+ case 5:
+ /* Set minutes */
+ nwipe_gui_set_system_minute();
+ keystroke = 0;
+ break;
+ }
+ }
+
+ } while( keystroke != KEY_ENTER && keystroke != ' ' && keystroke != 10 && terminate_signal != 1 );
+
+} /* end of nwipe_gui_set_date_time( void ) */
+
+void nwipe_gui_set_system_year( void )
+{
+ /**
+ * Allows the user to edit the host systems year
+ *
+ * @modifies system year
+ * @modifies main_window
+ *
+ */
+
+ /* The first tabstop. */
+ const int tab1 = 2;
+
+ /* The current working row. */
+ int yy = 2;
+
+ /* Input buffer. */
+ int keystroke;
+
+ /* Various output from the date command is processed in this buffer */
+ char date_buffer[256];
+ date_buffer[0] = 0;
+
+ char year[5] = "";
+ char month[3] = "";
+ char day[3] = "";
+ char hours[3] = "";
+ char minutes[3] = "";
+ char seconds[3] = "";
+
+ /* buffer index */
+ int idx = 0;
+
+ int status = 0;
+
+ FILE* fp;
+
+ extern int terminate_signal;
+
+ /* Update the footer window. */
+ werase( footer_window );
+ nwipe_gui_title( footer_window, selection_footer_text_entry );
+ wrefresh( footer_window );
+
+ fp = popen( "date +%Y", "r" );
+ if( fp == NULL )
+ {
+ nwipe_log( NWIPE_LOG_INFO, "popen:Failed to retrieve date +%Y %s", date_buffer );
+ mvwprintw( main_window, yy + 4, tab1, "popen:date command failed retrieving year" );
+ }
+
+ if( fgets( date_buffer, sizeof( date_buffer ), fp ) == NULL )
+ {
+ nwipe_log( NWIPE_LOG_INFO, "fgets:failed to retrieve year %s", date_buffer );
+ mvwprintw( main_window, yy + 5, tab1, "fgets:failed retrieving year" );
+ }
+
+ /* terminate string after fourth character removing any lf */
+ date_buffer[4] = 0;
+
+ pclose( fp );
+
+ /* Set the buffer index to point to the end of the string, i.e the NULL */
+ idx = strlen( date_buffer );
+
+ do
+ {
+ /* Erase the main window. */
+ werase( main_window );
+
+ nwipe_gui_create_all_windows_on_terminal_resize( 0, selection_footer_text_entry );
+
+ /* Add a border. */
+ box( main_window, 0, 0 );
+
+ /* Add a title. */
+ nwipe_gui_title( main_window, " Set System Year " );
+
+ /* Initialize the working row. */
+ yy = 4;
+
+ mvwprintw( main_window, yy++, tab1, "Enter the current year, four numeric digits, return key to submit" );
+
+ /* Print this line last so that the cursor is in the right place. */
+ mvwprintw( main_window, 2, tab1, ">%s", date_buffer );
+
+ /* Reveal the cursor. */
+ curs_set( 1 );
+
+ /* Refresh the window. */
+ wrefresh( main_window );
+
+ /* Wait 250ms for input from getch, if nothing getch will then continue,
+ * This is necessary so that the while loop can be exited by the
+ * terminate_signal e.g.. the user pressing control-c to exit.
+ * Do not change this value, a higher value means the keys become
+ * sluggish, any slower and more time is spent unnecessarily looping
+ * which wastes CPU cycles.
+ */
+ timeout( 250 ); // block getch() for 250ms.
+ keystroke = getch(); // Get a keystroke.
+ timeout( -1 ); // Switch back to blocking mode.
+
+ switch( keystroke )
+ {
+ /* Escape key. */
+ case 27:
+ return;
+
+ case KEY_BACKSPACE:
+ case KEY_LEFT:
+ case 127:
+
+ if( idx > 0 )
+ {
+ date_buffer[--idx] = 0;
+ }
+
+ break;
+
+ } /* switch keystroke */
+
+ if( ( keystroke >= ' ' && keystroke <= '~' ) && keystroke != '\"' && idx < FIELD_LENGTH && idx < 4 )
+ {
+ date_buffer[idx++] = keystroke;
+ date_buffer[idx] = 0;
+ mvwprintw( main_window, 2, tab1, ">%s", date_buffer );
+ }
+
+ /* Hide the cursor. */
+ curs_set( 0 );
+
+ } while( keystroke != 10 && terminate_signal != 1 );
+
+ /* Write year back to system */
+ status = read_system_datetime( year, month, day, hours, minutes, seconds );
+ if( status != 0 )
+ {
+ nwipe_log( NWIPE_LOG_ERROR, "func:read_system_datetime failed, see previous messages for detail" );
+ }
+
+ strncpy( year, date_buffer, 4 );
+
+ status = write_system_datetime( year, month, day, hours, minutes, seconds );
+ if( status != 0 )
+ {
+ nwipe_log( NWIPE_LOG_ERROR, "func:write_system_datetime failed, see previous messages for detail" );
+ }
+
+} /* End of nwipe_gui_set_system_year() */
+
+void nwipe_gui_set_system_month( void )
+{
+ /**
+ * Allows the user to edit the host systems year
+ *
+ * @modifies system month
+ * @modifies main_window
+ *
+ */
+
+ /* The first tabstop. */
+ const int tab1 = 2;
+
+ /* The current working row. */
+ int yy = 2;
+
+ /* Input buffer. */
+ int keystroke;
+
+ /* Various output from the date command is processed in this buffer */
+ char date_buffer[256];
+ date_buffer[0] = 0;
+
+ char year[5] = "";
+ char month[3] = "";
+ char day[3] = "";
+ char hours[3] = "";
+ char minutes[3] = "";
+ char seconds[3] = "";
+
+ /* buffer index */
+ int idx = 0;
+
+ int status = 0;
+
+ FILE* fp;
+
+ extern int terminate_signal;
+
+ /* Update the footer window. */
+ werase( footer_window );
+ nwipe_gui_title( footer_window, selection_footer_text_entry );
+ wrefresh( footer_window );
+
+ fp = popen( "date +%m", "r" );
+ if( fp == NULL )
+ {
+ nwipe_log( NWIPE_LOG_INFO, "popen:Failed to retrieve date +%m %s", date_buffer );
+ mvwprintw( main_window, yy + 4, tab1, "popen:date command failed retrieving month" );
+ }
+
+ if( fgets( date_buffer, sizeof( date_buffer ), fp ) == NULL )
+ {
+ nwipe_log( NWIPE_LOG_INFO, "fgets:failed to retrieve month %s", date_buffer );
+ mvwprintw( main_window, yy + 5, tab1, "fgets:failed retrieving month" );
+ }
+
+ /* terminate string after fourth character removing any lf */
+ date_buffer[2] = 0;
+
+ pclose( fp );
+
+ /* Set the buffer index to point to the end of the string, i.e the NULL */
+ idx = strlen( date_buffer );
+
+ do
+ {
+ /* Erase the main window. */
+ werase( main_window );
+
+ nwipe_gui_create_all_windows_on_terminal_resize( 0, selection_footer_text_entry );
+
+ /* Add a border. */
+ box( main_window, 0, 0 );
+
+ /* Add a title. */
+ nwipe_gui_title( main_window, " Set System Month " );
+
+ /* Initialize the working row. */
+ yy = 4;
+
+ mvwprintw(
+ main_window, yy++, tab1, "Enter the current month, two numeric digits, i.e 01, return key to submit" );
+
+ /* Print this line last so that the cursor is in the right place. */
+ mvwprintw( main_window, 2, tab1, ">%s", date_buffer );
+
+ /* Reveal the cursor. */
+ curs_set( 1 );
+
+ /* Refresh the window. */
+ wrefresh( main_window );
+
+ /* Wait 250ms for input from getch, if nothing getch will then continue,
+ * This is necessary so that the while loop can be exited by the
+ * terminate_signal e.g.. the user pressing control-c to exit.
+ * Do not change this value, a higher value means the keys become
+ * sluggish, any slower and more time is spent unnecessarily looping
+ * which wastes CPU cycles.
+ */
+ timeout( 250 ); // block getch() for 250ms.
+ keystroke = getch(); // Get a keystroke.
+ timeout( -1 ); // Switch back to blocking mode.
+
+ switch( keystroke )
+ {
+ /* Escape key. */
+ case 27:
+ return;
+
+ case KEY_BACKSPACE:
+ case KEY_LEFT:
+ case 127:
+
+ if( idx > 0 )
+ {
+ date_buffer[--idx] = 0;
+ }
+
+ break;
+
+ } /* switch keystroke */
+
+ if( ( keystroke >= ' ' && keystroke <= '~' ) && keystroke != '\"' && idx < FIELD_LENGTH && idx < 4 )
+ {
+ date_buffer[idx++] = keystroke;
+ date_buffer[idx] = 0;
+ mvwprintw( main_window, 2, tab1, ">%s", date_buffer );
+ }
+
+ /* Hide the cursor. */
+ curs_set( 0 );
+
+ } while( keystroke != 10 && terminate_signal != 1 );
+
+ /* Write year back to system */
+ status = read_system_datetime( year, month, day, hours, minutes, seconds );
+ if( status != 0 )
+ {
+ nwipe_log( NWIPE_LOG_ERROR, "func:read_system_datetime failed, see previous messages for detail" );
+ }
+
+ strncpy( month, date_buffer, 2 );
+
+ status = write_system_datetime( year, month, day, hours, minutes, seconds );
+ if( status != 0 )
+ {
+ nwipe_log( NWIPE_LOG_ERROR, "func:write_system_datetime failed, see previous messages for detail" );
+ }
+
+} /* End of nwipe_gui_set_system_month() */
+
+void nwipe_gui_set_system_day( void )
+{
+ /**
+ * Allows the user to edit the host systems year
+ *
+ * @modifies system day of the month
+ * @modifies main_window
+ *
+ */
+
+ /* The first tabstop. */
+ const int tab1 = 2;
+
+ /* The current working row. */
+ int yy = 2;
+
+ /* Input buffer. */
+ int keystroke;
+
+ /* Various output from the date command is processed in this buffer */
+ char date_buffer[256];
+ date_buffer[0] = 0;
+
+ char year[5] = "";
+ char month[3] = "";
+ char day[3] = "";
+ char hours[3] = "";
+ char minutes[3] = "";
+ char seconds[3] = "";
+
+ /* buffer index */
+ int idx = 0;
+
+ int status = 0;
+
+ FILE* fp;
+
+ extern int terminate_signal;
+
+ /* Update the footer window. */
+ werase( footer_window );
+ nwipe_gui_title( footer_window, selection_footer_text_entry );
+ wrefresh( footer_window );
+
+ fp = popen( "date +%d", "r" );
+ if( fp == NULL )
+ {
+ nwipe_log( NWIPE_LOG_INFO, "popen:Failed to retrieve date +%d %s", date_buffer );
+ mvwprintw( main_window, yy + 4, tab1, "popen:date command failed retrieving day of month" );
+ }
+
+ if( fgets( date_buffer, sizeof( date_buffer ), fp ) == NULL )
+ {
+ nwipe_log( NWIPE_LOG_INFO, "fgets:failed to retrieve day of month %s", date_buffer );
+ mvwprintw( main_window, yy + 5, tab1, "fgets:failed retrieving day of month" );
+ }
+
+ /* terminate string after fourth character removing any lf */
+ date_buffer[2] = 0;
+
+ pclose( fp );
+
+ /* Set the buffer index to point to the end of the string, i.e the NULL */
+ idx = strlen( date_buffer );
+
+ do
+ {
+ /* Erase the main window. */
+ werase( main_window );
+
+ nwipe_gui_create_all_windows_on_terminal_resize( 0, selection_footer_text_entry );
+
+ /* Add a border. */
+ box( main_window, 0, 0 );
+
+ /* Add a title. */
+ nwipe_gui_title( main_window, " Set System Day of Month " );
+
+ /* Initialize the working row. */
+ yy = 4;
+
+ mvwprintw( main_window,
+ yy++,
+ tab1,
+ "Enter the current day of month, two numeric digits, i.e 01, return key to submit" );
+
+ /* Print this line last so that the cursor is in the right place. */
+ mvwprintw( main_window, 2, tab1, ">%s", date_buffer );
+
+ /* Reveal the cursor. */
+ curs_set( 1 );
+
+ /* Refresh the window. */
+ wrefresh( main_window );
+
+ /* Wait 250ms for input from getch, if nothing getch will then continue,
+ * This is necessary so that the while loop can be exited by the
+ * terminate_signal e.g.. the user pressing control-c to exit.
+ * Do not change this value, a higher value means the keys become
+ * sluggish, any slower and more time is spent unnecessarily looping
+ * which wastes CPU cycles.
+ */
+ timeout( 250 ); // block getch() for 250ms.
+ keystroke = getch(); // Get a keystroke.
+ timeout( -1 ); // Switch back to blocking mode.
+
+ switch( keystroke )
+ {
+ /* Escape key. */
+ case 27:
+ return;
+
+ case KEY_BACKSPACE:
+ case KEY_LEFT:
+ case 127:
+
+ if( idx > 0 )
+ {
+ date_buffer[--idx] = 0;
+ }
+
+ break;
+
+ } /* switch keystroke */
+
+ if( ( keystroke >= ' ' && keystroke <= '~' ) && keystroke != '\"' && idx < FIELD_LENGTH && idx < 4 )
+ {
+ date_buffer[idx++] = keystroke;
+ date_buffer[idx] = 0;
+ mvwprintw( main_window, 2, tab1, ">%s", date_buffer );
+ }
+
+ /* Hide the cursor. */
+ curs_set( 0 );
+
+ } while( keystroke != 10 && terminate_signal != 1 );
+
+ /* Write year back to system */
+ status = read_system_datetime( year, month, day, hours, minutes, seconds );
+ if( status != 0 )
+ {
+ nwipe_log( NWIPE_LOG_ERROR, "func:read_system_datetime failed, see previous messages for detail" );
+ }
+
+ strncpy( day, date_buffer, 2 );
+
+ status = write_system_datetime( year, month, day, hours, minutes, seconds );
+ if( status != 0 )
+ {
+ nwipe_log( NWIPE_LOG_ERROR, "func:write_system_datetime failed, see previous messages for detail" );
+ }
+
+} /* End of nwipe_gui_set_system_day() */
+
+void nwipe_gui_set_system_hour( void )
+{
+ /**
+ * Allows the user to edit the host systems year
+ *
+ * @modifies system hour
+ * @modifies main_window
+ *
+ */
+
+ /* The first tabstop. */
+ const int tab1 = 2;
+
+ /* The current working row. */
+ int yy = 2;
+
+ /* Input buffer. */
+ int keystroke;
+
+ /* Various output from the date command is processed in this buffer */
+ char date_buffer[256];
+ date_buffer[0] = 0;
+
+ char year[5] = "";
+ char month[3] = "";
+ char day[3] = "";
+ char hours[3] = "";
+ char minutes[3] = "";
+ char seconds[3] = "";
+
+ /* buffer index */
+ int idx = 0;
+
+ int status = 0;
+
+ FILE* fp;
+
+ extern int terminate_signal;
+
+ /* Update the footer window. */
+ werase( footer_window );
+ nwipe_gui_title( footer_window, selection_footer_text_entry );
+ wrefresh( footer_window );
+
+ fp = popen( "date +%H", "r" );
+ if( fp == NULL )
+ {
+ nwipe_log( NWIPE_LOG_INFO, "popen:Failed to retrieve date +%H %s", date_buffer );
+ mvwprintw( main_window, yy + 4, tab1, "popen:date command failed retrieving hour" );
+ }
+
+ if( fgets( date_buffer, sizeof( date_buffer ), fp ) == NULL )
+ {
+ nwipe_log( NWIPE_LOG_INFO, "fgets:failed to retrieve the hour %s", date_buffer );
+ mvwprintw( main_window, yy + 5, tab1, "fgets:failed retrieving the hour" );
+ }
+
+ /* terminate string after fourth character removing any lf */
+ date_buffer[2] = 0;
+
+ pclose( fp );
+
+ /* Set the buffer index to point to the end of the string, i.e the NULL */
+ idx = strlen( date_buffer );
+
+ do
+ {
+ /* Erase the main window. */
+ werase( main_window );
+
+ nwipe_gui_create_all_windows_on_terminal_resize( 0, selection_footer_text_entry );
+
+ /* Add a border. */
+ box( main_window, 0, 0 );
+
+ /* Add a title. */
+ nwipe_gui_title( main_window, " Set System Hour " );
+
+ /* Initialize the working row. */
+ yy = 4;
+
+ mvwprintw(
+ main_window, yy++, tab1, "Enter the current hour, two numeric digits, i.e 01, return key to submit" );
+
+ /* Print this line last so that the cursor is in the right place. */
+ mvwprintw( main_window, 2, tab1, ">%s", date_buffer );
+
+ /* Reveal the cursor. */
+ curs_set( 1 );
+
+ /* Refresh the window. */
+ wrefresh( main_window );
+
+ /* Wait 250ms for input from getch, if nothing getch will then continue,
+ * This is necessary so that the while loop can be exited by the
+ * terminate_signal e.g.. the user pressing control-c to exit.
+ * Do not change this value, a higher value means the keys become
+ * sluggish, any slower and more time is spent unnecessarily looping
+ * which wastes CPU cycles.
+ */
+ timeout( 250 ); // block getch() for 250ms.
+ keystroke = getch(); // Get a keystroke.
+ timeout( -1 ); // Switch back to blocking mode.
+
+ switch( keystroke )
+ {
+ /* Escape key. */
+ case 27:
+ return;
+
+ case KEY_BACKSPACE:
+ case KEY_LEFT:
+ case 127:
+
+ if( idx > 0 )
+ {
+ date_buffer[--idx] = 0;
+ }
+
+ break;
+
+ } /* switch keystroke */
+
+ if( ( keystroke >= ' ' && keystroke <= '~' ) && keystroke != '\"' && idx < FIELD_LENGTH && idx < 4 )
+ {
+ date_buffer[idx++] = keystroke;
+ date_buffer[idx] = 0;
+ mvwprintw( main_window, 2, tab1, ">%s", date_buffer );
+ }
+
+ /* Hide the cursor. */
+ curs_set( 0 );
+
+ } while( keystroke != 10 && terminate_signal != 1 );
+
+ /* Write year back to system */
+ status = read_system_datetime( year, month, day, hours, minutes, seconds );
+ if( status != 0 )
+ {
+ nwipe_log( NWIPE_LOG_ERROR, "func:read_system_datetime failed, see previous messages for detail" );
+ }
+
+ strncpy( hours, date_buffer, 2 );
+
+ status = write_system_datetime( year, month, day, hours, minutes, seconds );
+ if( status != 0 )
+ {
+ nwipe_log( NWIPE_LOG_ERROR, "func:write_system_datetime failed, see previous messages for detail" );
+ }
+
+} /* End of nwipe_gui_set_system_hour() */
+
+void nwipe_gui_set_system_minute( void )
+{
+ /**
+ * Allows the user to edit the host systems year
+ *
+ * @modifies system minute
+ * @modifies main_window
+ *
+ */
+
+ /* The first tabstop. */
+ const int tab1 = 2;
+
+ /* The current working row. */
+ int yy = 2;
+
+ /* Input buffer. */
+ int keystroke;
+
+ /* Various output from the date command is processed in this buffer */
+ char date_buffer[256];
+ date_buffer[0] = 0;
+
+ char year[5] = "";
+ char month[3] = "";
+ char day[3] = "";
+ char hours[3] = "";
+ char minutes[3] = "";
+ char seconds[3] = "";
+
+ /* buffer index */
+ int idx = 0;
+
+ int status = 0;
+
+ FILE* fp;
+
+ extern int terminate_signal;
+
+ /* Update the footer window. */
+ werase( footer_window );
+ nwipe_gui_title( footer_window, selection_footer_text_entry );
+ wrefresh( footer_window );
+
+ fp = popen( "date +%M", "r" );
+ if( fp == NULL )
+ {
+ nwipe_log( NWIPE_LOG_INFO, "popen:Failed to retrieve date +%M %s", date_buffer );
+ mvwprintw( main_window, yy + 4, tab1, "popen:date command failed retrieving minute" );
+ }
+
+ if( fgets( date_buffer, sizeof( date_buffer ), fp ) == NULL )
+ {
+ nwipe_log( NWIPE_LOG_INFO, "fgets:failed to retrieve the minute %s", date_buffer );
+ mvwprintw( main_window, yy + 5, tab1, "fgets:failed retrieving the minute" );
+ }
+
+ /* terminate string after fourth character removing any lf */
+ date_buffer[2] = 0;
+
+ pclose( fp );
+
+ /* Set the buffer index to point to the end of the string, i.e the NULL */
+ idx = strlen( date_buffer );
+
+ do
+ {
+ /* Erase the main window. */
+ werase( main_window );
+
+ nwipe_gui_create_all_windows_on_terminal_resize( 0, selection_footer_text_entry );
+
+ /* Add a border. */
+ box( main_window, 0, 0 );
+
+ /* Add a title. */
+ nwipe_gui_title( main_window, " Set System Minute " );
+
+ /* Initialize the working row. */
+ yy = 4;
+
+ mvwprintw(
+ main_window, yy++, tab1, "Enter the current minute, two numeric digits, i.e 01, return key to submit" );
+
+ /* Print this line last so that the cursor is in the right place. */
+ mvwprintw( main_window, 2, tab1, ">%s", date_buffer );
+
+ /* Reveal the cursor. */
+ curs_set( 1 );
+
+ /* Refresh the window. */
+ wrefresh( main_window );
+
+ /* Wait 250ms for input from getch, if nothing getch will then continue,
+ * This is necessary so that the while loop can be exited by the
+ * terminate_signal e.g.. the user pressing control-c to exit.
+ * Do not change this value, a higher value means the keys become
+ * sluggish, any slower and more time is spent unnecessarily looping
+ * which wastes CPU cycles.
+ */
+ timeout( 250 ); // block getch() for 250ms.
+ keystroke = getch(); // Get a keystroke.
+ timeout( -1 ); // Switch back to blocking mode.
+
+ switch( keystroke )
+ {
+ /* Escape key. */
+ case 27:
+ return;
+
+ case KEY_BACKSPACE:
+ case KEY_LEFT:
+ case 127:
+
+ if( idx > 0 )
+ {
+ date_buffer[--idx] = 0;
+ }
+
+ break;
+
+ } /* switch keystroke */
+
+ if( ( keystroke >= ' ' && keystroke <= '~' ) && keystroke != '\"' && idx < FIELD_LENGTH && idx < 4 )
+ {
+ date_buffer[idx++] = keystroke;
+ date_buffer[idx] = 0;
+ mvwprintw( main_window, 2, tab1, ">%s", date_buffer );
+ }
+
+ /* Hide the cursor. */
+ curs_set( 0 );
+
+ } while( keystroke != 10 && terminate_signal != 1 );
+
+ /* Write year back to system */
+ status = read_system_datetime( year, month, day, hours, minutes, seconds );
+ if( status != 0 )
+ {
+ nwipe_log( NWIPE_LOG_ERROR, "func:read_system_datetime failed, see previous messages for detail" );
+ }
+
+ strncpy( minutes, date_buffer, 2 );
+
+ status = write_system_datetime( year, month, day, hours, minutes, seconds );
+ if( status != 0 )
+ {
+ nwipe_log( NWIPE_LOG_ERROR, "func:write_system_datetime failed, see previous messages for detail" );
+ }
+
+} /* End of nwipe_gui_set_system_minute() */
+
+void nwipe_gui_load( void )
+{
+ /**
+ * Prints the system load average to the statistics window.
+ *
+ * @modifies stat_window Prints the system load average to the statistics window.
+ *
+ */
+
+ /* A file handle for the stat file. */
+ FILE* nwipe_fp;
+
+ /* The one, five, and fifteen minute load averages. */
+ float load_01;
+ float load_05;
+ float load_15;
+
+ /* Open the loadavg file. */
+ nwipe_fp = fopen( NWIPE_KNOB_LOADAVG, "r" );
+
+ /* Print the label. */
+ mvwprintw( stats_window, NWIPE_GUI_STATS_LOAD_Y, NWIPE_GUI_STATS_LOAD_X, "Load Averages:" );
+
+ if( nwipe_fp )
+ {
+ /* The load averages are the first three numbers in the file. */
+ if( 3 == fscanf( nwipe_fp, "%f %f %f", &load_01, &load_05, &load_15 ) )
+ {
+ /* Print the load average. */
+ mvwprintw( stats_window,
+ NWIPE_GUI_STATS_LOAD_Y,
+ NWIPE_GUI_STATS_TAB,
+ "%04.2f %04.2f %04.2f",
+ load_01,
+ load_05,
+ load_15 );
+ }
+ else
+ {
+ /* Print an error. */
+ mvwprintw( stats_window, NWIPE_GUI_STATS_LOAD_Y, NWIPE_GUI_STATS_TAB, "(fscanf error %i)", errno );
+ }
+
+ /* Close the loadavg file. */
+ fclose( nwipe_fp );
+ }
+ else
+ {
+ mvwprintw( stats_window, NWIPE_GUI_STATS_LOAD_Y, NWIPE_GUI_STATS_TAB, "(fopen error %i)", errno );
+ }
+
+} /* nwipe_gui_load */
+
+void* nwipe_gui_status( void* ptr )
+{
+ /**
+ * Shows runtime statistics and overall progress.
+ *
+ * @parameter count The number of contexts in the array.
+ * @parameter c An array of device contexts.
+ *
+ * @modifies main_window Prints information into the main window.
+ * @modifies c[].throughput Updates the i/o throughput value.
+ *
+ */
+
+ extern int terminate_signal;
+
+ nwipe_thread_data_ptr_t* nwipe_thread_data_ptr;
+ nwipe_thread_data_ptr = (nwipe_thread_data_ptr_t*) ptr;
+
+ nwipe_context_t** c;
+ nwipe_misc_thread_data_t* nwipe_misc_thread_data;
+ int count;
+
+ c = nwipe_thread_data_ptr->c;
+ nwipe_misc_thread_data = nwipe_thread_data_ptr->nwipe_misc_thread_data;
+ count = nwipe_misc_thread_data->nwipe_selected;
+
+ char nomenclature_result_str[NOMENCLATURE_RESULT_STR_SIZE]; /* temporary usage */
+
+ /* Spinner character */
+ char spinner_string[2];
+
+ /* Create the finish message, this changes based on whether PDF creation is enabled
+ * and whether a logfile has been specified
+ */
+ char finish_message[NWIPE_GUI_FOOTER_W + 132];
+ if( nwipe_options.logfile[0] == 0 && nwipe_options.PDF_enable != 0 )
+ {
+ snprintf( finish_message,
+ sizeof( finish_message ),
+ "Wipe finished - press enter to create pdfs & exit. Logged to STDOUT" );
+ }
+ else
+ {
+ if( nwipe_options.logfile[0] != 0 && nwipe_options.PDF_enable != 0 )
+ {
+ snprintf( finish_message,
+ sizeof( finish_message ),
+ "Wipe finished - press enter to create pdfs & exit. Logged to %s",
+ nwipe_options.logfile );
+ }
+ else
+ {
+ if( nwipe_options.logfile[0] != 0 && nwipe_options.PDF_enable == 0 )
+ {
+ snprintf( finish_message,
+ sizeof( finish_message ),
+ "Wipe finished - press enter to exit (pdfs disabled in config). Logged to %s",
+ nwipe_options.logfile );
+ }
+ else
+ {
+ if( nwipe_options.logfile[0] == 0 && nwipe_options.PDF_enable == 0 )
+ {
+ snprintf( finish_message,
+ sizeof( finish_message ),
+ "Wipe finished - press enter to exit (pdfs disabled in config). Logged to STDOUT" );
+ }
+ else
+ {
+ /* This is a catch all something unexpected happens with the above logic */
+ snprintf( finish_message,
+ sizeof( finish_message ),
+ "Wipe finished - press enter to exit. Logged to STDOUT" );
+ }
+ }
+ }
+ }
+
+ /* We count time from when this function is first called. */
+ static time_t nwipe_time_start = 0;
+
+ /* Whether the screen has been blanked by the user. */
+ static int nwipe_gui_blank = 0;
+
+ /* The current time. */
+ time_t nwipe_time_now;
+
+ /* The time when all wipes ended */
+ time_t nwipe_time_stopped;
+
+ /* The index of the element that is visible in the first slot. */
+ static int offset;
+
+ /* The number of elements that we can show in the window. */
+ int slots;
+
+ /* Window dimensions. */
+ int wlines;
+ int wcols;
+
+ /* Generic loop variable. */
+ int i;
+
+ /* The current working line in the main window. */
+ int yy;
+
+ /* User input buffer. */
+ int keystroke;
+
+ /* controls main while loop */
+ int loop_control;
+
+ /* The combined througput of all processes. */
+ nwipe_misc_thread_data->throughput = 0;
+
+ /* The estimated runtime of the slowest device. */
+ nwipe_misc_thread_data->maxeta = 0;
+
+ /* The combined number of errors of all processes. */
+ nwipe_misc_thread_data->errors = 0;
+
+ /* Time values. */
+ int nwipe_hh;
+ int nwipe_mm;
+ int nwipe_ss;
+
+ struct timespec tim, tim2;
+ tim.tv_sec = 0;
+ tim.tv_nsec = 100000000L; /* sleep for 0.1 seconds */
+
+ /* Throughput variables */
+ u64 nwipe_throughput;
+
+ /* The number of active wipe processes. */
+ /* Set to 1 initially to start loop. */
+ int nwipe_active = 1;
+
+ /* Used in the gui status loop to trap a failure of the halfdelay(), getch() mechanism to block for the designated
+ * period */
+ int expected_iterations;
+
+ /* Used in the selection loop to trap a failure of the timeout(), getch() mechanism to block for the designated
+ * period, initialise the counter */
+ int iteration_counter = 0;
+
+ time_t previous_iteration_timestamp = time( NULL );
+
+ /* Calculate Maximum allowed iterations per second (typically 20), which is double the expected iterations
+ * (typically 10) */
+ expected_iterations = ( 1000 / GETCH_GUI_STATS_UPDATE_MS ) * 2;
+
+ if( nwipe_time_start == 0 )
+ {
+ /* This is the first time that we have been called. */
+ nwipe_time_start = time( NULL ) - 1;
+ }
+
+ nwipe_gui_title( footer_window, end_wipe_footer );
+
+ loop_control = 1;
+
+ while( loop_control )
+ {
+ /* IMPORTANT ! Halfdelay(1) causes getch() to pause for 0.1 secs. This is important for two reasons.
+ * 1. Pauses the getch for 0.1 secs so that the screen is only updated max 10 times/sec. Without
+ * this delay the loop would run hundreds of times per sec maxing out the core.
+ * 2. By keeping the delay below 0.2 seconds, i.e 0.1, it makes the keypress and resizing
+ * nice and responsive.
+ */
+ halfdelay( GETCH_GUI_STATS_UPDATE_MS ); // Important, don't change this unless you know what you are doing !
+ // Related to getch().
+
+ keystroke = getch(); // Get user input.
+
+ iteration_counter++;
+
+ /* Much like the same check we perform in the nwipe_gui_select() function, here we check that we are not looping
+ * any faster than as defined by the halfdelay() function above, typically this loop runs at 10 times a second.
+ * This check makes sure that if the loop runs faster than double this value i.e 20 times a second then the
+ * program exits. This check is therefore determining whether the getch() function is returning immediately
+ * rather than blocking for the defined period of 100ms. Why is this necessary? Some terminals (konsole &
+ * deriviatives) that are exited while nwipe is still running fail to terminate nwipe this causes the
+ * halfdelay()/getch() functions to immediately fail causing the loop frequency to drastically increase. We
+ * detect that speed increase here and therefore close down nwipe. This doesn't affect the use of the tmux
+ * terminal by which you can detach and reattach to running nwipe processes. tmux still works correctly.
+ */
+ if( previous_iteration_timestamp == time( NULL ) )
+ {
+ if( iteration_counter > expected_iterations )
+ {
+ nwipe_log( NWIPE_LOG_ERROR,
+ "GUI.c,nwipe_gui_status(), loop runaway, did you close the terminal without exiting "
+ "nwipe? Initiating shutdown now." );
+ /* Issue signal to nwipe to shutdown immediately but gracefully */
+ terminate_signal = 1;
+ }
+ }
+ else
+ {
+ /* new second, so reset counter */
+ iteration_counter = 0;
+ previous_iteration_timestamp = time( NULL );
+ }
+
+ /* Get the current time. */
+ if( nwipe_active && terminate_signal != 1 )
+ {
+ nwipe_time_now = time( NULL );
+ nwipe_time_stopped = nwipe_time_now;
+ }
+ else
+ {
+ nwipe_time_now = nwipe_time_stopped;
+ }
+
+ /* Erase the main window. */
+ werase( main_window );
+
+ /* Erase the stats window. */
+ werase( stats_window );
+
+ /* Erase the footer window */
+ werase( footer_window );
+
+ /* Only repaint the windows on terminal resize if the user hasn't blanked the screen */
+ if( nwipe_gui_blank == 0 )
+ {
+ if( nwipe_active != 0 )
+ {
+ /* if resizing the terminal during a wipe a specific footer is required */
+ nwipe_gui_create_all_windows_on_terminal_resize( 0, end_wipe_footer );
+ }
+ else
+ {
+ /* and if the wipes have finished a different footer is required */
+ nwipe_gui_create_all_windows_on_terminal_resize( 0, finish_message );
+ }
+ }
+
+ /* Initialize our working offset to the third line. */
+ yy = 2;
+
+ /* Get the window dimensions. */
+ getmaxyx( main_window, wlines, wcols );
+
+ /* Less four lines for the box and padding. */
+ slots = wlines - 4;
+
+ /* Each element prints three lines. */
+ slots /= 3;
+
+ if( nwipe_active == 0 || terminate_signal == 1 )
+ {
+ nwipe_gui_title( footer_window, finish_message );
+
+ // Refresh the footer_window ;
+ wnoutrefresh( footer_window );
+ }
+
+ if( terminate_signal == 1 )
+ {
+ loop_control = 0;
+ }
+
+ if( keystroke > 0x0a && keystroke < 0x7e && nwipe_gui_blank == 1 )
+ {
+ tft_saver = 0;
+ nwipe_init_pairs();
+ nwipe_gui_create_all_windows_on_terminal_resize( 1, end_wipe_footer );
+
+ /* Show screen */
+ nwipe_gui_blank = 0;
+
+ /* Set background */
+ wbkgdset( stdscr, COLOR_PAIR( 1 ) );
+ wclear( stdscr );
+
+ /* Unhide panels */
+ show_panel( header_panel );
+ show_panel( footer_panel );
+ show_panel( stats_panel );
+ show_panel( options_panel );
+ show_panel( main_panel );
+
+ /* Reprint the footer */
+ nwipe_gui_title( footer_window, end_wipe_footer );
+
+ // Refresh the footer_window ;
+ wnoutrefresh( footer_window );
+
+ /* Update panels */
+ update_panels();
+ doupdate();
+ }
+ else if( keystroke > 0 )
+ {
+
+ switch( keystroke )
+ {
+
+ case 'b':
+ case 'B':
+
+ if( nwipe_gui_blank == 0 && tft_saver != 1 )
+ {
+ /* grey text on black background */
+ tft_saver = 1;
+ nwipe_init_pairs();
+ nwipe_gui_create_all_windows_on_terminal_resize( 1, end_wipe_footer );
+ }
+ else
+ {
+ if( nwipe_gui_blank == 0 && tft_saver == 1 )
+ {
+ /* Blank screen. */
+ tft_saver = 0;
+ nwipe_gui_blank = 1;
+ hide_panel( header_panel );
+ hide_panel( footer_panel );
+ hide_panel( stats_panel );
+ hide_panel( options_panel );
+ hide_panel( main_panel );
+ }
+
+ /* Set the background style. */
+ wbkgdset( stdscr, COLOR_PAIR( 7 ) );
+ wclear( stdscr );
+ }
+
+ break;
+
+ case KEY_DOWN:
+ case 'j':
+ case 'J':
+
+ /* Scroll down. */
+ offset += 1;
+
+ if( count < slots )
+ {
+ offset = 0;
+ }
+
+ else if( offset + slots > count )
+ {
+ offset = count - slots;
+ }
+
+ break;
+
+ case KEY_UP:
+ case 'k':
+ case 'K':
+
+ /* Scroll up. */
+ offset -= 1;
+
+ if( offset < 0 )
+ {
+ offset = 0;
+ }
+
+ break;
+
+ case ' ':
+ case 0x0a:
+
+ /* Check whether we have finished all wipes, if yes exit while loop if user pressed spacebar or
+ * return. */
+ if( !nwipe_active || terminate_signal == 1 )
+ {
+ loop_control = 0;
+ }
+
+ break;
+
+ default:
+
+ /* Do nothing. */
+ break;
+ }
+
+ } /* keystroke */
+
+ /* If wipe has completed and user has specified auto poweroff or nowait then we can skip waiting for the user to
+ * press return */
+ if( !nwipe_active )
+ {
+ if( nwipe_options.autopoweroff || nwipe_options.nowait )
+ {
+ loop_control = 0;
+ }
+ }
+
+ /* Update data in statistics & main windows only if we're in 'gui' mode and only if a wipe has started */
+ if( global_wipe_status == 1 )
+ {
+ /* Always run compute_stats() as we need to whether any threads are still active */
+ if( terminate_signal != 1 )
+ {
+ nwipe_active = compute_stats( ptr ); // Returns number of active wipe threads
+ }
+
+ /* Only print the stats if the user hasn't blanked the screen */
+ if( nwipe_gui_blank == 0 )
+ {
+
+ /* Print information for the user. */
+ for( i = offset; i < offset + slots && i < count; i++ )
+ {
+ /* Print the device details. */
+ mvwprintw( main_window,
+ yy++,
+ 2,
+ "%s %s [%s] ",
+ c[i]->device_name,
+ c[i]->device_type_str,
+ c[i]->device_size_text );
+ wprintw_temperature( c[i] );
+ wprintw( main_window, " %s/%s", c[i]->device_model, c[i]->device_serial_no );
+
+ /* Check whether the child process is still running the wipe. */
+ if( c[i]->wipe_status == 1 )
+ {
+ /* Print percentage and pass information. */
+ mvwprintw( main_window,
+ yy++,
+ 4,
+ "[%5.2f%%, round %i of %i, pass %i of %i] ",
+ c[i]->round_percent,
+ c[i]->round_working,
+ c[i]->round_count,
+ c[i]->pass_working,
+ c[i]->pass_count );
+
+ } /* child running */
+ else
+ {
+ if( c[i]->result == 0 )
+ {
+ mvwprintw( main_window, yy++, 4, "[%05.2f%% complete, SUCCESS! ", c[i]->round_percent );
+ }
+ else if( c[i]->signal )
+ {
+ wattron( main_window, COLOR_PAIR( 9 ) );
+ mvwprintw( main_window, yy++, 4, "(>>> FAILURE! <<<, signal %i) ", c[i]->signal );
+ wattroff( main_window, COLOR_PAIR( 9 ) );
+ }
+ else
+ {
+ wattron( main_window, COLOR_PAIR( 9 ) );
+ mvwprintw( main_window, yy++, 4, "(>>> IOERROR! <<<, code %i) ", c[i]->result );
+ wattroff( main_window, COLOR_PAIR( 9 ) );
+ }
+
+ } /* child returned */
+
+ if( c[i]->verify_errors )
+ {
+ wprintw( main_window, "[verr:%llu] ", c[i]->verify_errors );
+ }
+ if( c[i]->pass_errors )
+ {
+ wprintw( main_window, "[perr:%llu] ", c[i]->pass_errors );
+ }
+ if( c[i]->wipe_status == 1 )
+ {
+ switch( c[i]->pass_type )
+ {
+ /* Each text field in square brackets should be the same number of characters
+ * to retain output in columns */
+ case NWIPE_PASS_FINAL_BLANK:
+ if( !c[i]->sync_status )
+ {
+ wprintw( main_window, "[ blanking] " );
+ }
+ break;
+
+ case NWIPE_PASS_FINAL_OPS2:
+ if( !c[i]->sync_status )
+ {
+ wprintw( main_window, "[OPS2final] " );
+ }
+ break;
+
+ case NWIPE_PASS_WRITE:
+ if( !c[i]->sync_status )
+ {
+ wprintw( main_window, "[ writing ] " );
+ }
+ break;
+
+ case NWIPE_PASS_VERIFY:
+ if( !c[i]->sync_status )
+ {
+ wprintw( main_window, "[verifying] " );
+ }
+ break;
+
+ case NWIPE_PASS_NONE:
+ break;
+ }
+
+ if( c[i]->sync_status )
+ {
+ wprintw( main_window, "[ syncing ] " );
+ }
+ }
+
+ /* Determine throughput nomenclature for this drive and output drives throughput to GUI */
+ Determine_C_B_nomenclature(
+ c[i]->throughput, nomenclature_result_str, NOMENCLATURE_RESULT_STR_SIZE );
+
+ wprintw( main_window, "[%s/s] ", nomenclature_result_str );
+
+ /* Insert whitespace. */
+ yy += 1;
+
+ /* Increment the next spinner character for this context if the thread is active */
+ if( c[i]->wipe_status == 1 )
+ {
+ spinner( c, i );
+ spinner_string[0] = c[i]->spinner_character[0];
+ }
+ else
+ {
+ /* If the wipe thread is no longer active, replace the spinner with a space */
+ spinner_string[0] = ' ';
+ }
+ spinner_string[1] = 0;
+ wprintw( main_window, " %s ", spinner_string );
+ }
+
+ if( offset > 0 )
+ {
+ mvwprintw( main_window, 1, wcols - 8, " More " );
+ waddch( main_window, ACS_UARROW );
+ }
+
+ if( count - offset > slots )
+ {
+ mvwprintw( main_window, wlines - 2, wcols - 8, " More " );
+ waddch( main_window, ACS_DARROW );
+ }
+
+ /* Box the main window. */
+ box( main_window, 0, 0 );
+
+ /* Refresh the main window. */
+ wnoutrefresh( main_window );
+
+ /* Update the load average field, but only if we are still wiping */
+ if( nwipe_active && terminate_signal != 1 )
+ {
+ nwipe_gui_load();
+ }
+
+ nwipe_throughput = nwipe_misc_thread_data->throughput;
+
+ /* Determine the nomenclature for the combined throughput */
+ Determine_C_B_nomenclature( nwipe_throughput, nomenclature_result_str, NOMENCLATURE_RESULT_STR_SIZE );
+
+ /* Print the combined throughput. */
+ mvwprintw( stats_window, NWIPE_GUI_STATS_THROUGHPUT_Y, NWIPE_GUI_STATS_THROUGHPUT_X, "Throughput:" );
+
+ mvwprintw(
+ stats_window, NWIPE_GUI_STATS_THROUGHPUT_Y, NWIPE_GUI_STATS_TAB, "%s/s", nomenclature_result_str );
+
+ /* Change the current time into a delta. */
+ nwipe_time_now -= nwipe_time_start;
+
+ /* Put the delta into HH:mm:ss form. */
+ nwipe_hh = nwipe_time_now / 3600;
+ nwipe_time_now %= 3600;
+ nwipe_mm = nwipe_time_now / 60;
+ nwipe_time_now %= 60;
+ nwipe_ss = nwipe_time_now;
+
+ /* Print the runtime. */
+ mvwprintw( stats_window, NWIPE_GUI_STATS_RUNTIME_Y, 1, "Runtime:" );
+ mvwprintw( stats_window,
+ NWIPE_GUI_STATS_RUNTIME_Y,
+ NWIPE_GUI_STATS_TAB,
+ "%02i:%02i:%02i",
+ nwipe_hh,
+ nwipe_mm,
+ nwipe_ss );
+
+ mvwprintw( stats_window, NWIPE_GUI_STATS_ETA_Y, 1, "Remaining:" );
+
+ time_t nwipe_maxeta = nwipe_misc_thread_data->maxeta;
+ if( nwipe_maxeta > 0 )
+ {
+ /* Do it again for the estimated runtime remaining. */
+ nwipe_hh = nwipe_maxeta / 3600;
+ nwipe_maxeta %= 3600;
+ nwipe_mm = nwipe_maxeta / 60;
+ nwipe_maxeta %= 60;
+ nwipe_ss = nwipe_maxeta;
+
+ /* Print the estimated runtime remaining. */
+ mvwprintw( stats_window,
+ NWIPE_GUI_STATS_ETA_Y,
+ NWIPE_GUI_STATS_TAB,
+ "%02i:%02i:%02i",
+ nwipe_hh,
+ nwipe_mm,
+ nwipe_ss );
+ }
+
+ /* Print the error count. */
+ mvwprintw( stats_window, NWIPE_GUI_STATS_ERRORS_Y, NWIPE_GUI_STATS_ERRORS_X, "Errors:" );
+ mvwprintw( stats_window,
+ NWIPE_GUI_STATS_ERRORS_Y,
+ NWIPE_GUI_STATS_TAB,
+ " %llu",
+ nwipe_misc_thread_data->errors );
+
+ /* Add a border. */
+ box( stats_window, 0, 0 );
+
+ /* Add a title. */
+ mvwprintw( stats_window, 0, ( NWIPE_GUI_STATS_W - strlen( stats_title ) ) / 2, "%s", stats_title );
+
+ /* Refresh internal representation of stats window */
+ wnoutrefresh( stats_window );
+
+ /* Output all windows to screen */
+ doupdate();
+
+ } // end blank screen if
+
+ } // end wipes have started if
+
+ } /* End of while loop */
+
+ nwipe_gui_title( footer_window, finish_message );
+ terminate_signal = 1;
+
+ return NULL;
+} /* nwipe_gui_status */
+
+int compute_stats( void* ptr )
+{
+ nwipe_thread_data_ptr_t* nwipe_thread_data_ptr;
+ nwipe_thread_data_ptr = (nwipe_thread_data_ptr_t*) ptr;
+
+ nwipe_context_t** c;
+ nwipe_misc_thread_data_t* nwipe_misc_thread_data;
+
+ c = nwipe_thread_data_ptr->c;
+ nwipe_misc_thread_data = nwipe_thread_data_ptr->nwipe_misc_thread_data;
+ int count = nwipe_misc_thread_data->nwipe_selected;
+
+ int nwipe_active = 0;
+ int i;
+
+ time_t nwipe_time_now = time( NULL );
+
+ nwipe_misc_thread_data->throughput = 0;
+ nwipe_misc_thread_data->maxeta = 0;
+ nwipe_misc_thread_data->errors = 0;
+
+ /* Enumerate all contexts to compute statistics. */
+ for( i = 0; i < count; i++ )
+ {
+ /* Check whether the child process is still running the wipe. */
+ if( c[i]->wipe_status == 1 )
+ {
+ /* Increment the child counter. */
+ nwipe_active += 1;
+
+ /* Even if the wipe has finished ALWAYS run the stats one last time so the final SUCCESS percentage value is
+ * correct. Maintain a rolling average of throughput. */
+ nwipe_update_speedring( &c[i]->speedring, c[i]->round_done, nwipe_time_now );
+
+ if( c[i]->speedring.timestotal > 0 && c[i]->wipe_status == 1 )
+ {
+ /* Update the current average throughput in bytes-per-second. */
+ c[i]->throughput = c[i]->speedring.bytestotal / c[i]->speedring.timestotal;
+
+ /* Only update the estimated remaining runtime if the
+ * throughput for a given drive is greater than 100,000 bytes per second
+ * This prevents enormous ETA's being calculated on an unresponsive
+ * drive */
+ if( c[i]->throughput > 100000 )
+ {
+ c[i]->eta = ( c[i]->round_size - c[i]->round_done ) / c[i]->throughput;
+
+ if( c[i]->eta > nwipe_misc_thread_data->maxeta )
+ {
+ nwipe_misc_thread_data->maxeta = c[i]->eta;
+ }
+ }
+ }
+
+ /* Calculate the average throughput */
+ c[i]->throughput = (double) c[i]->round_done / (double) difftime( nwipe_time_now, c[i]->start_time );
+ }
+
+ /* Update the percentage value. */
+ c[i]->round_percent = (double) c[i]->round_done / (double) c[i]->round_size * 100;
+
+ if( c[i]->wipe_status == 1 )
+ {
+ /* Accumulate combined throughput. */
+ nwipe_misc_thread_data->throughput += c[i]->throughput;
+ }
+
+ /* Accumulate the error count. */
+ nwipe_misc_thread_data->errors += c[i]->pass_errors;
+ nwipe_misc_thread_data->errors += c[i]->verify_errors;
+ nwipe_misc_thread_data->errors += c[i]->fsyncdata_errors;
+
+ /* Read the drive temperature values */
+ // if( nwipe_time_now > ( c[i]->temp1_time + 60 ) )
+ // {
+ // nwipe_update_temperature( c[i] );
+ // }
+
+ } /* for statistics */
+
+ return nwipe_active;
+}
+
+void nwipe_update_speedring( nwipe_speedring_t* speedring, u64 speedring_bytes, time_t speedring_now )
+{
+
+ if( speedring->timeslast == 0 )
+ {
+ /* Ignore the first sample and initialize. */
+ speedring->timeslast = speedring_now;
+ return;
+ }
+
+ if( speedring_now - speedring->timeslast < NWIPE_KNOB_SPEEDRING_GRANULARITY )
+ {
+ /* Avoid jitter caused by frequent updates. */
+ return;
+ }
+
+ /* Subtract the oldest speed sample from the accumulator. */
+ speedring->bytestotal -= speedring->bytes[speedring->position];
+ speedring->timestotal -= speedring->times[speedring->position];
+
+ /* Put the latest bytes-per-second sample into the ring buffer. */
+ speedring->bytes[speedring->position] = speedring_bytes - speedring->byteslast;
+ speedring->times[speedring->position] = speedring_now - speedring->timeslast;
+
+ /* Add the newest speed sample to the accumulator. */
+ speedring->bytestotal += speedring->bytes[speedring->position];
+ speedring->timestotal += speedring->times[speedring->position];
+
+ /* Remember the last sample. */
+ speedring->byteslast = speedring_bytes;
+ speedring->timeslast = speedring_now;
+
+ if( ++speedring->position >= NWIPE_KNOB_SPEEDRING_SIZE )
+ {
+ speedring->position = 0;
+ }
+}
+
+int spinner( nwipe_context_t** ptr, int device_idx )
+{
+ nwipe_context_t** c;
+
+ c = ptr;
+
+ /* The spinner characters |/-\|/-\ */
+ char sc[9] = "|/-\\|/-\\/";
+
+ /* Check sanity of index */
+ if( c[device_idx]->spinner_idx < 0 || c[device_idx]->spinner_idx > 7 )
+ {
+ return 1;
+ }
+
+ c[device_idx]->spinner_character[0] = sc[c[device_idx]->spinner_idx];
+
+ c[device_idx]->spinner_idx++;
+
+ if( c[device_idx]->spinner_idx > 7 )
+ {
+ c[device_idx]->spinner_idx = 0;
+ }
+
+ return 0;
+}
+
+void temp1_flash( nwipe_context_t* c )
+{
+ if( c->temp1_flash_rate_counter < c->temp1_flash_rate )
+ {
+ c->temp1_flash_rate_counter++;
+ }
+ else
+ {
+ c->temp1_flash_rate_counter = 0;
+ if( c->temp1_flash_rate_status == 0 )
+ {
+ c->temp1_flash_rate_status = 1;
+ }
+ else
+ {
+ c->temp1_flash_rate_status = 0;
+ }
+ }
+}
+
+void wprintw_temperature( nwipe_context_t* c )
+{
+ /* See header for description of function
+ */
+
+ int temp_highest_limit;
+ int temp_high_limit;
+ int temp_low_limit;
+ int temp_lowest_limit;
+
+ int local_temp1_input = c->temp1_input;
+ int local_temp1_crit = c->temp1_crit;
+ int local_temp1_max = c->temp1_max;
+ int local_temp1_min = c->temp1_min;
+ int local_temp1_lcrit = c->temp1_lcrit;
+
+ /* Initialise */
+ temp_highest_limit = NO_TEMPERATURE_DATA;
+ temp_high_limit = NO_TEMPERATURE_DATA;
+ temp_low_limit = NO_TEMPERATURE_DATA;
+ temp_lowest_limit = NO_TEMPERATURE_DATA;
+
+#if 0
+ /* NOTE TEST Function, TEST Function #if 0 when not testing
+ * Should increment temperature back and forth between +10 to -10
+ * while changing the color based on the settings such as
+ * c->temp1_crit and others below
+ */
+ if( c->test_use1 > 12 || c->test_use1 < -12 )
+ {
+ c->test_use1 = 0; // the value
+ c->test_use2 = 0; // direction 0 = -- or 1 = ++
+ }
+ if( c->test_use1 >= 10 )
+ {
+ c->test_use2 = 0;
+ }
+ else
+ {
+ if( c->test_use1 <= -10 )
+ {
+ c->test_use2 = 1;
+ }
+ }
+ if( c->test_use2 == 0 )
+ {
+ c->test_use1--;
+ }
+ else
+ {
+ c->test_use1++;
+ }
+
+ /* Five test cases to test temperature color logic
+ * test only with temperatures in the range -8 to +8
+ * test with each set of five for expected result if
+ * changes are made.
+ *
+ * Uncomment only one group in turn to be tested.
+ */
+ local_temp1_input = c->test_use1;
+ //
+ // Expected result - white on blue between +5 & -5, white on red above 5
+ // white on black below -5
+ local_temp1_crit = NO_TEMPERATURE_DATA;
+ local_temp1_max = 5;
+ local_temp1_min = -5;
+ local_temp1_lcrit = NO_TEMPERATURE_DATA;
+ //
+ // Expected result - white on blue between +5 & -5, white on red above 5
+ // white on black below -5
+ //local_temp1_crit = 5;
+ //local_temp1_max = NO_TEMPERATURE_DATA;
+ //local_temp1_min = NO_TEMPERATURE_DATA;
+ //local_temp1_lcrit = -5;
+ //
+ // Expected result - white on blue 5 to -5, 5-7 red on blue,
+ // 8+ white on red, -5 to-7 black on blue, less than -7 white on black
+ //local_temp1_crit = 8;
+ //local_temp1_max = 5;
+ //local_temp1_min = -5;
+ //local_temp1_lcrit = -8;
+ //
+ // Expected result - white on blue 5 to -5, 5-7 red on blue,
+ // 8+ white on red, -5 to-7 black on blue, less than -7 white on black
+ //local_temp1_crit = 5;
+ //local_temp1_max = 8;
+ //local_temp1_min = -8;
+ //local_temp1_lcrit = -5;
+ //
+ // Expected result - always white text on blue background
+ //local_temp1_crit = NO_TEMPERATURE_DATA;
+ //local_temp1_max = NO_TEMPERATURE_DATA;
+ //local_temp1_min = NO_TEMPERATURE_DATA;
+ //local_temp1_lcrit = NO_TEMPERATURE_DATA;
+#endif
+
+ /* Depending upon the drive firmware, the meaning of 'high critical' & 'max'
+ * and 'low critical' & 'low' can be interchanged. First validate for 'no data'
+ * (1000000) and also a 0 in 'high critical' and 'max', then assign the values
+ * to our four variables in the appropriate order.
+ */
+
+ /* Validate critical high value & max for 0, if 0 it's invalid, change to 1000000
+ */
+
+ /* Assign temp1_crit & temp1_max to local variables as we are going to alter
+ * them if 0, as they may be updated elsewhere we don't want them changed back half
+ * way through our processing. This is only necessary for the high temperatures and
+ * not the low temperatures as low temperatures may well be 0.
+ */
+
+ if( local_temp1_crit == 0 )
+ {
+ local_temp1_crit = NO_TEMPERATURE_DATA;
+ }
+ if( local_temp1_max == 0 )
+ {
+ local_temp1_max = NO_TEMPERATURE_DATA;
+ }
+
+ /* Check which way around they are */
+ if( local_temp1_crit != NO_TEMPERATURE_DATA && local_temp1_max != NO_TEMPERATURE_DATA )
+ {
+ if( local_temp1_crit > local_temp1_max )
+ {
+ temp_highest_limit = local_temp1_crit;
+ temp_high_limit = local_temp1_max;
+ }
+ else
+ {
+ temp_highest_limit = local_temp1_max;
+ temp_high_limit = local_temp1_crit;
+ }
+ }
+ else
+ {
+ /* If only one or the other is present then assign that value to both high critical and max
+ */
+ if( local_temp1_crit == NO_TEMPERATURE_DATA && local_temp1_max != NO_TEMPERATURE_DATA )
+ {
+ temp_highest_limit = local_temp1_max;
+ temp_high_limit = local_temp1_max;
+ }
+ else
+ {
+ /* If high critical is present but max is not, assign high critical to both */
+ if( local_temp1_crit != NO_TEMPERATURE_DATA && local_temp1_max == NO_TEMPERATURE_DATA )
+ {
+ temp_highest_limit = local_temp1_crit;
+ temp_high_limit = local_temp1_crit;
+ }
+ else
+ {
+ /* neither is present so mark both locals as not present */
+ temp_highest_limit = NO_TEMPERATURE_DATA;
+ temp_high_limit = NO_TEMPERATURE_DATA;
+ }
+ }
+ }
+
+ /* Now do the same for the low critical limit and low limit. */
+
+ /* Check which way around they are */
+ if( local_temp1_lcrit != NO_TEMPERATURE_DATA && local_temp1_min != NO_TEMPERATURE_DATA )
+ {
+ if( local_temp1_lcrit < local_temp1_min )
+ {
+ temp_lowest_limit = local_temp1_lcrit;
+ temp_low_limit = local_temp1_min;
+ }
+ else
+ {
+ temp_lowest_limit = local_temp1_min;
+ temp_low_limit = local_temp1_lcrit;
+ }
+ }
+ else
+ {
+ /* If only one or the other is present then assign that value to both high critical and max
+ */
+ if( local_temp1_lcrit == NO_TEMPERATURE_DATA && local_temp1_min != NO_TEMPERATURE_DATA )
+ {
+ temp_lowest_limit = local_temp1_min;
+ temp_low_limit = local_temp1_min;
+ }
+ else
+ {
+ /* If high critical is present but max is not, assign high critical to both */
+ if( local_temp1_lcrit != NO_TEMPERATURE_DATA && local_temp1_min == NO_TEMPERATURE_DATA )
+ {
+ temp_lowest_limit = local_temp1_lcrit;
+ temp_low_limit = local_temp1_lcrit;
+ }
+ else
+ {
+ /* neither is present so mark both locals as not present */
+ temp_lowest_limit = NO_TEMPERATURE_DATA;
+ temp_low_limit = NO_TEMPERATURE_DATA;
+ }
+ }
+ }
+
+ /* if drive temperature has exceeded the critical temperature if available
+ */
+ if( ( local_temp1_input >= temp_highest_limit ) && ( local_temp1_input != NO_TEMPERATURE_DATA )
+ && ( temp_highest_limit != NO_TEMPERATURE_DATA ) )
+ {
+ /* white on red */
+ wattron( main_window, COLOR_PAIR( 6 ) );
+ wprintw( main_window, "[%dC]", local_temp1_input );
+ wattroff( main_window, COLOR_PAIR( 6 ) );
+ }
+ else
+ {
+ /* if drive temperature has exceeded the max temperature if available
+ */
+ if( ( local_temp1_input >= temp_high_limit ) && ( local_temp1_input <= temp_highest_limit )
+ && ( local_temp1_input != NO_TEMPERATURE_DATA ) && ( temp_high_limit != NO_TEMPERATURE_DATA ) )
+ {
+ /* red on blue */
+ wattron( main_window, COLOR_PAIR( 3 ) );
+ wprintw( main_window, "[%dC]", local_temp1_input );
+ wattroff( main_window, COLOR_PAIR( 3 ) );
+ }
+ else
+ {
+ /* if drive temperature is below the lowest critical temperature and the critical value is present
+ */
+ if( ( local_temp1_input <= temp_lowest_limit ) && ( temp_lowest_limit != NO_TEMPERATURE_DATA )
+ && ( local_temp1_input != NO_TEMPERATURE_DATA ) )
+ {
+ /* white on black */
+ wattron( main_window, COLOR_PAIR( 14 ) );
+ wprintw( main_window, "[%dC]", local_temp1_input );
+ wattroff( main_window, COLOR_PAIR( 14 ) );
+ }
+ else
+ {
+ /* if drive temperature is below the minimum but above the lowest temperature and the value is present
+ */
+ if( ( ( local_temp1_input <= temp_low_limit ) && ( local_temp1_input >= temp_lowest_limit )
+ && ( local_temp1_input != NO_TEMPERATURE_DATA ) && ( temp_low_limit != NO_TEMPERATURE_DATA ) ) )
+ {
+ /* black on blue */
+ wattron( main_window, COLOR_PAIR( 11 ) );
+ wprintw( main_window, "[%dC]", local_temp1_input );
+ wattroff( main_window, COLOR_PAIR( 11 ) );
+ }
+ else
+ {
+ if( local_temp1_input != NO_TEMPERATURE_DATA )
+ {
+ /* Default white on blue */
+ wprintw( main_window, "[%dC]", local_temp1_input );
+ }
+ else
+ {
+ /* Default white on blue */
+ wprintw( main_window, "[--C]" );
+ }
+ }
+ }
+ }
+ }
+}
+
+char* str_truncate( int wcols, int start_column, const char* input, char* output, int output_length )
+{
+ /***
+ * Truncate a string based on start position and terminal width
+ */
+
+ int length, idx = 0;
+
+ length = wcols - start_column - 1;
+ idx = 0;
+ while( idx < output_length && idx < length )
+ {
+ output[idx] = input[idx];
+ idx++;
+ }
+ /* terminate the string */
+ output[idx] = 0;
+
+ return output;
+}
diff --git a/src/gui.h b/src/gui.h
new file mode 100644
index 0000000..923c85e
--- /dev/null
+++ b/src/gui.h
@@ -0,0 +1,145 @@
+/*
+ * gui.h: An ncurses GUI for nwipe.
+ *
+ * Copyright Darik Horn <dajhorn-dban@vanadac.com>.
+ *
+ * Modifications to original dwipe Copyright Andy Beverley <andy@andybev.com>
+ *
+ * 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, version 2.
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#ifndef GUI_H_
+#define GUI_H_
+
+void nwipe_gui_free( void ); // Stop the GUI.
+void nwipe_gui_init( void ); // Start the GUI.
+void nwipe_gui_create_main_window( void ); // Create the main window
+void nwipe_gui_create_header_window( void ); // Create the header window
+void nwipe_gui_create_footer_window( const char* ); // Create the footer window and write text
+void nwipe_gui_create_options_window( void ); // Create the options window
+void nwipe_gui_create_stats_window( void ); // Create the stats window
+void nwipe_gui_create_all_windows_on_terminal_resize(
+ int force_creation,
+ const char* footer_text ); // If terminal is resized recreate all windows
+
+/**
+ * The primary user interface. Allows the user to
+ * change options and specify the devices to be wiped.
+ *
+ * @parameter count The number of contexts in the array.
+ * @parameter c An array of device contexts.
+ *
+ * @modifies c[].select Sets the select flag according to user input.
+ * @modifies options Sets program options according to to user input.
+ *
+ */
+void nwipe_gui_select( int count, nwipe_context_t** c ); // Select devices to wipe.
+void* nwipe_gui_status( void* ptr ); // Update operation progress.
+void nwipe_gui_method( void ); // Change the method option.
+void nwipe_gui_options( void ); // Update the options window.
+void nwipe_gui_prng( void ); // Change the prng option.
+void nwipe_gui_rounds( void ); // Change the rounds option.
+void nwipe_gui_verify( void ); // Change the verify option.
+void nwipe_gui_noblank( void ); // Change the noblank option.
+void nwipe_gui_config( void ); // Change the nwipe settings
+void nwipe_gui_edit_organisation( void ); // Edit organisation performing the erasure
+void nwipe_gui_organisation_business_name( const char* ); // Edit business name performing erase
+void nwipe_gui_organisation_business_address( const char* ); // Edit business address performing erase
+void nwipe_gui_organisation_contact_name( const char* ); // Edit business contact name
+void nwipe_gui_organisation_contact_phone( const char* ); // Edit business contact phone
+void nwipe_gui_organisation_op_tech_name( const char* ); // Edit the name of the operator/technician
+void nwipe_gui_list( int, char* window_title, char**, int* );
+void nwipe_gui_add_customer( void ); // Add new customer
+void nwipe_gui_add_customer_name( char* ); // Add new customer name
+void nwipe_gui_add_customer_address( char* ); // Add new customer address
+void nwipe_gui_add_customer_contact_name( char* ); // Add new customer contact name
+void nwipe_gui_add_customer_contact_phone( char* ); // Add new customer contact phone
+int nwipe_gui_yes_no_footer( void ); // Change footer to yes no
+
+/** nwipe_gui_preview_org_customer( int )
+ * Display a editable preview of organisation, customer and date/time
+ *
+ * @param int mode 0 = use prior to drive selection
+ * 1 = use in config menus
+ * The different modes simply change the text in the footer menu and in the case
+ * of mode 0 enable the A key which means accept & display drive selection.
+ */
+void nwipe_gui_preview_org_customer( int ); // Preview window for wipe organisation and customer
+
+void nwipe_gui_set_system_year( void ); // Set the systems current year
+void nwipe_gui_set_system_month( void ); // Set the systems month
+void nwipe_gui_set_system_day( void ); // Set the system day of the month
+void nwipe_gui_set_system_hour( void ); // Set the system hour
+void nwipe_gui_set_system_minute( void ); // Set the system minute
+
+/**
+ * Truncate a string based on start position and terminal width
+ *
+ * @parameter wcols Width of window, obtained from getmaxyx(..)
+ * @parameter start_column Start column where the string starts
+ * @parameter input_string The string to be truncated if necessary
+ * @parameter ouput_string The possibly truncated string
+ * @parameter ouput_string_length Max length of output string
+ * @Return returns a pointer to the output string
+ */
+char* str_truncate( int, int, const char*, char*, int ); // Truncate string based on start column and terminal width
+
+/**
+ * Set system date and time
+ *
+ * @parameter void
+ * @Return void
+ */
+void nwipe_gui_set_date_time( void );
+
+int spinner( nwipe_context_t** ptr, int ); // Return the next spinner character
+void temp1_flash( nwipe_context_t* ); // toggles term1_flash_status, which flashes the temperature
+
+/**
+ * If the current drive temperature is available, print it to the GUI.
+ * This function determines if the drive temperature limits are specified &
+ * if so, whether the temperature should be printed as white text on blue if the
+ * drive is operating within it's temperature specification or red text on
+ * blue if the drive has exceeded the critical high temperature or black on
+ * blue if the drive has dropped below the drives minimum temperature specification.
+ * @param pointer to the drive context
+ */
+void wprintw_temperature( nwipe_context_t* );
+
+int compute_stats( void* ptr );
+void nwipe_update_speedring( nwipe_speedring_t* speedring, u64 speedring_done, time_t speedring_now );
+
+#define NOMENCLATURE_RESULT_STR_SIZE 8
+
+/* Note Do not change unless you understand how this value affects keyboard response and screen refresh when
+ * the drive selection screen is displayed. (prior to wipe starting). */
+#define GETCH_BLOCK_MS 250 /* millisecond block time for getch() */
+
+/* Note The value of 1 (100ms) is the ideal speed for screen refresh during a wipe, a value of 2 is noticeably slower,
+ * don't change unless you understand how this value affects keyboard responsiveness and speed of screen stats/spinner
+ * updating */
+#define GETCH_GUI_STATS_UPDATE_MS 1 /* 1 * 100 = 1/10/sec = millisecond block time for gui stats screen updates */
+
+#define FIELD_LENGTH 256
+
+#define MAX_TITLE_LENGTH 76
+
+#define YES 1
+#define NO 0
+
+#define SHOWING_PRIOR_TO_DRIVE_SELECTION 0
+#define SHOWING_IN_CONFIG_MENUS 1
+
+#endif /* GUI_H_ */
diff --git a/src/hddtemp_scsi/get_scsi_temp.c b/src/hddtemp_scsi/get_scsi_temp.c
new file mode 100644
index 0000000..e36d357
--- /dev/null
+++ b/src/hddtemp_scsi/get_scsi_temp.c
@@ -0,0 +1,159 @@
+/*
+ * get_scsi_temp.c: functions that populate the drive temperature variables
+ * in SCSI/SAS drives context structure.
+ * Routines from hddtemp are used here.
+ *
+ * Author: Gerold Gruber <Gerold.Gruber@edv2g.de>
+ *
+ * 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, version 2.
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+//#define _LARGEFILE64_SOURCE
+//#define _FILE_OFFSET_BITS 64
+#define _BSD_SOURCE
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <errno.h>
+#include <dirent.h>
+#include <assert.h>
+
+#include "nwipe.h"
+#include "context.h"
+#include "method.h"
+#include "device.h"
+#include "prng.h"
+#include "options.h"
+#include "device.h"
+#include "logging.h"
+#include "temperature.h"
+#include "miscellaneous.h"
+#include "hddtemp.h"
+#include "scsi.h"
+
+int scsi_get_temperature( struct disk * );
+
+int nwipe_init_scsi_temperature( nwipe_context_t* c )
+{
+
+/* dsk anlegen, malloc */
+ struct disk *dsk = (struct disk *) malloc(sizeof(struct disk));
+
+ /* Check the allocation. */
+ if( !dsk )
+ {
+ nwipe_perror( errno, __FUNCTION__, "malloc" );
+ nwipe_log( NWIPE_LOG_FATAL, "Unable to get memory for disk struct for %s",
+ c->device_name );
+ exit( 1 ) ;
+ }
+
+ assert(dsk);
+
+ memset(dsk, 0, sizeof(*dsk));
+
+ /* save the dsk pointer for later use */
+ c->templ_disk = dsk;
+
+ /* initialize */
+ dsk->drive = c->device_name;
+ dsk->type = BUS_SCSI; /* we know this as we are only called in this case */
+
+ errno = 0;
+ dsk->errormsg[0] = '\0';
+ if( (dsk->fd = open(dsk->drive, O_RDONLY | O_NONBLOCK)) < 0) {
+ snprintf(dsk->errormsg, MAX_ERRORMSG_SIZE, "open: %s\n", strerror(errno));
+ dsk->type = ERROR;
+ return 1;
+ }
+
+ // sg_logs -t <device>
+ if( scsi_get_temperature( dsk ) == GETTEMP_SUCCESS )
+ {
+ c->temp1_input = dsk->value;
+ c->temp1_crit = dsk->refvalue;
+ c->temp1_lcrit = -40; /* just to give it a value with some kind of sense */
+ c->temp1_highest = dsk->value;
+ c->temp1_lowest = dsk->value;
+ c->temp1_max = dsk->refvalue - 5; /* seems to be kind of useful */
+ }
+ else
+ {
+ nwipe_log( NWIPE_LOG_ERROR, "Can not read SCSI temperature for %s, %s",
+ dsk->drive, dsk->errormsg );
+ close( dsk->fd );
+ free( dsk );
+ c->templ_disk = NULL;
+ return 1;
+ }
+
+ return 0;
+}
+
+
+int nwipe_get_scsi_temperature( nwipe_context_t* c )
+{
+ struct disk *dsk;
+
+ dsk = c->templ_disk;
+
+ if( c->templ_disk != NULL && c->templ_disk->fd != -1 )
+ {
+ if( scsi_get_temperature( dsk ) == GETTEMP_SUCCESS )
+ {
+ c->temp1_input = dsk->value;
+
+/* not at all of interest */
+ if( c->temp1_input > c->temp1_highest )
+ {
+ c->temp1_highest = c->temp1_input;
+ }
+ if( c->temp1_input < c->temp1_lowest )
+ {
+ c->temp1_lowest = c->temp1_input;
+ }
+
+/* end not at all of interest ;-) */
+
+ }
+ else
+ {
+ nwipe_log( NWIPE_LOG_ERROR, "Could not read SCSI temperature for %s, %s",
+ dsk->drive, dsk->errormsg );
+ return 2;
+ }
+ }
+ else
+ {
+ nwipe_log( NWIPE_LOG_INFO, "no SCSI temperature reading for %s", dsk->drive );
+ return 1;
+ }
+ return 0;
+}
+
+void nwipe_shut_scsi_temperature( nwipe_context_t* c )
+{
+ if( c->templ_disk->fd != -1 )
+ {
+ close( c->templ_disk->fd );
+ }
+ if( c->templ_disk != NULL )
+ {
+ free( c->templ_disk );
+ }
+
+ return;
+}
diff --git a/src/hddtemp_scsi/hddtemp.h b/src/hddtemp_scsi/hddtemp.h
new file mode 100644
index 0000000..6565991
--- /dev/null
+++ b/src/hddtemp_scsi/hddtemp.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2002 Emmanuel VARAGNAT <hddtemp@guzu.net>
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef __HDDTEMP_H__
+#define __HDDTEMP_H__
+
+#include <time.h>
+// #include "db.h"
+
+//#ifdef ARCH_I386
+//typedef unsigned short u16;
+//#endif
+#include <linux/types.h>
+typedef __u16 u16;
+
+#define MAX_ERRORMSG_SIZE 128
+#define DEFAULT_ATTRIBUTE_ID 194
+
+#define F_to_C(val) (int)(((double)(val)-32.0)/1.8)
+#define C_to_F(val) (int)(((double)(val)*(double)1.8) + (double)32.0)
+
+enum e_bustype { ERROR = 0, BUS_UNKNOWN, BUS_SATA, BUS_ATA, BUS_SCSI, BUS_TYPE_MAX };
+
+#define GETTEMP_SUCCESS 0
+#define GETTEMP_ERROR 1
+#define GETTEMP_NOT_APPLICABLE 2
+#define GETTEMP_UNKNOWN 3
+#define GETTEMP_GUESS 4
+#define GETTEMP_KNOWN 5
+#define GETTEMP_NOSENSOR 6
+#define GETTEMP_DRIVE_SLEEP 7
+
+enum e_powermode {
+ PWM_UNKNOWN,
+ PWM_ACTIVE,
+ PWM_SLEEPING,
+ PWM_STANDBY
+};
+
+
+struct disk {
+ struct disk * next;
+
+ int fd;
+ const char * drive;
+ const char * model;
+ enum e_bustype type;
+ int value; /* the drive's temperature */
+ int refvalue; /* aka trip temperature */
+ struct harddrive_entry * db_entry;
+
+ char errormsg[MAX_ERRORMSG_SIZE];
+// enum e_gettemp ret;
+ int ret;
+ time_t last_time;
+};
+
+struct bustype {
+ char *name;
+ int (*probe)(int);
+ const char *(*model)(int);
+ enum e_gettemp (*get_temperature)(struct disk *);
+};
+
+
+extern struct bustype * bus[BUS_TYPE_MAX];
+extern char errormsg[MAX_ERRORMSG_SIZE];
+extern int tcp_daemon, debug, quiet, wakeup, af_hint;
+extern char separator;
+extern long portnum, syslog_interval;
+extern char * listen_addr;
+
+int value_to_unit(struct disk *dsk);
+char get_unit(struct disk *dsk);
+
+#endif
diff --git a/src/hddtemp_scsi/scsi.c b/src/hddtemp_scsi/scsi.c
new file mode 100644
index 0000000..ab00ad6
--- /dev/null
+++ b/src/hddtemp_scsi/scsi.c
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2002 Emmanuel VARAGNAT <hddtemp@guzu.net>
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+/*
+ * Adapted from a patch sended by : Frederic LOCHON <lochon@roulaise.net>
+ */
+/*
+ * Adapted for use with nwipe by : Gerold Gruber <Gerold.Gruber@edv2g.de>
+ */
+
+// Include file generated by ./configure
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+// Gettext includes
+#if ENABLE_NLS
+#include <libintl.h>
+#define _(String) gettext (String)
+#else
+#define _(String) (String)
+#endif
+
+// Standard includes
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <scsi/scsi.h>
+
+// Application specific includes
+#include "scsicmds.h"
+#include "hddtemp.h"
+
+int scsi_get_temperature(struct disk *dsk) {
+ int i;
+ int tempPage = 0;
+ unsigned char buffer[1024];
+
+ /*
+ on triche un peu
+ we cheat a little and do not really read form drivedb as SCSI disks are not included there
+ original code omitted as there is no need for it in the context of nwipe
+ */
+
+ /*
+ Enable SMART
+ */
+ if (scsi_smartDEXCPTdisable(dsk->fd) != 0) {
+ snprintf(dsk->errormsg, MAX_ERRORMSG_SIZE, "%s", strerror(errno));
+ close(dsk->fd);
+ dsk->fd = -1;
+ return GETTEMP_ERROR;
+ }
+
+ /*
+ Temp. capable
+ */
+ if (scsi_logsense(dsk->fd , SUPPORT_LOG_PAGES, buffer, sizeof(buffer)) != 0) {
+ snprintf(dsk->errormsg, MAX_ERRORMSG_SIZE, _("log sense failed : %s"), strerror(errno));
+ close(dsk->fd);
+ dsk->fd = -1;
+ return GETTEMP_ERROR;
+ }
+
+ for ( i = 4; i < buffer[3] + LOGPAGEHDRSIZE ; i++) {
+ if (buffer[i] == TEMPERATURE_PAGE) {
+ tempPage = 1;
+ break;
+ }
+ }
+
+ if(tempPage) {
+ /*
+ get temperature (from scsiGetTemp (scsicmd.c))
+ */
+ if (scsi_logsense(dsk->fd , TEMPERATURE_PAGE, buffer, sizeof(buffer)) != 0) {
+ snprintf(dsk->errormsg, MAX_ERRORMSG_SIZE, _("log sense failed : %s"), strerror(errno));
+ close(dsk->fd);
+ dsk->fd = -1;
+ return GETTEMP_ERROR;
+ }
+
+ if( (int)buffer[7] == 2 ) /* PARAMETER LENGTH */
+ {
+ dsk->value = buffer[9];
+ }
+ else
+ {
+ snprintf(dsk->errormsg, MAX_ERRORMSG_SIZE, _("parameter length unexpected: %d"), (int)buffer[7] );
+ return GETTEMP_UNKNOWN;
+ }
+ dsk->refvalue = buffer[15];
+ if( (int)buffer[13] == 2 ) /* PARAMETER LENGTH */
+ {
+ dsk->refvalue = buffer[15];
+ }
+ else
+ {
+ snprintf(dsk->errormsg, MAX_ERRORMSG_SIZE, _("parameter ref length unexpected: %d"), (int)buffer[13] );
+ return GETTEMP_UNKNOWN;
+ }
+ return GETTEMP_SUCCESS;
+ } else {
+ return GETTEMP_NOSENSOR;
+ }
+}
diff --git a/src/hddtemp_scsi/scsi.h b/src/hddtemp_scsi/scsi.h
new file mode 100644
index 0000000..c349e37
--- /dev/null
+++ b/src/hddtemp_scsi/scsi.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2002 Emmanuel VARAGNAT <hddtemp@guzu.net>
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef SCSIC_H_
+#define SCSIC_H_
+
+extern struct bustype scsi_bus;
+
+#endif
diff --git a/src/hddtemp_scsi/scsicmds.c b/src/hddtemp_scsi/scsicmds.c
new file mode 100644
index 0000000..3449203
--- /dev/null
+++ b/src/hddtemp_scsi/scsicmds.c
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2002 Emmanuel VARAGNAT <hddtemp@guzu.net>
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+// Include file generated by ./configure
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+// Gettext includes
+#if ENABLE_NLS
+#include <libintl.h>
+#define _(String) gettext (String)
+#else
+#define _(String) (String)
+#endif
+
+// Standard includes
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <scsi/scsi.h>
+#include <scsi/sg.h>
+#include <scsi/scsi_ioctl.h>
+
+// Application specific includes
+#include "scsicmds.h"
+
+static void scsi_fixstring(unsigned char *s, int bytecount)
+{
+ unsigned char *p;
+ unsigned char *end;
+
+ p = s;
+ end = s + bytecount;
+
+ /* strip leading blanks */
+ while (s != end && *s == ' ')
+ ++s;
+ /* compress internal blanks and strip trailing blanks */
+ while (s != end && *s) {
+ if (*s++ != ' ' || (s != end && *s && *s != ' '))
+ *p++ = *(s-1);
+ }
+ /* wipe out trailing garbage */
+ while (p != end)
+ *p++ = '\0';
+}
+
+int scsi_SG_IO(int device, unsigned char *cdb, int cdb_len, unsigned char *buffer, int buffer_len, unsigned char *sense, unsigned char sense_len, int dxfer_direction) {
+ struct sg_io_hdr io_hdr;
+
+ memset(&io_hdr, 0, sizeof(struct sg_io_hdr));
+ io_hdr.interface_id = 'S';
+ io_hdr.cmdp = cdb;
+ io_hdr.cmd_len = cdb_len;
+ io_hdr.dxfer_len = buffer_len;
+ io_hdr.dxferp = buffer;
+ io_hdr.mx_sb_len = sense_len;
+ io_hdr.sbp = sense;
+ io_hdr.dxfer_direction = dxfer_direction;
+ io_hdr.timeout = 3000; /* 3 seconds should be ample */
+
+ return ioctl(device, SG_IO, &io_hdr);
+}
+
+int scsi_SEND_COMMAND(int device, unsigned char *cdb, int cdb_len, unsigned char *buffer, int buffer_len, int dxfer_direction)
+{
+ unsigned char buf[2048];
+ unsigned int inbufsize, outbufsize, ret;
+
+ switch(dxfer_direction) {
+ case SG_DXFER_FROM_DEV:
+ inbufsize = 0;
+ outbufsize = buffer_len;
+ break;
+ case SG_DXFER_TO_DEV:
+ inbufsize = buffer_len;
+ outbufsize = 0;
+ break;
+ default:
+ inbufsize = 0;
+ outbufsize = 0;
+ break;
+ }
+ memcpy(buf, &inbufsize , sizeof(inbufsize));
+ memcpy(buf + sizeof(inbufsize), &outbufsize , sizeof(outbufsize));
+ memcpy(buf + sizeof(inbufsize) + sizeof(outbufsize), cdb, cdb_len);
+ memcpy(buf + sizeof(inbufsize) + sizeof(outbufsize) + cdb_len, buffer, buffer_len);
+
+ ret = ioctl(device, SCSI_IOCTL_SEND_COMMAND, buf);
+
+ memcpy(buffer, buf + sizeof(inbufsize) + sizeof(outbufsize), buffer_len);
+
+ return ret;
+}
+
+int scsi_command(int device, unsigned char *cdb, int cdb_len, unsigned char *buffer, int buffer_len, int dxfer_direction)
+{
+ static int SG_IO_supported = -1;
+ int ret;
+
+ if (SG_IO_supported == 1)
+ return scsi_SG_IO(device, cdb, cdb_len, buffer, buffer_len, NULL, 0, dxfer_direction);
+ else if (SG_IO_supported == 0)
+ return scsi_SEND_COMMAND(device, cdb, cdb_len, buffer, buffer_len, dxfer_direction);
+ else {
+ ret = scsi_SG_IO(device, cdb, cdb_len, buffer, buffer_len, NULL, 0, dxfer_direction);
+ if (ret == 0) {
+ SG_IO_supported = 1;
+ return ret;
+ } else {
+ SG_IO_supported = 0;
+ return scsi_SEND_COMMAND(device, cdb, cdb_len, buffer, buffer_len, dxfer_direction);
+ }
+ }
+}
+
+int scsi_inquiry(int device, unsigned char *buffer)
+{
+ unsigned char cdb[6];
+
+ memset(cdb, 0, sizeof(cdb));
+ cdb[0] = INQUIRY;
+ cdb[4] = 36; /* should be 36 for unsafe devices (like USB mass storage stuff)
+ * otherwise they can lock up! SPC sections 7.4 and 8.6 */
+
+ if (scsi_command(device, cdb, sizeof(cdb), buffer, cdb[4], SG_DXFER_FROM_DEV) != 0)
+ return 1;
+ else {
+ scsi_fixstring(buffer + 8, 24);
+ return 0;
+ }
+}
+
+int scsi_modesense(int device, unsigned char pagenum, unsigned char *buffer, int buffer_len) {
+ unsigned char cdb[6];
+ int ret;
+
+ memset(cdb, 0, sizeof(cdb));
+ cdb[0] = MODE_SENSE;
+ cdb[2] = pagenum;
+ cdb[4] = 0xff;
+
+ ret = scsi_command(device, cdb, sizeof(cdb), buffer, buffer_len, SG_DXFER_FROM_DEV);
+ if (ret == 0) {
+ if ((buffer[3] + 5) > buffer[0]) /* response length too short */
+ return -1;
+ }
+ return ret;
+}
+
+int scsi_modeselect(int device, char *buffer) {
+ unsigned char cdb[6];
+
+ memset(cdb, 0, sizeof(cdb));
+ cdb[0] = MODE_SELECT;
+ cdb[1] = 0x11;
+ cdb[4] = buffer[0] + 1;
+
+ memset(buffer, 0, 12);
+ buffer[3] = 0x08;
+ buffer[10] = 0x02;
+ buffer[12] &= 0x3f;
+
+ return scsi_command(device, cdb, sizeof(cdb), buffer, cdb[4], SG_DXFER_TO_DEV);
+}
+
+int scsi_logsense(int device, int pagenum, unsigned char *buffer, int buffer_len) {
+ unsigned char cdb[10];
+
+ memset(cdb, 0, sizeof(cdb));
+ cdb[0] = LOG_SENSE;
+ cdb[2] = 0x40 | pagenum;
+ cdb[7] = 0x04;
+
+ return scsi_command(device, cdb, sizeof(cdb), buffer, buffer_len, SG_DXFER_FROM_DEV);
+}
+
+int scsi_smartsupport(int device) {
+ unsigned char buf[255];
+
+ if (scsi_modesense (device, EXCEPTIONS_CONTROL_PAGE, buf, sizeof(buf)) != 0)
+ return 0;
+ else
+ return (buf[14] & 0x08) == 0;
+}
+
+int scsi_smartDEXCPTdisable(int device) {
+ unsigned char buf[255];
+
+ if (scsi_modesense (device, EXCEPTIONS_CONTROL_PAGE, buf, sizeof(buf)) != 0)
+ return 1;
+
+ if (buf[14] & 0x08) {
+ buf[14] &= 0xf7;
+ buf[15] = 0x04;
+ return scsi_modeselect (device, buf);
+ }
+ else
+ return 0;
+}
+
diff --git a/src/hddtemp_scsi/scsicmds.h b/src/hddtemp_scsi/scsicmds.h
new file mode 100644
index 0000000..46cd268
--- /dev/null
+++ b/src/hddtemp_scsi/scsicmds.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2002 Emmanuel VARAGNAT <hddtemp@guzu.net>
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+
+#ifndef SCSICMDS_H_
+#define SCSICMDS_H_
+
+#define SUPPORT_LOG_PAGES 0x00
+#define TEMPERATURE_PAGE 0x0d
+#define EXCEPTIONS_CONTROL_PAGE 0x1c
+#define LOGPAGEHDRSIZE 4
+
+int scsi_SG_IO(int device, unsigned char *cdb, int cdb_len, unsigned char *buffer, int buffer_len, unsigned char *sense, unsigned char sense_len, int dxfer_direction);
+int scsi_inquiry(int device, unsigned char *buffer);
+int scsi_modesense(int device, unsigned char pagenum, unsigned char *buffer, int buffer_len);
+int scsi_modeselect(int device, char *buffer);
+int scsi_logsense(int device, int pagenum, unsigned char *buffer, int buffer_len);
+int scsi_smartsupport(int device);
+int scsi_smartDEXCPTdisable(int device);
+
+#endif
diff --git a/src/hpa_dco.c b/src/hpa_dco.c
new file mode 100644
index 0000000..160d454
--- /dev/null
+++ b/src/hpa_dco.c
@@ -0,0 +1,858 @@
+/*
+ * hpa_dco.c: functions that handle the host protected area (HPA) and
+ * device configuration overlay (DCO)
+ *
+ * Copyright PartialVolume <https://github.com/PartialVolume>.
+ *
+ * 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, version 2.
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#ifndef _DEFAULT_SOURCE
+#define _DEFAULT_SOURCE
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdarg.h>
+#include <string.h>
+#include <scsi/sg.h>
+#include <scsi/scsi_ioctl.h>
+#include "nwipe.h"
+#include "context.h"
+#include "version.h"
+#include "method.h"
+#include "logging.h"
+#include "options.h"
+#include "hpa_dco.h"
+#include "miscellaneous.h"
+
+/* This function makes use of both the hdparm program to determine HPA/DCO status and we also access
+ * the device configuration overlay identify data structure via the sg driver with ioctl calls.
+ * I would prefer to write these functions without any reliance on hdparm however for the time being
+ * we will utilize both methods. However, I don't like doing it like this as a change in formatted
+ * output of hdparm could potentially break HPA/DCO detection requiring a fix. Time permitting I may
+ * come back to this and fully implement it without any reliance on hdparm.
+ */
+
+int hpa_dco_status( nwipe_context_t* ptr )
+{
+ nwipe_context_t* c;
+ c = ptr;
+
+ int r; // A result buffer.
+ int set_return_value;
+ int exit_status;
+ int hpa_line_found;
+ int dco_line_found;
+
+ FILE* fp;
+ char path_hdparm_cmd1_get_hpa[] = "hdparm --verbose -N";
+ char path_hdparm_cmd2_get_hpa[] = "/sbin/hdparm --verbose -N";
+ char path_hdparm_cmd3_get_hpa[] = "/usr/bin/hdparm --verbose -N";
+
+ char path_hdparm_cmd4_get_dco[] = "hdparm --verbose --dco-identify";
+ char path_hdparm_cmd5_get_dco[] = "/sbin/hdparm --verbose --dco-identify";
+ char path_hdparm_cmd6_get_dco[] = "/usr/bin/hdparm --verbose --dco-identify";
+
+ char pipe_std_err[] = "2>&1";
+
+ char result[512];
+
+ u64 nwipe_dco_real_max_sectors;
+
+ char* p;
+
+ /* Use the longest of the 'path_hdparm_cmd.....' strings above to
+ *determine size in the strings below
+ */
+ char hdparm_cmd_get_hpa[sizeof( path_hdparm_cmd3_get_hpa ) + sizeof( c->device_name ) + sizeof( pipe_std_err )];
+ char hdparm_cmd_get_dco[sizeof( path_hdparm_cmd6_get_dco ) + sizeof( c->device_name ) + sizeof( pipe_std_err )];
+
+ /* Initialise return value */
+ set_return_value = 0;
+
+ /* Construct the command including path to the binary if required, I do it like this to cope
+ * with distros that don't setup their paths in a standard way or maybe don't even define a
+ * path. By doing this we avoid the 'No such file or directory' message you would otherwise
+ * get on some distros. -> debian SID
+ */
+
+ if( system( "which hdparm > /dev/null 2>&1" ) )
+ {
+ if( system( "which /sbin/hdparm > /dev/null 2>&1" ) )
+ {
+ if( system( "which /usr/bin/hdparm > /dev/null 2>&1" ) )
+ {
+ nwipe_log( NWIPE_LOG_WARNING, "hdparm command not found." );
+ nwipe_log( NWIPE_LOG_WARNING,
+ "Required by nwipe for HPA/DCO detection & correction and ATA secure erase." );
+ nwipe_log( NWIPE_LOG_WARNING, "** Please install hdparm **\n" );
+ cleanup();
+ exit( 1 );
+ }
+ else
+ {
+ snprintf( hdparm_cmd_get_hpa,
+ sizeof( hdparm_cmd_get_hpa ),
+ "%s %s %s\n",
+ path_hdparm_cmd3_get_hpa,
+ c->device_name,
+ pipe_std_err );
+ snprintf( hdparm_cmd_get_dco,
+ sizeof( hdparm_cmd_get_dco ),
+ "%s %s %s\n",
+ path_hdparm_cmd6_get_dco,
+ c->device_name,
+ pipe_std_err );
+ }
+ }
+ else
+ {
+ snprintf( hdparm_cmd_get_hpa,
+ sizeof( hdparm_cmd_get_hpa ),
+ "%s %s %s\n",
+ path_hdparm_cmd2_get_hpa,
+ c->device_name,
+ pipe_std_err );
+ snprintf( hdparm_cmd_get_dco,
+ sizeof( hdparm_cmd_get_dco ),
+ "%s %s %s\n",
+ path_hdparm_cmd5_get_dco,
+ c->device_name,
+ pipe_std_err );
+ }
+ }
+ else
+ {
+ snprintf( hdparm_cmd_get_hpa,
+ sizeof( hdparm_cmd_get_hpa ),
+ "%s %s %s\n",
+ path_hdparm_cmd1_get_hpa,
+ c->device_name,
+ pipe_std_err );
+ snprintf( hdparm_cmd_get_dco,
+ sizeof( hdparm_cmd_get_dco ),
+ "%s %s %s\n",
+ path_hdparm_cmd4_get_dco,
+ c->device_name,
+ pipe_std_err );
+ }
+
+ /* Initialise the results buffer, so we don't some how inadvertently process a past result */
+ memset( result, 0, sizeof( result ) );
+
+ if( hdparm_cmd_get_hpa[0] != 0 )
+ {
+
+ fp = popen( hdparm_cmd_get_hpa, "r" );
+
+ if( fp == NULL )
+ {
+ nwipe_log( NWIPE_LOG_WARNING, "hpa_dco_status: Failed to create stream to %s", hdparm_cmd_get_hpa );
+
+ set_return_value = 1;
+ }
+
+ if( fp != NULL )
+ {
+ hpa_line_found = 0; //* init */
+
+ /* Read the output a line at a time - output it. */
+ while( fgets( result, sizeof( result ) - 1, fp ) != NULL )
+ {
+ if( nwipe_options.verbose )
+ {
+ nwipe_log( NWIPE_LOG_DEBUG, "%s \n%s", hdparm_cmd_get_hpa, result );
+ }
+
+ /* Change the output of hdparm to lower case and search using lower case strings, to try
+ * to avoid minor changes in case in hdparm's output from breaking HPA/DCO detection
+ */
+ strlower( result ); // convert the result to lower case
+
+ /* Scan the hdparm results for HPA is disabled
+ */
+ if( strstr( result, "sg_io: bad/missing sense data" ) != 0 )
+ {
+ c->HPA_status = HPA_UNKNOWN;
+ nwipe_log( NWIPE_LOG_ERROR, "SG_IO bad/missing sense data %s", hdparm_cmd_get_hpa );
+ break;
+ }
+ else
+ {
+ if( strstr( result, "hpa is disabled" ) != 0 )
+ {
+ c->HPA_status = HPA_DISABLED;
+
+ nwipe_log( NWIPE_LOG_DEBUG,
+ "hdparm says the host protected area is disabled on %s but this information may or "
+ "may not be correct, as occurs when you get a SG_IO error and 0/1 sectors and it "
+ "says HPA is enabled. Further checks are conducted below..",
+ c->device_name );
+ hpa_line_found = 1;
+ break;
+ }
+ else
+ {
+ if( strstr( result, "hpa is enabled" ) != 0 )
+ {
+ c->HPA_status = HPA_ENABLED;
+ nwipe_log( NWIPE_LOG_DEBUG,
+ "hdparm says the host protected area is enabled on %s but this information may "
+ "or may not be correct, as occurs when you get a SG_IO error and 0/1 sectors "
+ "and it says HPA is enabled. Further checks are conducted below..",
+ c->device_name );
+ hpa_line_found = 1;
+ break;
+ }
+ else
+ {
+ if( strstr( result, "accessible max address disabled" ) != 0 )
+ {
+ c->HPA_status = HPA_DISABLED;
+ nwipe_log( NWIPE_LOG_DEBUG,
+ "hdparm says the accessible max address disabled on %s"
+ "this means that there are no hidden sectors, "
+ "",
+ c->device_name );
+ hpa_line_found = 1;
+ break;
+ }
+ else
+ {
+ if( strstr( result, "accessible max address enabled" ) != 0 )
+ {
+ c->HPA_status = HPA_ENABLED;
+ nwipe_log( NWIPE_LOG_DEBUG,
+ "hdparm says the accessible max address enabled on %s"
+ "this means that there are hidden sectors",
+ c->device_name );
+ hpa_line_found = 1;
+ break;
+ }
+ else
+ {
+ if( strstr( result, "invalid" ) != 0 )
+ {
+ nwipe_log(
+ NWIPE_LOG_WARNING,
+ "hdparm reports invalid output, sector information may be invalid, buggy "
+ "drive firmware on %s?",
+ c->device_name );
+ // We'll assume the HPA values are in the string as we may be able to extract
+ // something meaningful
+ hpa_line_found = 1;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /* if the HPA line was found then process the line,
+ * extracting the 'hpa set' and 'hpa real' values.
+ */
+ if( hpa_line_found == 1 )
+ {
+ /* Extract the 'HPA set' value, the first value in the line and convert
+ * to binary and save in context */
+
+ nwipe_log( NWIPE_LOG_INFO, "HPA: %s on %s", result, c->device_name );
+
+ c->HPA_reported_set = str_ascii_number_to_ll( result );
+
+ /* Check whether the number was too large or no number found & log */
+ if( c->HPA_reported_set == -1 )
+ {
+ nwipe_log( NWIPE_LOG_INFO, "HPA_set_value: HPA set value too large on %s", c->device_name );
+ c->HPA_reported_set = 0;
+ }
+ else
+ {
+ if( c->HPA_reported_set == -2 )
+ {
+ nwipe_log( NWIPE_LOG_INFO, "HPA_set_value: No HPA set value found %s", c->device_name );
+ c->HPA_reported_set = 0;
+ }
+ }
+
+ /* Extract the 'HPA real' value, the second value in the line and convert
+ * to binary and save in context, this is a little more difficult as sometimes
+ * a odd value is returned so instead of nnnnn/nnnnn you get nnnnnn/1(nnnnnn).
+ * So first we scan for a open bracket '(' then if there is no '(' we then start the
+ * search immediately after the '/'.
+ */
+ if( ( p = strstr( result, "(" ) ) )
+ {
+ c->HPA_reported_real = str_ascii_number_to_ll( p + 1 );
+ }
+ else
+ {
+ if( ( p = strstr( result, "/" ) ) )
+ {
+ c->HPA_reported_real = str_ascii_number_to_ll( p + 1 );
+ }
+ }
+
+ /* Check whether the number was too large or no number found & log */
+ if( c->HPA_reported_real == -1 )
+ {
+ nwipe_log( NWIPE_LOG_INFO, "HPA_set_value: HPA real value too large on %s", c->device_name );
+ c->HPA_reported_real = 0;
+ }
+ else
+ {
+ if( c->HPA_reported_real == -2 )
+ {
+ nwipe_log( NWIPE_LOG_INFO, "HPA_set_value: No HPA real value found %s", c->device_name );
+ c->HPA_reported_real = 0;
+ }
+ }
+
+ nwipe_log( NWIPE_LOG_INFO,
+ "HPA values %lli / %lli on %s",
+ c->HPA_reported_set,
+ c->HPA_reported_real,
+ c->device_name );
+ }
+ else
+ {
+ c->HPA_status = HPA_UNKNOWN;
+ nwipe_log( NWIPE_LOG_WARNING,
+ "[UNKNOWN] We can't find the HPA line, has hdparm ouput unknown/changed? %s",
+ c->device_name );
+ }
+
+ /* close */
+ r = pclose( fp );
+ if( r > 0 )
+ {
+ exit_status = WEXITSTATUS( r );
+ if( nwipe_options.verbose )
+ {
+ nwipe_log( NWIPE_LOG_WARNING,
+ "hpa_dco_status(): hdparm failed, \"%s\" exit status = %u",
+ hdparm_cmd_get_hpa,
+ exit_status );
+ }
+
+ if( exit_status == 127 )
+ {
+ nwipe_log( NWIPE_LOG_WARNING, "Command not found. Installing hdparm is mandatory !" );
+ set_return_value = 2;
+ if( nwipe_options.nousb )
+ {
+ return set_return_value;
+ }
+ }
+ }
+ }
+ }
+
+ /* Initialise the results buffer again, so we don't
+ * some how inadvertently process a past result */
+ memset( result, 0, sizeof( result ) );
+
+ /* -----------------------------------------------
+ * Run the dco identify command and determine the
+ * real max sectors, store it in the drive context
+ * for comparison against the hpa reported drive
+ * size values.
+ */
+
+ dco_line_found = 0;
+
+ if( hdparm_cmd_get_dco[0] != 0 )
+ {
+
+ fp = popen( hdparm_cmd_get_dco, "r" );
+
+ if( fp == NULL )
+ {
+ nwipe_log( NWIPE_LOG_WARNING, "hpa_dco_status: Failed to create stream to %s", hdparm_cmd_get_dco );
+
+ set_return_value = 1;
+ }
+
+ if( fp != NULL )
+ {
+ /* Read the output a line at a time - output it. */
+ while( fgets( result, sizeof( result ) - 1, fp ) != NULL )
+ {
+ /* Change the output of hdparm to lower case and search using lower case strings, to try
+ * to avoid minor changes in case in hdparm's output from breaking HPA/DCO detection */
+ strlower( result );
+
+ if( nwipe_options.verbose )
+ {
+ nwipe_log( NWIPE_LOG_DEBUG, "%s \n%s", hdparm_cmd_get_dco, result );
+ }
+
+ if( strstr( result, "real max sectors" ) != 0 )
+ {
+ /* extract the real max sectors, convert to binary and store in drive context */
+ dco_line_found = 1;
+ break;
+ }
+ }
+ /* DCO line found, now process it */
+ if( dco_line_found == 1 )
+ {
+ c->DCO_reported_real_max_sectors = str_ascii_number_to_ll( result );
+ nwipe_log( NWIPE_LOG_INFO,
+ "hdparm:DCO Real max sectors reported as %lli on %s",
+ c->DCO_reported_real_max_sectors,
+ c->device_name );
+
+ /* Validate the real max sectors to detect extreme or impossible
+ * values, so the size must be greater than zero but less than
+ * 200TB (429496729600 sectors). As its 2023 and the largest drive
+ * available is 20TB I wonder if somebody in the future will be looking
+ * at this and thinking, yep we need to increase that value... and I'm
+ * wondering what year that will be. This validation is necessary all
+ * because of a bug in hdparm v9.60 (and maybe other versions) which
+ * produced wildly inaccurate values, often negative.
+ */
+ if( c->DCO_reported_real_max_sectors > 0 && c->DCO_reported_real_max_sectors < 429496729600 )
+ {
+ nwipe_log( NWIPE_LOG_INFO,
+ "NWipe: DCO Real max sectors reported as %lli on %s",
+ c->DCO_reported_real_max_sectors,
+ c->device_name );
+ }
+ else
+ {
+ /* Call nwipe's own low level function to retrieve the drive configuration
+ * overlay and retrieve the real max sectors. We may remove reliance on hdparm
+ * if nwipes own low level drive access code works well.
+ */
+ c->DCO_reported_real_max_sectors = nwipe_read_dco_real_max_sectors( c->device_name );
+
+ /* Check our real max sectors function is returning sensible data too */
+ if( c->DCO_reported_real_max_sectors > 0 && c->DCO_reported_real_max_sectors < 429496729600 )
+ {
+ nwipe_log( NWIPE_LOG_INFO,
+ "NWipe: DCO Real max sectors reported as %lli on %s",
+ c->DCO_reported_real_max_sectors,
+ c->device_name );
+ }
+ else
+ {
+ c->DCO_reported_real_max_sectors = 0;
+ nwipe_log( NWIPE_LOG_INFO, "DCO Real max sectors not found" );
+ }
+ }
+ }
+ else
+ {
+ c->DCO_reported_real_max_sectors = 0;
+ nwipe_log( NWIPE_LOG_INFO, "DCO Real max sectors not found" );
+ }
+
+ nwipe_log(
+ NWIPE_LOG_INFO,
+ "libata: apparent max sectors reported as %lli with sector size as %i/%i (logical/physical) on %s",
+ c->device_size_in_sectors,
+ c->device_sector_size, // logical
+ c->device_phys_sector_size, // physical
+ c->device_name );
+
+ /* close */
+ r = pclose( fp );
+ if( r > 0 )
+ {
+ exit_status = WEXITSTATUS( r );
+ if( nwipe_options.verbose )
+ {
+ nwipe_log( NWIPE_LOG_WARNING,
+ "hpa_dco_status(): hdparm failed, \"%s\" exit status = %u",
+ hdparm_cmd_get_dco,
+ exit_status );
+ }
+
+ if( exit_status == 127 )
+ {
+ nwipe_log( NWIPE_LOG_WARNING, "Command not found. Installing hdparm is mandatory !" );
+ set_return_value = 2;
+ if( nwipe_options.nousb )
+ {
+ return set_return_value;
+ }
+ }
+ }
+ }
+ }
+
+ /* Compare the results of hdparm -N (HPA set / HPA real)
+ * and hdparm --dco-identidy (real max sectors). All three
+ * values may be different or perhaps 'HPA set' and 'HPA real' are
+ * different and 'HPA real' matches 'real max sectors'.
+ *
+ * A perfect HPA disabled result would be where all three
+ * values are the same. It can then be considered that the
+ * HPA is disabled.
+ *
+ * If 'HPA set' and 'HPA real' are different then it
+ * can be considered that HPA is enabled, assuming 'HPA set'
+ * and 'HPA real' are not 0/1 which occurs when a SG_IO error
+ * occurs. That also is checked for as it often indicates a
+ * poor USB device that does not have ATA pass through support.
+ *
+ * However we also need to consider that more recent drives
+ * no longer support HPA/DCO such as the Seagate ST10000NM0016,
+ * ST4000NM0033 and ST1000DM003. If you try to issue those drives
+ * with the ATA command code 0xB1 (device configuration overlay)
+ * you will get a generic illegal request in the returned sense data.
+ *
+ * One other thing to note, we use HPA enabled/disabled to mean
+ * hidden area detected or not detected, this could be caused by
+ * either the dco-setmax being issued or Np, either way an area
+ * of the disc can be hidden. From the user interface we just call
+ * it a HPA/DCO hidden area detected (or not) which is more
+ * meaningful than just saying HDA enabled or disabled and a user
+ * not familiar with the term HPA or DCO not understanding why a
+ * HDA being detected could be significant.
+ */
+
+ /* Determine, based on the values of 'HPA set', 'HPA real and
+ * 'real max sectors' whether we set the HPA flag as HPA_DISABLED,
+ * HPA_ENABLED, HPA_UNKNOWN or HPA_NOT_APPLICABLE. The HPA flag
+ * will be displayed in the GUI and on the certificate and is
+ * used to determine whether to reset the HPA.
+ *
+ */
+
+ /* WARNING temp assignments WARNING
+ * s=28,r=28,rm=0
+ *
+ */
+#if 0
+ c->HPA_reported_set = 10;
+ c->HPA_reported_real = 28;
+ c->DCO_reported_real_max_sectors = 0;
+
+ c->HPA_reported_set = 28;
+ c->HPA_reported_real = 28;
+ c->DCO_reported_real_max_sectors = 0;
+
+ c->HPA_reported_set = 1000;
+ c->HPA_reported_real = 2048;
+ c->DCO_reported_real_max_sectors = 2048;
+#endif
+
+ /* If any of the HPA or DCO values are larger than the apparent size then HPA is enabled. */
+ if( /*c->HPA_reported_set > c->device_size_in_sectors || */ c->HPA_reported_real > c->device_size_in_512byte_sectors
+ || c->DCO_reported_real_max_sectors > c->device_size_in_512byte_sectors )
+ {
+ c->HPA_status = HPA_ENABLED;
+ nwipe_log( NWIPE_LOG_WARNING, " *********************************" );
+ nwipe_log( NWIPE_LOG_WARNING, " *** HIDDEN SECTORS DETECTED ! *** on %s", c->device_name );
+ nwipe_log( NWIPE_LOG_WARNING, " *********************************" );
+ }
+ else
+ {
+ /* This occurs when a SG_IO error occurs with USB devices that don't support ATA pass
+ * through */
+ if( c->HPA_reported_set == 0 && c->HPA_reported_real == 0 && c->DCO_reported_real_max_sectors <= 1 )
+ {
+ c->HPA_status = HPA_UNKNOWN;
+ if( c->device_bus == NWIPE_DEVICE_USB )
+ {
+ nwipe_log( NWIPE_LOG_WARNING,
+ "HIDDEN SECTORS INDETERMINATE! on %s, Some USB adapters & memory sticks don't support "
+ "ATA pass through",
+ c->device_name );
+ }
+ }
+ else
+ {
+ c->HPA_status = HPA_DISABLED;
+ nwipe_log( NWIPE_LOG_INFO, "No hidden sectors on %s", c->device_name );
+ }
+ }
+
+ c->DCO_reported_real_max_size = c->DCO_reported_real_max_sectors * c->device_sector_size;
+
+ nwipe_dco_real_max_sectors = nwipe_read_dco_real_max_sectors( c->device_name );
+
+ /* Analyse all the variations to produce the final real max bytes which takes into
+ * account drives that don't support DCO or HPA. This result is used in the PDF
+ * creation functions.
+ */
+
+ if( c->device_type == NWIPE_DEVICE_NVME || c->device_type == NWIPE_DEVICE_VIRT
+ || c->HPA_status == HPA_NOT_APPLICABLE )
+ {
+ c->Calculated_real_max_size_in_bytes = c->device_size;
+ }
+ else
+ {
+ /* If the DCO is reporting a real max sectors > the apparent size
+ * as reported by libata then that is what we will use as the real disc size
+ */
+ if( c->DCO_reported_real_max_size > c->device_size_in_512byte_sectors )
+ {
+ c->Calculated_real_max_size_in_bytes = c->DCO_reported_real_max_sectors * c->device_sector_size;
+ }
+ else
+ {
+ /* If HPA is enabled and DCO real max sectors did not exist, then we have to assume - c->HPA_reported_real
+ * is the value we need, however if that is zero, then c->HPA_reported_set and if that is zero then
+ * c->device_size as reported by libata
+ */
+ if( c->HPA_reported_real > c->device_size_in_512byte_sectors )
+ {
+ c->Calculated_real_max_size_in_bytes = c->HPA_reported_real * c->device_sector_size;
+ }
+ else
+ {
+ if( c->HPA_reported_set > c->device_size_in_512byte_sectors )
+ {
+ c->Calculated_real_max_size_in_bytes = c->HPA_reported_set * c->device_sector_size;
+ }
+ else
+ {
+ c->Calculated_real_max_size_in_bytes = c->device_size;
+ }
+ }
+ }
+ }
+
+ /* -------------------------------------------------------------------
+ * create two variables for later use by the PDF creation function
+ * based on real max sectors and calculated real max size in bytes.
+ *
+ * DCO_reported_real_max_size = real max sectors * sector size = bytes
+ * DCO_reported_real_max_size_text = human readable string, i.e 1TB etc.
+ */
+
+ Determine_C_B_nomenclature(
+ c->DCO_reported_real_max_size, c->DCO_reported_real_max_size_text, NWIPE_DEVICE_SIZE_TXT_LENGTH );
+ Determine_C_B_nomenclature(
+ c->Calculated_real_max_size_in_bytes, c->Calculated_real_max_size_in_bytes_text, NWIPE_DEVICE_SIZE_TXT_LENGTH );
+
+ /* ----------------------------------------------------------------------------------
+ * Determine the size of the HPA if it's enabled and store the results in the context
+ */
+
+ if( c->HPA_status == HPA_ENABLED )
+ {
+ if( c->Calculated_real_max_size_in_bytes != c->device_size )
+ {
+ c->HPA_sectors =
+ ( (u64) ( c->Calculated_real_max_size_in_bytes - c->device_size ) / c->device_sector_size );
+ }
+ else
+ {
+ c->HPA_sectors = 0;
+ }
+
+ /* Convert the size to a human readable format and save in context */
+ Determine_C_B_nomenclature( c->HPA_sectors, c->HPA_size_text, NWIPE_DEVICE_SIZE_TXT_LENGTH );
+ }
+ else
+ {
+ /* HPA not enabled so initialise these values */
+ c->HPA_sectors = 0;
+ c->HPA_size_text[0] = 0;
+ }
+
+ nwipe_log( NWIPE_LOG_DEBUG,
+ "c->Calculated_real_max_size_in_bytes=%lli, c->device_size=%lli, c->device_sector_size=%lli, "
+ "c->DCO_reported_real_max_size=%lli, c->DCO_reported_real_max_sectors=%lli, c->HPA_sectors=%lli, "
+ "c->HPA_reported_set=%lli, c->HPA_reported_real=%lli, c->device_type=%i, "
+ "libata:c->device_size_in_sectors=%lli ",
+ "libata:c->device_size_in_512byte_sectors=%lli ",
+ c->Calculated_real_max_size_in_bytes,
+ c->device_size,
+ c->device_sector_size,
+ c->DCO_reported_real_max_size,
+ c->DCO_reported_real_max_sectors,
+ c->HPA_sectors,
+ c->HPA_reported_set,
+ c->HPA_reported_real,
+ c->device_type,
+ c->device_size_in_sectors,
+ c->device_size_in_512byte_sectors );
+
+ return set_return_value;
+}
+
+u64 nwipe_read_dco_real_max_sectors( char* device )
+{
+ /* This function sends a device configuration overlay identify command 0xB1 (dco-identify)
+ * to the drive and extracts the real max sectors. The value is incremented by 1 and
+ * then returned. We rely upon this function to determine real max sectors as there
+ * is a bug in hdparm 9.60, including possibly earlier or later versions but which is
+ * fixed in 9.65, that returns a incorrect (negative) value
+ * for some drives that are possibly over a certain size.
+ */
+
+ /* TODO Add checks in case of failure, especially with recent drives that may not
+ * support drive configuration overlay commands.
+ */
+
+#define LBA_SIZE 512
+#define CMD_LEN 16
+#define BLOCK_MAX 65535
+#define LBA_MAX ( 1 << 30 )
+#define SENSE_BUFFER_SIZE 32
+
+ u64 nwipe_real_max_sectors;
+
+ /* This command issues command 0xb1 (dco-identify) 15th byte */
+ unsigned char cmd_blk[CMD_LEN] = { 0x85, 0x08, 0x0e, 0x00, 0xc2, 0, 0x01, 0, 0, 0, 0, 0, 0, 0x40, 0xb1, 0 };
+
+ sg_io_hdr_t io_hdr;
+ unsigned char buffer[LBA_SIZE]; // Received data block
+ unsigned char sense_buffer[SENSE_BUFFER_SIZE]; // Sense data
+
+ /* three characters represent one byte of sense data, i.e
+ * two characters and a space "01 AE 67"
+ */
+ char sense_buffer_hex[( SENSE_BUFFER_SIZE * 3 ) + 1];
+
+ int i, i2; // index
+ int fd; // file descripter
+
+ if( ( fd = open( device, O_RDWR ) ) < 0 )
+ {
+ /* Unable to open device */
+ return -1;
+ }
+
+ /******************************************
+ * Initialise the sg header for reading the
+ * device configuration overlay identify data
+ */
+ memset( &io_hdr, 0, sizeof( sg_io_hdr_t ) );
+ io_hdr.interface_id = 'S';
+ io_hdr.cmd_len = sizeof( cmd_blk );
+ io_hdr.mx_sb_len = sizeof( sense_buffer );
+ io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
+ io_hdr.dxfer_len = LBA_SIZE;
+ io_hdr.dxferp = buffer;
+ io_hdr.cmdp = cmd_blk;
+ io_hdr.sbp = sense_buffer;
+ io_hdr.timeout = 20000;
+
+ if( ioctl( fd, SG_IO, &io_hdr ) < 0 )
+ {
+ nwipe_log( NWIPE_LOG_ERROR, "IOCTL command failed retrieving DCO" );
+ i2 = 0;
+ for( i = 0, i2 = 0; i < SENSE_BUFFER_SIZE; i++, i2 += 3 )
+ {
+ /* IOCTL returned an error */
+ snprintf( &sense_buffer_hex[i2], sizeof( sense_buffer_hex ), "%02x ", sense_buffer[i] );
+ }
+ sense_buffer_hex[i2] = 0; // terminate string
+ nwipe_log( NWIPE_LOG_DEBUG, "Sense buffer from failed DCO identify cmd:%s", sense_buffer_hex );
+ return -2;
+ }
+
+ /* Close the device */
+ close( fd );
+
+ /***************************************************************
+ * Extract the real max sectors from the returned 512 byte block.
+ * Assuming the first word/byte is 0. We extract the bytes & switch
+ * the endian. Words 3-6(bytes 6-13) contain the max sector address
+ */
+ nwipe_real_max_sectors = (u64) ( (u64) buffer[13] << 56 ) | ( (u64) buffer[12] << 48 ) | ( (u64) buffer[11] << 40 )
+ | ( (u64) buffer[10] << 32 ) | ( (u64) buffer[9] << 24 ) | ( (u64) buffer[8] << 16 ) | ( (u64) buffer[7] << 8 )
+ | buffer[6];
+
+ /* Don't really understand this but hdparm adds 1 to
+ * the real max sectors too, counting zero as sector?
+ * but only increment if it's already greater than zero
+ */
+ if( nwipe_real_max_sectors > 0 )
+ {
+ nwipe_real_max_sectors++;
+ }
+
+ nwipe_log(
+ NWIPE_LOG_INFO, "func:nwipe_read_dco_real_max_sectors(), DCO real max sectors = %lli", nwipe_real_max_sectors );
+
+ return nwipe_real_max_sectors;
+}
+
+int ascii2binary_array( char* input, unsigned char* output_bin, int bin_size )
+{
+ /* Converts ascii sense data output by hdparm to binary.
+ * Scans a character string that contains hexadecimal ascii data, ignores spaces
+ * and extracts and converts the hexadecimal ascii data to binary and places in a array.
+ * Typically for dco_identify sense data the bin size will be 512 bytes but for error
+ * sense data this would be 32 bytes.
+ */
+ int idx_in; // Index into ascii input string
+ int idx_out; // Index into the binary output array
+ int byte_count; // Counts which 4 bit value we are working on
+ char upper4bits;
+ char lower4bits;
+
+ byte_count = 0;
+ idx_in = 0;
+ idx_out = 0;
+ while( input[idx_in] != 0 )
+ {
+ if( input[idx_in] >= '0' && input[idx_in] <= '9' )
+ {
+ if( byte_count == 0 )
+ {
+ upper4bits = input[idx_in] - 0x30;
+ byte_count++;
+ }
+ else
+ {
+ lower4bits = input[idx_in] - 0x30;
+ output_bin[idx_out++] = ( upper4bits << 4 ) | ( lower4bits );
+ byte_count = 0;
+
+ if( idx_out >= bin_size )
+ {
+ return 0; // output array full.
+ }
+ }
+ }
+ else
+ {
+ if( input[idx_in] >= 'a' && input[idx_in] <= 'f' )
+ {
+ if( byte_count == 0 )
+ {
+ upper4bits = input[idx_in] - 0x57;
+ byte_count++;
+ }
+ else
+ {
+ lower4bits = input[idx_in] - 0x57;
+ output_bin[idx_out++] = ( upper4bits << 4 ) | ( lower4bits );
+ byte_count = 0;
+
+ if( idx_out >= bin_size )
+ {
+ return 0; // output array full.
+ }
+ }
+ }
+ }
+ idx_in++; // next byte in the input string
+ }
+ return 0;
+}
diff --git a/src/hpa_dco.h b/src/hpa_dco.h
new file mode 100644
index 0000000..edbae65
--- /dev/null
+++ b/src/hpa_dco.h
@@ -0,0 +1,43 @@
+/*.
+ * hpa_dco.h: The header file for the host protected area (HPA) and
+ * disk configuration overlay routines
+ *
+ * Copyright https://github.com/PartialVolume/shredos.x86_64
+ *
+ * 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, version 2.
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+#ifndef HPA_DCO_H_
+#define HPA_DCO_H_
+
+#define HPA_DISABLED 0
+#define HPA_ENABLED 1
+#define HPA_UNKNOWN 2
+#define HPA_NOT_APPLICABLE 3
+#define HPA_NOT_SUPPORTED_BY_DRIVE 4
+
+int hpa_dco_status( nwipe_context_t* );
+
+u64 nwipe_read_dco_real_max_sectors( char* );
+
+typedef struct nwipe_sense_dco_identify_t_t_
+{
+ /* This struct contains some of the decoded fields from the sense data after a
+ * ATA 0xB1 device configuration overlay command has been issued. We mainly
+ * use it to decode the real max sectors
+ */
+ u64 dco_real_max_sectors;
+} nwipe_sense_dco_identify_t_t_;
+
+#endif /* HPA_DCO_H_ */
diff --git a/src/isaac_rand/isaac64.c b/src/isaac_rand/isaac64.c
new file mode 100644
index 0000000..3e4203b
--- /dev/null
+++ b/src/isaac_rand/isaac64.c
@@ -0,0 +1,119 @@
+/*
+------------------------------------------------------------------------------
+isaac64.c: My random number generator for 64-bit machines.
+By Bob Jenkins, 1996. Public Domain.
+------------------------------------------------------------------------------
+*/
+#ifndef STANDARD
+#include "isaac_standard.h"
+#endif
+#ifndef ISAAC64
+#include "isaac64.h"
+#endif
+
+
+#define ind(mm,x) (*(ub8 *)((ub1 *)(mm) + ((x) & ((RANDSIZ-1)<<3))))
+#define rngstep(mix,a,b,mm,m,m2,r,x) \
+{ \
+ x = *m; \
+ a = (mix) + *(m2++); \
+ *(m++) = y = ind(mm,x) + a + b; \
+ *(r++) = b = ind(mm,y>>RANDSIZL) + x; \
+}
+
+void isaac64(rand64ctx *ctx)
+{
+ register ub8 a,b,x,y,*m,*mm,*m2,*r,*mend;
+ mm=ctx->mm; r=ctx->randrsl;
+ a = ctx->aa; b = ctx->bb + (++ctx->cc);
+ for (m = mm, mend = m2 = m+(RANDSIZ/2); m<mend; )
+ {
+ rngstep(~(a^(a<<21)), a, b, mm, m, m2, r, x);
+ rngstep( a^(a>>5) , a, b, mm, m, m2, r, x);
+ rngstep( a^(a<<12) , a, b, mm, m, m2, r, x);
+ rngstep( a^(a>>33) , a, b, mm, m, m2, r, x);
+ }
+ for (m2 = mm; m2<mend; )
+ {
+ rngstep(~(a^(a<<21)), a, b, mm, m, m2, r, x);
+ rngstep( a^(a>>5) , a, b, mm, m, m2, r, x);
+ rngstep( a^(a<<12) , a, b, mm, m, m2, r, x);
+ rngstep( a^(a>>33) , a, b, mm, m, m2, r, x);
+ }
+ ctx->bb = b; ctx->aa = a;
+}
+
+#define mix(a,b,c,d,e,f,g,h) \
+{ \
+ a-=e; f^=h>>9; h+=a; \
+ b-=f; g^=a<<9; a+=b; \
+ c-=g; h^=b>>23; b+=c; \
+ d-=h; a^=c<<15; c+=d; \
+ e-=a; b^=d>>14; d+=e; \
+ f-=b; c^=e<<20; e+=f; \
+ g-=c; d^=f>>17; f+=g; \
+ h-=d; e^=g<<14; g+=h; \
+}
+
+void rand64init(rand64ctx *ctx, word flag)
+{
+ word i;
+ ub8 a,b,c,d,e,f,g,h;
+ ub8 *mm, *randrsl;
+ ctx->aa = ctx->bb = ctx->cc = (ub8)0;
+ mm=ctx->mm;
+ randrsl=ctx->randrsl;
+ a=b=c=d=e=f=g=h=0x9e3779b97f4a7c13LL; /* the golden ratio */
+
+ for (i=0; i<4; ++i) /* scramble it */
+ {
+ mix(a,b,c,d,e,f,g,h);
+ }
+
+ for (i=0; i<RANDSIZ; i+=8) /* fill in mm[] with messy stuff */
+ {
+ if (flag) /* use all the information in the seed */
+ {
+ a+=randrsl[i ]; b+=randrsl[i+1]; c+=randrsl[i+2]; d+=randrsl[i+3];
+ e+=randrsl[i+4]; f+=randrsl[i+5]; g+=randrsl[i+6]; h+=randrsl[i+7];
+ }
+ mix(a,b,c,d,e,f,g,h);
+ mm[i ]=a; mm[i+1]=b; mm[i+2]=c; mm[i+3]=d;
+ mm[i+4]=e; mm[i+5]=f; mm[i+6]=g; mm[i+7]=h;
+ }
+
+ if (flag)
+ { /* do a second pass to make all of the seed affect all of mm */
+ for (i=0; i<RANDSIZ; i+=8)
+ {
+ a+=mm[i ]; b+=mm[i+1]; c+=mm[i+2]; d+=mm[i+3];
+ e+=mm[i+4]; f+=mm[i+5]; g+=mm[i+6]; h+=mm[i+7];
+ mix(a,b,c,d,e,f,g,h);
+ mm[i ]=a; mm[i+1]=b; mm[i+2]=c; mm[i+3]=d;
+ mm[i+4]=e; mm[i+5]=f; mm[i+6]=g; mm[i+7]=h;
+ }
+ }
+
+ isaac64(ctx); /* fill in the first set of results */
+ ctx->randcnt=RANDSIZ; /* prepare to use the first set of results */
+}
+
+#ifdef NEVER
+int main()
+{
+ ub8 i,j;
+ rand64ctx ctx;
+ ctx.aa=ctx.bb=ctx.cc=(ub8)0;
+ for (i=0; i<RANDSIZ; ++i) ctx.mm[i]=(ub8)0;
+ rand64init(&ctx, TRUE);
+ for (i=0; i<2; ++i)
+ {
+ isaac64(&ctx);
+ for (j=0; j<RANDSIZ; ++j)
+ {
+ printf("%.8lx%.8lx",(ub4)(ctx.randrsl[j]>>32),(ub4)ctx.randrsl[j]);
+ if ((j&3)==3) printf("\n");
+ }
+ }
+}
+#endif
diff --git a/src/isaac_rand/isaac64.h b/src/isaac_rand/isaac64.h
new file mode 100644
index 0000000..e2ab55b
--- /dev/null
+++ b/src/isaac_rand/isaac64.h
@@ -0,0 +1,41 @@
+/*
+------------------------------------------------------------------------------
+isaac64.h: definitions for a random number generator
+Bob Jenkins, 1996, Public Domain
+------------------------------------------------------------------------------
+*/
+#ifndef ISAAC64
+#define ISAAC64
+
+#include "isaac_standard.h"
+
+struct rand64ctx
+{
+ ub8 randrsl[RANDSIZ], randcnt;
+ ub8 mm[RANDSIZ];
+ ub8 aa, bb, cc;
+};
+typedef struct rand64ctx rand64ctx;
+
+/*
+------------------------------------------------------------------------------
+ If (flag==TRUE), then use the contents of randrsl[0..255] as the seed.
+------------------------------------------------------------------------------
+*/
+void rand64init(rand64ctx *r, word flag);
+
+void isaac64(rand64ctx *ctx);
+
+
+/*
+------------------------------------------------------------------------------
+ Call rand64() to retrieve a single 64-bit random value
+------------------------------------------------------------------------------
+*/
+#define isaac64_rand() \
+ (!(r)->randcnt-- ? \
+ (isaac64(r), (r)->randcnt=RANDSIZ-1, (r)->randrsl[(r)->>randcnt]) : \
+ (r)->randrsl[(r)->randcnt])
+
+#endif /* ISAAC64 */
+
diff --git a/src/isaac_rand/isaac_rand.c b/src/isaac_rand/isaac_rand.c
new file mode 100644
index 0000000..c9220e0
--- /dev/null
+++ b/src/isaac_rand/isaac_rand.c
@@ -0,0 +1,169 @@
+/*
+------------------------------------------------------------------------------
+rand.c: By Bob Jenkins. My random number generator, ISAAC. Public Domain.
+MODIFIED:
+ 960327: Creation (addition of randinit, really)
+ 970719: use context, not global variables, for internal state
+ 980324: added main (ifdef'ed out), also rearranged randinit()
+ 010626: Note that this is public domain
+------------------------------------------------------------------------------
+*/
+#ifndef STANDARD
+#include "isaac_standard.h"
+#endif
+#ifndef RAND
+#include "isaac_rand.h"
+#endif
+
+
+#define ind(mm,x) (*(ub4 *)((ub1 *)(mm) + ((x) & ((RANDSIZ-1)<<2))))
+#define rngstep(mix,a,b,mm,m,m2,r,x) \
+{ \
+ x = *m; \
+ a = (a^(mix)) + *(m2++); \
+ *(m++) = y = ind(mm,x) + a + b; \
+ *(r++) = b = ind(mm,y>>RANDSIZL) + x; \
+}
+
+void isaac(ctx)
+randctx *ctx;
+{
+ register ub4 a,b,x,y,*m,*mm,*m2,*r,*mend;
+ mm=ctx->randmem;
+ r=ctx->randrsl;
+ a = ctx->randa;
+ b = ctx->randb + (++ctx->randc);
+ for (m = mm, mend = m2 = m+(RANDSIZ/2); m<mend; )
+ {
+ rngstep( a<<13, a, b, mm, m, m2, r, x);
+ rngstep( a>>6 , a, b, mm, m, m2, r, x);
+ rngstep( a<<2 , a, b, mm, m, m2, r, x);
+ rngstep( a>>16, a, b, mm, m, m2, r, x);
+ }
+ for (m2 = mm; m2<mend; )
+ {
+ rngstep( a<<13, a, b, mm, m, m2, r, x);
+ rngstep( a>>6 , a, b, mm, m, m2, r, x);
+ rngstep( a<<2 , a, b, mm, m, m2, r, x);
+ rngstep( a>>16, a, b, mm, m, m2, r, x);
+ }
+ ctx->randb = b; ctx->randa = a;
+}
+
+
+#define mix(a,b,c,d,e,f,g,h) \
+{ \
+ a^=b<<11; d+=a; b+=c; \
+ b^=c>>2; e+=b; c+=d; \
+ c^=d<<8; f+=c; d+=e; \
+ d^=e>>16; g+=d; e+=f; \
+ e^=f<<10; h+=e; f+=g; \
+ f^=g>>4; a+=f; g+=h; \
+ g^=h<<8; b+=g; h+=a; \
+ h^=a>>9; c+=h; a+=b; \
+}
+
+/* if (flag==TRUE), then use the contents of randrsl[] to initialize mm[]. */
+void randinit(ctx, flag)
+randctx *ctx;
+word flag;
+{
+ word i;
+ ub4 a,b,c,d,e,f,g,h;
+ ub4 *m,*r;
+ ctx->randa = ctx->randb = ctx->randc = 0;
+ m=ctx->randmem;
+ r=ctx->randrsl;
+ a=b=c=d=e=f=g=h=0x9e3779b9; /* the golden ratio */
+
+ for (i=0; i<4; ++i) /* scramble it */
+ {
+ mix(a,b,c,d,e,f,g,h);
+ }
+
+ if (flag)
+ {
+ /* initialize using the contents of r[] as the seed */
+ for (i=0; i<RANDSIZ; i+=8)
+ {
+ a+=r[i ];
+ b+=r[i+1];
+ c+=r[i+2];
+ d+=r[i+3];
+ e+=r[i+4];
+ f+=r[i+5];
+ g+=r[i+6];
+ h+=r[i+7];
+ mix(a,b,c,d,e,f,g,h);
+ m[i ]=a;
+ m[i+1]=b;
+ m[i+2]=c;
+ m[i+3]=d;
+ m[i+4]=e;
+ m[i+5]=f;
+ m[i+6]=g;
+ m[i+7]=h;
+ }
+ /* do a second pass to make all of the seed affect all of m */
+ for (i=0; i<RANDSIZ; i+=8)
+ {
+ a+=m[i ];
+ b+=m[i+1];
+ c+=m[i+2];
+ d+=m[i+3];
+ e+=m[i+4];
+ f+=m[i+5];
+ g+=m[i+6];
+ h+=m[i+7];
+ mix(a,b,c,d,e,f,g,h);
+ m[i ]=a;
+ m[i+1]=b;
+ m[i+2]=c;
+ m[i+3]=d;
+ m[i+4]=e;
+ m[i+5]=f;
+ m[i+6]=g;
+ m[i+7]=h;
+ }
+ }
+ else
+ {
+ /* fill in m[] with messy stuff */
+ for (i=0; i<RANDSIZ; i+=8)
+ {
+ mix(a,b,c,d,e,f,g,h);
+ m[i ]=a;
+ m[i+1]=b;
+ m[i+2]=c;
+ m[i+3]=d;
+ m[i+4]=e;
+ m[i+5]=f;
+ m[i+6]=g;
+ m[i+7]=h;
+ }
+ }
+
+ isaac(ctx); /* fill in the first set of results */
+ ctx->randcnt=RANDSIZ; /* prepare to use the first set of results */
+}
+
+
+#ifdef NEVER
+int main()
+{
+ ub4 i,j;
+ randctx ctx;
+ ctx.randa=ctx.randb=ctx.randc=(ub4)0;
+ for (i=0; i<256; ++i) ctx.randrsl[i]=(ub4)0;
+ randinit(&ctx, TRUE);
+ for (i=0; i<2; ++i)
+ {
+ isaac(&ctx);
+ for (j=0; j<256; ++j)
+ {
+ printf("%.8lx",ctx.randrsl[j]);
+ if ((j&7)==7) printf("\n");
+ }
+ }
+}
+#endif
diff --git a/src/isaac_rand/isaac_rand.h b/src/isaac_rand/isaac_rand.h
new file mode 100644
index 0000000..167a2fd
--- /dev/null
+++ b/src/isaac_rand/isaac_rand.h
@@ -0,0 +1,52 @@
+/*
+------------------------------------------------------------------------------
+rand.h: definitions for a random number generator
+By Bob Jenkins, 1996, Public Domain
+MODIFIED:
+ 960327: Creation (addition of randinit, really)
+ 970719: use context, not global variables, for internal state
+ 980324: renamed seed to flag
+ 980605: recommend RANDSIZL=4 for noncryptography.
+ 010626: note this is public domain
+------------------------------------------------------------------------------
+*/
+#ifndef RAND
+#define RAND
+
+#include "isaac_standard.h"
+
+/* context of random number generator */
+struct randctx
+{
+ ub4 randcnt;
+ ub4 randrsl[RANDSIZ];
+ ub4 randmem[RANDSIZ];
+ ub4 randa;
+ ub4 randb;
+ ub4 randc;
+};
+typedef struct randctx randctx;
+
+/*
+------------------------------------------------------------------------------
+ If (flag==TRUE), then use the contents of randrsl[0..RANDSIZ-1] as the seed.
+------------------------------------------------------------------------------
+*/
+void randinit(/*_ randctx *r, word flag _*/);
+
+void isaac(/*_ randctx *r _*/);
+
+
+/*
+------------------------------------------------------------------------------
+ Call rand(/o_ randctx *r _o/) to retrieve a single 32-bit random value
+------------------------------------------------------------------------------
+*/
+#define isaac_rand(r) \
+ (!(r)->randcnt-- ? \
+ (isaac(r), (r)->randcnt=RANDSIZ-1, (r)->randrsl[(r)->randcnt]) : \
+ (r)->randrsl[(r)->randcnt])
+
+#endif /* RAND */
+
+
diff --git a/src/isaac_rand/isaac_standard.h b/src/isaac_rand/isaac_standard.h
new file mode 100644
index 0000000..5323d3f
--- /dev/null
+++ b/src/isaac_rand/isaac_standard.h
@@ -0,0 +1,60 @@
+/*
+------------------------------------------------------------------------------
+Standard definitions and types, Bob Jenkins
+------------------------------------------------------------------------------
+*/
+#ifndef STANDARD
+# define STANDARD
+# ifndef STDIO
+# include <stdio.h>
+# define STDIO
+# endif
+# ifndef STDDEF
+# include <stddef.h>
+# define STDDEF
+# endif
+typedef unsigned long long ub8;
+#define UB8MAXVAL 0xffffffffffffffffLL
+#define UB8BITS 64
+typedef signed long long sb8;
+#define SB8MAXVAL 0x7fffffffffffffffLL
+typedef unsigned long int ub4; /* unsigned 4-byte quantities */
+#define UB4MAXVAL 0xffffffff
+typedef signed long int sb4;
+#define UB4BITS 32
+#define SB4MAXVAL 0x7fffffff
+typedef unsigned short int ub2;
+#define UB2MAXVAL 0xffff
+#define UB2BITS 16
+typedef signed short int sb2;
+#define SB2MAXVAL 0x7fff
+typedef unsigned char ub1;
+#define UB1MAXVAL 0xff
+#define UB1BITS 8
+typedef signed char sb1; /* signed 1-byte quantities */
+#define SB1MAXVAL 0x7f
+typedef int word; /* fastest type available */
+
+#define bis(target,mask) ((target) |= (mask))
+#define bic(target,mask) ((target) &= ~(mask))
+#define bit(target,mask) ((target) & (mask))
+#ifndef min
+# define min(a,b) (((a)<(b)) ? (a) : (b))
+#endif /* min */
+#ifndef max
+# define max(a,b) (((a)<(b)) ? (b) : (a))
+#endif /* max */
+#ifndef align
+# define align(a) (((ub4)a+(sizeof(void *)-1))&(~(sizeof(void *)-1)))
+#endif /* align */
+#ifndef abs
+# define abs(a) (((a)>0) ? (a) : -(a))
+#endif
+#define TRUE 1
+#define FALSE 0
+#define SUCCESS 0 /* 1 on VAX */
+
+#define RANDSIZL (8) /* I recommend 8 for crypto, 4 for simulations */
+#define RANDSIZ (1<<RANDSIZL)
+
+#endif /* STANDARD */
diff --git a/src/logging.c b/src/logging.c
new file mode 100644
index 0000000..ca103ef
--- /dev/null
+++ b/src/logging.c
@@ -0,0 +1,959 @@
+/*
+ * logging.c: Logging facilities for nwipe.
+ *
+ * Copyright Darik Horn <dajhorn-dban@vanadac.com>.
+ *
+ * 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, version 2.
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#ifndef _DEFAULT_SOURCE
+#define _DEFAULT_SOURCE
+#endif
+
+#ifndef _POSIX_SOURCE
+#define _POSIX_SOURCE
+#endif
+
+#include "stdio.h"
+#include "stdlib.h"
+#include "string.h"
+#include "stdarg.h"
+#include "nwipe.h"
+#include "context.h"
+#include "method.h"
+#include "prng.h"
+#include "options.h"
+#include "logging.h"
+#include "create_pdf.h"
+#include "miscellaneous.h"
+
+/* Global array to hold log values to print when logging to STDOUT */
+char** log_lines;
+int log_current_element = 0;
+int log_elements_allocated = 0;
+int log_elements_displayed = 0;
+pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;
+
+void nwipe_log( nwipe_log_t level, const char* format, ... )
+{
+ /**
+ * Writes a message to the program log file.
+ *
+ */
+
+ extern int terminate_signal;
+ extern int user_abort;
+
+ char** result;
+ char* malloc_result;
+ char message_buffer[MAX_LOG_LINE_CHARS * sizeof( char )];
+ int chars_written;
+
+ int message_buffer_length;
+ int r; /* result buffer */
+
+ /* A time buffer. */
+ time_t t;
+
+ /* A pointer to the system time struct. */
+ struct tm* p;
+ r = pthread_mutex_lock( &mutex1 );
+ if( r != 0 )
+ {
+ fprintf( stderr, "nwipe_log: pthread_mutex_lock failed. Code %i \n", r );
+ return;
+ }
+
+ /* Get the current time. */
+ t = time( NULL );
+ p = localtime( &t );
+
+ /* Position of writing to current log string */
+ int line_current_pos = 0;
+
+ /* initialise characters written */
+ chars_written = 0;
+
+ /* Only log messages with the debug label if the command line --verbose
+ * options has been specified, otherwise just return
+ */
+ if( level == NWIPE_LOG_DEBUG && nwipe_options.verbose == 0 )
+ {
+ r = pthread_mutex_unlock( &mutex1 );
+ if( r != 0 )
+ {
+ fprintf( stderr, "nwipe_log: pthread_mutex_unlock failed. Code %i \n", r );
+ }
+ return;
+ }
+
+ /* Print the date. The rc script uses the same format. */
+ if( level != NWIPE_LOG_NOTIMESTAMP )
+ {
+ chars_written = snprintf( message_buffer,
+ MAX_LOG_LINE_CHARS,
+ "[%i/%02i/%02i %02i:%02i:%02i] ",
+ 1900 + p->tm_year,
+ 1 + p->tm_mon,
+ p->tm_mday,
+ p->tm_hour,
+ p->tm_min,
+ p->tm_sec );
+ }
+
+ /*
+ * Has the end of the buffer been reached ?, snprintf returns the number of characters that would have been
+ * written if MAX_LOG_LINE_CHARS had not been reached, it does not return the actual characters written in
+ * all circumstances, hence why we need to check whether it's greater than MAX_LOG_LINE_CHARS and if so set
+ * it to MAX_LOG_LINE_CHARS, preventing a buffer overrun further down this function.
+ */
+
+ /* check if there was a complete failure to write this part of the message, in which case return */
+ if( chars_written < 0 )
+ {
+ fprintf( stderr, "nwipe_log: snprintf error when writing log line to memory.\n" );
+ r = pthread_mutex_unlock( &mutex1 );
+ if( r != 0 )
+ {
+ fprintf( stderr, "nwipe_log: pthread_mutex_unlock failed. Code %i \n", r );
+ return;
+ }
+ }
+ else
+ {
+ if( ( line_current_pos + chars_written ) > MAX_LOG_LINE_CHARS )
+ {
+ fprintf( stderr,
+ "nwipe_log: Warning! The log line has been truncated as it exceeded %i characters\n",
+ MAX_LOG_LINE_CHARS );
+ line_current_pos = MAX_LOG_LINE_CHARS;
+ }
+ else
+ {
+ line_current_pos += chars_written;
+ }
+ }
+
+ if( line_current_pos < MAX_LOG_LINE_CHARS )
+ {
+ switch( level )
+ {
+
+ case NWIPE_LOG_NONE:
+ case NWIPE_LOG_NOTIMESTAMP:
+ /* Do nothing. */
+ break;
+
+ /* NOTE! The debug labels, i.e. debug, info, notice etc should be left padded with spaces, in order
+ * to maintain column alignment. Pad a label to achieve the length of whatever the longest label happens
+ * to be. Important to know if you are thinking of adding another label.
+ */
+
+ case NWIPE_LOG_DEBUG:
+ chars_written =
+ snprintf( message_buffer + line_current_pos, MAX_LOG_LINE_CHARS - line_current_pos, " debug: " );
+ break;
+
+ case NWIPE_LOG_INFO:
+ chars_written =
+ snprintf( message_buffer + line_current_pos, MAX_LOG_LINE_CHARS - line_current_pos, " info: " );
+ break;
+
+ case NWIPE_LOG_NOTICE:
+ chars_written =
+ snprintf( message_buffer + line_current_pos, MAX_LOG_LINE_CHARS - line_current_pos, " notice: " );
+ break;
+
+ case NWIPE_LOG_WARNING:
+ chars_written =
+ snprintf( message_buffer + line_current_pos, MAX_LOG_LINE_CHARS - line_current_pos, "warning: " );
+ break;
+
+ case NWIPE_LOG_ERROR:
+ chars_written =
+ snprintf( message_buffer + line_current_pos, MAX_LOG_LINE_CHARS - line_current_pos, " error: " );
+ break;
+
+ case NWIPE_LOG_FATAL:
+ chars_written =
+ snprintf( message_buffer + line_current_pos, MAX_LOG_LINE_CHARS - line_current_pos, " fatal: " );
+ break;
+
+ case NWIPE_LOG_SANITY:
+ /* TODO: Request that the user report the log. */
+ chars_written =
+ snprintf( message_buffer + line_current_pos, MAX_LOG_LINE_CHARS - line_current_pos, " sanity: " );
+ break;
+
+ default:
+ chars_written = snprintf(
+ message_buffer + line_current_pos, MAX_LOG_LINE_CHARS - line_current_pos, "level %i: ", level );
+ }
+
+ /*
+ * Has the end of the buffer been reached ?
+ */
+ if( chars_written < 0 )
+ {
+ fprintf( stderr, "nwipe_log: snprintf error when writing log line to memory.\n" );
+ r = pthread_mutex_unlock( &mutex1 );
+ if( r != 0 )
+ {
+ fprintf( stderr, "nwipe_log: pthread_mutex_unlock failed. Code %i \n", r );
+ return;
+ }
+ }
+ else
+ {
+ if( ( line_current_pos + chars_written ) > MAX_LOG_LINE_CHARS )
+ {
+ fprintf( stderr,
+ "nwipe_log: Warning! The log line has been truncated as it exceeded %i characters\n",
+ MAX_LOG_LINE_CHARS );
+ line_current_pos = MAX_LOG_LINE_CHARS;
+ }
+ else
+ {
+ line_current_pos += chars_written;
+ }
+ }
+ }
+
+ /* The variable argument pointer. */
+ va_list ap;
+
+ /* Fetch the argument list. */
+ va_start( ap, format );
+
+ /* Print the event. */
+ if( line_current_pos < MAX_LOG_LINE_CHARS )
+ {
+ chars_written =
+ vsnprintf( message_buffer + line_current_pos, MAX_LOG_LINE_CHARS - line_current_pos - 1, format, ap );
+
+ if( chars_written < 0 )
+ {
+ fprintf( stderr, "nwipe_log: snprintf error when writing log line to memory.\n" );
+ r = pthread_mutex_unlock( &mutex1 );
+ if( r != 0 )
+ {
+ fprintf( stderr, "nwipe_log: pthread_mutex_unlock failed. Code %i \n", r );
+ va_end( ap );
+ return;
+ }
+ }
+ else
+ {
+ if( ( line_current_pos + chars_written ) > MAX_LOG_LINE_CHARS )
+ {
+ fprintf( stderr,
+ "nwipe_log: Warning! The log line has been truncated as it exceeded %i characters\n",
+ MAX_LOG_LINE_CHARS );
+ line_current_pos = MAX_LOG_LINE_CHARS;
+ }
+ else
+ {
+ line_current_pos += chars_written;
+ }
+ }
+ }
+
+ fflush( stdout );
+ /* Increase the current log element pointer - we will write here, deallocation is done in cleanup() in nwipe.c */
+ if( log_current_element == log_elements_allocated )
+ {
+ log_elements_allocated++;
+ result = realloc( log_lines, ( log_elements_allocated ) * sizeof( char* ) );
+ if( result == NULL )
+ {
+ fprintf( stderr, "nwipe_log: realloc failed when adding a log line.\n" );
+ r = pthread_mutex_unlock( &mutex1 );
+ if( r != 0 )
+ {
+ fprintf( stderr, "nwipe_log: pthread_mutex_unlock failed. Code %i \n", r );
+ va_end( ap );
+ return;
+ }
+ }
+ log_lines = result;
+
+ /* Allocate memory for storing a single log message, deallocation is done in cleanup() in nwipe.c */
+ message_buffer_length = strlen( message_buffer ) * sizeof( char );
+ malloc_result = malloc( ( message_buffer_length + 1 ) * sizeof( char ) );
+ if( malloc_result == NULL )
+ {
+ fprintf( stderr, "nwipe_log: malloc failed when adding a log line.\n" );
+ r = pthread_mutex_unlock( &mutex1 );
+ if( r != 0 )
+ {
+ fprintf( stderr, "nwipe_log: pthread_mutex_unlock failed. Code %i \n", r );
+ va_end( ap );
+ return;
+ }
+ }
+ log_lines[log_current_element] = malloc_result;
+ }
+
+ strcpy( log_lines[log_current_element], message_buffer );
+
+ /*
+ if( level >= NWIPE_LOG_WARNING )
+ {
+ vfprintf( stderr, format, ap );
+ }
+ */
+
+ /* Release the argument list. */
+ va_end( ap );
+
+ /*
+ if( level >= NWIPE_LOG_WARNING )
+ {
+ fprintf( stderr, "\n" );
+ }
+ */
+
+ /* The log file pointer. */
+ FILE* fp;
+
+ /* The log file descriptor. */
+ int fd;
+
+ if( nwipe_options.logfile[0] == '\0' )
+ {
+ if( nwipe_options.nogui )
+ {
+ printf( "%s\n", log_lines[log_current_element] );
+ log_elements_displayed++;
+ }
+ }
+ else
+ {
+ /* Open the log file for appending. */
+ fp = fopen( nwipe_options.logfile, "a" );
+
+ if( fp != NULL )
+ {
+
+ /* Get the file descriptor of the log file. */
+ fd = fileno( fp );
+
+ /* Block and lock. */
+ r = flock( fd, LOCK_EX );
+
+ if( r != 0 )
+ {
+ perror( "nwipe_log: flock:" );
+ fprintf( stderr, "nwipe_log: Unable to lock '%s' for logging.\n", nwipe_options.logfile );
+ r = pthread_mutex_unlock( &mutex1 );
+ if( r != 0 )
+ {
+ fprintf( stderr, "nwipe_log: pthread_mutex_unlock failed. Code %i \n", r );
+
+ /* Unlock the file. */
+ r = flock( fd, LOCK_UN );
+ fclose( fp );
+ return;
+ }
+ }
+
+ fprintf( fp, "%s\n", log_lines[log_current_element] );
+
+ /* Unlock the file. */
+ r = flock( fd, LOCK_UN );
+
+ if( r != 0 )
+ {
+ perror( "nwipe_log: flock:" );
+ fprintf( stderr, "Error: Unable to unlock '%s' after logging.\n", nwipe_options.logfile );
+ }
+
+ /* Close the stream. */
+ r = fclose( fp );
+
+ if( r != 0 )
+ {
+ perror( "nwipe_log: fclose:" );
+ fprintf( stderr, "Error: Unable to close '%s' after logging.\n", nwipe_options.logfile );
+ }
+ }
+ else
+ {
+ /* Tell user we can't create/open the log and terminate nwipe */
+ fprintf(
+ stderr, "\nERROR:Unable to create/open '%s' for logging, permissions?\n\n", nwipe_options.logfile );
+ r = pthread_mutex_unlock( &mutex1 );
+ if( r != 0 )
+ {
+ fprintf( stderr, "nwipe_log: pthread_mutex_unlock failed. Code %i \n", r );
+ }
+ user_abort = 1;
+ terminate_signal = 1;
+ }
+ }
+
+ log_current_element++;
+
+ r = pthread_mutex_unlock( &mutex1 );
+ if( r != 0 )
+ {
+ fprintf( stderr, "nwipe_log: pthread_mutex_unlock failed. Code %i \n", r );
+ }
+ return;
+
+} /* nwipe_log */
+
+void nwipe_perror( int nwipe_errno, const char* f, const char* s )
+{
+ /**
+ * Wrapper for perror().
+ */
+
+ nwipe_log( NWIPE_LOG_ERROR, "%s: %s: %s", f, s, strerror( nwipe_errno ) );
+
+} /* nwipe_perror */
+
+void nwipe_log_OSinfo()
+{
+ /* Read /proc/version, format and write to the log */
+
+ FILE* fp = NULL;
+ char OS_info_temp[MAX_SIZE_OS_STRING + 1];
+ char OS_info[MAX_SIZE_OS_STRING + 1];
+ int idx;
+ int idx2;
+ int idx3;
+ int idx4;
+
+ /* initialise OS_info & OS_info_temp strings */
+ idx = 0;
+ while( idx < MAX_SIZE_OS_STRING + 1 )
+ {
+ OS_info_temp[idx] = 0;
+ OS_info[idx] = 0;
+ idx++;
+ }
+
+ /* Open a pipe to /proc/version for reading */
+ fp = popen( "cat /proc/version", "r" );
+ if( fp == NULL )
+ {
+ nwipe_log( NWIPE_LOG_WARNING, "Unable to create a pipe to /proc/version" );
+ return;
+ }
+
+ /* Read the OS info */
+ if( fgets( OS_info_temp, MAX_SIZE_OS_STRING, fp ) == NULL )
+ {
+ nwipe_log( NWIPE_LOG_WARNING, "fget failed to read /proc/version" );
+ fclose( fp );
+ return;
+ }
+
+ /* Format the string for the log, place on multiple lines as necessary,
+ * column aligned, left offset with n (OS_info_Line_offset) spaces */
+ idx = 0;
+ idx2 = 0;
+ idx3 = OS_info_Line_Length;
+
+ while( OS_info_temp[idx] != 0 )
+ {
+ while( idx2 < idx3 && idx2 < MAX_SIZE_OS_STRING )
+ {
+ /* remove newlines from the source */
+ if( OS_info_temp[idx] == 0x0a )
+ {
+ idx++;
+ }
+
+ /* copy the character */
+ OS_info[idx2++] = OS_info_temp[idx++];
+ }
+ if( OS_info_temp[idx] != 0 )
+ {
+ OS_info[idx2++] = 0x0a;
+ idx4 = 0;
+
+ /* left indent with spaces */
+ while( idx4 < OS_info_Line_offset && idx2 < MAX_SIZE_OS_STRING )
+ {
+ OS_info[idx2++] = ' ';
+ idx4++;
+ }
+
+ /* calculate idx3 ready for next line */
+ idx3 += OS_info_Line_offset + OS_info_Line_Length;
+ }
+ else
+ {
+ continue;
+ }
+ }
+
+ nwipe_log( NWIPE_LOG_INFO, "%s", OS_info );
+ fclose( fp );
+ return;
+}
+
+int nwipe_log_sysinfo()
+{
+ FILE* fp;
+ char path[256];
+ int len;
+ int r; // A result buffer.
+
+ /*
+ * Remove or add keywords to be searched, depending on what information is to
+ * be logged, making sure the last entry in the array is a NULL string. To remove
+ * an entry simply comment out the keyword with //
+ */
+
+ /* The 0/1 after the keyword determines whether the data for this
+ * keyword is displayed when -q (anonymize) has been specified
+ * by the user. An quick reminder about multi dimensional arrays, the first
+ * []=the keyword (0-21) including the empty string. The second [] is the
+ * 1 or 0 value (0 or 1). The third [] is the index value into either string.
+ */
+ char dmidecode_keywords[][2][24] = {
+ { "bios-version", "1" },
+ { "bios-release-date", "1" },
+ { "system-manufacturer", "1" },
+ { "system-product-name", "1" },
+ { "system-version", "1" },
+ { "system-serial-number", "0" },
+ { "system-uuid", "0" },
+ { "baseboard-manufacturer", "1" },
+ { "baseboard-product-name", "1" },
+ { "baseboard-version", "1" },
+ { "baseboard-serial-number", "0" },
+ { "baseboard-asset-tag", "0" },
+ { "chassis-manufacturer", "1" },
+ { "chassis-type", "1" },
+ { "chassis-version", "1" },
+ { "chassis-serial-number", "0" },
+ { "chassis-asset-tag", "0" },
+ { "processor-family", "1" },
+ { "processor-manufacturer", "1" },
+ { "processor-version", "1" },
+ { "processor-frequency", "1" },
+ { "", "" } // terminates the keyword array. DO NOT REMOVE
+ };
+
+ char dmidecode_command[] = "dmidecode -s %s";
+ char dmidecode_command2[] = "/sbin/dmidecode -s %s";
+ char dmidecode_command3[] = "/usr/bin/dmidecode -s %s";
+ char* p_dmidecode_command;
+
+ char cmd[sizeof( dmidecode_keywords ) + sizeof( dmidecode_command2 )];
+
+ unsigned int keywords_idx;
+
+ keywords_idx = 0;
+
+ p_dmidecode_command = 0;
+
+ if( system( "which dmidecode > /dev/null 2>&1" ) )
+ {
+ if( system( "which /sbin/dmidecode > /dev/null 2>&1" ) )
+ {
+ if( system( "which /usr/bin/dmidecode > /dev/null 2>&1" ) )
+ {
+ nwipe_log( NWIPE_LOG_WARNING, "Command not found. Install dmidecode !" );
+ }
+ else
+ {
+ p_dmidecode_command = &dmidecode_command3[0];
+ }
+ }
+ else
+ {
+ p_dmidecode_command = &dmidecode_command2[0];
+ }
+ }
+ else
+ {
+ p_dmidecode_command = &dmidecode_command[0];
+ }
+
+ if( p_dmidecode_command != 0 )
+ {
+
+ /* Run the dmidecode command to retrieve each dmidecode keyword, one at a time */
+ while( dmidecode_keywords[keywords_idx][0][0] != 0 )
+ {
+ sprintf( cmd, p_dmidecode_command, &dmidecode_keywords[keywords_idx][0][0] );
+ fp = popen( cmd, "r" );
+ if( fp == NULL )
+ {
+ nwipe_log( NWIPE_LOG_WARNING, "nwipe_log_sysinfo: Failed to create stream to %s", cmd );
+ return 1;
+ }
+ /* Read the output a line at a time - output it. */
+ while( fgets( path, sizeof( path ) - 1, fp ) != NULL )
+ {
+ /* Remove any trailing return from the string, as nwipe_log automatically adds a return */
+ len = strlen( path );
+ if( path[len - 1] == '\n' )
+ {
+ path[len - 1] = 0;
+ }
+ if( nwipe_options.quiet )
+ {
+ if( *( &dmidecode_keywords[keywords_idx][1][0] ) == '0' )
+ {
+ nwipe_log(
+ NWIPE_LOG_INFO, "%s = %s", &dmidecode_keywords[keywords_idx][0][0], "XXXXXXXXXXXXXXX" );
+ }
+ else
+ {
+ nwipe_log( NWIPE_LOG_INFO, "%s = %s", &dmidecode_keywords[keywords_idx][0][0], path );
+ }
+ }
+ else
+ {
+ nwipe_log( NWIPE_LOG_INFO, "%s = %s", &dmidecode_keywords[keywords_idx][0][0], path );
+ }
+ }
+ /* close */
+ r = pclose( fp );
+ if( r > 0 )
+ {
+ nwipe_log( NWIPE_LOG_WARNING,
+ "nwipe_log_sysinfo(): dmidecode failed, \"%s\" exit status = %u",
+ cmd,
+ WEXITSTATUS( r ) );
+ return 1;
+ }
+ keywords_idx++;
+ }
+ }
+ return 0;
+}
+
+void nwipe_log_summary( nwipe_context_t** ptr, int nwipe_selected )
+{
+ /* Prints two summary tables, the first is the device pass and verification summary
+ * and the second is the main summary table detaining the drives, status, throughput,
+ * model and serial number.
+ *
+ * This function also calls the create_pdf() function that creates the PDF erasure
+ * report file. A page report on the success or failure of the erasure operation
+ */
+
+ int i;
+ int idx_src;
+ int idx_dest;
+ char device[18];
+ char status[9];
+ char throughput[13];
+ char total_throughput_string[13];
+ char summary_top_border[256];
+ char summary_top_column_titles[256];
+ char blank[3];
+ char verify[3];
+ // char duration[5];
+ char duration[314];
+ char model[18];
+ char serial_no[NWIPE_SERIALNUMBER_LENGTH + 1];
+ char exclamation_flag[2];
+ int hours;
+ int minutes;
+ int seconds;
+ u64 total_duration_seconds;
+ u64 total_throughput;
+ nwipe_context_t** c;
+ c = ptr;
+
+ exclamation_flag[0] = 0;
+ device[0] = 0;
+ status[0] = 0;
+ throughput[0] = 0;
+ summary_top_border[0] = 0;
+ summary_top_column_titles[0] = 0;
+ blank[0] = 0;
+ verify[0] = 0;
+ duration[0] = 0;
+ model[0] = 0;
+ serial_no[0] = 0;
+ hours = 0;
+ minutes = 0;
+ seconds = 0;
+
+ /* A time buffer. */
+ time_t t;
+
+ /* A pointer to the system time struct. */
+ struct tm* p;
+
+ /* Nothing to do, user never started a wipe so no summary table required. */
+ if( global_wipe_status == 0 )
+ {
+ return;
+ }
+
+ /* Print the pass and verifications table */
+
+ /* IMPORTANT: Keep maximum columns (line length) to 80 characters for use with 80x30 terminals, Shredos, ALT-F2 etc
+ * --------------------------------01234567890123456789012345678901234567890123456789012345678901234567890123456789-*/
+ nwipe_log( NWIPE_LOG_NOTIMESTAMP, "" );
+ nwipe_log( NWIPE_LOG_NOTIMESTAMP,
+ "******************************** Error Summary *********************************" );
+ nwipe_log( NWIPE_LOG_NOTIMESTAMP, "! Device | Pass Errors | Verifications Errors | Fdatasync I\\O Errors" );
+ nwipe_log( NWIPE_LOG_NOTIMESTAMP,
+ "--------------------------------------------------------------------------------" );
+
+ for( i = 0; i < nwipe_selected; i++ )
+ {
+ if( c[i]->pass_errors != 0 || c[i]->verify_errors != 0 || c[i]->fsyncdata_errors != 0 )
+ {
+ strncpy( exclamation_flag, "!", 1 );
+ exclamation_flag[1] = 0;
+ }
+ else
+ {
+ strncpy( exclamation_flag, " ", 1 );
+ exclamation_flag[1] = 0;
+ }
+
+ /* Device name, strip any prefixed /dev/.. leaving up to 6 right justified
+ * characters eg " sda", prefixed with space to 6 characters, note that
+ * we are processing the strings right to left */
+
+ idx_dest = 6;
+ device[idx_dest--] = 0;
+ idx_src = strlen( c[i]->device_name );
+ idx_src--;
+
+ nwipe_strip_path( device, c[i]->device_name );
+
+ nwipe_log( NWIPE_LOG_NOTIMESTAMP,
+ "%s %s | %10llu | %10llu | %10llu",
+ exclamation_flag,
+ device,
+ c[i]->pass_errors,
+ c[i]->verify_errors,
+ c[i]->fsyncdata_errors );
+ }
+
+ nwipe_log( NWIPE_LOG_NOTIMESTAMP,
+ "********************************************************************************" );
+
+ /* Print the main summary table */
+
+ /* initialise */
+ total_throughput = 0;
+
+ /* Get the current time. */
+ t = time( NULL );
+ p = localtime( &t );
+ /* IMPORTANT: Keep maximum columns (line length) to 80 characters for use with 80x30 terminals, Shredos, ALT-F2 etc
+ * --------------------------------01234567890123456789012345678901234567890123456789012345678901234567890123456789-*/
+ nwipe_log( NWIPE_LOG_NOTIMESTAMP, "" );
+ nwipe_log( NWIPE_LOG_NOTIMESTAMP,
+ "********************************* Drive Status *********************************" );
+ nwipe_log( NWIPE_LOG_NOTIMESTAMP, "! Device | Status | Thru-put | HH:MM:SS | Model/Serial Number" );
+ nwipe_log( NWIPE_LOG_NOTIMESTAMP,
+ "--------------------------------------------------------------------------------" );
+ /* Example layout:
+ * "! sdb |--FAIL--| 120MB/s | 01:22:01 | WD6788.8488YNHj/ZX677888388-N "
+ * ); " sdc | Erased | 120MB/s | 01:25:04 | WD6784.8488JKGG/ZX677888388-N " ); " sdv | Erased |
+ * 120MB/s | 01:19:07 | WD6788.848HHDDR/ZX677888388-N " ); End of Example layout */
+
+ for( i = 0; i < nwipe_selected; i++ )
+ {
+ /* Device name, strip any prefixed /dev/.. leaving up to 8 right justified
+ * characters eg " sda", prefixed with space to 8 characters, note that
+ * we are processing the strings right to left */
+
+ nwipe_strip_path( device, c[i]->device_name );
+
+ extern int user_abort;
+
+ /* Any errors ? if so set the exclamation_flag and fail message,
+ * All status messages should be eight characters EXACTLY !
+ */
+ if( c[i]->pass_errors != 0 || c[i]->verify_errors != 0 || c[i]->fsyncdata_errors != 0 )
+ {
+ strncpy( exclamation_flag, "!", 1 );
+ exclamation_flag[1] = 0;
+
+ strncpy( status, "-FAILED-", 8 );
+ status[8] = 0;
+
+ strcpy( c[i]->wipe_status_txt, "FAILED" ); // copy to context for use by certificate
+ }
+ else
+ {
+ if( c[i]->wipe_status == 0 /* && user_abort != 1 */ )
+ {
+ strncpy( exclamation_flag, " ", 1 );
+ exclamation_flag[1] = 0;
+
+ strncpy( status, " Erased ", 8 );
+ status[8] = 0;
+
+ strcpy( c[i]->wipe_status_txt, "ERASED" ); // copy to context for use by certificate
+ }
+ else
+ {
+ if( c[i]->wipe_status == 1 && user_abort == 1 )
+ {
+ strncpy( exclamation_flag, "!", 1 );
+ exclamation_flag[1] = 0;
+
+ strncpy( status, "UABORTED", 8 );
+ status[8] = 0;
+
+ strcpy( c[i]->wipe_status_txt, "ABORTED" ); // copy to context for use by certificate
+ }
+ else
+ {
+ /* If this ever happens, there is a bug ! */
+ strncpy( exclamation_flag, " ", 1 );
+ exclamation_flag[1] = 0;
+
+ strncpy( status, "INSANITY", 8 );
+ status[8] = 0;
+
+ strcpy( c[i]->wipe_status_txt, "INSANITY" ); // copy to context for use by certificate
+ }
+ }
+ }
+
+ /* Determine the size of throughput so that the correct nomenclature can be used */
+ Determine_C_B_nomenclature( c[i]->throughput, throughput, 13 );
+
+ /* write the duration string to the drive context for later use by create_pdf() */
+ snprintf( c[i]->throughput_txt, sizeof( c[i]->throughput_txt ), "%s", throughput );
+
+ /* Add this devices throughput to the total throughput */
+ total_throughput += c[i]->throughput;
+
+ /* Retrieve the duration of the wipe in seconds and convert to hours and minutes and seconds */
+
+ if( c[i]->start_time != 0 && c[i]->end_time != 0 )
+ {
+ /* For a summary when the wipe has finished */
+ c[i]->duration = difftime( c[i]->end_time, c[i]->start_time );
+ }
+ else
+ {
+ if( c[i]->start_time != 0 && c[i]->end_time == 0 )
+ {
+ /* For a summary in the event of a system shutdown, user abort */
+ c[i]->duration = difftime( t, c[i]->start_time );
+
+ /* If end_time is zero, which may occur if the wipe is aborted, then set
+ * end_time to current time. Important to do as endtime is used by
+ * the PDF report function */
+ c[i]->end_time = time( &t );
+ }
+ }
+
+ total_duration_seconds = (u64) c[i]->duration;
+
+ /* Convert binary seconds into three binary variables, hours, minutes and seconds */
+ convert_seconds_to_hours_minutes_seconds( total_duration_seconds, &hours, &minutes, &seconds );
+
+ /* write the duration string to the drive context for later use by create_pdf() */
+ snprintf( c[i]->duration_str, sizeof( c[i]->duration_str ), "%02i:%02i:%02i", hours, minutes, seconds );
+
+ /* Device Model */
+ strncpy( model, c[i]->device_model, 17 );
+ model[17] = 0;
+
+ /* Serial No. */
+ strncpy( serial_no, c[i]->device_serial_no, NWIPE_SERIALNUMBER_LENGTH );
+ serial_no[NWIPE_SERIALNUMBER_LENGTH] = 0;
+ model[17] = 0;
+
+ nwipe_log( NWIPE_LOG_NOTIMESTAMP,
+ "%s %s |%s| %s/s | %02i:%02i:%02i | %s/%s",
+ exclamation_flag,
+ device,
+ status,
+ throughput,
+ hours,
+ minutes,
+ seconds,
+ model,
+ serial_no );
+
+ /* Create the PDF report/certificate */
+ if( nwipe_options.PDF_enable == 1 )
+ // if( strcmp( nwipe_options.PDFreportpath, "noPDF" ) != 0 )
+ {
+ /* to have some progress indication. can help if there are many/slow disks */
+ fprintf( stderr, "." );
+ create_pdf( c[i] );
+ }
+ }
+
+ /* Determine the size of throughput so that the correct nomenclature can be used */
+ Determine_C_B_nomenclature( total_throughput, total_throughput_string, 13 );
+
+ /* Blank abbreviations used in summary table B=blank, NB=no blank */
+ if( nwipe_options.noblank )
+ {
+ strcpy( blank, "NB" );
+ }
+ else
+ {
+ strcpy( blank, "B" );
+ }
+
+ /* Verify abbreviations used in summary table */
+ switch( nwipe_options.verify )
+ {
+ case NWIPE_VERIFY_NONE:
+ strcpy( verify, "NV" );
+ break;
+
+ case NWIPE_VERIFY_LAST:
+ strcpy( verify, "VL" );
+ break;
+
+ case NWIPE_VERIFY_ALL:
+ strcpy( verify, "VA" );
+ break;
+ }
+
+ nwipe_log( NWIPE_LOG_NOTIMESTAMP,
+ "--------------------------------------------------------------------------------" );
+ nwipe_log( NWIPE_LOG_NOTIMESTAMP,
+ "[%i/%02i/%02i %02i:%02i:%02i] Total Throughput %s/s, %s, %iR+%s+%s",
+ 1900 + p->tm_year,
+ 1 + p->tm_mon,
+ p->tm_mday,
+ p->tm_hour,
+ p->tm_min,
+ p->tm_sec,
+ total_throughput_string,
+ nwipe_method_label( nwipe_options.method ),
+ nwipe_options.rounds,
+ blank,
+ verify );
+ nwipe_log( NWIPE_LOG_NOTIMESTAMP,
+ "********************************************************************************" );
+ nwipe_log( NWIPE_LOG_NOTIMESTAMP, "" );
+
+ /* Log information regarding where the PDF certificate is saved but log after the summary table so
+ * this information is only printed once.
+ */
+ if( strcmp( nwipe_options.PDFreportpath, "noPDF" ) != 0 )
+ {
+ nwipe_log( NWIPE_LOG_NOTIMESTAMP, "Creating PDF report in %s\n", nwipe_options.PDFreportpath );
+ }
+}
diff --git a/src/logging.h b/src/logging.h
new file mode 100644
index 0000000..a8e3432
--- /dev/null
+++ b/src/logging.h
@@ -0,0 +1,65 @@
+/*
+ * logging.c: Logging facilities for nwipe.
+ *
+ * Copyright Darik Horn <dajhorn-dban@vanadac.com>.
+ *
+ * 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, version 2.
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#ifndef LOGGING_H_
+#define LOGGING_H_
+
+/* Maximum size of a log message */
+#define MAX_LOG_LINE_CHARS 1024
+
+#define MAX_SIZE_OS_STRING 1024 /* Maximum size of acceptable OS string */
+
+#define OS_info_Line_offset 31 /* OS_info line offset in log */
+#define OS_info_Line_Length 48 /* OS_info line length */
+
+typedef enum nwipe_log_t_ {
+ NWIPE_LOG_NONE = 0,
+ NWIPE_LOG_DEBUG, // Output only when --verbose option used on cmd line.
+ NWIPE_LOG_INFO, // General Info not specifically relevant to the wipe.
+ NWIPE_LOG_NOTICE, // Most logging happens at this level related to wiping.
+ NWIPE_LOG_WARNING, // Things that the user should know about.
+ NWIPE_LOG_ERROR, // Non-fatal errors that result in failure.
+ NWIPE_LOG_FATAL, // Errors that cause the program to exit.
+ NWIPE_LOG_SANITY, // Programming errors.
+ NWIPE_LOG_NOTIMESTAMP // logs the message without the timestamp
+} nwipe_log_t;
+
+/**
+ * Writes a string to the log. nwipe_log timestamps the string
+ * @param level the tag to display:
+ * NWIPE_LOG_NONE Don't display a tag
+ * NWIPE_LOG_DEBUG, Very verbose logging.
+ * NWIPE_LOG_INFO, Verbose logging.
+ * NWIPE_LOG_NOTICE, Most logging happens at this level.
+ * NWIPE_LOG_WARNING, Things that the user should know about.
+ * NWIPE_LOG_ERROR, Non-fatal errors that result in failure.
+ * NWIPE_LOG_FATAL, Errors that cause the program to exit.
+ * NWIPE_LOG_SANITY, Programming errors.
+ * NWIPE_LOG_NOTIMESTAMP logs the message without the timestamp
+ * @param format the string to be logged
+ */
+void nwipe_log( nwipe_log_t level, const char* format, ... );
+
+void nwipe_perror( int nwipe_errno, const char* f, const char* s );
+void nwipe_log_OSinfo();
+int nwipe_log_sysinfo();
+void nwipe_log_summary( nwipe_context_t**, int ); // This produces the wipe status table on exit
+
+#endif /* LOGGING_H_ */
diff --git a/src/method.c b/src/method.c
new file mode 100644
index 0000000..cbc4e23
--- /dev/null
+++ b/src/method.c
@@ -0,0 +1,1392 @@
+/*
+ * method.c: Method implementations for nwipe.
+ *
+ * Copyright Darik Horn <dajhorn-dban@vanadac.com>.
+ *
+ * Modifications to original dwipe Copyright Andy Beverley <andy@andybev.com>
+ *
+ * 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, version 2.
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/* HOWTO: Add another wipe method.
+ *
+ * 1. Create a new function here and add the prototype to the 'method.h' file.
+ * 2. Update nwipe_method_label() appropriately.
+ * 3. Put the passes that you wish to run into a nwipe_pattern_t array.
+ * 4. Call nwipe_runmethod() with your array of patterns.
+ * 5. Copy-and-paste within the 'options.c' file so that the new method can be invoked.
+ * 6. Optionally try to plug your function into 'gui.c'.
+ * 7. Update the function 'calculate_round_size()' with the new method.
+ *
+ *
+ * WARNING: Remember to pad all pattern arrays with { 0, NULL }.
+ *
+ * WARNING: Never change nwipe_options after calling a method.
+ *
+ * NOTE: The nwipe_runmethod function appends a user selectable final blanking (zero) pass to all methods.
+ *
+ */
+
+#include <stdint.h>
+
+#include "nwipe.h"
+#include "context.h"
+#include "method.h"
+#include "prng.h"
+#include "options.h"
+#include "pass.h"
+#include "logging.h"
+
+/*
+ * Comment Legend
+ *
+ * "method" An ordered set of patterns.
+ * "pattern" The magic bits that will be written to a device.
+ * "pass" Reading or writing one pattern to an entire device.
+ * "rounds" The number of times that a method will be applied to a device.
+ *
+ */
+
+const char* nwipe_dod522022m_label = "DoD 5220.22-M";
+const char* nwipe_dodshort_label = "DoD Short";
+const char* nwipe_gutmann_label = "Gutmann Wipe";
+const char* nwipe_ops2_label = "RCMP TSSIT OPS-II";
+const char* nwipe_random_label = "PRNG Stream";
+const char* nwipe_zero_label = "Fill With Zeros";
+const char* nwipe_one_label = "Fill With Ones";
+const char* nwipe_verify_zero_label = "Verify Zeros (0x00)";
+const char* nwipe_verify_one_label = "Verify Ones (0xFF)";
+const char* nwipe_is5enh_label = "HMG IS5 Enhanced";
+
+const char* nwipe_unknown_label = "Unknown Method (FIXME)";
+
+const char* nwipe_method_label( void* method )
+{
+ /**
+ * Returns a pointer to the name of the method function.
+ *
+ */
+
+ if( method == &nwipe_dod522022m )
+ {
+ return nwipe_dod522022m_label;
+ }
+ if( method == &nwipe_dodshort )
+ {
+ return nwipe_dodshort_label;
+ }
+ if( method == &nwipe_gutmann )
+ {
+ return nwipe_gutmann_label;
+ }
+ if( method == &nwipe_ops2 )
+ {
+ return nwipe_ops2_label;
+ }
+ if( method == &nwipe_random )
+ {
+ return nwipe_random_label;
+ }
+ if( method == &nwipe_zero )
+ {
+ return nwipe_zero_label;
+ }
+ if( method == &nwipe_one )
+ {
+ return nwipe_one_label;
+ }
+ if( method == &nwipe_verify_zero )
+ {
+ return nwipe_verify_zero_label;
+ }
+ if( method == &nwipe_verify_one )
+ {
+ return nwipe_verify_one_label;
+ }
+ if( method == &nwipe_is5enh )
+ {
+ return nwipe_is5enh_label;
+ }
+
+ /* else */
+ return nwipe_unknown_label;
+
+} /* nwipe_method_label */
+
+void* nwipe_zero( void* ptr )
+{
+ /**
+ * Fill the device with zeroes.
+ */
+
+ nwipe_context_t* c;
+ c = (nwipe_context_t*) ptr;
+
+ /* get current time at the start of the wipe */
+ time( &c->start_time );
+
+ /* set wipe in progress flag for GUI */
+ c->wipe_status = 1;
+
+ /* setup for a zero-fill. */
+
+ char zerofill[1] = { '\x00' };
+ nwipe_pattern_t patterns[] = { { 1, &zerofill[0] }, // pass 1: 0s
+ { 0, NULL } };
+
+ /* Run the method. */
+ c->result = nwipe_runmethod( c, patterns );
+
+ /* Finished. Set the wipe_status flag so that the GUI knows */
+ c->wipe_status = 0;
+
+ /* get current time at the end of the wipe */
+ time( &c->end_time );
+
+ return NULL;
+} /* nwipe_zero */
+
+void* nwipe_one( void* ptr )
+{
+ /**
+ * Fill the device with ones.
+ */
+
+ nwipe_context_t* c;
+ c = (nwipe_context_t*) ptr;
+
+ /* get current time at the start of the wipe */
+ time( &c->start_time );
+
+ /* set wipe in progress flag for GUI */
+ c->wipe_status = 1;
+
+ /* setup for a one-fill. */
+
+ char onefill[1] = { '\xFF' };
+ nwipe_pattern_t patterns[] = { { 1, &onefill[0] }, // pass 1: 1s
+ { 0, NULL } };
+
+ /* Run the method. */
+ c->result = nwipe_runmethod( c, patterns );
+
+ /* Finished. Set the wipe_status flag so that the GUI knows */
+ c->wipe_status = 0;
+
+ /* get current time at the end of the wipe */
+ time( &c->end_time );
+
+ return NULL;
+} /* nwipe_one */
+
+void* nwipe_verify_zero( void* ptr )
+{
+ /**
+ * Verify the device is full of zeros.
+ */
+
+ nwipe_context_t* c;
+ c = (nwipe_context_t*) ptr;
+
+ /* get current time at the start of the wipe */
+ time( &c->start_time );
+
+ /* set wipe in progress flag for GUI */
+ c->wipe_status = 1;
+
+ /* Do nothing because nwipe_runmethod appends a zero-fill. */
+ nwipe_pattern_t patterns[] = { { 0, NULL } };
+
+ /* Run the method. */
+ c->result = nwipe_runmethod( c, patterns );
+
+ /* Finished. Set the wipe_status flag so that the GUI knows */
+ c->wipe_status = 0;
+
+ /* get current time at the end of the wipe */
+ time( &c->end_time );
+
+ return NULL;
+} /* nwipe_verify zeros */
+
+void* nwipe_verify_one( void* ptr )
+{
+ /**
+ * Verify the device is full of ones.
+ */
+
+ nwipe_context_t* c;
+ c = (nwipe_context_t*) ptr;
+
+ /* get current time at the start of the wipe */
+ time( &c->start_time );
+
+ /* set wipe in progress flag for GUI */
+ c->wipe_status = 1;
+
+ /* Do nothing because nwipe_runmethod appends a zero-fill. */
+ nwipe_pattern_t patterns[] = { { 0, NULL } };
+
+ /* Run the method. */
+ c->result = nwipe_runmethod( c, patterns );
+
+ /* Finished. Set the wipe_status flag so that the GUI knows */
+ c->wipe_status = 0;
+
+ /* get current time at the end of the wipe */
+ time( &c->end_time );
+
+ return NULL;
+} /* nwipe_verify */
+
+void* nwipe_dod522022m( void* ptr )
+{
+ /**
+ * United States Department of Defense 5220.22-M standard wipe.
+ *
+ */
+
+ nwipe_context_t* c;
+ c = (nwipe_context_t*) ptr;
+
+ /* get current time at the start of the wipe */
+ time( &c->start_time );
+
+ /* set wipe in progress flag for GUI */
+ c->wipe_status = 1;
+
+ /* A result holder. */
+ int r;
+
+ /* Random characters. (Elements 2 and 6 are unused.) */
+ char dod[7];
+
+ nwipe_pattern_t patterns[] = { { 1, &dod[0] }, // Pass 1: A random character.
+ { 1, &dod[1] }, // Pass 2: The bitwise complement of pass 1.
+ { -1, "" }, // Pass 3: A random stream.
+ { 1, &dod[3] }, // Pass 4: A random character.
+ { 1, &dod[4] }, // Pass 5: A random character.
+ { 1, &dod[5] }, // Pass 6: The bitwise complement of pass 5.
+ { -1, "" }, // Pass 7: A random stream.
+ { 0, NULL } };
+
+ /* Load the array with random characters. */
+ r = read( c->entropy_fd, &dod, sizeof( dod ) );
+
+ /* NOTE: Only the random data in dod[0], dod[3], and dod[4] is actually used. */
+
+ /* Check the result. */
+ if( r != sizeof( dod ) )
+ {
+ r = errno;
+ nwipe_perror( r, __FUNCTION__, "read" );
+ nwipe_log( NWIPE_LOG_FATAL, "Unable to seed the %s method.", nwipe_dod522022m_label );
+
+ /* Ensure a negative return. */
+ if( r < 0 )
+ {
+ c->result = r;
+ return NULL;
+ }
+ else
+ {
+ c->result = -1;
+ return NULL;
+ }
+ }
+
+ /* Pass 2 is the bitwise complement of Pass 1. */
+ dod[1] = ~dod[0];
+
+ /* Pass 4 is the bitwise complement of Pass 3. */
+ dod[5] = ~dod[4];
+
+ /* Run the DoD 5220.22-M method. */
+ c->result = nwipe_runmethod( c, patterns );
+
+ /* Finished. Set the wipe_status flag so that the GUI knows */
+ c->wipe_status = 0;
+
+ /* get current time at the end of the wipe */
+ time( &c->end_time );
+
+ return NULL;
+} /* nwipe_dod522022m */
+
+void* nwipe_dodshort( void* ptr )
+{
+ /**
+ * United States Department of Defense 5220.22-M short wipe.
+ * This method is comprised of passes 1,2,7 from the standard wipe.
+ *
+ */
+
+ nwipe_context_t* c;
+ c = (nwipe_context_t*) ptr;
+
+ /* get current time at the start of the wipe */
+ time( &c->start_time );
+
+ /* set wipe in progress flag for GUI */
+ c->wipe_status = 1;
+
+ /* A result holder. */
+ int r;
+
+ /* Random characters. (Element 3 is unused.) */
+ char dod[3];
+
+ nwipe_pattern_t patterns[] = { { 1, &dod[0] }, // Pass 1: A random character.
+ { 1, &dod[1] }, // Pass 2: The bitwise complement of pass 1.
+ { -1, "" }, // Pass 3: A random stream.
+ { 0, NULL } };
+
+ /* Load the array with random characters. */
+ r = read( c->entropy_fd, &dod, sizeof( dod ) );
+
+ /* NOTE: Only the random data in dod[0] is actually used. */
+
+ /* Check the result. */
+ if( r != sizeof( dod ) )
+ {
+ r = errno;
+ nwipe_perror( r, __FUNCTION__, "read" );
+ nwipe_log( NWIPE_LOG_FATAL, "Unable to seed the %s method.", nwipe_dodshort_label );
+
+ /* Ensure a negative return. */
+ if( r < 0 )
+ {
+ c->result = r;
+ return NULL;
+ }
+ else
+ {
+ c->result = -1;
+ return NULL;
+ }
+ }
+
+ /* Pass 2 is the bitwise complement of Pass 1. */
+ dod[1] = ~dod[0];
+
+ /* Run the DoD 5220.022-M short method. */
+ c->result = nwipe_runmethod( c, patterns );
+
+ /* Finished. Set the wipe_status flag so that the GUI knows */
+ c->wipe_status = 0;
+
+ /* get current time at the end of the wipe */
+ time( &c->end_time );
+
+ return NULL;
+} /* nwipe_dodshort */
+
+void* nwipe_gutmann( void* ptr )
+{
+ /**
+ * Peter Gutmann's wipe.
+ *
+ */
+
+ nwipe_context_t* c;
+ c = (nwipe_context_t*) ptr;
+
+ /* get current time at the start of the wipe */
+ time( &c->start_time );
+
+ /* set wipe in progress flag for GUI */
+ c->wipe_status = 1;
+
+ /* Define the Gutmann method. */
+ nwipe_pattern_t book[] = { { -1, "" }, // Random pass.
+ { -1, "" }, // Random pass.
+ { -1, "" }, // Random pass.
+ { -1, "" }, // Random pass.
+ { 3, "\x55\x55\x55" }, // Static pass: 0x555555 01010101 01010101 01010101
+ { 3, "\xAA\xAA\xAA" }, // Static pass: 0XAAAAAA 10101010 10101010 10101010
+ { 3, "\x92\x49\x24" }, // Static pass: 0x924924 10010010 01001001 00100100
+ { 3, "\x49\x24\x92" }, // Static pass: 0x492492 01001001 00100100 10010010
+ { 3, "\x24\x92\x49" }, // Static pass: 0x249249 00100100 10010010 01001001
+ { 3, "\x00\x00\x00" }, // Static pass: 0x000000 00000000 00000000 00000000
+ { 3, "\x11\x11\x11" }, // Static pass: 0x111111 00010001 00010001 00010001
+ { 3, "\x22\x22\x22" }, // Static pass: 0x222222 00100010 00100010 00100010
+ { 3, "\x33\x33\x33" }, // Static pass: 0x333333 00110011 00110011 00110011
+ { 3, "\x44\x44\x44" }, // Static pass: 0x444444 01000100 01000100 01000100
+ { 3, "\x55\x55\x55" }, // Static pass: 0x555555 01010101 01010101 01010101
+ { 3, "\x66\x66\x66" }, // Static pass: 0x666666 01100110 01100110 01100110
+ { 3, "\x77\x77\x77" }, // Static pass: 0x777777 01110111 01110111 01110111
+ { 3, "\x88\x88\x88" }, // Static pass: 0x888888 10001000 10001000 10001000
+ { 3, "\x99\x99\x99" }, // Static pass: 0x999999 10011001 10011001 10011001
+ { 3, "\xAA\xAA\xAA" }, // Static pass: 0xAAAAAA 10101010 10101010 10101010
+ { 3, "\xBB\xBB\xBB" }, // Static pass: 0xBBBBBB 10111011 10111011 10111011
+ { 3, "\xCC\xCC\xCC" }, // Static pass: 0xCCCCCC 11001100 11001100 11001100
+ { 3, "\xDD\xDD\xDD" }, // Static pass: 0xDDDDDD 11011101 11011101 11011101
+ { 3, "\xEE\xEE\xEE" }, // Static pass: 0xEEEEEE 11101110 11101110 11101110
+ { 3, "\xFF\xFF\xFF" }, // Static pass: 0xFFFFFF 11111111 11111111 11111111
+ { 3, "\x92\x49\x24" }, // Static pass: 0x924924 10010010 01001001 00100100
+ { 3, "\x49\x24\x92" }, // Static pass: 0x492492 01001001 00100100 10010010
+ { 3, "\x24\x92\x49" }, // Static pass: 0x249249 00100100 10010010 01001001
+ { 3, "\x6D\xB6\xDB" }, // Static pass: 0x6DB6DB 01101101 10110110 11011011
+ { 3, "\xB6\xDB\x6D" }, // Static pass: 0xB6DB6D 10110110 11011011 01101101
+ { 3, "\xDB\x6D\xB6" }, // Static pass: 0XDB6DB6 11011011 01101101 10110110
+ { -1, "" }, // Random pass.
+ { -1, "" }, // Random pass.
+ { -1, "" }, // Random pass.
+ { -1, "" }, // Random pass.
+ { 0, NULL } };
+
+ /* Put the book array into this array in random order. */
+ nwipe_pattern_t patterns[36];
+
+ /* An entropy buffer. */
+ u16 s[27];
+
+ /* Load the array with random characters. */
+ ssize_t r = read( c->entropy_fd, &s, sizeof( s ) );
+ if( r != sizeof( s ) )
+ {
+ r = errno;
+ nwipe_perror( r, __FUNCTION__, "read" );
+ nwipe_log( NWIPE_LOG_FATAL, "Unable to seed the %s method.", nwipe_gutmann_label );
+
+ /* Ensure a negative return. */
+ if( r < 0 )
+ {
+ c->result = r;
+ return NULL;
+ }
+ else
+ {
+ c->result = -1;
+ return NULL;
+ }
+ }
+
+ // First 4 random passes
+ for( int i = 0; i <= 3; ++i )
+ {
+ patterns[i] = book[i];
+ }
+ // Middle 27 passes in random order
+ for( int i = 26; i >= 0; --i )
+ {
+ /* Get a random integer that is less than the first index 'i'. */
+ int n = (int) ( (double) ( s[i] ) / (double) ( 0x0000FFFF + 1 ) * (double) ( i + 1 ) );
+
+ /* Initialize the secondary index. */
+ int j = 3;
+
+ while( n-- >= 0 )
+ {
+ /* Advance 'j' by 'n' positions... */
+ j += 1;
+
+ /* ... but don't count 'book' elements that have already been copied. */
+ while( book[j].length == 0 )
+ {
+ j += 1;
+ }
+ }
+
+ /* Copy the element. */
+ patterns[i + 4] = book[j];
+
+ /* Mark this element as having been used. */
+ book[j].length = 0;
+ }
+ // Last 4 random passes
+ for( int i = 31; i <= 34; ++i )
+ {
+ patterns[i] = book[i];
+ }
+
+ /* Ensure that the array is terminated. */
+ patterns[35].length = 0;
+ patterns[35].s = NULL;
+
+ /* Run the Gutmann method. */
+ c->result = nwipe_runmethod( c, patterns );
+
+ /* Finished. Set the wipe_status flag so that the GUI knows */
+ c->wipe_status = 0;
+
+ /* get current time at the end of the wipe */
+ time( &c->end_time );
+
+ return NULL;
+} /* nwipe_gutmann */
+
+void* nwipe_ops2( void* ptr )
+{
+ /**
+ * Royal Canadian Mounted Police
+ * Technical Security Standard for Information Technology
+ * Appendix OPS-II: Media Sanitization
+ *
+ * NOTE: The last pass of this method is specially handled by nwipe_runmethod.
+ *
+ */
+
+ nwipe_context_t* c;
+ c = (nwipe_context_t*) ptr;
+
+ /* get current time at the start of the wipe */
+ time( &c->start_time );
+
+ /* set wipe in progress flag for GUI */
+ c->wipe_status = 1;
+
+ /* A generic array index. */
+ int i;
+
+ /* A generic result buffer. */
+ int r;
+
+ /* A buffer for random characters. */
+ char* s;
+
+ /* A buffer for the bitwise complements of 's'. */
+ char* t;
+
+ /* The element count of 's' and 't'. */
+ u32 u;
+
+ /* The pattern array for this method is dynamically allocated. */
+ nwipe_pattern_t* patterns;
+
+ /* The element count of 'patterns'. */
+ u32 q;
+
+ /* We need one random character per round. */
+ u = 1 * nwipe_options.rounds;
+
+ /* Allocate the array of random characters. */
+ s = malloc( sizeof( char ) * u );
+
+ if( s == NULL )
+ {
+ nwipe_perror( errno, __FUNCTION__, "malloc" );
+ nwipe_log( NWIPE_LOG_FATAL, "Unable to allocate the random character array." );
+ c->result = -1;
+ return NULL;
+ }
+
+ /* Allocate the array of complement characters. */
+ t = malloc( sizeof( char ) * u );
+
+ if( t == NULL )
+ {
+ nwipe_perror( errno, __FUNCTION__, "malloc" );
+ nwipe_log( NWIPE_LOG_FATAL, "Unable to allocate the complement character array." );
+ c->result = -1;
+ free( s );
+ return NULL;
+ }
+
+ /* We need eight pattern elements per round, plus one for padding. */
+ q = 8 * u + 1;
+
+ /* Allocate the pattern array. */
+ patterns = malloc( sizeof( nwipe_pattern_t ) * q );
+
+ if( patterns == NULL )
+ {
+ nwipe_perror( errno, __FUNCTION__, "malloc" );
+ nwipe_log( NWIPE_LOG_FATAL, "Unable to allocate the pattern array." );
+ c->result = -1;
+ free( s );
+ free( t );
+ return NULL;
+ }
+
+ /* Load the array of random characters. */
+ r = read( c->entropy_fd, s, u );
+
+ if( r != u )
+ {
+ r = errno;
+ nwipe_perror( r, __FUNCTION__, "read" );
+ nwipe_log( NWIPE_LOG_FATAL, "Unable to seed the %s method.", nwipe_ops2_label );
+
+ if( r < 0 )
+ {
+ c->result = r;
+ free( s );
+ free( t );
+ free( patterns );
+ return NULL;
+ }
+ else
+ {
+ c->result = -1;
+ free( s );
+ free( t );
+ free( patterns );
+ return NULL;
+ }
+ }
+
+ for( i = 0; i < u; i += 1 )
+ {
+ /* Populate the array of complements. */
+ t[i] = ~s[i];
+ }
+
+ for( i = 0; i < u; i += 8 )
+ {
+ /* Populate the array of patterns. */
+
+ /* Even elements point to the random characters. */
+ patterns[i * 4 + 0].length = 1;
+ patterns[i * 4 + 0].s = &s[i];
+ patterns[i * 4 + 2].length = 1;
+ patterns[i * 4 + 2].s = &s[i];
+ patterns[i * 4 + 4].length = 1;
+ patterns[i * 4 + 4].s = &s[i];
+ patterns[i * 4 + 6].length = 1;
+ patterns[i * 4 + 6].s = &s[i];
+
+ /* Odd elements point to the complement characters. */
+ patterns[i * 4 + 1].length = 1;
+ patterns[i * 4 + 1].s = &t[i];
+ patterns[i * 4 + 3].length = 1;
+ patterns[i * 4 + 3].s = &t[i];
+ patterns[i * 4 + 5].length = 1;
+ patterns[i * 4 + 5].s = &t[i];
+ patterns[i * 4 + 7].length = 1;
+ patterns[i * 4 + 7].s = &t[i];
+ }
+
+ /* Ensure that the array is terminated. */
+ patterns[q - 1].length = 0;
+ patterns[q - 1].s = NULL;
+
+ /* Run the TSSIT OPS-II method. */
+ c->result = nwipe_runmethod( c, patterns );
+
+ /* Release the random character buffer. */
+ free( s );
+
+ /* Release the complement character buffer */
+ free( t );
+
+ /* Release the pattern buffer. */
+ free( patterns );
+
+ /* We're done. */
+
+ /* Finished. Set the wipe_status flag so that the GUI knows */
+ c->wipe_status = 0;
+
+ /* get current time at the end of the wipe */
+ time( &c->end_time );
+
+ return NULL;
+} /* nwipe_ops2 */
+
+void* nwipe_is5enh( void* ptr )
+{
+ nwipe_context_t* c = (nwipe_context_t*) ptr;
+
+ /* get current time at the start of the wipe */
+ time( &c->start_time );
+
+ c->wipe_status = 1;
+
+ char is5enh[3] = { '\x00', '\xFF', '\x00' };
+ nwipe_pattern_t patterns[] = { { 1, &is5enh[0] }, // Pass 1: 0s
+ { 1, &is5enh[1] }, // Pass 2: 1s
+ { -1, &is5enh[2] }, // Pass 3: random bytes with verification
+ { 0, NULL } };
+ c->result = nwipe_runmethod( c, patterns );
+
+ c->wipe_status = 0;
+
+ /* get current time at the end of the wipe */
+ time( &c->end_time );
+
+ return NULL;
+} /* nwipe_is5enh */
+
+void* nwipe_random( void* ptr )
+{
+ /**
+ * Fill the device with a stream from the PRNG.
+ *
+ */
+
+ nwipe_context_t* c;
+ c = (nwipe_context_t*) ptr;
+
+ /* get current time at the start of the wipe */
+ time( &c->start_time );
+
+ /* set wipe in progress flag for GUI */
+ c->wipe_status = 1;
+
+ /* Define the random method. */
+ nwipe_pattern_t patterns[] = { { -1, "" }, { 0, NULL } };
+
+ /* Run the method. */
+ c->result = nwipe_runmethod( c, patterns );
+
+ /* Finished. Set the wipe_status flag so that the GUI knows */
+ c->wipe_status = 0;
+
+ /* get current time at the end of the wipe */
+ time( &c->end_time );
+
+ return NULL;
+} /* nwipe_random */
+
+int nwipe_runmethod( nwipe_context_t* c, nwipe_pattern_t* patterns )
+{
+ /**
+ * Writes patterns to the device.
+ *
+ */
+
+ /* The result holder. */
+ int r;
+
+ /* An index variable. */
+ int i = 0;
+
+ /* Variable to track if it is the last pass */
+ int lastpass = 0;
+
+ i = 0;
+
+ /* The zero-fill pattern for the final pass of most methods. */
+ nwipe_pattern_t pattern_zero = { 1, "\x00" };
+
+ /* The one-fill pattern for verification of the ones fill */
+ nwipe_pattern_t pattern_one = { 1, "\xFF" };
+
+ /* Create the PRNG state buffer. */
+ c->prng_seed.length = NWIPE_KNOB_PRNG_STATE_LENGTH;
+ c->prng_seed.s = malloc( c->prng_seed.length );
+
+ /* Check the memory allocation. */
+ if( !c->prng_seed.s )
+ {
+ nwipe_perror( errno, __FUNCTION__, "malloc" );
+ nwipe_log( NWIPE_LOG_FATAL, "Unable to allocate memory for the prng seed buffer." );
+ return -1;
+ }
+
+ /* Count the number of patterns in the array. */
+ while( patterns[i].length )
+ {
+ i += 1;
+ }
+
+ /* Tell the parent the number of device passes that will be run in one round. */
+ c->pass_count = i;
+
+ /* Set the number of bytes that will be written across all passes in one round. */
+ c->pass_size = c->pass_count * c->device_size;
+
+ /* For the selected method, calculate the correct round_size value (for correct percentage calculation) */
+ calculate_round_size( c );
+
+ /* If only verifying then the round size is the device size */
+ if( nwipe_options.method == &nwipe_verify_zero || nwipe_options.method == &nwipe_verify_one )
+ {
+ c->round_size = c->device_size;
+ }
+
+ /* Initialize the working round counter. */
+ c->round_working = 0;
+
+ nwipe_log(
+ NWIPE_LOG_NOTICE, "Invoking method '%s' on %s", nwipe_method_label( nwipe_options.method ), c->device_name );
+
+ while( c->round_working < c->round_count )
+ {
+ /* Increment the round counter. */
+ c->round_working += 1;
+
+ nwipe_log(
+ NWIPE_LOG_NOTICE, "Starting round %i of %i on %s", c->round_working, c->round_count, c->device_name );
+
+ /* Initialize the working pass counter. */
+ c->pass_working = 0;
+
+ for( i = 0; i < c->pass_count; i++ )
+ {
+ /* Increment the working pass. */
+ c->pass_working += 1;
+
+ /* Check if this is the last pass. */
+ if( nwipe_options.verify == NWIPE_VERIFY_LAST && nwipe_options.method != &nwipe_ops2 )
+ {
+ if( nwipe_options.noblank == 1 && c->round_working == c->round_count
+ && c->pass_working == c->pass_count )
+ {
+ lastpass = 1;
+ }
+ }
+
+ nwipe_log( NWIPE_LOG_NOTICE,
+ "Starting pass %i/%i, round %i/%i, on %s",
+ c->pass_working,
+ c->pass_count,
+ c->round_working,
+ c->round_count,
+ c->device_name );
+
+ if( patterns[i].length == 0 )
+ {
+ /* Caught insanity. */
+ nwipe_log( NWIPE_LOG_SANITY, "nwipe_runmethod: A non-terminating pattern element has zero length." );
+ return -1;
+ }
+
+ if( patterns[i].length > 0 )
+ {
+
+ /* Write a static pass. */
+ c->pass_type = NWIPE_PASS_WRITE;
+ r = nwipe_static_pass( c, &patterns[i] );
+ c->pass_type = NWIPE_PASS_NONE;
+
+ /* Log number of bytes written to disk */
+ nwipe_log( NWIPE_LOG_NOTICE, "%llu bytes written to %s", c->pass_done, c->device_name );
+
+ /* Check for a fatal error. */
+ if( r < 0 )
+ {
+ return r;
+ }
+
+ if( nwipe_options.verify == NWIPE_VERIFY_ALL || lastpass == 1 )
+ {
+
+ nwipe_log( NWIPE_LOG_NOTICE,
+ "Verifying pass %i of %i, round %i of %i, on %s",
+ c->pass_working,
+ c->pass_count,
+ c->round_working,
+ c->round_count,
+ c->device_name );
+
+ /* Verify this pass. */
+ c->pass_type = NWIPE_PASS_VERIFY;
+ r = nwipe_static_verify( c, &patterns[i] );
+ c->pass_type = NWIPE_PASS_NONE;
+
+ nwipe_log( NWIPE_LOG_NOTICE, "%llu bytes read from %s", c->pass_done, c->device_name );
+
+ /* Check for a fatal error. */
+ if( r < 0 )
+ {
+ return r;
+ }
+
+ nwipe_log( NWIPE_LOG_NOTICE,
+ "Verified pass %i of %i, round %i of %i, on '%s'.",
+ c->pass_working,
+ c->pass_count,
+ c->round_working,
+ c->round_count,
+ c->device_name );
+ }
+
+ } /* static pass */
+
+ else
+ {
+ c->pass_type = NWIPE_PASS_WRITE;
+
+ /* Seed the PRNG. */
+ r = read( c->entropy_fd, c->prng_seed.s, c->prng_seed.length );
+
+ /* Check the result. */
+ if( r < 0 )
+ {
+ c->pass_type = NWIPE_PASS_NONE;
+ nwipe_perror( errno, __FUNCTION__, "read" );
+ nwipe_log( NWIPE_LOG_FATAL, "Unable to seed the PRNG." );
+ return -1;
+ }
+
+ /* Check for a partial read. */
+ if( r != c->prng_seed.length )
+ {
+ /* TODO: Handle partial reads. */
+ nwipe_log( NWIPE_LOG_FATAL, "Insufficient entropy is available." );
+ return -1;
+ }
+
+ /* Write the random pass. */
+ r = nwipe_random_pass( c );
+ c->pass_type = NWIPE_PASS_NONE;
+
+ /* Log number of bytes written to disk */
+ nwipe_log( NWIPE_LOG_NOTICE, "%llu bytes written to %s", c->pass_done, c->device_name );
+
+ /* Check for a fatal error. */
+ if( r < 0 )
+ {
+ return r;
+ }
+
+ /* Make sure IS5 enhanced always verifies its PRNG pass regardless */
+ /* of the current combination of the --noblank (which influences */
+ /* the lastpass variable) and --verify options. */
+ if( nwipe_options.verify == NWIPE_VERIFY_ALL || lastpass == 1 || nwipe_options.method == &nwipe_is5enh )
+ {
+ nwipe_log( NWIPE_LOG_NOTICE,
+ "Verifying pass %i of %i, round %i of %i, on %s",
+ c->pass_working,
+ c->pass_count,
+ c->round_working,
+ c->round_count,
+ c->device_name );
+
+ /* Verify this pass. */
+ c->pass_type = NWIPE_PASS_VERIFY;
+ r = nwipe_random_verify( c );
+ c->pass_type = NWIPE_PASS_NONE;
+
+ nwipe_log( NWIPE_LOG_NOTICE, "%llu bytes read from %s", c->pass_done, c->device_name );
+
+ /* Check for a fatal error. */
+ if( r < 0 )
+ {
+ return r;
+ }
+
+ nwipe_log( NWIPE_LOG_NOTICE,
+ "Verified pass %i of %i, round %i of %i, on '%s'.",
+ c->pass_working,
+ c->pass_count,
+ c->round_working,
+ c->round_count,
+ c->device_name );
+ }
+
+ } /* random pass */
+
+ nwipe_log( NWIPE_LOG_NOTICE,
+ "Finished pass %i/%i, round %i/%i, on %s",
+ c->pass_working,
+ c->pass_count,
+ c->round_working,
+ c->round_count,
+ c->device_name );
+
+ } /* for passes */
+
+ if( c->round_working < c->round_count )
+ {
+ nwipe_log(
+ NWIPE_LOG_NOTICE, "Finished round %i of %i on %s", c->round_working, c->round_count, c->device_name );
+ }
+ else
+ {
+ nwipe_log( NWIPE_LOG_NOTICE,
+ "Finished final round %i of %i on %s",
+ c->round_working,
+ c->round_count,
+ c->device_name );
+ }
+
+ } /* while rounds */
+
+ if( nwipe_options.method == &nwipe_ops2 )
+ {
+ /* NOTE: The OPS-II method specifically requires that a random pattern be left on the device. */
+
+ /* Tell the parent that we are running the final pass. */
+ c->pass_type = NWIPE_PASS_FINAL_OPS2;
+
+ /* Seed the PRNG. */
+ r = read( c->entropy_fd, c->prng_seed.s, c->prng_seed.length );
+
+ /* Check the result. */
+ if( r < 0 )
+ {
+ nwipe_perror( errno, __FUNCTION__, "read" );
+ nwipe_log( NWIPE_LOG_FATAL, "Unable to seed the PRNG." );
+ return -1;
+ }
+
+ /* Check for a partial read. */
+ if( r != c->prng_seed.length )
+ {
+ /* TODO: Handle partial reads. */
+ nwipe_log( NWIPE_LOG_FATAL, "Insufficient entropy is available." );
+ return -1;
+ }
+
+ nwipe_log( NWIPE_LOG_NOTICE, "Writing final random pattern to '%s'.", c->device_name );
+
+ /* The final ops2 pass. */
+ r = nwipe_random_pass( c );
+
+ nwipe_log( NWIPE_LOG_NOTICE, "%llu bytes written to %s", c->pass_done, c->device_name );
+
+ /* Check for a fatal error. */
+ if( r < 0 )
+ {
+ return r;
+ }
+
+ if( nwipe_options.verify == NWIPE_VERIFY_LAST || nwipe_options.verify == NWIPE_VERIFY_ALL )
+ {
+ nwipe_log( NWIPE_LOG_NOTICE, "Verifying final random pattern FRP on %s", c->device_name );
+
+ /* Verify the final zero pass. */
+ r = nwipe_random_verify( c );
+
+ nwipe_log( NWIPE_LOG_NOTICE, "%llu bytes read from %s", c->pass_done, c->device_name );
+
+ /* Check for a fatal error. */
+ if( r < 0 )
+ {
+ return r;
+ }
+
+ nwipe_log( NWIPE_LOG_NOTICE, "[SUCCESS] Verified FRP on '%s' matches", c->device_name );
+ }
+
+ } /* final ops2 */
+
+ else if( nwipe_options.method == &nwipe_verify_zero )
+ {
+ nwipe_log( NWIPE_LOG_NOTICE, "Verifying that %s is zeroed", c->device_name );
+
+ /* Verify the final zero pass. */
+ c->pass_type = NWIPE_PASS_VERIFY;
+ r = nwipe_static_verify( c, &pattern_zero );
+ c->pass_type = NWIPE_PASS_NONE;
+
+ /* Check for a fatal error. */
+ if( r < 0 )
+ {
+ return r;
+ }
+ if( c->verify_errors == 0 )
+ {
+ nwipe_log( NWIPE_LOG_NOTICE, "[SUCCESS] Verified that %s is Zeroed.", c->device_name );
+ }
+ else
+ {
+ nwipe_log( NWIPE_LOG_ERROR, "[FAILURE] %s has not been Zeroed .", c->device_name );
+ }
+
+ } /* verify */
+
+ else if( nwipe_options.method == &nwipe_verify_one )
+ {
+ nwipe_log( NWIPE_LOG_NOTICE, "Verifying that %s is Ones (0xFF)", c->device_name );
+
+ /* Verify the final ones pass. */
+ c->pass_type = NWIPE_PASS_VERIFY;
+ r = nwipe_static_verify( c, &pattern_one );
+ c->pass_type = NWIPE_PASS_NONE;
+
+ /* Check for a fatal error. */
+ if( r < 0 )
+ {
+ return r;
+ }
+ if( c->verify_errors == 0 )
+ {
+ nwipe_log( NWIPE_LOG_NOTICE, "[SUCCESS] Verified that %s is full of ones (0xFF).", c->device_name );
+ }
+ else
+ {
+ nwipe_log( NWIPE_LOG_ERROR, "[FAILURE] %s is not full of ones (0xFF).", c->device_name );
+ }
+
+ } /* verify */
+
+ else if( nwipe_options.noblank == 0 )
+ {
+ /* Tell the user that we are on the final pass. */
+ c->pass_type = NWIPE_PASS_FINAL_BLANK;
+
+ nwipe_log( NWIPE_LOG_NOTICE, "Blanking device %s", c->device_name );
+
+ /* The final zero pass. */
+ r = nwipe_static_pass( c, &pattern_zero );
+
+ /* Log number of bytes written to disk */
+ nwipe_log( NWIPE_LOG_NOTICE, "%llu bytes written to %s", c->pass_done, c->device_name );
+
+ /* Check for a fatal error. */
+ if( r < 0 )
+ {
+ return r;
+ }
+
+ if( nwipe_options.verify == NWIPE_VERIFY_LAST || nwipe_options.verify == NWIPE_VERIFY_ALL )
+ {
+ nwipe_log( NWIPE_LOG_NOTICE, "Verifying that %s is empty.", c->device_name );
+
+ /* Verify the final zero pass. */
+ c->pass_type = NWIPE_PASS_VERIFY;
+ r = nwipe_static_verify( c, &pattern_zero );
+ c->pass_type = NWIPE_PASS_NONE;
+
+ /* Log number of bytes read from disk */
+ nwipe_log( NWIPE_LOG_NOTICE, "%llu bytes read from %s", c->pass_done, c->device_name );
+
+ /* Check for a fatal error. */
+ if( r < 0 )
+ {
+ return r;
+ }
+
+ if( c->verify_errors == 0 )
+ {
+ nwipe_log( NWIPE_LOG_NOTICE, "[SUCCESS] Verified that %s is empty.", c->device_name );
+ }
+ else
+ {
+ nwipe_log( NWIPE_LOG_NOTICE, "[FAILURE] %s Verification errors, not empty", c->device_name );
+ }
+ }
+
+ if( c->verify_errors == 0 && c->pass_errors == 0 )
+ {
+ nwipe_log( NWIPE_LOG_NOTICE, "[SUCCESS] Blanked device %s", c->device_name );
+ }
+ else
+ {
+ nwipe_log( NWIPE_LOG_NOTICE, "[FAILURE] %s may not be blanked", c->device_name );
+ }
+
+ } /* final blank */
+
+ /* Release the state buffer. */
+ c->prng_seed.length = 0;
+ free( c->prng_seed.s );
+
+ /* Tell the parent that we have fininshed the final pass. */
+ c->pass_type = NWIPE_PASS_NONE;
+
+ if( c->verify_errors > 0 )
+ {
+ /* We finished, but with non-fatal verification errors. */
+ nwipe_log( NWIPE_LOG_ERROR, "%llu verification errors on '%s'.", c->verify_errors, c->device_name );
+ }
+
+ if( c->pass_errors > 0 )
+ {
+ /* We finished, but with non-fatal wipe errors. */
+ nwipe_log( NWIPE_LOG_ERROR, "%llu wipe errors on '%s'.", c->pass_errors, c->device_name );
+ }
+
+ /* FIXME: The 'round_errors' context member is not being used. */
+
+ if( c->pass_errors > 0 || c->round_errors > 0 || c->verify_errors > 0 )
+ {
+ /* We finished, but with non-fatal errors. */
+ return 1;
+ }
+
+ /* We finished successfully. */
+ return 0;
+
+} /* nwipe_runmethod */
+
+void calculate_round_size( nwipe_context_t* c )
+{
+ /* This is where the round size is calculated. round_size is used in the running percentage completion
+ * calculation. round size is calculated based on pass_size, pass_count, number of rounds, blanking
+ * on/off and verification All/Last/None
+ *
+ * To hopefully make this calculation more understandable, I have separated the calculations that apply to
+ * all methods and processed first then created a switch statement that contains method specific changes if any
+ */
+
+ /* Don't change the order of these values as the case statements use their index in the array, New methods
+ * don't need to be added to this array unless they have complicated calculations like Ops2 and IS5. If you do
+ * add a method, just add it to the bottom of the array_methods array and also to the bottom of the switch
+ * statement.
+ */
+ void* array_methods[] = { &nwipe_zero,
+ &nwipe_ops2,
+ &nwipe_dodshort,
+ &nwipe_dod522022m,
+ &nwipe_gutmann,
+ &nwipe_random,
+ &nwipe_is5enh,
+ NULL };
+ int i;
+
+ /* This while loop allows us to effectively create a const that represents a method so we can use a case statement
+ * rather than if statements.
+ *
+ * Using a switch statement looks better than if statments as more methods may get added in the future expanding the
+ * list. The code could be condensed as some methods have identical adjustments, however as there are only a few
+ * methods I felt it was easier to understand as it is, however this could be changed if necessary.
+ */
+
+ /* Initialise, -1 = no additional calculation required */
+ int selected_method = -1;
+
+ i = 0;
+ while( array_methods[i] != NULL )
+ {
+ if( nwipe_options.method == array_methods[i] )
+ {
+ selected_method = i;
+ }
+ i++;
+ }
+
+ /* On exit from the while loop the selected method either equals an index to a method
+ * or it equals -1 which means no extra calculations are required that are method specific
+ */
+
+ if( nwipe_options.verify == NWIPE_VERIFY_ALL )
+ {
+ /* We must read back all passes, so double the byte count. */
+ c->pass_size *= 2;
+ }
+
+ /* Tell the parent the number of rounds that will be run. */
+ c->round_count = nwipe_options.rounds;
+
+ /* Set the initial number of bytes that will be written across all rounds.
+ c->pass_size includes write AND verification passes if 'verify_all' is selected
+ but does not include the final blanking pass or the verify_last option */
+ c->round_size = c->pass_size;
+
+ /* Multiple the round_size by the number of rounds (times) the user wants to wipe the drive with this method. */
+ c->round_size *= c->round_count;
+
+ /* Now increase size based on whether blanking is enabled and verification */
+ if( nwipe_options.noblank == 0 )
+ {
+ /* Blanking enabled so increase round size */
+ c->round_size += c->device_size;
+
+ if( nwipe_options.verify == NWIPE_VERIFY_LAST || nwipe_options.verify == NWIPE_VERIFY_ALL )
+ {
+ c->round_size += c->device_size;
+ }
+ }
+ else
+ {
+ /* Blanking not enabled, check for 'Verify_last', increase round size if enabled. */
+ if( nwipe_options.verify == NWIPE_VERIFY_LAST )
+ {
+ c->round_size += c->device_size;
+ }
+ }
+
+ /* Additional method specific round_size adjustments go in this switch statement */
+
+ switch( selected_method )
+ {
+ case 0:
+ /* NWIPE_ZERO - No additional calculation required
+ * ---------- */
+ break;
+
+ case 1:
+ /* NWIPE_OPS2
+ * ---------- */
+
+ /* Required for mandatory 9th and final random pass */
+ c->round_size += c->device_size;
+
+ /* Required for selectable 9th and final random verification */
+ if( nwipe_options.verify == NWIPE_VERIFY_ALL || nwipe_options.verify == NWIPE_VERIFY_LAST )
+ {
+ c->round_size += c->device_size;
+ }
+
+ /* As no final zero blanking pass is permitted by this standard reduce round size if it's selected */
+ if( nwipe_options.noblank == 0 )
+ {
+ /* Reduce for blanking pass */
+ c->round_size -= c->device_size;
+
+ /* Reduce for blanking pass verification */
+ if( nwipe_options.verify == NWIPE_VERIFY_ALL || nwipe_options.verify == NWIPE_VERIFY_LAST )
+ {
+ c->round_size -= c->device_size;
+ }
+ }
+ else
+ {
+ if( nwipe_options.verify == NWIPE_VERIFY_LAST )
+ {
+ /* If blanking off & verification on reduce round size */
+ c->round_size -= c->device_size;
+ }
+ }
+
+ break;
+
+ case 2:
+ /* DoD Short - No additional calculation required
+ * --------- */
+
+ break;
+
+ case 3:
+ /* DOD 522022m - No additional calculation required
+ * ----------- */
+
+ break;
+
+ case 4:
+ /* GutMann - No additional calculation required
+ * ------- */
+
+ break;
+
+ case 5:
+ /* PRNG (random) - No additional calculation required
+ * ------------- */
+
+ break;
+
+ case 6:
+ /* NWIPE_IS5ENH
+ * ------------ */
+
+ /* This method ALWAYS verifies the 3rd pass so increase by device size,
+ * but NOT if VERIFY_ALL has been selected, but first .. */
+
+ /* Reduce as Verify_Last already included previously if blanking was off */
+ if( nwipe_options.verify == NWIPE_VERIFY_LAST && nwipe_options.noblank == 1 )
+ {
+ c->round_size -= c->device_size;
+ }
+
+ /* Adjusts for verify on every third pass multiplied by number of rounds */
+ if( nwipe_options.verify != NWIPE_VERIFY_ALL )
+ {
+ c->round_size += ( c->device_size * c->round_count );
+ }
+
+ break;
+
+ case -1:
+ /* Method not listed so don't do any extra calculations
+ * ---------------------------------------------------- */
+ break;
+ }
+}
+
+/* eof */
diff --git a/src/method.h b/src/method.h
new file mode 100644
index 0000000..f6fdbc2
--- /dev/null
+++ b/src/method.h
@@ -0,0 +1,60 @@
+/*
+ * methods.c: Method implementations for nwipe.
+ *
+ * Copyright Darik Horn <dajhorn-dban@vanadac.com>.
+ *
+ * Modifications to original dwipe Copyright Andy Beverley <andy@andybev.com>
+ *
+ * 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, version 2.
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#ifndef METHOD_H_
+#define METHOD_H_
+
+/* The argument list for nwipe methods. */
+#define NWIPE_METHOD_SIGNATURE nwipe_context_t* c
+
+typedef enum nwipe_verify_t_ {
+ NWIPE_VERIFY_NONE = 0, // Do not read anything back from the device.
+ NWIPE_VERIFY_LAST, // Check the last pass.
+ NWIPE_VERIFY_ALL, // Check all passes.
+} nwipe_verify_t;
+
+/* The typedef of the function that will do the wipe. */
+typedef int ( *nwipe_method_t )( void* ptr );
+
+typedef struct
+{
+ int length; // Length of the pattern in bytes, -1 means random.
+ char* s; // The actual bytes of the pattern.
+} nwipe_pattern_t;
+
+const char* nwipe_method_label( void* method );
+int nwipe_runmethod( NWIPE_METHOD_SIGNATURE, nwipe_pattern_t* patterns );
+
+void* nwipe_dod522022m( void* ptr );
+void* nwipe_dodshort( void* ptr );
+void* nwipe_gutmann( void* ptr );
+void* nwipe_ops2( void* ptr );
+void* nwipe_is5enh( void* ptr );
+void* nwipe_random( void* ptr );
+void* nwipe_zero( void* ptr );
+void* nwipe_one( void* ptr );
+void* nwipe_verify_zero( void* ptr );
+void* nwipe_verify_one( void* ptr );
+
+void calculate_round_size( nwipe_context_t* );
+
+#endif /* METHOD_H_ */
diff --git a/src/miscellaneous.c b/src/miscellaneous.c
new file mode 100644
index 0000000..bc2dc01
--- /dev/null
+++ b/src/miscellaneous.c
@@ -0,0 +1,666 @@
+/*
+ * miscellaneous.c: functions that may be generally used throughout nwipes code,
+ * mainly string processing functions but also time related functions.
+ *
+ * Copyright PartialVolume <https://github.com/PartialVolume>.
+ *
+ * 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, version 2.
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#ifndef _DEFAULT_SOURCE
+#define _DEFAULT_SOURCE
+#endif
+
+#ifndef _POSIX_SOURCE
+#define _POSIX_SOURCE
+#endif
+
+#include <stdio.h>
+#include "nwipe.h"
+#include "context.h"
+#include "logging.h"
+#include "miscellaneous.h"
+
+/* Convert string to upper case
+ */
+void strupper( char* str )
+{
+ int idx;
+
+ idx = 0;
+ while( str[idx] != 0 )
+ {
+ /* If upper case alpha character, change to lower case */
+ if( str[idx] >= 'A' && str[idx] <= 'Z' )
+ {
+ str[idx] -= 32;
+ }
+
+ idx++;
+ }
+}
+
+/* Convert string to lower case
+ */
+void strlower( char* str )
+{
+ int idx;
+
+ idx = 0;
+ while( str[idx] != 0 )
+ {
+ /* If upper case alpha character, change to lower case */
+ if( str[idx] >= 'A' && str[idx] <= 'Z' )
+ {
+ str[idx] += 32;
+ }
+
+ idx++;
+ }
+}
+
+void strip_CR_LF( char* str )
+{
+ /* In the specified string, replace any CR or LF with a space */
+ int idx = 0;
+ int len = strlen( str );
+ while( idx < len )
+ {
+ if( str[idx] == 0x0A || str[idx] == 0x0D )
+ {
+ str[idx] = ' ';
+ }
+ idx++;
+ }
+}
+
+/* Search a string for a positive number, convert the first
+ * number found to binary and return the binary number.
+ * returns the number or -1 if number too large or -2 if
+ * no number found.
+ */
+
+u64 str_ascii_number_to_ll( char* str )
+{
+ int idx;
+ int idx2;
+ char number_copy[20];
+
+ idx = 0; // index into the main string we are searching
+ idx2 = 0; // index used for backup copy of ascii number
+
+ while( str[idx] != 0 )
+ {
+ /* Find the start of the number */
+ if( str[idx] >= '0' && str[idx] <= '9' )
+ {
+ while( str[idx] != 0 )
+ {
+ /* Find the end of the number */
+ if( str[idx] >= '0' && str[idx] <= '9' )
+ {
+ if( idx2 < sizeof( number_copy ) - 1 )
+ {
+ number_copy[idx2++] = str[idx++];
+ }
+ else
+ {
+ /* Number is too large ! */
+ return -1;
+ }
+ }
+ else
+ {
+ /* end found */
+ number_copy[idx2] = 0; // terminate our copy
+
+ /* convert ascii number to longlong */
+ return atoll( number_copy );
+ }
+ }
+ }
+ else
+ {
+ idx++;
+ }
+ }
+ return -2; /* no number found */
+}
+
+void Determine_C_B_nomenclature( u64 qty, char* result, int result_array_size )
+{
+
+ /* C_B ? Determine Capacity or Bandwidth nomenclature
+ *
+ * A pointer to a result character string with a minimum of 13 characters in length
+ * should be provided.
+ *
+ * Outputs a string of the form xxxTB, xxxGB, xxxMB, xxxKB B depending on the value of 'qty'
+ */
+
+ /* Initialise the output array */
+ int idx = 0;
+
+ while( idx < result_array_size )
+ {
+ result[idx++] = 0;
+ }
+
+ /* Determine the size of throughput so that the correct nomenclature can be used */
+ if( qty >= INT64_C( 10000000000000 ) )
+ {
+ snprintf( result, result_array_size, "%4llu TB", qty / INT64_C( 1000000000000 ) );
+ }
+ else if( qty >= INT64_C( 10000000000 ) )
+ {
+ snprintf( result, result_array_size, "%4llu GB", qty / INT64_C( 1000000000 ) );
+ }
+ else if( qty >= INT64_C( 10000000 ) )
+ {
+ snprintf( result, result_array_size, "%4llu MB", qty / INT64_C( 1000000 ) );
+ }
+ else if( qty >= INT64_C( 10000 ) )
+ {
+ snprintf( result, result_array_size, "%4llu KB", qty / INT64_C( 1000 ) );
+ }
+ else
+ {
+ snprintf( result, result_array_size, "%4llu B", qty / INT64_C( 1 ) );
+ }
+}
+
+void convert_seconds_to_hours_minutes_seconds( u64 total_seconds, int* hours, int* minutes, int* seconds )
+{
+ /* Convert binary seconds into binary hours, minutes and seconds */
+
+ if( total_seconds % 60 )
+ {
+ *minutes = total_seconds / 60;
+
+ *seconds = total_seconds - ( *minutes * 60 );
+ }
+ else
+ {
+ *minutes = total_seconds / 60;
+
+ *seconds = 0;
+ }
+ if( *minutes > 59 )
+ {
+ *hours = *minutes / 60;
+ if( *minutes % 60 )
+ {
+ *minutes = *minutes - ( *hours * 60 );
+ }
+ else
+ {
+ *minutes = 0;
+ }
+ }
+}
+
+int nwipe_strip_path( char* output, char* input )
+{
+ /* Take the input string, say "/dev/sda" and remove the "/dev/", prefix the result
+ * with 'length' spaces. So if length=8 and input=/dev/sda, output will
+ * be " sda", a string 8 characters long right justified with spaces.
+ */
+ int idx_dest;
+ int idx_src;
+ idx_dest = 8;
+ // idx_dest = length;
+ output[idx_dest--] = 0;
+ idx_src = strlen( input );
+ idx_src--;
+
+ while( idx_dest >= 0 )
+ {
+ /* if the device name contains a / start prefixing spaces */
+ if( input[idx_src] == '/' )
+ {
+ output[idx_dest--] = ' ';
+ continue;
+ }
+ if( idx_src >= 0 )
+ {
+ output[idx_dest--] = input[idx_src--];
+ }
+ }
+ return 0;
+}
+
+void replace_non_alphanumeric( char* str, char replacement_char )
+{
+ int i = 0;
+ while( str[i] != 0 )
+ {
+ if( str[i] < '0' || ( str[i] > '9' && str[i] < 'A' ) || ( str[i] > 'Z' && str[i] < 'a' ) || str[i] > 'z' )
+ {
+ str[i] = replacement_char;
+ }
+ i++;
+ }
+}
+
+void convert_double_to_string( char* output_str, double value )
+{
+ int idx = 0;
+ int idx2;
+ int idx3 = 0;
+
+ char percstr[512] = "";
+
+ snprintf( percstr, sizeof( percstr ), "%5.32lf", value );
+
+ while( percstr[idx] != 0 )
+ {
+ if( percstr[idx] == '.' )
+ {
+ for( idx2 = 0; idx2 < 3; idx2++ )
+ {
+ output_str[idx3++] = percstr[idx++];
+ }
+ break;
+ }
+ output_str[idx3++] = percstr[idx++];
+ }
+ output_str[idx3] = 0;
+}
+
+int read_system_datetime( char* year, char* month, char* day, char* hours, char* minutes, char* seconds )
+{
+ /* Reads system date & time and populates the caller provided strings.
+ * Each string is null terminated by this function. The calling program
+ * must provide the minimum string sizes as shown below.
+ *
+ * year 5 bytes (4 numeric digits plus NULL terminator)
+ * month 3 bytes (2 numeric digits plus NULL terminator)
+ * day 3 bytes (2 numeric digits plus NULL terminator)
+ * hours 3 bytes (2 numeric digits plus NULL terminator)
+ * minutes 3 bytes (2 numeric digits plus NULL terminator)
+ * seconds 3 bytes (2 numeric digits plus NULL terminator)
+ *
+ * return value:
+ * 0 = success
+ * -1 = Failure, see nwipe log for detail.
+ */
+ FILE* fp;
+ int r; // A result buffer.
+ int idx; // general index
+ int status = 0;
+
+ /**
+ * Obtain the year
+ */
+ fp = popen( "date +%Y", "r" );
+ if( fp == NULL )
+ {
+ nwipe_log( NWIPE_LOG_ERROR, "Failed to obtain system year using commmand = date +%Y" );
+ }
+ else
+ {
+ /* Read the first line and validate it. Should be 4 numeric digits */
+ if( fgets( year, FOUR_DIGITS + 1, fp ) != NULL )
+ {
+ idx = 0;
+ while( idx < 4 )
+ {
+ if( year[idx] >= '0' && year[idx] <= '9' )
+ {
+ idx++;
+ }
+ else
+ {
+ /* if we haven't reached the correct number of digits due to invalid data, log error */
+ year[++idx] = 0; /* terminate the string, prior to using in nwipe_log */
+ nwipe_log( NWIPE_LOG_ERROR,
+ "Obtained system year using command = date +%Y, but result appears invalid = %s",
+ year );
+ status = -1;
+ break;
+ }
+ }
+ year[idx] = 0; /* terminate the string */
+ }
+ r = pclose( fp );
+ }
+
+ /**
+ * Obtain the month
+ */
+ fp = popen( "date +%m", "r" );
+ if( fp == NULL )
+ {
+ nwipe_log( NWIPE_LOG_ERROR, "Failed to obtain system month using the command = date +%m" );
+ }
+ else
+ {
+ /* Read the first line and validate it. Should be 2 numeric digits */
+ if( fgets( month, TWO_DIGITS + 1, fp ) != NULL )
+ {
+ idx = 0;
+ while( idx < 2 )
+ {
+ if( month[idx] >= '0' && month[idx] <= '9' )
+ {
+ idx++;
+ }
+ else
+ {
+ /* if we haven't reached the correct number of digits due to invalid data, log error */
+ month[++idx] = 0; /* terminate the string, prior to using in nwipe_log */
+ nwipe_log( NWIPE_LOG_ERROR,
+ "Obtained system month using command = date +%m, but result appears invalid = %s",
+ month );
+ status = -1;
+ break;
+ }
+ }
+ month[idx] = 0; /* terminate the string */
+ }
+ r = pclose( fp );
+ }
+
+ /**
+ * Obtain the day
+ */
+ fp = popen( "date +\%d", "r" );
+ if( fp == NULL )
+ {
+ nwipe_log( NWIPE_LOG_ERROR, "Failed to obtain system day using the command = date +\%d" );
+ }
+ else
+ {
+ /* Read the first line and validate it. Should be 2 numeric digits */
+ if( fgets( day, TWO_DIGITS + 1, fp ) != NULL )
+ {
+ idx = 0;
+ while( idx < 2 )
+ {
+ if( day[idx] >= '0' && day[idx] <= '9' )
+ {
+ idx++;
+ }
+ else
+ {
+ /* if we haven't reached the correct number of digits due to invalid data, log error */
+ day[++idx] = 0; /* terminate the string, prior to using in nwipe_log */
+ nwipe_log( NWIPE_LOG_ERROR,
+ "Obtained system day using command = date +\%d, but result appears invalid = %s",
+ day );
+ status = -1;
+ break;
+ }
+ }
+ day[idx] = 0; /* terminate the string */
+ }
+ r = pclose( fp );
+ }
+
+ /**
+ * Obtain the hours
+ */
+ fp = popen( "date +%H", "r" );
+ if( fp == NULL )
+ {
+ nwipe_log( NWIPE_LOG_ERROR, "Failed to obtain system hour using the command = date +%H" );
+ }
+ else
+ {
+ /* Read the first line and validate it. Should be 2 numeric digits */
+ if( fgets( hours, TWO_DIGITS + 1, fp ) != NULL )
+ {
+ // nwipe_log( NWIPE_LOG_INFO, "Seconds = %s, Year = %s", seconds, year);
+ idx = 0;
+ while( idx < 2 )
+ {
+ if( hours[idx] >= '0' && hours[idx] <= '9' )
+ {
+ idx++;
+ }
+ else
+ {
+ /* if we haven't reached the correct number of digits due to invalid data, log error */
+ hours[++idx] = 0; /* terminate the string, prior to using in nwipe_log */
+ nwipe_log( NWIPE_LOG_ERROR,
+ "Obtained system hours using command = date +%H, but result appears invalid = %s",
+ hours );
+ status = -1;
+ break;
+ }
+ }
+ hours[idx] = 0; /* terminate the string */
+ }
+ r = pclose( fp );
+ }
+
+ /**
+ * Obtain the minutes
+ */
+ fp = popen( "date +%M", "r" );
+ if( fp == NULL )
+ {
+ nwipe_log( NWIPE_LOG_ERROR, "Failed to obtain system minutes using the command = date +%M" );
+ }
+ else
+ {
+ /* Read the first line and validate it. Should be 2 numeric digits */
+ if( fgets( minutes, TWO_DIGITS + 1, fp ) != NULL )
+ {
+ // nwipe_log( NWIPE_LOG_INFO, "Seconds = %s, Year = %s", seconds, year);
+ idx = 0;
+ while( idx < 2 )
+ {
+ if( minutes[idx] >= '0' && minutes[idx] <= '9' )
+ {
+ idx++;
+ }
+ else
+ {
+ /* if we haven't reached the correct number of digits due to invalid data, log the error */
+ minutes[++idx] = 0; /* terminate the string, prior to using in nwipe_log */
+ nwipe_log( NWIPE_LOG_ERROR,
+ "Obtained system minutes using command = date +%H, but result appears invalid = %s",
+ minutes );
+ status = -1;
+ break;
+ }
+ }
+ minutes[idx] = 0; /* terminate the string */
+ }
+ r = pclose( fp );
+ }
+
+ /**
+ * Obtain the seconds
+ */
+ fp = popen( "date +%S", "r" );
+ if( fp == NULL )
+ {
+ nwipe_log( NWIPE_LOG_ERROR, "Failed to obtain system seconds using the command = date +%S" );
+ }
+ else
+ {
+ /* Read the first line and validate it. Should be 2 numeric digits */
+ if( fgets( seconds, TWO_DIGITS + 1, fp ) != NULL )
+ {
+ // nwipe_log( NWIPE_LOG_INFO, "Seconds = %s, Year = %s", seconds, year);
+ idx = 0;
+ while( idx < 2 )
+ {
+ if( seconds[idx] >= '0' && seconds[idx] <= '9' )
+ {
+ idx++;
+ }
+ else
+ {
+ /* if we haven't reached the correct number of digits due to invalid data, log error */
+ seconds[++idx] = 0; /* terminate the string, prior to using in nwipe_log */
+ nwipe_log( NWIPE_LOG_ERROR,
+ "Obtained system seconds using command = date +%S, but result appears invalid = %s",
+ seconds );
+ status = -1;
+ break;
+ }
+ }
+ seconds[idx] = 0; /* terminate the string */
+ }
+ r = pclose( fp );
+ }
+
+ return status;
+}
+
+int write_system_datetime( char* year, char* month, char* day, char* hours, char* minutes, char* seconds )
+{
+ /* Writes the system date & time using data from the caller provided strings.
+ * The calling program must provide the minimum string sizes as shown below
+ * populated with current date and time data.
+ *
+ * year 5 bytes (4 numeric digits plus NULL terminator)
+ * month 3 bytes (2 numeric digits plus NULL terminator)
+ * day 3 bytes (2 numeric digits plus NULL terminator)
+ * hours 3 bytes (2 numeric digits plus NULL terminator)
+ * minutes 3 bytes (2 numeric digits plus NULL terminator)
+ * seconds 3 bytes (2 numeric digits plus NULL terminator)
+ *
+ * return value:
+ * 0 = success
+ * -1 = Failure, see nwipe log for detail.
+ */
+ FILE* fp;
+ int r; // A result buffer.
+ int idx; // general index
+ int strIdx; // Index into each string
+ int bufferIdx; // Index into the buffer
+ char buffer[5];
+
+ /**
+ * Basic validation that confirms the input strings are numeric and of the correct length, we do this
+ * by first constructing three arrays. The first are the names of the variables in order
+ * year, month, day, hours, minutes and seconds. The second array contains the address of
+ * each of those strings. The third array are the lengths.
+ * This allows us to create a single loop to validate all fields.
+ */
+
+ char* names[] = { "year", "month", "day", "hours", "minutes", "seconds" };
+ char* pdata[] = { year, month, day, hours, minutes, seconds };
+ int lengths[] = { 4, 2, 2, 2, 2, 2 };
+ char cmd_format[] = "date %s%s%s%s%s.%s >/dev/null 2>&1";
+ char cmd[256];
+
+ for( idx = 0; idx < 6; idx++ )
+ {
+ strIdx = 0; // initialise string index
+
+ /* check each characters is numeric */
+ while( strIdx < lengths[idx] )
+ {
+ if( pdata[idx][strIdx] >= '0' && pdata[idx][strIdx] <= '9' )
+ {
+ strIdx++;
+ }
+ else
+ {
+ /* if we haven't reached the correct number of digits due to invalid data, log error,
+ * but first we read the valid data acquired so far into a buffer, this is done to avoid
+ * writing to the user provided string because if they did not size the string correctly
+ * writing a zero at the end could cause a segfault.
+ */
+
+ for( bufferIdx = 0; bufferIdx < strIdx + 1; bufferIdx++ )
+ {
+ buffer[bufferIdx] = pdata[idx][bufferIdx];
+ }
+ buffer[bufferIdx] = 0; /* terminate the string, prior to using in nwipe_log */
+
+ /* A typical error will look like ..
+ * "User provided year data that appear invalid = 202£" */
+ nwipe_log( NWIPE_LOG_ERROR, "User provided %s data that appears invalid = %s", names[idx], buffer );
+ return -1;
+ }
+ }
+ }
+
+ /**
+ * Now using the validated strings construct the date command that we will use to write the system date/time
+ */
+ sprintf( cmd, cmd_format, month, day, hours, minutes, year, seconds );
+
+ /**
+ * Run the date command to write the new date/time
+ */
+
+ fp = popen( cmd, "w" );
+ r = pclose( fp );
+
+ if( fp == NULL || r != 0 )
+ {
+ nwipe_log( NWIPE_LOG_ERROR, "Failed to write system date/time using command = %s", cmd );
+ }
+ else
+ {
+ nwipe_log( NWIPE_LOG_INFO, "Date/time succesfully writen to system using command = %s", cmd );
+ }
+
+ return 0;
+}
+
+void fix_endian_model_names( char* model )
+{
+ /* Some IDE USB adapters get the endian wrong, we can't determine the endian from the drive
+ * as the drive standard doesn't provide that information, so we have to resort to matching
+ * the model name against known strings with the endian incorrect, then reverse the endian.
+ */
+
+ int idx = 0;
+ int idx2 = 0;
+ unsigned int length = 0;
+ char* tmp_string;
+
+ length = strlen( model );
+
+ tmp_string = calloc( length, 1 );
+
+ /* "ASSMNU G" = "SAMSUNG ", tested against model Samsung HM160HC so that
+ * "ASSMNU G MH61H0 C" becomes "SAMSUNG HM160HC ")
+ */
+ if( !( strncmp( model, "ASSMNU G", 8 ) ) )
+ {
+ while( model[idx] != 0 )
+ {
+ tmp_string[idx2 + 1] = model[idx];
+ if( model[idx + 1] != 0 )
+ {
+ tmp_string[idx2] = model[idx + 1];
+ }
+ else
+ {
+ break;
+ }
+
+ if( tmp_string[idx2 + 1] == ' ' && model[idx + 2] == ' ' )
+ {
+ idx++;
+ }
+
+ idx += 2;
+ idx2 += 2;
+ }
+
+ tmp_string[idx2 + 1] = 0;
+ strcpy( model, tmp_string );
+ }
+}
diff --git a/src/miscellaneous.h b/src/miscellaneous.h
new file mode 100644
index 0000000..4d89a63
--- /dev/null
+++ b/src/miscellaneous.h
@@ -0,0 +1,132 @@
+/*
+ * miscellaneous.h: header file for miscellaneous.c ..
+ *
+ * functions that may be generally used throughout nwipes code,
+ * mainly string processing related functions.
+ *
+ * Copyright PartialVolume <https://github.com/PartialVolume>.
+ *
+ * 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, version 2.
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#ifndef MISCELLANEOUS_H_
+#define MISCELLANEOUS_H_
+
+#define FOUR_DIGITS 4
+#define TWO_DIGITS 2
+
+/**
+ * Convert the string from lower to upper case
+ * @param pointer to a null terminated string
+ * @return void
+ */
+void strupper( char* );
+
+/**
+ * Convert the string from upper to lower case
+ * @param pointer to a null terminated string
+ * @return void
+ */
+void strlower( char* str );
+
+/**
+ * Search a string for a positive number, convert the first
+ * number found to binary and return the binary number.
+ * returns the number or -1 if number too large or -2 if
+ * no number found.
+ *
+ * @param pointer to a null terminated string
+ * @return longlong returns:
+ * the number
+ * -1 = number too large
+ * -2 = no number found.
+ */
+u64 str_ascii_number_to_ll( char* );
+
+void Determine_C_B_nomenclature( u64, char*, int );
+void convert_seconds_to_hours_minutes_seconds( u64, int*, int*, int* );
+int nwipe_strip_path( char*, char* );
+
+/**
+ * Scan a string and replace any characters that are not alpha-numeric with
+ * the character_char.
+ * Example:
+ * char str[] = 18:21:56;
+ * calling the function replace_non_alphanumeric( &str, '_' )
+ * would result in str changing from 18:21:56 to 18_21_56
+ * @param char* pointer to the string to be processed
+ * @param char the character used to replace non alpha-numeric characters
+ * @return void
+ */
+void replace_non_alphanumeric( char*, char );
+
+/**
+ * I found this function necessary when converting a double of say
+ * 99.999999999999999999 to text using printf. I only wanted 99.99
+ * printed but if you specified a precision of %.2f in printf i.e 2 digits
+ * after the decimal point you get 100.00 and not 99.99 If you increase
+ * the precision to %.10f then you get 99.9999999999 but I only want
+ * two significant digits displayed.i.e 99.99% not 100%
+ * So this function converts to double retaining sufficient precision
+ * so that a 30TB disc with one hidden sector will display as 99.99% erased
+ * As an example if the double value to be converted is 99.999999999999999987
+ * this function will always output 99.99 unlike printf which outputs 100.00
+ * @param char* pointer to the string we write our percentage to. Needs to be
+ * a minimum of 7 bytes, i.e 100.00 plus null terminator.
+ * @param double the floating point value to be converted to a string.
+ * @return void
+ */
+void convert_double_to_string( char*, double );
+
+/**
+ * Reads system date & time and populates the caller provided strings.
+ * Each string is null terminated by this function. The calling
+ * program must provide the minimum string sizes as shown below.
+ *
+ * @param char* year 5 bytes (4 numeric digits plus NULL terminator)
+ * @param char* month 3 bytes (2 numeric digits plus NULL terminator)
+ * @param char* day 3 bytes (2 numeric digits plus NULL terminator)
+ * @param char* hours 3 bytes (2 numeric digits plus NULL terminator)
+ * @param char* minutes 3 bytes (2 numeric digits plus NULL terminator)
+ * @param char* seconds 3 bytes (2 numeric digits plus NULL terminator)
+ * @return 0 = success, -1 = failure. See nwipe log for detail.
+ */
+int read_system_datetime( char*, char*, char*, char*, char*, char* );
+
+/**
+ * Writes system date & time from the caller provided strings.
+ * The calling program must provide the minimum populated string sizes
+ * as shown below.
+ *
+ * @param char* year 5 bytes (4 numeric digits plus NULL terminator)
+ * @param char* month 3 bytes (2 numeric digits plus NULL terminator)
+ * @param char* day 3 bytes (2 numeric digits plus NULL terminator)
+ * @param char* hours 3 bytes (2 numeric digits plus NULL terminator)
+ * @param char* minutes 3 bytes (2 numeric digits plus NULL terminator)
+ * @param char* seconds 3 bytes (2 numeric digits plus NULL terminator)
+ * @return 0 = success, -1 = failure. See nwipe log for detail.
+ */
+int write_system_datetime( char*, char*, char*, char*, char*, char* );
+
+/**
+ * Fixes drive model names that have the incorrect endian. This happens
+ * with some IDE USB adapters.
+ *
+ * @param char* pointer to the drive model names
+ * @return void
+ */
+void fix_endian_model_names( char* model );
+
+#endif /* HPA_DCO_H_ */
diff --git a/src/mt19937ar-cok/mt19937ar-cok.c b/src/mt19937ar-cok/mt19937ar-cok.c
new file mode 100644
index 0000000..63ef659
--- /dev/null
+++ b/src/mt19937ar-cok/mt19937ar-cok.c
@@ -0,0 +1,139 @@
+/*
+ This code is modified for use in nwipe.
+
+ A C-program for MT19937, with initialization improved 2002/2/10.
+ Coded by Takuji Nishimura and Makoto Matsumoto.
+ This is a faster version by taking Shawn Cokus's optimization,
+ Matthe Bellew's simplification, Isaku Wada's real version.
+
+ Before using, initialize the state by using init_genrand(seed)
+ or init_by_array(init_key, key_length).
+
+ Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura,
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ 3. The names of its contributors may not be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+ Any feedback is very welcome.
+ http://www.math.keio.ac.jp/matumoto/emt.html
+ email: matumoto@math.keio.ac.jp
+*/
+
+#include <stdio.h>
+#include "mt19937ar-cok.h"
+
+/* initializes state[N] with a seed */
+void init_genrand( twister_state_t* state, unsigned long s)
+{
+ int j;
+ state->array[0]= s & 0xffffffffUL;
+ for( j = 1; j < N; j++ )
+ {
+ state->array[j] = (1812433253UL * (state->array[j-1] ^ (state->array[j-1] >> 30)) + j);
+ state->array[j] &= 0xffffffffUL; /* for >32 bit machines */
+ }
+ state->left = 1;
+ state->initf = 1;
+}
+
+
+void twister_init( twister_state_t* state, unsigned long init_key[], unsigned long key_length )
+{
+ int i = 1;
+ int j = 0;
+ int k = ( N > key_length ? N : key_length );
+
+ init_genrand( state, 19650218UL );
+
+ for( ; k; k-- )
+ {
+ state->array[i] = (state->array[i] ^ ((state->array[i-1] ^ (state->array[i-1] >> 30)) * 1664525UL)) + init_key[j] + j;
+ state->array[i] &= 0xffffffffUL; /* for WORDSIZE > 32 machines */
+ ++i;
+ ++j;
+
+ if ( i >= N )
+ {
+ state->array[0] = state->array[N-1];
+ i = 1;
+ }
+
+ if ( j >= key_length )
+ {
+ j = 0;
+ }
+ }
+
+ for( k = N -1; k; k-- )
+ {
+ state->array[i] = (state->array[i] ^ ((state->array[i-1] ^ (state->array[i-1] >> 30)) * 1566083941UL)) - i;
+ state->array[i] &= 0xffffffffUL;
+ ++i;
+
+ if ( i >= N )
+ {
+ state->array[0] = state->array[N-1];
+ i = 1;
+ }
+ }
+
+ state->array[0] = 0x80000000UL; /* MSB is 1; assuring non-zero initial array */
+ state->left = 1;
+ state->initf = 1;
+}
+
+static void next_state( twister_state_t* state )
+{
+ unsigned long *p = state->array;
+ int j;
+
+ if( state->initf == 0) { init_genrand( state, 5489UL ); }
+ state->left = N;
+ state->next = state->array;
+ for( j = N - M + 1; --j; p++ ) { *p = p[M] ^ TWIST(p[0], p[1]); }
+ for( j = M; --j; p++ ) { *p = p[M-N] ^ TWIST(p[0], p[1]); }
+ *p = p[M-N] ^ TWIST(p[0], state->array[0]);
+}
+
+/* generates a random number on [0,0xffffffff]-interval */
+unsigned long twister_genrand_int32( twister_state_t* state )
+{
+ unsigned long y;
+
+ if ( --state->left == 0 ) { next_state( state ); }
+ y = *state->next++;
+
+ /* Tempering */
+ y ^= (y >> 11);
+ y ^= (y << 7) & 0x9d2c5680UL;
+ y ^= (y << 15) & 0xefc60000UL;
+ y ^= (y >> 18);
+
+ return y;
+}
diff --git a/src/mt19937ar-cok/mt19937ar-cok.h b/src/mt19937ar-cok/mt19937ar-cok.h
new file mode 100644
index 0000000..afcc873
--- /dev/null
+++ b/src/mt19937ar-cok/mt19937ar-cok.h
@@ -0,0 +1,32 @@
+/*
+ * mt19937ar-cok.h: The Mersenne Twister PRNG implementation for nwipe.
+ *
+ */
+
+#ifndef MT19937AR_H_
+#define MT19937AR_H_
+
+/* Period parameters */
+#define N 624
+#define M 397
+#define MATRIX_A 0x9908b0dfUL /* constant vector a */
+#define UMASK 0x80000000UL /* most significant w-r bits */
+#define LMASK 0x7fffffffUL /* least significant r bits */
+#define MIXBITS(u,v) ( ((u) & UMASK) | ((v) & LMASK) )
+#define TWIST(u,v) ((MIXBITS(u,v) >> 1) ^ ((v)&1UL ? MATRIX_A : 0UL))
+
+typedef struct twister_state_t_
+{
+ unsigned long array[N];
+ int left;
+ int initf;
+ unsigned long *next;
+} twister_state_t;
+
+/* Initialize the MT state. ( 0 < key_length <= 624 ). */
+void twister_init( twister_state_t* state, unsigned long init_key[], unsigned long key_length);
+
+/* Generate a random integer on the [0,0xffffffff] interval. */
+unsigned long twister_genrand_int32( twister_state_t* state );
+
+#endif /* MT19937AR_H_ */
diff --git a/src/nwipe.c b/src/nwipe.c
new file mode 100644
index 0000000..9dc8110
--- /dev/null
+++ b/src/nwipe.c
@@ -0,0 +1,1134 @@
+/*
+ * nwipe.c: Darik's Wipe.
+ *
+ * Copyright Darik Horn <dajhorn-dban@vanadac.com>.
+ *
+ * Modifications to original dwipe Copyright Andy Beverley <andy@andybev.com>
+ *
+ * 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, version 2.
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+#ifndef _DEFAULT_SOURCE
+#define _DEFAULT_SOURCE
+#endif
+
+#ifndef _POSIX_SOURCE
+#define _POSIX_SOURCE
+#endif
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <signal.h>
+#include <pthread.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "nwipe.h"
+#include "context.h"
+#include "method.h"
+#include "prng.h"
+#include "options.h"
+#include "device.h"
+#include "logging.h"
+#include "gui.h"
+#include "temperature.h"
+#include "miscellaneous.h"
+
+#include <sys/ioctl.h> /* FIXME: Twice Included */
+#include <sys/shm.h>
+#include <wait.h>
+
+#include <parted/parted.h>
+#include <parted/debug.h>
+#include "conf.h"
+#include "version.h"
+#include "hpa_dco.h"
+#include "conf.h"
+#include <libconfig.h>
+
+int terminate_signal;
+int user_abort;
+int global_wipe_status;
+
+/* helper function for sorting */
+int devnamecmp( const void* a, const void* b )
+{
+ // nwipe_log( NWIPE_LOG_DEBUG, "a: %s, b: %s", ( *( nwipe_context_t** ) a)->device_name, ( *( nwipe_context_t** )
+ // b)->device_name );
+
+ int ldiff = strlen( ( *(nwipe_context_t**) a )->device_name ) - strlen( ( *(nwipe_context_t**) b )->device_name );
+ if( ldiff != 0 )
+ {
+ return ldiff;
+ }
+ int ret = strcmp( ( *(nwipe_context_t**) a )->device_name, ( *(nwipe_context_t**) b )->device_name );
+ return ( ret );
+}
+
+int main( int argc, char** argv )
+{
+ int nwipe_optind; // The result of nwipe_options().
+ int nwipe_enumerated; // The number of contexts that have been enumerated.
+ int nwipe_error = 0; // An error counter.
+ int nwipe_selected = 0; // The number of contexts that have been selected.
+ int any_threads_still_running; // used in wipe thread cancellation wait loop
+ int thread_timeout_counter; // timeout thread cancellation after THREAD_CANCELLATION_TIMEOUT seconds
+ pthread_t nwipe_gui_thread = 0; // The thread ID of the GUI thread.
+ pthread_t nwipe_temperature_thread = 0; // The thread ID of the temperature update thread
+ pthread_t nwipe_sigint_thread; // The thread ID of the sigint handler.
+
+ char modprobe_command[] = "modprobe %s";
+ char modprobe_command2[] = "/sbin/modprobe %s";
+ char modprobe_command3[] = "/usr/sbin/modprobe %s";
+ char module_shortform[50];
+ char final_cmd_modprobe[sizeof( modprobe_command ) + sizeof( module_shortform )];
+
+ /* The entropy source file handle. */
+ int nwipe_entropy;
+
+ /* The generic index variables. */
+ int i;
+ int j;
+
+ /* The generic result buffer. */
+ int r;
+
+ /* Initialise the termintaion signal, 1=terminate nwipe */
+ terminate_signal = 0;
+
+ /* Initialise the user abort signal, 1=User aborted with CNTRL-C,SIGTERM, SIGQUIT, SIGINT etc.. */
+ user_abort = 0;
+
+ /* nwipes return status value, set prior to exit at the end of nwipe, as no other exit points allowed */
+ int return_status = 0;
+
+ /* Initialise, flag indicating whether a wipe has actually started or not 0=no, 1=yes */
+ global_wipe_status = 0;
+
+ /* Initialise flags that indicate whether a fatal or non fatal error occurred on ANY drive */
+ int fatal_errors_flag = 0;
+ int non_fatal_errors_flag = 0;
+
+ /* Two arrays are used, containing pointers to the the typedef for each disk */
+ /* The first array (c1) points to all devices, the second points to only */
+ /* the disks selected for wiping. */
+
+ /* The array of pointers to enumerated contexts. */
+ /* Initialised and populated in device scan. */
+ nwipe_context_t** c1 = 0;
+
+ if( geteuid() != 0 )
+ {
+ printf( "nwipe must run with root permissions, which is not the case.\nAborting\n" );
+ exit( 99 );
+ }
+
+ int wipe_threads_started = 0;
+
+ /** NOTE ** NOTE ** NOTE ** NOTE ** NOTE ** NOTE ** NOTE ** NOTE ** NOTE **
+ * Important Note: if you want nwipe_log messages to go into the logfile
+ * any 'nwipe_log()' commands must appear after the options are parsed here,
+ * else they will appear in the console but not in the logfile, that is,
+ * assuming you specified a log file on the command line as an nwipe option.
+ */
+
+ /*****************************
+ * Parse command line options.
+ */
+
+ /* Initialise the libconfig code that handles nwipe.conf */
+ nwipe_conf_init();
+
+ nwipe_optind = nwipe_options_parse( argc, argv );
+
+ /* Log nwipes version */
+ nwipe_log( NWIPE_LOG_INFO, "%s", banner );
+
+ /* Log OS info */
+ nwipe_log_OSinfo();
+
+ /* Check that hdparm exists, we use hdparm for some HPA/DCO detection etc, if not
+ * exit nwipe. These checks are required if the PATH environment is not setup !
+ * Example: Debian sid 'su' as opposed to 'su -'
+ */
+ if( system( "which hdparm > /dev/null 2>&1" ) )
+ {
+ if( system( "which /sbin/hdparm > /dev/null 2>&1" ) )
+ {
+ if( system( "which /usr/bin/hdparm > /dev/null 2>&1" ) )
+ {
+ nwipe_log( NWIPE_LOG_WARNING, "hdparm command not found." );
+ nwipe_log( NWIPE_LOG_WARNING,
+ "Required by nwipe for HPA/DCO detection & correction and ATA secure erase." );
+ nwipe_log( NWIPE_LOG_WARNING, "** Please install hdparm **\n" );
+ cleanup();
+ exit( 1 );
+ }
+ }
+ }
+
+ /* Check if the given path for PDF reports is a writeable directory */
+ if( strcmp( nwipe_options.PDFreportpath, "noPDF" ) != 0 )
+ {
+ if( access( nwipe_options.PDFreportpath, W_OK ) != 0 )
+ {
+ nwipe_log( NWIPE_LOG_ERROR, "PDFreportpath %s is not a writeable directory.", nwipe_options.PDFreportpath );
+ cleanup();
+ exit( 2 );
+ }
+ }
+
+ if( nwipe_optind == argc )
+ {
+ /* File names were not given by the user. Scan for devices. */
+ nwipe_enumerated = nwipe_device_scan( &c1 );
+
+ if( terminate_signal == 1 )
+ {
+ cleanup();
+ exit( 1 );
+ }
+
+ if( nwipe_enumerated == 0 )
+ {
+ nwipe_log( NWIPE_LOG_INFO,
+ "Storage devices not found. Nwipe should be run as root or sudo/su, i.e sudo nwipe etc" );
+ cleanup();
+ return -1;
+ }
+ else
+ {
+ nwipe_log( NWIPE_LOG_INFO, "Automatically enumerated %i devices.", nwipe_enumerated );
+ }
+ }
+ else
+ {
+ argv += nwipe_optind;
+ argc -= nwipe_optind;
+
+ nwipe_enumerated = nwipe_device_get( &c1, argv, argc );
+ if( nwipe_enumerated == 0 )
+ {
+ nwipe_log( NWIPE_LOG_ERROR, "Devices not found. Check you're not excluding drives unnecessarily," );
+ nwipe_log( NWIPE_LOG_ERROR, "and you are running nwipe as sudo or as root." );
+ printf( "Devices not found, check you're not excluding drives unnecessarily \n and you are running nwipe "
+ "as sudo or as root." );
+ cleanup();
+ exit( 1 );
+ }
+ }
+
+ /* sort list of devices here */
+ qsort( (void*) c1, (size_t) nwipe_enumerated, sizeof( nwipe_context_t* ), devnamecmp );
+
+ if( terminate_signal == 1 )
+ {
+ cleanup();
+ exit( 1 );
+ }
+
+ /* Log the System information */
+ nwipe_log_sysinfo();
+
+ /* The array of pointers to contexts that will actually be wiped. */
+ nwipe_context_t** c2 = (nwipe_context_t**) malloc( nwipe_enumerated * sizeof( nwipe_context_t* ) );
+ if( c2 == NULL )
+ {
+ nwipe_log( NWIPE_LOG_ERROR, "memory allocation for c2 failed" );
+ cleanup();
+ exit( 1 );
+ }
+
+ /* Open the entropy source. */
+ nwipe_entropy = open( NWIPE_KNOB_ENTROPY, O_RDONLY );
+
+ /* Check the result. */
+ if( nwipe_entropy < 0 )
+ {
+ nwipe_perror( errno, __FUNCTION__, "open" );
+ nwipe_log( NWIPE_LOG_FATAL, "Unable to open entropy source %s.", NWIPE_KNOB_ENTROPY );
+ cleanup();
+ free( c2 );
+ return errno;
+ }
+
+ nwipe_log( NWIPE_LOG_NOTICE, "Opened entropy source '%s'.", NWIPE_KNOB_ENTROPY );
+
+ /* Block relevant signals in main thread. Any other threads that are */
+ /* created after this will also block those signals. */
+ sigset_t sigset;
+ sigemptyset( &sigset );
+ sigaddset( &sigset, SIGHUP );
+ sigaddset( &sigset, SIGTERM );
+ sigaddset( &sigset, SIGQUIT );
+ sigaddset( &sigset, SIGINT );
+ sigaddset( &sigset, SIGUSR1 );
+ pthread_sigmask( SIG_SETMASK, &sigset, NULL );
+
+ /* Create a signal handler thread. This thread will catch all */
+ /* signals and decide what to do with them. This will only */
+ /* catch nondirected signals. (I.e., if a thread causes a SIGFPE */
+ /* then that thread will get that signal. */
+
+ /* Pass a pointer to a struct containing all data to the signal handler. */
+ nwipe_misc_thread_data_t nwipe_misc_thread_data;
+ nwipe_thread_data_ptr_t nwipe_thread_data_ptr;
+
+ nwipe_thread_data_ptr.c = c2;
+ nwipe_misc_thread_data.nwipe_enumerated = nwipe_enumerated;
+ nwipe_misc_thread_data.nwipe_selected = 0;
+ if( !nwipe_options.nogui )
+ nwipe_misc_thread_data.gui_thread = &nwipe_gui_thread;
+ nwipe_thread_data_ptr.nwipe_misc_thread_data = &nwipe_misc_thread_data;
+
+ if( !nwipe_options.nosignals )
+ {
+ pthread_attr_t pthread_attr;
+ pthread_attr_init( &pthread_attr );
+ pthread_attr_setdetachstate( &pthread_attr, PTHREAD_CREATE_DETACHED );
+
+ pthread_create( &nwipe_sigint_thread, &pthread_attr, signal_hand, &nwipe_thread_data_ptr );
+ }
+
+ /* Makesure the drivetemp module is loaded, else drives hwmon entries won't appear in /sys/class/hwmon */
+ final_cmd_modprobe[0] = 0;
+
+ /* The kernel module we are going to load */
+ strcpy( module_shortform, "drivetemp" );
+
+ /* Determine whether we can access modprobe, required if the PATH environment is not setup ! (Debian sid 'su' as
+ * opposed to 'su -' */
+
+ if( system( "which modprobe > /dev/null 2>&1" ) )
+ {
+ if( system( "which /sbin/modprobe > /dev/null 2>&1" ) )
+ {
+ if( system( "which /usr/sbin/modprobe > /dev/null 2>&1" ) )
+ {
+ nwipe_log( NWIPE_LOG_WARNING, "modprobe command not found. Install kmod package (modprobe)) !" );
+ nwipe_log( NWIPE_LOG_WARNING, "Most temperature monitoring may be unavailable as module drivetemp" );
+ nwipe_log( NWIPE_LOG_WARNING, "could not be loaded. drivetemp is not available on kernels < v5.5" );
+ }
+ else
+ {
+ sprintf( final_cmd_modprobe, modprobe_command3, module_shortform );
+ }
+ }
+ else
+ {
+ sprintf( final_cmd_modprobe, modprobe_command2, module_shortform );
+ }
+ }
+ else
+ {
+ sprintf( final_cmd_modprobe, modprobe_command, module_shortform );
+ }
+
+ /* load the drivetemp module */
+ if( system( final_cmd_modprobe ) != 0 )
+ {
+ nwipe_log( NWIPE_LOG_WARNING, "hwmon: Unable to load module drivetemp, temperatures may be unavailable." );
+ nwipe_log( NWIPE_LOG_WARNING, "hwmon: It's possible the drivetemp software isn't modular but built-in" );
+ nwipe_log( NWIPE_LOG_WARNING, "hwmon: to the kernel, as is the case with ShredOS.x86_64 in which case" );
+ nwipe_log( NWIPE_LOG_WARNING, "hwmon: the temperatures will actually be available despite this issue." );
+ }
+ else
+ {
+ nwipe_log( NWIPE_LOG_NOTICE, "hwmon: Module drivetemp loaded, drive temperatures available" );
+ }
+
+ /* A context struct for each device has already been created. */
+ /* Now set specific nwipe options */
+ for( i = 0; i < nwipe_enumerated; i++ )
+ {
+
+ /* Set the entropy source. */
+ c1[i]->entropy_fd = nwipe_entropy;
+
+ if( nwipe_options.autonuke )
+ {
+ /* When the autonuke option is set, select all disks. */
+ // TODO - partitions
+ // if( c1[i].device_part == 0 ) { c1[i].select = NWIPE_SELECT_TRUE; }
+ // else { c1[i].select = NWIPE_SELECT_TRUE_PARENT; }
+ c1[i]->select = NWIPE_SELECT_TRUE;
+ }
+ else
+ {
+ /* The user must manually select devices. */
+ c1[i]->select = NWIPE_SELECT_FALSE;
+ }
+
+ /* Initialise temperature variables for device */
+ nwipe_init_temperature( c1[i] );
+ if( nwipe_options.verbose )
+ {
+ nwipe_log( NWIPE_LOG_NOTICE, "hwmon: Device %s hwmon path = %s", c1[i]->device_name, c1[i]->temp1_path );
+ }
+
+ // nwipe_update_temperature( c1[i] );
+
+ /* Log the temperature crtical, highest, lowest and lowest critical temperature
+ * limits to nwipes log file using the INFO catagory
+ */
+
+ nwipe_log_drives_temperature_limits( c1[i] );
+ }
+
+ /* Check for initialization errors. */
+ if( nwipe_error )
+ {
+ nwipe_log( NWIPE_LOG_ERROR, "Initialization error %i\n", nwipe_error );
+ cleanup();
+ return -1;
+ }
+
+ /* Set up the data structures to pass the temperature thread the data it needs */
+ nwipe_thread_data_ptr_t nwipe_temperature_thread_data;
+ nwipe_temperature_thread_data.c = c1;
+ nwipe_temperature_thread_data.nwipe_misc_thread_data = &nwipe_misc_thread_data;
+
+ /* Fork the temperature thread */
+ errno = pthread_create(
+ &nwipe_temperature_thread, NULL, nwipe_update_temperature_thread, &nwipe_temperature_thread_data );
+
+ /* Start the ncurses interface. */
+ if( !nwipe_options.nogui )
+ nwipe_gui_init();
+
+ if( nwipe_options.autonuke == 1 )
+ {
+ /* Print the options window. */
+ if( !nwipe_options.nogui )
+ nwipe_gui_options();
+ }
+ else
+ {
+ /* Get device selections from the user. */
+ if( nwipe_options.nogui )
+ {
+ printf( "--nogui option must be used with autonuke option\n" );
+ cleanup();
+ exit( 1 );
+ }
+ else
+ {
+ if( nwipe_options.PDF_preview_details == 1 )
+ {
+ nwipe_gui_preview_org_customer( SHOWING_PRIOR_TO_DRIVE_SELECTION );
+ }
+
+ nwipe_gui_select( nwipe_enumerated, c1 );
+ }
+ }
+
+ /* Initialise some of the variables in the drive contexts
+ */
+ for( i = 0; i < nwipe_enumerated; i++ )
+ {
+ /* Set the PRNG implementation, which must always come after the function nwipe_gui_select ! */
+ c1[i]->prng = nwipe_options.prng;
+ c1[i]->prng_seed.length = 0;
+ c1[i]->prng_seed.s = 0;
+ c1[i]->prng_state = 0;
+
+ /* Count the number of selected contexts. */
+ if( c1[i]->select == NWIPE_SELECT_TRUE )
+ {
+ nwipe_selected += 1;
+ }
+
+ /* Initialise the wipe result value */
+ c1[i]->result = 0;
+
+ /* Initialise the variable that tracks how much of the drive has been erased */
+ c1[i]->bytes_erased = 0;
+ }
+
+ /* Pass the number selected to the struct for other threads */
+ nwipe_misc_thread_data.nwipe_selected = nwipe_selected;
+
+ /* Populate the array of selected contexts. */
+ for( i = 0, j = 0; i < nwipe_enumerated; i++ )
+ {
+ if( c1[i]->select == NWIPE_SELECT_TRUE )
+ {
+ /* Copy the context. */
+ c2[j++] = c1[i];
+ }
+ }
+
+ /* TODO: free c1 and c2 memory. */
+ if( user_abort == 0 )
+ {
+ /* Log the wipe options that have been selected immediately prior to the start of the wipe */
+ nwipe_options_log();
+
+ /* The wipe has been initiated */
+ global_wipe_status = 1;
+
+ for( i = 0; i < nwipe_selected; i++ )
+ {
+ /* A result buffer for the BLKGETSIZE64 ioctl. */
+ u64 size64;
+
+ /* Initialise the spinner character index */
+ c2[i]->spinner_idx = 0;
+
+ /* Initialise the start and end time of the wipe */
+ c2[i]->start_time = 0;
+ c2[i]->end_time = 0;
+
+ /* Initialise the wipe_status flag, -1 = wipe not yet started */
+ c2[i]->wipe_status = -1;
+
+ /* Open the file for reads and writes. */
+ c2[i]->device_fd = open( c2[i]->device_name, O_RDWR );
+
+ /* Check the open() result. */
+ if( c2[i]->device_fd < 0 )
+ {
+ nwipe_perror( errno, __FUNCTION__, "open" );
+ nwipe_log( NWIPE_LOG_WARNING, "Unable to open device '%s'.", c2[i]->device_name );
+ c2[i]->select = NWIPE_SELECT_DISABLED;
+ continue;
+ }
+
+ /* Stat the file. */
+ if( fstat( c2[i]->device_fd, &c2[i]->device_stat ) != 0 )
+ {
+ nwipe_perror( errno, __FUNCTION__, "fstat" );
+ nwipe_log( NWIPE_LOG_ERROR, "Unable to stat file '%s'.", c2[i]->device_name );
+ nwipe_error++;
+ continue;
+ }
+
+ /* Check that the file is a block device. */
+ if( !S_ISBLK( c2[i]->device_stat.st_mode ) )
+ {
+ nwipe_log( NWIPE_LOG_ERROR, "'%s' is not a block device.", c2[i]->device_name );
+ nwipe_error++;
+ continue;
+ }
+
+ /* TODO: Lock the file for exclusive access. */
+ /*
+ if( flock( c2[i]->device_fd, LOCK_EX | LOCK_NB ) != 0 )
+ {
+ nwipe_perror( errno, __FUNCTION__, "flock" );
+ nwipe_log( NWIPE_LOG_ERROR, "Unable to lock the '%s' file.", c2[i]->device_name );
+ nwipe_error++;
+ continue;
+ }
+ */
+
+ /* Print serial number of device if it exists. */
+ if( strlen( (const char*) c2[i]->device_serial_no ) )
+ {
+ nwipe_log( NWIPE_LOG_NOTICE, "%s has serial number %s", c2[i]->device_name, c2[i]->device_serial_no );
+ }
+
+ /* Do sector size and block size checking. I don't think this does anything useful as logical/Physical
+ * sector sizes are obtained by libparted in check.c */
+ if( ioctl( c2[i]->device_fd, BLKSSZGET, &c2[i]->device_sector_size ) == 0 )
+ {
+
+ if( ioctl( c2[i]->device_fd, BLKBSZGET, &c2[i]->device_block_size ) != 0 )
+ {
+ nwipe_log( NWIPE_LOG_WARNING, "Device '%s' failed BLKBSZGET ioctl.", c2[i]->device_name );
+ c2[i]->device_block_size = 0;
+ }
+ }
+ else
+ {
+ nwipe_log( NWIPE_LOG_WARNING, "Device '%s' failed BLKSSZGET ioctl.", c2[i]->device_name );
+ c2[i]->device_sector_size = 0;
+ c2[i]->device_block_size = 0;
+ }
+
+ /* The st_size field is zero for block devices. */
+ /* ioctl( c2[i]->device_fd, BLKGETSIZE64, &c2[i]->device_size ); */
+
+ /* Seek to the end of the device to determine its size. */
+ c2[i]->device_size = lseek( c2[i]->device_fd, 0, SEEK_END );
+
+ /* Also ask the driver for the device size. */
+ /* if( ioctl( c2[i]->device_fd, BLKGETSIZE64, &size64 ) ) */
+ if( ioctl( c2[i]->device_fd, _IOR( 0x12, 114, size_t ), &size64 ) )
+ {
+ /* The ioctl failed. */
+ fprintf( stderr, "Error: BLKGETSIZE64 failed on '%s'.\n", c2[i]->device_name );
+ nwipe_log( NWIPE_LOG_ERROR, "BLKGETSIZE64 failed on '%s'.\n", c2[i]->device_name );
+ nwipe_error++;
+ }
+ c2[i]->device_size = size64;
+
+ /* Check whether the two size values agree. */
+ if( c2[i]->device_size != size64 )
+ {
+ /* This could be caused by the linux last-odd-block problem. */
+ fprintf( stderr, "Error: Last-odd-block detected on '%s'.\n", c2[i]->device_name );
+ nwipe_log( NWIPE_LOG_ERROR, "Last-odd-block detected on '%s'.", c2[i]->device_name );
+ nwipe_error++;
+ }
+
+ if( c2[i]->device_size == (long long) -1 )
+ {
+ /* We cannot determine the size of this device. */
+ nwipe_perror( errno, __FUNCTION__, "lseek" );
+ nwipe_log( NWIPE_LOG_ERROR, "Unable to determine the size of '%s'.", c2[i]->device_name );
+ nwipe_error++;
+ }
+ else
+ {
+ /* Reset the file pointer. */
+ r = lseek( c2[i]->device_fd, 0, SEEK_SET );
+
+ if( r == (off64_t) -1 )
+ {
+ nwipe_perror( errno, __FUNCTION__, "lseek" );
+ nwipe_log( NWIPE_LOG_ERROR, "Unable to reset the '%s' file offset.", c2[i]->device_name );
+ nwipe_error++;
+ }
+ }
+
+ if( c2[i]->device_size == 0 )
+ {
+ nwipe_log( NWIPE_LOG_ERROR,
+ "%s, sect/blk/dev %i/%i/%llu",
+ c2[i]->device_name,
+ c2[i]->device_sector_size,
+ c2[i]->device_block_size,
+ c2[i]->device_size );
+ nwipe_error++;
+ continue;
+ }
+ else
+ {
+ nwipe_log( NWIPE_LOG_NOTICE,
+ "%s, sect/blk/dev %i/%i/%llu",
+ c2[i]->device_name,
+ c2[i]->device_sector_size,
+ c2[i]->device_block_size,
+ c2[i]->device_size );
+ }
+
+ /* Fork a child process. */
+ errno = pthread_create( &c2[i]->thread, NULL, nwipe_options.method, (void*) c2[i] );
+ if( errno )
+ {
+ nwipe_perror( errno, __FUNCTION__, "pthread_create" );
+ if( !nwipe_options.nogui )
+ nwipe_gui_free();
+ return errno;
+ }
+ else
+ {
+ wipe_threads_started = 1;
+ }
+ }
+ }
+
+ /* Change the terminal mode to non-blocking input. */
+ nodelay( stdscr, 0 );
+
+ /* Set getch to delay in order to slow screen updates. */
+ halfdelay( NWIPE_KNOB_SLEEP * 10 );
+
+ /* Set up data structs to pass the GUI thread the data it needs. */
+ nwipe_thread_data_ptr_t nwipe_gui_data;
+ if( !nwipe_options.nogui )
+ {
+ nwipe_gui_data.c = c2;
+ nwipe_gui_data.nwipe_misc_thread_data = &nwipe_misc_thread_data;
+ /* Fork the GUI thread. */
+ errno = pthread_create( &nwipe_gui_thread, NULL, nwipe_gui_status, &nwipe_gui_data );
+ }
+
+ /* Wait for all the wiping threads to finish, but don't wait if we receive the terminate signal */
+
+ /* set getch delay to 2/10th second. */
+ halfdelay( 10 );
+
+ i = 0;
+ while( i < nwipe_selected && terminate_signal == 0 )
+ {
+ if( i == nwipe_selected )
+ {
+ break;
+ }
+
+ if( c2[i]->wipe_status != 0 )
+ {
+ i = 0;
+ }
+ else
+ {
+ i++;
+ continue;
+ }
+ sleep( 1 ); /* DO NOT REMOVE ! Stops the routine hogging CPU cycles */
+ }
+
+ if( terminate_signal != 1 )
+ {
+ if( !nwipe_options.nowait && !nwipe_options.autopoweroff )
+ {
+ do
+ {
+ sleep( 1 );
+
+ } while( terminate_signal != 1 );
+ }
+ }
+ if( nwipe_options.verbose )
+ {
+ nwipe_log( NWIPE_LOG_INFO, "Exit in progress" );
+ }
+ /* Send a REQUEST for the wipe threads to be cancelled */
+ for( i = 0; i < nwipe_selected; i++ )
+ {
+
+ if( c2[i]->thread )
+ {
+ if( nwipe_options.verbose )
+ {
+ nwipe_log( NWIPE_LOG_INFO, "Requesting wipe thread cancellation for %s", c2[i]->device_name );
+ }
+ pthread_cancel( c2[i]->thread );
+ }
+ }
+
+ /* Kill the GUI thread */
+ if( nwipe_gui_thread )
+ {
+ if( nwipe_options.verbose )
+ {
+ nwipe_log( NWIPE_LOG_INFO, "Cancelling the GUI thread." );
+ }
+
+ /* We don't want to use pthread_cancel as our GUI thread is aware of the control-c
+ * signal and will exit itself we just join the GUI thread and wait for confirmation
+ */
+ r = pthread_join( nwipe_gui_thread, NULL );
+ if( r != 0 )
+ {
+ nwipe_log( NWIPE_LOG_WARNING, "main()>pthread_join():Error when waiting for GUI thread to cancel." );
+ }
+ else
+ {
+ if( nwipe_options.verbose )
+ {
+ nwipe_log( NWIPE_LOG_INFO, "GUI compute_stats thread has been cancelled" );
+ }
+ }
+ }
+
+ /* Release the gui. */
+ if( !nwipe_options.nogui )
+ {
+ nwipe_gui_free();
+ }
+
+ /* Now join the wipe threads and wait until they have terminated */
+ any_threads_still_running = 1;
+ thread_timeout_counter = THREAD_CANCELLATION_TIMEOUT;
+ while( any_threads_still_running )
+ {
+ /* quit waiting if we've tried 'thread_timeout_counter' times */
+ if( thread_timeout_counter == 0 )
+ {
+ break;
+ }
+
+ any_threads_still_running = 0;
+ for( i = 0; i < nwipe_selected; i++ )
+ {
+ if( c2[i]->thread )
+ {
+ printf( "\nWaiting for wipe thread to cancel for %s\n", c2[i]->device_name );
+
+ /* Joins the thread and waits for completion before continuing */
+ r = pthread_join( c2[i]->thread, NULL );
+ if( r != 0 )
+ {
+ nwipe_log( NWIPE_LOG_ERROR,
+ "Error joining the wipe thread when waiting for thread to cancel.",
+ c2[i]->device_name );
+
+ if( r == EDEADLK )
+ {
+ nwipe_log( NWIPE_LOG_ERROR,
+ "Error joining the wipe thread: EDEADLK: Deadlock detected.",
+ c2[i]->device_name );
+ }
+ else
+ {
+ if( r == EINVAL )
+ {
+ nwipe_log( NWIPE_LOG_ERROR,
+ "Error joining the wipe thread: %s EINVAL: thread is not joinable.",
+ c2[i]->device_name );
+ }
+ else
+ {
+ if( r == ESRCH )
+ {
+ nwipe_log( NWIPE_LOG_ERROR,
+ "Error joining the wipe thread: %s ESRCH: no matching thread found",
+ c2[i]->device_name );
+ }
+ }
+ }
+
+ any_threads_still_running = 1;
+ }
+ else
+ {
+ c2[i]->thread = 0; /* Zero the thread so we know it's been cancelled */
+
+ if( nwipe_options.verbose )
+ {
+ nwipe_log( NWIPE_LOG_INFO, "Wipe thread for device %s has terminated", c2[i]->device_name );
+ }
+
+ /* Close the device file descriptor. */
+ close( c2[i]->device_fd );
+ }
+ }
+ }
+ thread_timeout_counter--;
+ sleep( 1 );
+ }
+
+ /* Now all the wipe threads have finished, we can issue a terminate_signal = 1
+ * which will cause the temperature update thread to terminate, this is necessary
+ * because in gui mode the terminate_signal is set when the user presses a key to
+ * exit on completion of all the wipes, however in non gui mode that code isn't
+ * active (being in the gui section) so here we need to set the terminate signal
+ * specifically for a completed wipes/s just for non gui mode.
+ */
+ terminate_signal = 1;
+
+ /* Kill the temperature update thread */
+ if( nwipe_temperature_thread )
+ {
+ if( nwipe_options.verbose )
+ {
+ nwipe_log( NWIPE_LOG_INFO, "Cancelling the temperature thread." );
+ }
+
+ /* We don't want to use pthread_cancel as our temperature thread is aware of the control-c
+ * signal and will exit itself we just join the temperature thread and wait for confirmation
+ */
+ r = pthread_join( nwipe_temperature_thread, NULL );
+ if( r != 0 )
+ {
+ nwipe_log( NWIPE_LOG_WARNING,
+ "main()>pthread_join():Error when waiting for temperature thread to cancel." );
+ }
+ else
+ {
+ if( nwipe_options.verbose )
+ {
+ nwipe_log( NWIPE_LOG_INFO, "temperature thread has been cancelled" );
+ }
+ }
+ }
+
+ if( nwipe_options.verbose )
+ {
+ for( i = 0; i < nwipe_selected; i++ )
+ {
+ nwipe_log( NWIPE_LOG_DEBUG,
+ "Status: %s, result=%d, pass_errors=%llu, verify_errors=%llu, fsync_errors=%llu",
+ c2[i]->device_name,
+ c2[i]->result,
+ c2[i]->pass_errors,
+ c2[i]->verify_errors,
+ c2[i]->fsyncdata_errors );
+ }
+ }
+
+ /* if no wipe threads started then zero each selected drive result flag,
+ * as we don't need to report fatal/non fatal errors if no wipes were ever started ! */
+ if( wipe_threads_started == 0 )
+ {
+ for( i = 0; i < nwipe_selected; i++ )
+ {
+ c2[i]->result = 0;
+ }
+ }
+ else
+ {
+ for( i = 0; i < nwipe_selected; i++ )
+ {
+ /* Check for errors. */
+ if( c2[i]->result != 0 || c2[i]->pass_errors != 0 || c2[i]->verify_errors != 0
+ || c2[i]->fsyncdata_errors != 0 )
+ {
+ /* If the run_method ever returns anything other than zero then makesure there is at least one pass
+ * error This is so that the log summary tables correctly show a failure when one occurs as it only
+ * shows pass, verification and fdatasync errors. */
+ if( c2[i]->result != 0 )
+ {
+ if( c2[i]->pass_errors == 0 )
+ {
+ c2[i]->pass_errors = 1;
+ }
+ }
+
+ nwipe_log( NWIPE_LOG_FATAL,
+ "Nwipe exited with errors on device = %s, see log for specific error\n",
+ c2[i]->device_name );
+ nwipe_log( NWIPE_LOG_DEBUG,
+ "Status: %s, result=%d, pass_errors=%llu, verify_errors=%llu, fsync_errors=%llu",
+ c2[i]->device_name,
+ c2[i]->result,
+ c2[i]->pass_errors,
+ c2[i]->verify_errors,
+ c2[i]->fsyncdata_errors );
+ non_fatal_errors_flag = 1;
+ return_status = 1;
+ }
+ }
+ }
+
+ /* Generate and send the drive status summary to the log */
+ nwipe_log_summary( c2, nwipe_selected );
+
+ /* Print a one line status message for the user */
+ if( return_status == 0 || return_status == 1 )
+ {
+ if( user_abort == 1 )
+ {
+ if( global_wipe_status == 1 )
+ {
+ nwipe_log( NWIPE_LOG_INFO,
+ "Nwipe was aborted by the user. Check the summary table for the drive status." );
+ }
+ else
+ {
+ nwipe_log( NWIPE_LOG_INFO, "Nwipe was aborted by the user prior to the wipe starting." );
+ }
+ }
+ else
+ {
+ if( fatal_errors_flag == 1 || non_fatal_errors_flag == 1 )
+ {
+ nwipe_log( NWIPE_LOG_INFO,
+ "Nwipe exited with errors, check the log & summary table for individual drive status." );
+ }
+ else
+ {
+ nwipe_log( NWIPE_LOG_INFO, "Nwipe successfully completed. See summary table for details." );
+ }
+ }
+ }
+
+ cleanup();
+
+ check_for_autopoweroff();
+
+ /* Exit. */
+ return return_status;
+}
+
+void* signal_hand( void* ptr )
+{
+ int sig;
+ int hours;
+ int minutes;
+ int seconds;
+
+ hours = 0;
+ minutes = 0;
+ seconds = 0;
+
+ // Define signals that this handler should react to
+ sigset_t sigset;
+ sigemptyset( &sigset );
+ sigaddset( &sigset, SIGHUP );
+ sigaddset( &sigset, SIGTERM );
+ sigaddset( &sigset, SIGQUIT );
+ sigaddset( &sigset, SIGINT );
+ sigaddset( &sigset, SIGUSR1 );
+
+ int i;
+ char eta[9];
+
+ /* Set up the structs we will use for the data required. */
+ nwipe_thread_data_ptr_t* nwipe_thread_data_ptr;
+ nwipe_context_t** c;
+ nwipe_misc_thread_data_t* nwipe_misc_thread_data;
+
+ /* Retrieve from the pointer passed to the function. */
+ nwipe_thread_data_ptr = (nwipe_thread_data_ptr_t*) ptr;
+ c = nwipe_thread_data_ptr->c;
+ nwipe_misc_thread_data = nwipe_thread_data_ptr->nwipe_misc_thread_data;
+
+ while( 1 )
+ {
+ /* wait for a signal to arrive */
+ sigwait( &sigset, &sig );
+
+ switch( sig )
+ {
+
+ // Log current status. All values are automatically updated by the GUI
+ case SIGUSR1:
+ compute_stats( ptr );
+
+ for( i = 0; i < nwipe_misc_thread_data->nwipe_selected; i++ )
+ {
+
+ if( c[i]->thread )
+ {
+ char* status = "";
+ switch( c[i]->pass_type )
+ {
+ case NWIPE_PASS_FINAL_BLANK:
+ status = "[blanking]";
+ break;
+
+ case NWIPE_PASS_FINAL_OPS2:
+ status = "[OPS-II final]";
+ break;
+
+ case NWIPE_PASS_WRITE:
+ status = "[writing]";
+ break;
+
+ case NWIPE_PASS_VERIFY:
+ status = "[verifying]";
+ break;
+
+ case NWIPE_PASS_NONE:
+ break;
+ }
+ if( c[i]->sync_status )
+ {
+ status = "[syncing]";
+ }
+
+ convert_seconds_to_hours_minutes_seconds( c[i]->eta, &hours, &minutes, &seconds );
+
+ nwipe_log( NWIPE_LOG_INFO,
+ "%s: %05.2f%%, round %i of %i, pass %i of %i, eta %02i:%02i:%02i, %s",
+ c[i]->device_name,
+ c[i]->round_percent,
+ c[i]->round_working,
+ c[i]->round_count,
+ c[i]->pass_working,
+ c[i]->pass_count,
+ hours,
+ minutes,
+ seconds,
+ status );
+ }
+ else
+ {
+ if( c[i]->result == 0 )
+ {
+ nwipe_log( NWIPE_LOG_INFO, "%s: Success", c[i]->device_name );
+ }
+ else if( c[i]->signal )
+ {
+ nwipe_log(
+ NWIPE_LOG_INFO, "%s: >>> FAILURE! <<<: signal %i", c[i]->device_name, c[i]->signal );
+ }
+ else
+ {
+ nwipe_log(
+ NWIPE_LOG_INFO, "%s: >>> FAILURE! <<<: code %i", c[i]->device_name, c[i]->result );
+ }
+ }
+ }
+
+ break;
+
+ case SIGHUP:
+ case SIGINT:
+ case SIGQUIT:
+ case SIGTERM:
+ /* Set termination flag for main() which will do housekeeping prior to exit */
+ terminate_signal = 1;
+
+ /* Set the user abort flag */
+ user_abort = 1;
+
+ /* Return control to the main thread, returning the signal received */
+ return ( (void*) 0 );
+
+ break;
+ }
+ }
+
+ return ( 0 );
+}
+
+int cleanup()
+{
+ int i;
+ extern int log_elements_displayed; // initialised and found in logging.c
+ extern int log_elements_allocated; // initialised and found in logging.c
+ extern char** log_lines;
+ extern config_t nwipe_cfg;
+
+ /* Print the logs held in memory to the console */
+ for( i = log_elements_displayed; i < log_elements_allocated; i++ )
+ {
+ printf( "%s\n", log_lines[i] );
+ }
+ fflush( stdout );
+
+ /* Deallocate memory used by logging */
+ if( log_elements_allocated != 0 )
+ {
+ for( i = 0; i < log_elements_allocated; i++ )
+ {
+ free( log_lines[i] );
+ }
+ log_elements_allocated = 0; // zeroed just in case cleanup is called twice.
+ free( log_lines );
+ }
+
+ /* Deallocate libconfig resources */
+ config_destroy( &nwipe_cfg );
+
+ /* TODO: Any other cleanup required ? */
+
+ return 0;
+}
+void check_for_autopoweroff( void )
+{
+ char cmd[] = "shutdown -Ph +1 \"System going down in one minute\"";
+ FILE* fp;
+ int r; // A result buffer.
+
+ /* User request auto power down ? */
+ if( nwipe_options.autopoweroff == 1 )
+ {
+ fp = popen( cmd, "r" );
+ if( fp == NULL )
+ {
+ nwipe_log( NWIPE_LOG_INFO, "Failed to autopoweroff to %s", cmd );
+ return;
+ }
+ r = pclose( fp );
+ }
+}
diff --git a/src/nwipe.h b/src/nwipe.h
new file mode 100644
index 0000000..4f1c495
--- /dev/null
+++ b/src/nwipe.h
@@ -0,0 +1,114 @@
+/*.
+ * nwipe.h: The header file of the nwipe program.
+ *
+ * Copyright Darik Horn <dajhorn-dban@vanadac.com>.
+ *
+ * Modifications to original dwipe Copyright Andy Beverley <andy@andybev.com>
+ *
+ * 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, version 2.
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#ifndef NWIPE_H_
+#define NWIPE_H_
+
+/* Function prototypes */
+int cleanup();
+void check_for_autopoweroff( void );
+void* signal_hand( void* );
+
+#ifndef _LARGEFILE64_SOURCE
+#define _LARGEFILE64_SOURCE
+#endif
+
+#ifndef _FILE_OFFSET_BITS
+#define _FILE_OFFSET_BITS 64
+#endif
+
+/* Busybox headers. */
+#ifdef BB_VER
+#include "busybox.h"
+#endif
+
+/* System headers. */
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <math.h>
+#include <pthread.h>
+#include <regex.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+/* workaround for Fedora */
+#ifndef off64_t
+#ifndef off_t
+#define off64_t int64_t
+#else
+#define off64_t off_t
+#endif
+#endif
+
+/*#include "config.h"*/
+
+/* System errors. */
+extern int errno;
+
+/* 0=wipe not yet started, 1=wipe has been started by the user */
+extern int global_wipe_status;
+
+/* Ncurses headers. */
+#ifdef NCURSES_IN_SUBDIR
+#include <ncurses/ncurses.h>
+#else
+#include <ncurses.h>
+#endif
+#ifdef PANEL_IN_SUBDIR
+#include <ncurses/panel.h>
+#else
+#include <panel.h>
+#endif
+
+/* Kernel device headers. */
+#include <linux/hdreg.h>
+
+/* These types are usually defined in <asm/types.h> for __KERNEL__ code. */
+typedef unsigned long long u64;
+typedef unsigned long u32;
+typedef unsigned short u16;
+typedef unsigned char u8;
+
+/* This is required for ioctl BLKGETSIZE64, but it conflicts with <wait.h>. */
+/* #include <linux/fs.h> */
+
+/* Define ioctls that cannot be included. */
+#define BLKSSZGET _IO( 0x12, 104 )
+#define BLKBSZGET _IOR( 0x12, 112, size_t )
+#define BLKBSZSET _IOW( 0x12, 113, size_t )
+#define BLKGETSIZE64 _IOR( 0x12, 114, sizeof( u64 ) )
+
+#define THREAD_CANCELLATION_TIMEOUT 10
+
+/* This is required for ioctl FDFLUSH. */
+#include <linux/fd.h>
+
+#endif /* NWIPE_H_ */
diff --git a/src/options.c b/src/options.c
new file mode 100644
index 0000000..95050db
--- /dev/null
+++ b/src/options.c
@@ -0,0 +1,707 @@
+/*
+ * options.c: Command line processing routines for nwipe.
+ *
+ * Copyright Darik Horn <dajhorn-dban@vanadac.com>.
+ *
+ * Modifications to original dwipe Copyright Andy Beverley <andy@andybev.com>
+ *
+ * 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, version 2.
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include "nwipe.h"
+#include "context.h"
+#include "method.h"
+#include "prng.h"
+#include "options.h"
+#include "logging.h"
+#include "version.h"
+#include "conf.h"
+
+/* The global options struct. */
+nwipe_options_t nwipe_options;
+
+int nwipe_options_parse( int argc, char** argv )
+{
+ extern char* optarg; // The working getopt option argument.
+ extern int optind; // The working getopt index into argv.
+ extern int optopt; // The last unhandled getopt option.
+ extern int opterr; // The last getopt error number.
+
+ extern nwipe_prng_t nwipe_twister;
+ extern nwipe_prng_t nwipe_isaac;
+ extern nwipe_prng_t nwipe_isaac64;
+
+ /* The getopt() result holder. */
+ int nwipe_opt;
+
+ /* Excluded drive indexes */
+ int idx_drive_chr;
+ int idx_optarg;
+ int idx_drive;
+
+ /* Array index variable. */
+ int i;
+
+ /* The list of acceptable short options. */
+ char nwipe_options_short[] = "Vvhl:P:m:p:qr:e:";
+
+ /* Used when reading value fron nwipe.conf */
+ const char* read_value = NULL;
+
+ int ret;
+
+ /* The list of acceptable long options. */
+ static struct option nwipe_options_long[] = {
+ /* Set when the user wants to wipe without a confirmation prompt. */
+ { "autonuke", no_argument, 0, 0 },
+
+ /* Set when the user wants to have the system powerdown on completion of wipe. */
+ { "autopoweroff", no_argument, 0, 0 },
+
+ /* A GNU standard option. Corresponds to the 'h' short option. */
+ { "help", no_argument, 0, 'h' },
+
+ /* The wipe method. Corresponds to the 'm' short option. */
+ { "method", required_argument, 0, 'm' },
+
+ /* Log file. Corresponds to the 'l' short option. */
+ { "logfile", required_argument, 0, 'l' },
+
+ /* PDFreport path. Corresponds to the 'P' short option. */
+ { "PDFreportpath", required_argument, 0, 'P' },
+
+ /* Exclude devices, comma separated list */
+ { "exclude", required_argument, 0, 'e' },
+
+ /* The Pseudo Random Number Generator. */
+ { "prng", required_argument, 0, 'p' },
+
+ /* The number of times to run the method. */
+ { "rounds", required_argument, 0, 'r' },
+
+ /* Whether to blank the disk after wiping. */
+ { "noblank", no_argument, 0, 0 },
+
+ /* Whether to ignore all USB devices. */
+ { "nousb", no_argument, 0, 0 },
+
+ /* Whether to exit after wiping or wait for a keypress. */
+ { "nowait", no_argument, 0, 0 },
+
+ /* Whether to allow signals to interrupt a wipe. */
+ { "nosignals", no_argument, 0, 0 },
+
+ /* Whether to display the gui. */
+ { "nogui", no_argument, 0, 0 },
+
+ /* Whether to anonymize the serial numbers. */
+ { "quiet", no_argument, 0, 'q' },
+
+ /* A flag to indicate whether the devices would be opened in sync mode. */
+ { "sync", required_argument, 0, 0 },
+
+ /* Verify that wipe patterns are being written to the device. */
+ { "verify", required_argument, 0, 0 },
+
+ /* Display program version. */
+ { "verbose", no_argument, 0, 'v' },
+
+ /* Display program version. */
+ { "version", no_argument, 0, 'V' },
+
+ /* Requisite padding for getopt(). */
+ { 0, 0, 0, 0 } };
+
+ /* Set default options. */
+ nwipe_options.autonuke = 0;
+ nwipe_options.autopoweroff = 0;
+ nwipe_options.method = &nwipe_dodshort;
+ nwipe_options.prng = ( sizeof( unsigned long int ) >= 8 ) ? &nwipe_isaac64 : &nwipe_isaac;
+ nwipe_options.rounds = 1;
+ nwipe_options.noblank = 0;
+ nwipe_options.nousb = 0;
+ nwipe_options.nowait = 0;
+ nwipe_options.nosignals = 0;
+ nwipe_options.nogui = 0;
+ nwipe_options.quiet = 0;
+ nwipe_options.sync = DEFAULT_SYNC_RATE;
+ nwipe_options.verbose = 0;
+ nwipe_options.verify = NWIPE_VERIFY_LAST;
+ memset( nwipe_options.logfile, '\0', sizeof( nwipe_options.logfile ) );
+ memset( nwipe_options.PDFreportpath, '\0', sizeof( nwipe_options.PDFreportpath ) );
+ strncpy( nwipe_options.PDFreportpath, ".", 2 );
+
+ /* Read PDF settings from nwipe.conf if available */
+ if( ( ret = nwipe_conf_read_setting( "PDF_Certificate.PDF_Enable", &read_value ) ) )
+ {
+ /* error occurred */
+ nwipe_log( NWIPE_LOG_ERROR,
+ "nwipe_conf_read_setting():Error reading PDF_Certificate.PDF_Enable from nwipe.conf, ret code %i",
+ ret );
+
+ /* Use default values */
+ nwipe_options.PDF_enable = 1;
+ }
+ else
+ {
+ if( !strcmp( read_value, "ENABLED" ) )
+ {
+ nwipe_options.PDF_enable = 1;
+ }
+ else
+ {
+ if( !strcmp( read_value, "DISABLED" ) )
+ {
+ nwipe_options.PDF_enable = 0;
+ }
+ else
+ {
+ // error occurred
+ nwipe_log(
+ NWIPE_LOG_ERROR,
+ "PDF_Certificate.PDF_Enable in nwipe.conf returned a value that was neither ENABLED or DISABLED" );
+ nwipe_options.PDF_enable = 1; // Default to Enabled
+ }
+ }
+ }
+
+ /* PDF Preview enable/disable */
+ if( ( ret = nwipe_conf_read_setting( "PDF_Certificate.PDF_Preview", &read_value ) ) )
+ {
+ /* error occurred */
+ nwipe_log( NWIPE_LOG_ERROR,
+ "nwipe_conf_read_setting():Error reading PDF_Certificate.PDF_Preview from nwipe.conf, ret code %i",
+ ret );
+
+ /* Use default values */
+ nwipe_options.PDF_enable = 1;
+ }
+ else
+ {
+ if( !strcmp( read_value, "ENABLED" ) )
+ {
+ nwipe_options.PDF_preview_details = 1;
+ }
+ else
+ {
+ if( !strcmp( read_value, "DISABLED" ) )
+ {
+ nwipe_options.PDF_preview_details = 0;
+ }
+ else
+ {
+ /* error occurred */
+ nwipe_log(
+ NWIPE_LOG_ERROR,
+ "PDF_Certificate.PDF_Preview in nwipe.conf returned a value that was neither ENABLED or DISABLED" );
+ nwipe_options.PDF_preview_details = 1; /* Default to Enabled */
+ }
+ }
+ }
+
+ /* Initialise each of the strings in the excluded drives array */
+ for( i = 0; i < MAX_NUMBER_EXCLUDED_DRIVES; i++ )
+ {
+ nwipe_options.exclude[i][0] = 0;
+ }
+
+ /* Parse command line options. */
+ while( 1 )
+ {
+ /* Get the next command line option with (3)getopt. */
+ nwipe_opt = getopt_long( argc, argv, nwipe_options_short, nwipe_options_long, &i );
+
+ /* Break when we have processed all of the given options. */
+ if( nwipe_opt < 0 )
+ {
+ break;
+ }
+
+ switch( nwipe_opt )
+ {
+ case 0: /* Long options without short counterparts. */
+
+ if( strcmp( nwipe_options_long[i].name, "autonuke" ) == 0 )
+ {
+ nwipe_options.autonuke = 1;
+ break;
+ }
+
+ if( strcmp( nwipe_options_long[i].name, "autopoweroff" ) == 0 )
+ {
+ nwipe_options.autopoweroff = 1;
+ break;
+ }
+
+ if( strcmp( nwipe_options_long[i].name, "noblank" ) == 0 )
+ {
+ nwipe_options.noblank = 1;
+ break;
+ }
+
+ if( strcmp( nwipe_options_long[i].name, "nousb" ) == 0 )
+ {
+ nwipe_options.nousb = 1;
+ break;
+ }
+
+ if( strcmp( nwipe_options_long[i].name, "nowait" ) == 0 )
+ {
+ nwipe_options.nowait = 1;
+ break;
+ }
+
+ if( strcmp( nwipe_options_long[i].name, "nosignals" ) == 0 )
+ {
+ nwipe_options.nosignals = 1;
+ break;
+ }
+
+ if( strcmp( nwipe_options_long[i].name, "nogui" ) == 0 )
+ {
+ nwipe_options.nogui = 1;
+ nwipe_options.nowait = 1;
+ break;
+ }
+
+ if( strcmp( nwipe_options_long[i].name, "verbose" ) == 0 )
+ {
+ nwipe_options.verbose = 1;
+ break;
+ }
+
+ if( strcmp( nwipe_options_long[i].name, "sync" ) == 0 )
+ {
+ if( sscanf( optarg, " %i", &nwipe_options.sync ) != 1 || nwipe_options.sync < 0 )
+ {
+ fprintf( stderr, "Error: The sync argument must be a positive integer or zero.\n" );
+ exit( EINVAL );
+ }
+ break;
+ }
+
+ if( strcmp( nwipe_options_long[i].name, "verify" ) == 0 )
+ {
+
+ if( strcmp( optarg, "0" ) == 0 || strcmp( optarg, "off" ) == 0 )
+ {
+ nwipe_options.verify = NWIPE_VERIFY_NONE;
+ break;
+ }
+
+ if( strcmp( optarg, "1" ) == 0 || strcmp( optarg, "last" ) == 0 )
+ {
+ nwipe_options.verify = NWIPE_VERIFY_LAST;
+ break;
+ }
+
+ if( strcmp( optarg, "2" ) == 0 || strcmp( optarg, "all" ) == 0 )
+ {
+ nwipe_options.verify = NWIPE_VERIFY_ALL;
+ break;
+ }
+
+ /* Else we do not know this verification level. */
+ fprintf( stderr, "Error: Unknown verification level '%s'.\n", optarg );
+ exit( EINVAL );
+ }
+
+ /* getopt_long should raise on invalid option, so we should never get here. */
+ exit( EINVAL );
+
+ case 'm': /* Method option. */
+
+ if( strcmp( optarg, "dod522022m" ) == 0 || strcmp( optarg, "dod" ) == 0 )
+ {
+ nwipe_options.method = &nwipe_dod522022m;
+ break;
+ }
+
+ if( strcmp( optarg, "dodshort" ) == 0 || strcmp( optarg, "dod3pass" ) == 0 )
+ {
+ nwipe_options.method = &nwipe_dodshort;
+ break;
+ }
+
+ if( strcmp( optarg, "gutmann" ) == 0 )
+ {
+ nwipe_options.method = &nwipe_gutmann;
+ break;
+ }
+
+ if( strcmp( optarg, "ops2" ) == 0 )
+ {
+ nwipe_options.method = &nwipe_ops2;
+ break;
+ }
+
+ if( strcmp( optarg, "random" ) == 0 || strcmp( optarg, "prng" ) == 0
+ || strcmp( optarg, "stream" ) == 0 )
+ {
+ nwipe_options.method = &nwipe_random;
+ break;
+ }
+
+ if( strcmp( optarg, "zero" ) == 0 || strcmp( optarg, "quick" ) == 0 )
+ {
+ nwipe_options.method = &nwipe_zero;
+ break;
+ }
+
+ if( strcmp( optarg, "one" ) == 0 )
+ {
+ nwipe_options.method = &nwipe_one;
+ break;
+ }
+
+ if( strcmp( optarg, "verify_zero" ) == 0 )
+ {
+ nwipe_options.method = &nwipe_verify_zero;
+ break;
+ }
+
+ if( strcmp( optarg, "verify_one" ) == 0 )
+ {
+ nwipe_options.method = &nwipe_verify_one;
+ break;
+ }
+
+ if( strcmp( optarg, "is5enh" ) == 0 )
+ {
+ nwipe_options.method = &nwipe_is5enh;
+ break;
+ }
+
+ /* Else we do not know this wipe method. */
+ fprintf( stderr, "Error: Unknown wipe method '%s'.\n", optarg );
+ exit( EINVAL );
+
+ case 'l': /* Log file option. */
+
+ nwipe_options.logfile[strlen( optarg )] = '\0';
+ strncpy( nwipe_options.logfile, optarg, sizeof( nwipe_options.logfile ) );
+ break;
+
+ case 'P': /* PDFreport path option. */
+
+ nwipe_options.PDFreportpath[strlen( optarg )] = '\0';
+ strncpy( nwipe_options.PDFreportpath, optarg, sizeof( nwipe_options.PDFreportpath ) );
+
+ /* Command line options will override what's in nwipe.conf */
+ if( strcmp( nwipe_options.PDFreportpath, "noPDF" ) == 0 )
+ {
+ nwipe_options.PDF_enable = 0;
+ nwipe_conf_update_setting( "PDF_Certificate.PDF_Enable", "DISABLED" );
+ }
+ else
+ {
+ if( strcmp( nwipe_options.PDFreportpath, "." ) )
+ {
+ /* and if the user has specified a PDF path then enable PDF */
+ nwipe_options.PDF_enable = 1;
+ nwipe_conf_update_setting( "PDF_Certificate.PDF_Enable", "ENABLED" );
+ }
+ }
+
+ break;
+
+ case 'e': /* exclude drives option */
+
+ idx_drive_chr = 0;
+ idx_optarg = 0;
+ idx_drive = 0;
+
+ /* Create an array of excluded drives from the comma separated string */
+ while( optarg[idx_optarg] != 0 && idx_drive < MAX_NUMBER_EXCLUDED_DRIVES )
+ {
+ /* drop the leading '=' character if used */
+ if( optarg[idx_optarg] == '=' && idx_optarg == 0 )
+ {
+ idx_optarg++;
+ continue;
+ }
+
+ if( optarg[idx_optarg] == ',' )
+ {
+ /* terminate string and move onto next drive */
+ nwipe_options.exclude[idx_drive++][idx_drive_chr] = 0;
+ idx_drive_chr = 0;
+ idx_optarg++;
+ }
+ else
+ {
+ if( idx_drive_chr < MAX_DRIVE_PATH_LENGTH )
+ {
+ nwipe_options.exclude[idx_drive][idx_drive_chr++] = optarg[idx_optarg++];
+ }
+ else
+ {
+ /* This section deals with file names that exceed MAX_DRIVE_PATH_LENGTH */
+ nwipe_options.exclude[idx_drive][idx_drive_chr] = 0;
+ while( optarg[idx_optarg] != 0 && optarg[idx_optarg] != ',' )
+ {
+ idx_optarg++;
+ }
+ }
+ }
+ if( idx_drive == MAX_NUMBER_EXCLUDED_DRIVES )
+ {
+ fprintf(
+ stderr,
+ "The number of excluded drives has reached the programs configured limit, aborting\n" );
+ exit( 130 );
+ }
+ }
+ break;
+
+ case 'h': /* Display help. */
+
+ display_help();
+ break;
+
+ case 'p': /* PRNG option. */
+
+ if( strcmp( optarg, "mersenne" ) == 0 || strcmp( optarg, "twister" ) == 0 )
+ {
+ nwipe_options.prng = &nwipe_twister;
+ break;
+ }
+
+ if( strcmp( optarg, "isaac" ) == 0 )
+ {
+ nwipe_options.prng = &nwipe_isaac;
+ break;
+ }
+
+ if( strcmp( optarg, "isaac64" ) == 0 )
+ {
+ nwipe_options.prng = &nwipe_isaac64;
+ break;
+ }
+
+ /* Else we do not know this PRNG. */
+ fprintf( stderr, "Error: Unknown prng '%s'.\n", optarg );
+ exit( EINVAL );
+
+ case 'q': /* Anonymize serial numbers */
+
+ nwipe_options.quiet = 1;
+ break;
+
+ case 'r': /* Rounds option. */
+
+ if( sscanf( optarg, " %i", &nwipe_options.rounds ) != 1 || nwipe_options.rounds < 1 )
+ {
+ fprintf( stderr, "Error: The rounds argument must be a positive integer.\n" );
+ exit( EINVAL );
+ }
+
+ break;
+
+ case 'v': /* verbose */
+
+ nwipe_options.verbose = 1;
+ break;
+
+ case 'V': /* Version option. */
+
+ printf( "%s version %s\n", program_name, version_string );
+ exit( EXIT_SUCCESS );
+
+ default:
+
+ /* Bogus command line argument. */
+ display_help();
+ exit( EINVAL );
+
+ } /* method */
+
+ } /* command line options */
+
+ /* Return the number of options that were processed. */
+ return optind;
+}
+
+void nwipe_options_log( void )
+{
+ extern nwipe_prng_t nwipe_twister;
+ extern nwipe_prng_t nwipe_isaac;
+ extern nwipe_prng_t nwipe_isaac64;
+
+ /**
+ * Prints a manifest of options to the log.
+ */
+
+ nwipe_log( NWIPE_LOG_NOTICE, "Program options are set as follows..." );
+
+ if( nwipe_options.autonuke )
+ {
+ nwipe_log( NWIPE_LOG_NOTICE, " autonuke = %i (on)", nwipe_options.autonuke );
+ }
+ else
+ {
+ nwipe_log( NWIPE_LOG_NOTICE, " autonuke = %i (off)", nwipe_options.autonuke );
+ }
+
+ if( nwipe_options.autopoweroff )
+ {
+ nwipe_log( NWIPE_LOG_NOTICE, " autopoweroff = %i (on)", nwipe_options.autopoweroff );
+ }
+ else
+ {
+ nwipe_log( NWIPE_LOG_NOTICE, " autopoweroff = %i (off)", nwipe_options.autopoweroff );
+ }
+
+ if( nwipe_options.noblank )
+ {
+ nwipe_log( NWIPE_LOG_NOTICE, " do not perform a final blank pass" );
+ }
+
+ if( nwipe_options.nowait )
+ {
+ nwipe_log( NWIPE_LOG_NOTICE, " do not wait for a key before exiting" );
+ }
+
+ if( nwipe_options.nosignals )
+ {
+ nwipe_log( NWIPE_LOG_NOTICE, " do not allow signals to interrupt a wipe" );
+ }
+
+ if( nwipe_options.nogui )
+ {
+ nwipe_log( NWIPE_LOG_NOTICE, " do not show GUI interface" );
+ }
+
+ nwipe_log( NWIPE_LOG_NOTICE, " banner = %s", banner );
+
+ if( nwipe_options.prng == &nwipe_twister )
+ {
+ nwipe_log( NWIPE_LOG_NOTICE, " prng = Mersenne Twister" );
+ }
+ else
+ {
+ if( nwipe_options.prng == &nwipe_isaac )
+ {
+ nwipe_log( NWIPE_LOG_NOTICE, " prng = Isaac" );
+ }
+ else
+ {
+ if( nwipe_options.prng == &nwipe_isaac64 )
+ {
+ nwipe_log( NWIPE_LOG_NOTICE, " prng = Isaac64" );
+ }
+ else
+ {
+ nwipe_log( NWIPE_LOG_NOTICE, " prng = Undefined" );
+ }
+ }
+ }
+
+ nwipe_log( NWIPE_LOG_NOTICE, " method = %s", nwipe_method_label( nwipe_options.method ) );
+ nwipe_log( NWIPE_LOG_NOTICE, " quiet = %i", nwipe_options.quiet );
+ nwipe_log( NWIPE_LOG_NOTICE, " rounds = %i", nwipe_options.rounds );
+ nwipe_log( NWIPE_LOG_NOTICE, " sync = %i", nwipe_options.sync );
+
+ switch( nwipe_options.verify )
+ {
+ case NWIPE_VERIFY_NONE:
+ nwipe_log( NWIPE_LOG_NOTICE, " verify = %i (off)", nwipe_options.verify );
+ break;
+
+ case NWIPE_VERIFY_LAST:
+ nwipe_log( NWIPE_LOG_NOTICE, " verify = %i (last pass)", nwipe_options.verify );
+ break;
+
+ case NWIPE_VERIFY_ALL:
+ nwipe_log( NWIPE_LOG_NOTICE, " verify = %i (all passes)", nwipe_options.verify );
+ break;
+
+ default:
+ nwipe_log( NWIPE_LOG_NOTICE, " verify = %i", nwipe_options.verify );
+ break;
+ }
+}
+
+void display_help()
+{
+ /**
+ * displays the help section to STDOUT and exits.
+ */
+
+ printf( "Usage: %s [options] [device1] [device2] ...\n", program_name );
+ printf( "Options:\n" );
+ /* Limit line length to a maximum of 80 characters so it looks good in 80x25 terminals i.e shredos */
+ /* ___12345678901234567890123456789012345678901234567890123456789012345678901234567890< Do not exceed */
+ puts( " -V, --version Prints the version number\n" );
+ puts( " -v, --verbose Prints more messages to the log\n" );
+ puts( " -h, --help Prints this help\n" );
+ puts( " --autonuke If no devices have been specified on the command line," );
+ puts( " starts wiping all devices immediately. If devices have" );
+ puts( " been specified, starts wiping only those specified" );
+ puts( " devices immediately.\n" );
+ puts( " --autopoweroff Power off system on completion of wipe delayed for" );
+ puts( " for one minute. During this one minute delay you can" );
+ puts( " abort the shutdown by typing sudo shutdown -c\n" );
+ printf( " --sync=NUM Will perform a sync after NUM writes (default: %d)\n", DEFAULT_SYNC_RATE );
+ puts( " 0 - fdatasync after the disk is completely written" );
+ puts( " fdatasync errors not detected until completion." );
+ puts( " 0 is not recommended as disk errors may cause" );
+ puts( " nwipe to appear to hang" );
+ puts( " 1 - fdatasync after every write" );
+ puts( " Warning: Lower values will reduce wipe speeds." );
+ puts( " 1000 - fdatasync after 1000 writes etc.\n" );
+ puts( " --verify=TYPE Whether to perform verification of erasure" );
+ puts( " (default: last)" );
+ puts( " off - Do not verify" );
+ puts( " last - Verify after the last pass" );
+ puts( " all - Verify every pass\n" );
+ puts( " -m, --method=METHOD The wiping method. See man page for more details." );
+ puts( " (default: dodshort)" );
+ puts( " dod522022m / dod - 7 pass DOD 5220.22-M method" );
+ puts( " dodshort / dod3pass - 3 pass DOD method" );
+ puts( " gutmann - Peter Gutmann's Algorithm" );
+ puts( " ops2 - RCMP TSSIT OPS-II" );
+ puts( " random / prng / stream - PRNG Stream" );
+ puts( " zero / quick - Overwrite with zeros" );
+ puts( " one - Overwrite with ones (0xFF)" );
+ puts( " verify_zero - Verifies disk is zero filled" );
+ puts( " verify_one - Verifies disk is 0xFF filled\n" );
+ puts( " -l, --logfile=FILE Filename to log to. Default is STDOUT\n" );
+ puts( " -P, --PDFreportpath=PATH Path to write PDF reports to. Default is \".\"" );
+ puts( " If set to \"noPDF\" no PDF reports are written.\n" );
+ puts( " -p, --prng=METHOD PRNG option (mersenne|twister|isaac|isaac64)\n" );
+ puts( " -q, --quiet Anonymize logs and the GUI by removing unique data, i.e." );
+ puts( " serial numbers, LU WWN Device ID, and SMBIOS/DMI data" );
+ puts( " XXXXXX = S/N exists, ????? = S/N not obtainable\n" );
+ puts( " -r, --rounds=NUM Number of times to wipe the device using the selected" );
+ puts( " method (default: 1)\n" );
+ puts( " --noblank Do NOT blank disk after wipe" );
+ puts( " (default is to complete a final blank pass)\n" );
+ puts( " --nowait Do NOT wait for a key before exiting" );
+ puts( " (default is to wait)\n" );
+ puts( " --nosignals Do NOT allow signals to interrupt a wipe" );
+ puts( " (default is to allow)\n" );
+ puts( " --nogui Do NOT show the GUI interface. Automatically invokes" );
+ puts( " the nowait option. Must be used with the --autonuke" );
+ puts( " option. Send SIGUSR1 to log current stats\n" );
+ puts( " --nousb Do NOT show or wipe any USB devices whether in GUI" );
+ puts( " mode, --nogui or --autonuke modes.\n" );
+ puts( " -e, --exclude=DEVICES Up to ten comma separated devices to be excluded" );
+ puts( " --exclude=/dev/sdc" );
+ puts( " --exclude=/dev/sdc,/dev/sdd" );
+ puts( " --exclude=/dev/sdc,/dev/sdd,/dev/mapper/cryptswap1\n" );
+ puts( "" );
+ exit( EXIT_SUCCESS );
+}
diff --git a/src/options.h b/src/options.h
new file mode 100644
index 0000000..96aeefc
--- /dev/null
+++ b/src/options.h
@@ -0,0 +1,76 @@
+/*
+ * options.h: Command line processing routines for nwipe.
+ *
+ * Copyright Darik Horn <dajhorn-dban@vanadac.com>.
+ *
+ * Modifications to original dwipe Copyright Andy Beverley <andy@andybev.com>
+ *
+ * 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, version 2.
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#ifndef OPTIONS_H_
+#define OPTIONS_H_
+
+/* Program knobs. */
+#define NWIPE_KNOB_ENTROPY "/dev/urandom"
+#define NWIPE_KNOB_IDENTITY_SIZE 512
+#define NWIPE_KNOB_LABEL_SIZE 128
+#define NWIPE_KNOB_LOADAVG "/proc/loadavg"
+#define NWIPE_KNOB_LOG_BUFFERSIZE 1024 // Maximum length of a log event.
+#define NWIPE_KNOB_PARTITIONS "/proc/partitions"
+#define NWIPE_KNOB_PARTITIONS_PREFIX "/dev/"
+#define NWIPE_KNOB_PRNG_STATE_LENGTH 512 // 128 words
+#define NWIPE_KNOB_SCSI "/proc/scsi/scsi"
+#define NWIPE_KNOB_SLEEP 1
+#define NWIPE_KNOB_STAT "/proc/stat"
+#define MAX_NUMBER_EXCLUDED_DRIVES 32
+#define MAX_DRIVE_PATH_LENGTH 200 // e.g. /dev/sda is only 8 characters long, so 200 should be plenty.
+#define DEFAULT_SYNC_RATE 100000
+#define PATHNAME_MAX 2048
+
+/* Function prototypes for loading options from the environment and command line. */
+int nwipe_options_parse( int argc, char** argv );
+void nwipe_options_log( void );
+
+/* Function to display help text */
+void display_help();
+
+typedef struct
+{
+ int autonuke; // Do not prompt the user for confirmation when set.
+ int autopoweroff; // Power off on completion of wipe
+ int noblank; // Do not perform a final blanking pass.
+ int nousb; // Do not show or wipe any USB devices.
+ int nowait; // Do not wait for a final key before exiting.
+ int nosignals; // Do not allow signals to interrupt a wipe.
+ int nogui; // Do not show the GUI.
+ char* banner; // The product banner shown on the top line of the screen.
+ void* method; // A function pointer to the wipe method that will be used.
+ char logfile[FILENAME_MAX]; // The filename to log the output to.
+ char PDFreportpath[PATHNAME_MAX]; // The path to write the PDF report to.
+ char exclude[MAX_NUMBER_EXCLUDED_DRIVES][MAX_DRIVE_PATH_LENGTH]; // Drives excluded from the search.
+ nwipe_prng_t* prng; // The pseudo random number generator implementation. pointer to the function.
+ int quiet; // Anonymize serial numbers
+ int rounds; // The number of times that the wipe method should be called.
+ int sync; // A flag to indicate whether and how often writes should be sync'd.
+ int verbose; // Make log more verbose
+ int PDF_enable; // 0=PDF creation disabled, 1=PDF creation enabled
+ int PDF_preview_details; // 0=Disable preview Org/Cust/date/time before drive selection, 1=Enable Preview
+ nwipe_verify_t verify; // A flag to indicate whether writes should be verified.
+} nwipe_options_t;
+
+extern nwipe_options_t nwipe_options;
+
+#endif /* OPTIONS_H_ */
diff --git a/src/pass.c b/src/pass.c
new file mode 100644
index 0000000..9c730da
--- /dev/null
+++ b/src/pass.c
@@ -0,0 +1,911 @@
+/*
+ * pass.c: Routines that read and write patterns to block devices.
+ *
+ * Copyright Darik Horn <dajhorn-dban@vanadac.com>.
+ *
+ * Modifications to original dwipe Copyright Andy Beverley <andy@andybev.com>
+ *
+ * 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, version 2.
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#define _POSIX_C_SOURCE 200809L
+
+#include <stdint.h>
+#include "nwipe.h"
+#include "context.h"
+#include "method.h"
+#include "prng.h"
+#include "options.h"
+#include "pass.h"
+#include "logging.h"
+#include "gui.h"
+
+int nwipe_random_verify( nwipe_context_t* c )
+{
+ /**
+ * Verifies that a random pass was correctly written to the device.
+ *
+ */
+
+ /* The result holder. */
+ int r;
+
+ /* The IO size. */
+ size_t blocksize;
+
+ /* The result buffer for calls to lseek. */
+ off64_t offset;
+
+ /* The input buffer. */
+ char* b;
+
+ /* The pattern buffer that is used to check the input buffer. */
+ char* d;
+
+ /* The number of bytes remaining in the pass. */
+ u64 z = c->device_size;
+
+ if( c->prng_seed.s == NULL )
+ {
+ nwipe_log( NWIPE_LOG_SANITY, "Null seed pointer." );
+ return -1;
+ }
+
+ if( c->prng_seed.length <= 0 )
+ {
+ nwipe_log( NWIPE_LOG_SANITY, "The entropy length member is %i.", c->prng_seed.length );
+ return -1;
+ }
+
+ /* Create the input buffer. */
+ b = malloc( c->device_stat.st_blksize );
+
+ /* Check the memory allocation. */
+ if( !b )
+ {
+ nwipe_perror( errno, __FUNCTION__, "malloc" );
+ nwipe_log( NWIPE_LOG_FATAL, "Unable to allocate memory for the input buffer." );
+ return -1;
+ }
+
+ /* Create the pattern buffer */
+ d = malloc( c->device_stat.st_blksize );
+
+ /* Check the memory allocation. */
+ if( !d )
+ {
+ nwipe_perror( errno, __FUNCTION__, "malloc" );
+ nwipe_log( NWIPE_LOG_FATAL, "Unable to allocate memory for the pattern buffer." );
+ free( b );
+ return -1;
+ }
+
+ /* Reset the file pointer. */
+ offset = lseek( c->device_fd, 0, SEEK_SET );
+
+ /* Reset the pass byte counter. */
+ c->pass_done = 0;
+
+ if( offset == (off64_t) -1 )
+ {
+ nwipe_perror( errno, __FUNCTION__, "lseek" );
+ nwipe_log( NWIPE_LOG_FATAL, "Unable to reset the '%s' file offset.", c->device_name );
+ free( b );
+ free( d );
+ return -1;
+ }
+
+ if( offset != 0 )
+ {
+ /* This is system insanity. */
+ nwipe_log( NWIPE_LOG_SANITY, "lseek() returned a bogus offset on '%s'.", c->device_name );
+ free( b );
+ free( d );
+ return -1;
+ }
+
+ /* Tell our parent that we are syncing the device. */
+ c->sync_status = 1;
+
+ /* Sync the device. */
+ r = fdatasync( c->device_fd );
+
+ /* Tell our parent that we have finished syncing the device. */
+ c->sync_status = 0;
+
+ if( r != 0 )
+ {
+ nwipe_perror( errno, __FUNCTION__, "fdatasync" );
+ nwipe_log( NWIPE_LOG_WARNING, "Buffer flush failure on '%s'.", c->device_name );
+ c->fsyncdata_errors++;
+ }
+
+ /* Reseed the PRNG. */
+ c->prng->init( &c->prng_state, &c->prng_seed );
+
+ while( z > 0 )
+ {
+ if( c->device_stat.st_blksize <= z )
+ {
+ blocksize = c->device_stat.st_blksize;
+ }
+ else
+ {
+ /* This is a seatbelt for buggy drivers and programming errors because */
+ /* the device size should always be an even multiple of its blocksize. */
+ blocksize = z;
+ nwipe_log( NWIPE_LOG_WARNING,
+ "%s: The size of '%s' is not a multiple of its block size %i.",
+ __FUNCTION__,
+ c->device_name,
+ c->device_stat.st_blksize );
+ }
+
+ /* Fill the output buffer with the random pattern. */
+ c->prng->read( &c->prng_state, d, blocksize );
+
+ /* Read the buffer in from the device. */
+ r = read( c->device_fd, b, blocksize );
+
+ /* Check the result. */
+ if( r < 0 )
+ {
+ nwipe_perror( errno, __FUNCTION__, "read" );
+ nwipe_log( NWIPE_LOG_ERROR, "Unable to read from '%s'.", c->device_name );
+ return -1;
+ }
+
+ /* Check for a partial read. */
+ if( r != blocksize )
+ {
+ /* TODO: Handle a partial read. */
+
+ /* The number of bytes that were not read. */
+ int s = blocksize - r;
+
+ nwipe_log(
+ NWIPE_LOG_WARNING, "%s: Partial read from '%s', %i bytes short.", __FUNCTION__, c->device_name, s );
+
+ /* Increment the error count. */
+ c->verify_errors += 1;
+
+ /* Bump the file pointer to the next block. */
+ offset = lseek( c->device_fd, s, SEEK_CUR );
+
+ if( offset == (off64_t) -1 )
+ {
+ nwipe_perror( errno, __FUNCTION__, "lseek" );
+ nwipe_log(
+ NWIPE_LOG_ERROR, "Unable to bump the '%s' file offset after a partial read.", c->device_name );
+ return -1;
+ }
+
+ } /* partial read */
+
+ /* Compare buffer contents. */
+ if( memcmp( b, d, blocksize ) != 0 )
+ {
+ c->verify_errors += 1;
+ }
+
+ /* Decrement the bytes remaining in this pass. */
+ z -= r;
+
+ /* Increment the total progress counters. */
+ c->pass_done += r;
+ c->round_done += r;
+
+ pthread_testcancel();
+
+ } /* while bytes remaining */
+
+ /* Release the buffers. */
+ free( b );
+ free( d );
+
+ /* We're done. */
+ return 0;
+
+} /* nwipe_random_verify */
+
+int nwipe_random_pass( NWIPE_METHOD_SIGNATURE )
+{
+ /**
+ * Writes a random pattern to the device.
+ *
+ */
+
+ /* The result holder. */
+ int r;
+
+ /* The IO size. */
+ size_t blocksize;
+
+ /* The result buffer for calls to lseek. */
+ off64_t offset;
+
+ /* The output buffer. */
+ char* b;
+
+ /* The number of bytes remaining in the pass. */
+ u64 z = c->device_size;
+
+ /* Number of writes to do before a fdatasync. */
+ int syncRate = nwipe_options.sync;
+
+ /* Counter to track when to do a fdatasync. */
+ int i = 0;
+
+ /* general index counter */
+ int idx;
+
+ if( c->prng_seed.s == NULL )
+ {
+ nwipe_log( NWIPE_LOG_SANITY, "__FUNCTION__: Null seed pointer." );
+ return -1;
+ }
+
+ if( c->prng_seed.length <= 0 )
+ {
+ nwipe_log( NWIPE_LOG_SANITY, "__FUNCTION__: The entropy length member is %i.", c->prng_seed.length );
+ return -1;
+ }
+
+ /* Create the initialised output buffer. Initialised because we don't want memory leaks
+ * to disk in the event of some future undetected bug in a prng or its implementation. */
+ b = calloc( c->device_stat.st_blksize, sizeof( char ) );
+
+ /* Check the memory allocation. */
+ if( !b )
+ {
+ nwipe_perror( errno, __FUNCTION__, "malloc" );
+ nwipe_log( NWIPE_LOG_FATAL, "Unable to allocate memory for the output buffer." );
+ return -1;
+ }
+
+ /* Seed the PRNG. */
+ c->prng->init( &c->prng_state, &c->prng_seed );
+
+ /* Reset the file pointer. */
+ offset = lseek( c->device_fd, 0, SEEK_SET );
+
+ /* Reset the pass byte counter. */
+ c->pass_done = 0;
+
+ if( offset == (off64_t) -1 )
+ {
+ nwipe_perror( errno, __FUNCTION__, "lseek" );
+ nwipe_log( NWIPE_LOG_FATAL, "Unable to reset the '%s' file offset.", c->device_name );
+ free( b );
+ return -1;
+ }
+
+ if( offset != 0 )
+ {
+ /* This is system insanity. */
+ nwipe_log( NWIPE_LOG_SANITY, "__FUNCTION__: lseek() returned a bogus offset on '%s'.", c->device_name );
+ free( b );
+ return -1;
+ }
+
+ while( z > 0 )
+ {
+ if( c->device_stat.st_blksize <= z )
+ {
+ blocksize = c->device_stat.st_blksize;
+ }
+ else
+ {
+ /* This is a seatbelt for buggy drivers and programming errors because */
+ /* the device size should always be an even multiple of its blocksize. */
+ blocksize = z;
+ nwipe_log( NWIPE_LOG_WARNING,
+ "%s: The size of '%s' is not a multiple of its block size %i.",
+ __FUNCTION__,
+ c->device_name,
+ c->device_stat.st_blksize );
+ }
+
+ /* Fill the output buffer with the random pattern. */
+ c->prng->read( &c->prng_state, b, blocksize );
+
+ /* For the first block only, check the prng actually wrote something to the buffer */
+ if( z == c->device_size )
+ {
+ idx = c->device_stat.st_blksize;
+ while( idx > 0 )
+ {
+ if( b[idx] != 0 )
+ {
+ nwipe_log( NWIPE_LOG_NOTICE, "prng stream is active" );
+ break;
+ }
+ idx--;
+ }
+ if( idx == 0 )
+ {
+ nwipe_log( NWIPE_LOG_FATAL, "ERROR, prng wrote nothing to the buffer" );
+ if( c->bytes_erased < ( c->device_size - z ) ) // How much of the device has been erased?
+ {
+ c->bytes_erased = c->device_size - z;
+ }
+ return -1;
+ }
+ }
+
+ /* Write the next block out to the device. */
+ r = write( c->device_fd, b, blocksize );
+
+ /* Check the result for a fatal error. */
+ if( r < 0 )
+ {
+ nwipe_perror( errno, __FUNCTION__, "write" );
+ nwipe_log( NWIPE_LOG_FATAL, "Unable to read from '%s'.", c->device_name );
+ if( c->bytes_erased < ( c->device_size - z ) ) // How much of the device has been erased?
+ {
+ c->bytes_erased = c->device_size - z;
+ }
+ return -1;
+ }
+
+ /* Check for a partial write. */
+ if( r != blocksize )
+ {
+ /* TODO: Handle a partial write. */
+
+ /* The number of bytes that were not written. */
+ int s = blocksize - r;
+
+ /* Increment the error count by the number of bytes that were not written. */
+ c->pass_errors += s;
+
+ nwipe_log( NWIPE_LOG_WARNING, "Partial write on '%s', %i bytes short.", c->device_name, s );
+
+ /* Bump the file pointer to the next block. */
+ offset = lseek( c->device_fd, s, SEEK_CUR );
+
+ if( offset == (off64_t) -1 )
+ {
+ nwipe_perror( errno, __FUNCTION__, "lseek" );
+ nwipe_log(
+ NWIPE_LOG_ERROR, "Unable to bump the '%s' file offset after a partial write.", c->device_name );
+ if( c->bytes_erased < ( c->device_size - z ) ) // How much of the device has been erased?
+ {
+ c->bytes_erased = c->device_size - z;
+ }
+ return -1;
+ }
+
+ } /* partial write */
+
+ /* Decrement the bytes remaining in this pass. */
+ z -= r;
+
+ /* Increment the total progress counters. */
+ c->pass_done += r;
+ c->round_done += r;
+
+ /* Perodic Sync */
+ if( syncRate > 0 )
+ {
+ i++;
+
+ if( i >= syncRate )
+ {
+ /* Tell our parent that we are syncing the device. */
+ c->sync_status = 1;
+
+ /* Sync the device. */
+ r = fdatasync( c->device_fd );
+
+ /* Tell our parent that we have finished syncing the device. */
+ c->sync_status = 0;
+
+ if( r != 0 )
+ {
+ nwipe_perror( errno, __FUNCTION__, "fdatasync" );
+ nwipe_log( NWIPE_LOG_WARNING, "Buffer flush failure on '%s'.", c->device_name );
+ nwipe_log( NWIPE_LOG_WARNING, "Wrote %llu bytes on '%s'.", c->pass_done, c->device_name );
+ c->fsyncdata_errors++;
+ free( b );
+ if( c->bytes_erased < ( c->device_size - z ) ) // How much of the device has been erased?
+ {
+ c->bytes_erased = c->device_size - z;
+ }
+ return -1;
+ }
+
+ i = 0;
+ }
+ }
+
+ pthread_testcancel();
+
+ /* If statement required so that it does not reset on subsequent passes */
+ if( c->bytes_erased < ( c->device_size - z ) ) // How much of the device has been erased?
+ {
+ c->bytes_erased = c->device_size - z;
+ }
+
+ } /* /remaining bytes */
+
+ /* Release the output buffer. */
+ free( b );
+
+ /* Tell our parent that we are syncing the device. */
+ c->sync_status = 1;
+
+ /* Sync the device. */
+ r = fdatasync( c->device_fd );
+
+ /* Tell our parent that we have finished syncing the device. */
+ c->sync_status = 0;
+
+ if( r != 0 )
+ {
+ nwipe_perror( errno, __FUNCTION__, "fdatasync" );
+ nwipe_log( NWIPE_LOG_WARNING, "Buffer flush failure on '%s'.", c->device_name );
+ c->fsyncdata_errors++;
+ if( c->bytes_erased < ( c->device_size - z - blocksize ) ) // How much of the device has been erased?
+ {
+ c->bytes_erased = c->device_size - z - blocksize;
+ }
+ return -1;
+ }
+
+ /* We're done. */
+ return 0;
+
+} /* nwipe_random_pass */
+
+int nwipe_static_verify( NWIPE_METHOD_SIGNATURE, nwipe_pattern_t* pattern )
+{
+ /**
+ * Verifies that a static pass was correctly written to the device.
+ */
+
+ /* The result holder. */
+ int r;
+
+ /* The IO size. */
+ size_t blocksize;
+
+ /* The result buffer for calls to lseek. */
+ off64_t offset;
+
+ /* The input buffer. */
+ char* b;
+
+ /* The pattern buffer that is used to check the input buffer. */
+ char* d;
+
+ /* A pointer into the pattern buffer. */
+ char* q;
+
+ /* The pattern buffer window offset. */
+ int w = 0;
+
+ /* The number of bytes remaining in the pass. */
+ u64 z = c->device_size;
+
+ if( pattern == NULL )
+ {
+ /* Caught insanity. */
+ nwipe_log( NWIPE_LOG_SANITY, "nwipe_static_verify: Null entropy pointer." );
+ return -1;
+ }
+
+ if( pattern->length <= 0 )
+ {
+ /* Caught insanity. */
+ nwipe_log( NWIPE_LOG_SANITY, "nwipe_static_verify: The pattern length member is %i.", pattern->length );
+ return -1;
+ }
+
+ /* Create the input buffer. */
+ b = malloc( c->device_stat.st_blksize );
+
+ /* Check the memory allocation. */
+ if( !b )
+ {
+ nwipe_perror( errno, __FUNCTION__, "malloc" );
+ nwipe_log( NWIPE_LOG_FATAL, "Unable to allocate memory for the input buffer." );
+ return -1;
+ }
+
+ /* Create the pattern buffer */
+ d = malloc( c->device_stat.st_blksize + pattern->length * 2 );
+
+ /* Check the memory allocation. */
+ if( !d )
+ {
+ nwipe_perror( errno, __FUNCTION__, "malloc" );
+ nwipe_log( NWIPE_LOG_FATAL, "Unable to allocate memory for the pattern buffer." );
+ free( b );
+ return -1;
+ }
+
+ for( q = d; q < d + c->device_stat.st_blksize + pattern->length; q += pattern->length )
+ {
+ /* Fill the pattern buffer with the pattern. */
+ memcpy( q, pattern->s, pattern->length );
+ }
+
+ /* Tell our parent that we are syncing the device. */
+ c->sync_status = 1;
+
+ /* Sync the device. */
+ r = fdatasync( c->device_fd );
+
+ /* Tell our parent that we have finished syncing the device. */
+ c->sync_status = 0;
+
+ if( r != 0 )
+ {
+ nwipe_perror( errno, __FUNCTION__, "fdatasync" );
+ nwipe_log( NWIPE_LOG_WARNING, "Buffer flush failure on '%s'.", c->device_name );
+ c->fsyncdata_errors++;
+ }
+
+ /* Reset the file pointer. */
+ offset = lseek( c->device_fd, 0, SEEK_SET );
+
+ /* Reset the pass byte counter. */
+ c->pass_done = 0;
+
+ if( offset == (off64_t) -1 )
+ {
+ nwipe_perror( errno, __FUNCTION__, "lseek" );
+ nwipe_log( NWIPE_LOG_FATAL, "Unable to reset the '%s' file offset.", c->device_name );
+ free( b );
+ return -1;
+ }
+
+ if( offset != 0 )
+ {
+ /* This is system insanity. */
+ nwipe_log( NWIPE_LOG_SANITY, "nwipe_static_verify: lseek() returned a bogus offset on '%s'.", c->device_name );
+ free( b );
+ free( d );
+ return -1;
+ }
+
+ while( z > 0 )
+ {
+ if( c->device_stat.st_blksize <= z )
+ {
+ blocksize = c->device_stat.st_blksize;
+ }
+ else
+ {
+ /* This is a seatbelt for buggy drivers and programming errors because */
+ /* the device size should always be an even multiple of its blocksize. */
+ blocksize = z;
+ nwipe_log( NWIPE_LOG_WARNING,
+ "%s: The size of '%s' is not a multiple of its block size %i.",
+ __FUNCTION__,
+ c->device_name,
+ c->device_stat.st_blksize );
+ }
+
+ /* Fill the output buffer with the random pattern. */
+ /* Read the buffer in from the device. */
+ r = read( c->device_fd, b, blocksize );
+
+ /* Check the result. */
+ if( r < 0 )
+ {
+ nwipe_perror( errno, __FUNCTION__, "read" );
+ nwipe_log( NWIPE_LOG_ERROR, "Unable to read from '%s'.", c->device_name );
+ return -1;
+ }
+
+ /* Check for a partial read. */
+ if( r == blocksize )
+ {
+ /* Check every byte in the buffer. */
+ if( memcmp( b, &d[w], r ) != 0 )
+ {
+ c->verify_errors += 1;
+ }
+ }
+ else
+ {
+ /* The number of bytes that were not read. */
+ int s = blocksize - r;
+
+ /* TODO: Handle a partial read. */
+
+ /* Increment the error count. */
+ c->verify_errors += 1;
+
+ nwipe_log( NWIPE_LOG_WARNING, "Partial read on '%s', %i bytes short.", c->device_name, s );
+
+ /* Bump the file pointer to the next block. */
+ offset = lseek( c->device_fd, s, SEEK_CUR );
+
+ if( offset == (off64_t) -1 )
+ {
+ nwipe_perror( errno, __FUNCTION__, "lseek" );
+ nwipe_log(
+ NWIPE_LOG_ERROR, "Unable to bump the '%s' file offset after a partial read.", c->device_name );
+ return -1;
+ }
+
+ } /* partial read */
+
+ /* Adjust the window. */
+ w = ( c->device_stat.st_blksize + w ) % pattern->length;
+
+ /* Intuition check:
+ * If the pattern length evenly divides the block size
+ * then ( w == 0 ) always.
+ */
+
+ /* Decrement the bytes remaining in this pass. */
+ z -= r;
+
+ /* Increment the total progress counters. */
+ c->pass_done += r;
+ c->round_done += r;
+
+ pthread_testcancel();
+
+ } /* while bytes remaining */
+
+ /* Release the buffers. */
+ free( b );
+ free( d );
+
+ /* We're done. */
+ return 0;
+
+} /* nwipe_static_verify */
+
+int nwipe_static_pass( NWIPE_METHOD_SIGNATURE, nwipe_pattern_t* pattern )
+{
+ /**
+ * Writes a static pattern to the device.
+ */
+
+ /* The result holder. */
+ int r;
+
+ /* The IO size. */
+ size_t blocksize;
+
+ /* The result buffer for calls to lseek. */
+ off64_t offset;
+
+ /* The output buffer. */
+ char* b;
+
+ /* A pointer into the output buffer. */
+ char* p;
+
+ /* The output buffer window offset. */
+ int w = 0;
+
+ /* The number of bytes remaining in the pass. */
+ u64 z = c->device_size;
+
+ /* Number of writes to do before a fdatasync. */
+ int syncRate = nwipe_options.sync;
+
+ /* Counter to track when to do a fdatasync. */
+ int i = 0;
+
+ if( pattern == NULL )
+ {
+ /* Caught insanity. */
+ nwipe_log( NWIPE_LOG_SANITY, "__FUNCTION__: Null pattern pointer." );
+ return -1;
+ }
+
+ if( pattern->length <= 0 )
+ {
+ /* Caught insanity. */
+ nwipe_log( NWIPE_LOG_SANITY, "__FUNCTION__: The pattern length member is %i.", pattern->length );
+ return -1;
+ }
+
+ /* Create the output buffer. */
+ b = malloc( c->device_stat.st_blksize + pattern->length * 2 );
+
+ /* Check the memory allocation. */
+ if( !b )
+ {
+ nwipe_perror( errno, __FUNCTION__, "malloc" );
+ nwipe_log( NWIPE_LOG_FATAL, "Unable to allocate memory for the pattern buffer." );
+ return -1;
+ }
+
+ for( p = b; p < b + c->device_stat.st_blksize + pattern->length; p += pattern->length )
+ {
+ /* Fill the output buffer with the pattern. */
+ memcpy( p, pattern->s, pattern->length );
+ }
+ ///
+ /* Reset the file pointer. */
+ offset = lseek( c->device_fd, 0, SEEK_SET );
+
+ /* Reset the pass byte counter. */
+ c->pass_done = 0;
+
+ if( offset == (off64_t) -1 )
+ {
+ nwipe_perror( errno, __FUNCTION__, "lseek" );
+ nwipe_log( NWIPE_LOG_FATAL, "Unable to reset the '%s' file offset.", c->device_name );
+ return -1;
+ }
+
+ if( offset != 0 )
+ {
+ /* This is system insanity. */
+ nwipe_log( NWIPE_LOG_SANITY, "__FUNCTION__: lseek() returned a bogus offset on '%s'.", c->device_name );
+ return -1;
+ }
+
+ while( z > 0 )
+ {
+ if( c->device_stat.st_blksize <= z )
+ {
+ blocksize = c->device_stat.st_blksize;
+ }
+ else
+ {
+ /* This is a seatbelt for buggy drivers and programming errors because */
+ /* the device size should always be an even multiple of its blocksize. */
+ blocksize = z;
+ nwipe_log( NWIPE_LOG_WARNING,
+ "%s: The size of '%s' is not a multiple of its block size %i.",
+ __FUNCTION__,
+ c->device_name,
+ c->device_stat.st_blksize );
+ }
+
+ /* Fill the output buffer with the random pattern. */
+ /* Write the next block out to the device. */
+ r = write( c->device_fd, &b[w], blocksize );
+
+ /* Check the result for a fatal error. */
+ if( r < 0 )
+ {
+ nwipe_perror( errno, __FUNCTION__, "write" );
+ nwipe_log( NWIPE_LOG_FATAL, "Unable to write to '%s'.", c->device_name );
+ if( c->bytes_erased < ( c->device_size - z ) ) // How much of the device has been erased?
+ {
+ c->bytes_erased = c->device_size - z;
+ }
+ return -1;
+ }
+
+ /* Check for a partial write. */
+ if( r != blocksize )
+ {
+ /* TODO: Handle a partial write. */
+
+ /* The number of bytes that were not written. */
+ int s = blocksize - r;
+
+ /* Increment the error count. */
+ c->pass_errors += s;
+
+ nwipe_log( NWIPE_LOG_WARNING, "Partial write on '%s', %i bytes short.", c->device_name, s );
+
+ /* Bump the file pointer to the next block. */
+ offset = lseek( c->device_fd, s, SEEK_CUR );
+
+ if( offset == (off64_t) -1 )
+ {
+ nwipe_perror( errno, __FUNCTION__, "lseek" );
+ nwipe_log(
+ NWIPE_LOG_ERROR, "Unable to bump the '%s' file offset after a partial write.", c->device_name );
+ if( c->bytes_erased < ( c->device_size - z ) ) // How much of the device has been erased?
+ {
+ c->bytes_erased = c->device_size - z;
+ }
+ return -1;
+ }
+
+ } /* partial write */
+
+ /* Adjust the window. */
+ w = ( c->device_stat.st_blksize + w ) % pattern->length;
+
+ /* Intuition check:
+ *
+ * If the pattern length evenly divides the block size
+ * then ( w == 0 ) always.
+ */
+
+ /* Decrement the bytes remaining in this pass. */
+ z -= r;
+
+ /* Increment the total progress counterr. */
+ c->pass_done += r;
+ c->round_done += r;
+
+ /* Perodic Sync */
+ if( syncRate > 0 )
+ {
+ i++;
+
+ if( i >= syncRate )
+ {
+ /* Tell our parent that we are syncing the device. */
+ c->sync_status = 1;
+
+ /* Sync the device. */
+ r = fdatasync( c->device_fd );
+
+ /* Tell our parent that we have finished syncing the device. */
+ c->sync_status = 0;
+
+ if( r != 0 )
+ {
+ nwipe_perror( errno, __FUNCTION__, "fdatasync" );
+ nwipe_log( NWIPE_LOG_WARNING, "Buffer flush failure on '%s'.", c->device_name );
+ nwipe_log( NWIPE_LOG_WARNING, "Wrote %llu bytes on '%s'.", c->pass_done, c->device_name );
+ c->fsyncdata_errors++;
+ free( b );
+ if( c->bytes_erased < ( c->device_size - z ) ) // How much of the device has been erased?
+ {
+ c->bytes_erased = c->device_size - z;
+ }
+ return -1;
+ }
+
+ i = 0;
+ }
+ }
+
+ pthread_testcancel();
+
+ if( c->bytes_erased < ( c->device_size - z ) ) // How much of the device has been erased?
+ {
+ c->bytes_erased = c->device_size - z;
+ }
+
+ } /* /remaining bytes */
+
+ /* Tell our parent that we are syncing the device. */
+ c->sync_status = 1;
+
+ /* Sync the device. */
+ r = fdatasync( c->device_fd );
+
+ /* Tell our parent that we have finished syncing the device. */
+ c->sync_status = 0;
+
+ if( r != 0 )
+ {
+ nwipe_perror( errno, __FUNCTION__, "fdatasync" );
+ nwipe_log( NWIPE_LOG_WARNING, "Buffer flush failure on '%s'.", c->device_name );
+ c->fsyncdata_errors++;
+ if( c->bytes_erased < ( c->device_size - z - blocksize ) ) // How much of the device has been erased?
+ {
+ c->bytes_erased = c->device_size - z - blocksize;
+ }
+ return -1;
+ }
+
+ /* Release the output buffer. */
+ free( b );
+
+ /* We're done. */
+ return 0;
+
+} /* nwipe_static_pass */
diff --git a/src/pass.h b/src/pass.h
new file mode 100644
index 0000000..8f27f43
--- /dev/null
+++ b/src/pass.h
@@ -0,0 +1,33 @@
+/*
+ * pass.h: Routines that read and write patterns to block devices.
+ *
+ * Copyright Darik Horn <dajhorn-dban@vanadac.com>.
+ *
+ * Modifications to original dwipe Copyright Andy Beverley <andy@andybev.com>
+ *
+ * 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, version 2.
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#ifndef PASS_H_
+#define PASS_H_
+
+int nwipe_random_pass( nwipe_context_t* c );
+int nwipe_random_verify( nwipe_context_t* c );
+int nwipe_static_pass( nwipe_context_t* c, nwipe_pattern_t* pattern );
+int nwipe_static_verify( nwipe_context_t* c, nwipe_pattern_t* pattern );
+
+void test_functionn( int count, nwipe_context_t** c );
+
+#endif /* PASS_H_ */
diff --git a/src/prng.c b/src/prng.c
new file mode 100644
index 0000000..45c6cb7
--- /dev/null
+++ b/src/prng.c
@@ -0,0 +1,252 @@
+/*
+ * prng.c: Pseudo Random Number Generator abstractions for nwipe.
+ *
+ * Copyright Darik Horn <dajhorn-dban@vanadac.com>.
+ *
+ * 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, version 2.
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "nwipe.h"
+#include "prng.h"
+#include "context.h"
+#include "logging.h"
+
+#include "mt19937ar-cok/mt19937ar-cok.h"
+#include "isaac_rand/isaac_rand.h"
+#include "isaac_rand/isaac64.h"
+
+nwipe_prng_t nwipe_twister = { "Mersenne Twister (mt19937ar-cok)", nwipe_twister_init, nwipe_twister_read };
+
+nwipe_prng_t nwipe_isaac = { "ISAAC (rand.c 20010626)", nwipe_isaac_init, nwipe_isaac_read };
+nwipe_prng_t nwipe_isaac64 = { "ISAAC-64 (isaac64.c)", nwipe_isaac64_init, nwipe_isaac64_read };
+
+/* Print given number of bytes from unsigned integer number to a byte stream buffer starting with low-endian. */
+static inline void u32_to_buffer( u8* restrict buffer, u32 val, const int len )
+{
+ for( int i = 0; i < len; ++i )
+ {
+ buffer[i] = (u8) ( val & 0xFFUL );
+ val >>= 8;
+ }
+}
+static inline void u64_to_buffer( u8* restrict buffer, u64 val, const int len )
+{
+ for( int i = 0; i < len; ++i )
+ {
+ buffer[i] = (u8) ( val & 0xFFULL );
+ val >>= 8;
+ }
+}
+static inline u32 isaac_nextval( randctx* restrict ctx )
+{
+ if( ctx->randcnt == 0 )
+ {
+ isaac( ctx );
+ ctx->randcnt = RANDSIZ;
+ }
+ ctx->randcnt--;
+ return ctx->randrsl[ctx->randcnt];
+}
+static inline u64 isaac64_nextval( rand64ctx* restrict ctx )
+{
+ if( ctx->randcnt == 0 )
+ {
+ isaac64( ctx );
+ ctx->randcnt = RANDSIZ;
+ }
+ ctx->randcnt--;
+ return ctx->randrsl[ctx->randcnt];
+}
+
+int nwipe_twister_init( NWIPE_PRNG_INIT_SIGNATURE )
+{
+ nwipe_log( NWIPE_LOG_NOTICE, "Initialising Mersenne Twister prng" );
+
+ if( *state == NULL )
+ {
+ /* This is the first time that we have been called. */
+ *state = malloc( sizeof( twister_state_t ) );
+ }
+ twister_init( (twister_state_t*) *state, (u32*) ( seed->s ), seed->length / sizeof( u32 ) );
+ return 0;
+}
+
+int nwipe_twister_read( NWIPE_PRNG_READ_SIGNATURE )
+{
+ u8* restrict bufpos = buffer;
+ size_t words = count / SIZE_OF_TWISTER; // the values of twister_genrand_int32 is strictly 4 bytes
+
+ /* Twister returns 4-bytes per call, so progress by 4 bytes. */
+ for( size_t ii = 0; ii < words; ++ii )
+ {
+ u32_to_buffer( bufpos, twister_genrand_int32( (twister_state_t*) *state ), SIZE_OF_TWISTER );
+ bufpos += SIZE_OF_TWISTER;
+ }
+
+ /* If there is some remainder copy only relevant number of bytes to not
+ * overflow the buffer. */
+ const size_t remain = count % SIZE_OF_TWISTER; // SIZE_OF_TWISTER is strictly 4 bytes
+ if( remain > 0 )
+ {
+ u32_to_buffer( bufpos, twister_genrand_int32( (twister_state_t*) *state ), remain );
+ }
+
+ return 0;
+}
+
+int nwipe_isaac_init( NWIPE_PRNG_INIT_SIGNATURE )
+{
+ int count;
+ randctx* isaac_state = *state;
+
+ nwipe_log( NWIPE_LOG_NOTICE, "Initialising Isaac prng" );
+
+ if( *state == NULL )
+ {
+ /* This is the first time that we have been called. */
+ *state = malloc( sizeof( randctx ) );
+ isaac_state = *state;
+
+ /* Check the memory allocation. */
+ if( isaac_state == 0 )
+ {
+ nwipe_perror( errno, __FUNCTION__, "malloc" );
+ nwipe_log( NWIPE_LOG_FATAL, "Unable to allocate memory for the isaac state." );
+ return -1;
+ }
+ }
+
+ /* Take the minimum of the isaac seed size and available entropy. */
+ if( sizeof( isaac_state->randrsl ) < seed->length )
+ {
+ count = sizeof( isaac_state->randrsl );
+ }
+ else
+ {
+ memset( isaac_state->randrsl, 0, sizeof( isaac_state->randrsl ) );
+ count = seed->length;
+ }
+
+ if( count == 0 )
+ {
+ /* Start ISACC without a seed. */
+ randinit( isaac_state, 0 );
+ }
+ else
+ {
+ /* Seed the ISAAC state with entropy. */
+ memcpy( isaac_state->randrsl, seed->s, count );
+
+ /* The second parameter indicates that randrsl is non-empty. */
+ randinit( isaac_state, 1 );
+ }
+
+ return 0;
+}
+
+int nwipe_isaac_read( NWIPE_PRNG_READ_SIGNATURE )
+{
+ randctx* isaac_state = *state;
+ u8* restrict bufpos = buffer;
+ size_t words = count / SIZE_OF_ISAAC; // the values of isaac is strictly 4 bytes
+
+ /* Isaac returns 4-bytes per call, so progress by 4 bytes. */
+ for( size_t ii = 0; ii < words; ++ii )
+ {
+ /* get the next 32bit random number */
+ u32_to_buffer( bufpos, isaac_nextval( isaac_state ), SIZE_OF_ISAAC );
+ bufpos += SIZE_OF_ISAAC;
+ }
+
+ /* If there is some remainder copy only relevant number of bytes to not overflow the buffer. */
+ const size_t remain = count % SIZE_OF_ISAAC; // SIZE_OF_ISAAC is strictly 4 bytes
+ if( remain > 0 )
+ {
+ u32_to_buffer( bufpos, isaac_nextval( isaac_state ), remain );
+ }
+
+ return 0;
+}
+
+int nwipe_isaac64_init( NWIPE_PRNG_INIT_SIGNATURE )
+{
+ int count;
+ rand64ctx* isaac_state = *state;
+
+ nwipe_log( NWIPE_LOG_NOTICE, "Initialising ISAAC-64 prng" );
+
+ if( *state == NULL )
+ {
+ /* This is the first time that we have been called. */
+ *state = malloc( sizeof( rand64ctx ) );
+ isaac_state = *state;
+
+ /* Check the memory allocation. */
+ if( isaac_state == 0 )
+ {
+ nwipe_perror( errno, __FUNCTION__, "malloc" );
+ nwipe_log( NWIPE_LOG_FATAL, "Unable to allocate memory for the isaac state." );
+ return -1;
+ }
+ }
+
+ /* Take the minimum of the isaac seed size and available entropy. */
+ if( sizeof( isaac_state->randrsl ) < seed->length )
+ {
+ count = sizeof( isaac_state->randrsl );
+ }
+ else
+ {
+ memset( isaac_state->randrsl, 0, sizeof( isaac_state->randrsl ) );
+ count = seed->length;
+ }
+
+ if( count == 0 )
+ {
+ /* Start ISACC without a seed. */
+ rand64init( isaac_state, 0 );
+ }
+ else
+ {
+ /* Seed the ISAAC state with entropy. */
+ memcpy( isaac_state->randrsl, seed->s, count );
+
+ /* The second parameter indicates that randrsl is non-empty. */
+ rand64init( isaac_state, 1 );
+ }
+
+ return 0;
+}
+
+int nwipe_isaac64_read( NWIPE_PRNG_READ_SIGNATURE )
+{
+ rand64ctx* isaac_state = *state;
+ u8* restrict bufpos = buffer;
+ size_t words = count / SIZE_OF_ISAAC64; // the values of ISAAC-64 is strictly 8 bytes
+
+ for( size_t ii = 0; ii < words; ++ii )
+ {
+ u64_to_buffer( bufpos, isaac64_nextval( isaac_state ), SIZE_OF_ISAAC64 );
+ bufpos += SIZE_OF_ISAAC64;
+ }
+
+ /* If there is some remainder copy only relevant number of bytes to not overflow the buffer. */
+ const size_t remain = count % SIZE_OF_ISAAC64; // SIZE_OF_ISAAC64 is strictly 8 bytes
+ if( remain > 0 )
+ {
+ u64_to_buffer( bufpos, isaac64_nextval( isaac_state ), remain );
+ }
+
+ return 0;
+}
diff --git a/src/prng.h b/src/prng.h
new file mode 100644
index 0000000..8dfa3d7
--- /dev/null
+++ b/src/prng.h
@@ -0,0 +1,65 @@
+/*
+ * prng.h: Pseudo Random Number Generator abstractions for nwipe.
+ *
+ * Copyright Darik Horn <dajhorn-dban@vanadac.com>.
+ *
+ * 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, version 2.
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#ifndef PRNG_H_
+#define PRNG_H_
+
+#include <sys/types.h>
+
+/* A chunk of random data. */
+typedef struct
+{
+ size_t length; // Length of the entropy string in bytes.
+ u8* s; // The actual bytes of the entropy string.
+} nwipe_entropy_t;
+
+#define NWIPE_PRNG_INIT_SIGNATURE void **state, nwipe_entropy_t *seed
+#define NWIPE_PRNG_READ_SIGNATURE void **state, void *buffer, size_t count
+
+/* Function pointers for PRNG actions. */
+typedef int ( *nwipe_prng_init_t )( NWIPE_PRNG_INIT_SIGNATURE );
+typedef int ( *nwipe_prng_read_t )( NWIPE_PRNG_READ_SIGNATURE );
+
+/* The generic PRNG definition. */
+typedef struct
+{
+ const char* label; // The name of the pseudo random number generator.
+ nwipe_prng_init_t init; // Inialize the prng state with the seed.
+ nwipe_prng_read_t read; // Read data from the prng.
+} nwipe_prng_t;
+
+/* Mersenne Twister prototypes. */
+int nwipe_twister_init( NWIPE_PRNG_INIT_SIGNATURE );
+int nwipe_twister_read( NWIPE_PRNG_READ_SIGNATURE );
+
+/* ISAAC prototypes. */
+int nwipe_isaac_init( NWIPE_PRNG_INIT_SIGNATURE );
+int nwipe_isaac_read( NWIPE_PRNG_READ_SIGNATURE );
+int nwipe_isaac64_init( NWIPE_PRNG_INIT_SIGNATURE );
+int nwipe_isaac64_read( NWIPE_PRNG_READ_SIGNATURE );
+
+/* Size of the twister is not derived from the architecture, but it is strictly 4 bytes */
+#define SIZE_OF_TWISTER 4
+
+/* Size of the isaac/isaac64 is not derived from the architecture, but it is strictly 4 or 8 bytes */
+#define SIZE_OF_ISAAC 4
+#define SIZE_OF_ISAAC64 8
+
+#endif /* PRNG_H_ */
diff --git a/src/redcross.jpg b/src/redcross.jpg
new file mode 100644
index 0000000..88fffe9
--- /dev/null
+++ b/src/redcross.jpg
Binary files differ
diff --git a/src/te.jpg b/src/te.jpg
new file mode 100644
index 0000000..3710146
--- /dev/null
+++ b/src/te.jpg
Binary files differ
diff --git a/src/temperature.c b/src/temperature.c
new file mode 100644
index 0000000..19f9446
--- /dev/null
+++ b/src/temperature.c
@@ -0,0 +1,495 @@
+/*
+ * temperature.c: functions that populate the drive temperature variables
+ * in each drives context structure.
+ *
+ * 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, version 2.
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+//#define _LARGEFILE64_SOURCE
+//#define _FILE_OFFSET_BITS 64
+#define _BSD_SOURCE
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <errno.h>
+#include <dirent.h>
+#include <sys/time.h>
+
+#include "nwipe.h"
+#include "context.h"
+#include "method.h"
+#include "device.h"
+#include "prng.h"
+#include "options.h"
+#include "device.h"
+#include "logging.h"
+#include "temperature.h"
+#include "miscellaneous.h"
+
+extern int terminate_signal;
+
+int nwipe_init_temperature( nwipe_context_t* c )
+{
+ /* See header definition for description of function
+ */
+ DIR* dir;
+ DIR* dir2;
+ const char dirpath[] = "/sys/class/hwmon";
+ char dirpath_tmp[256];
+ char dirpath_tmp2[256];
+ char dirpath_hwmonX[256];
+ char device[256];
+ char device_context_name[256];
+ // const char dirpath[] = "/home/nick/mouse/hwmon1";
+ struct dirent* dp;
+ struct dirent* dp2;
+
+ /* Why Initialise with 1000000 (defined as NO_TEMPERATURE_DATA)?
+ * Because the GUI needs to know whether data has been obtained
+ * so it can display appropriate information when a
+ * device is unable to provide temperature data */
+
+ c->templ_has_hwmon_data = 0;
+ c->temp1_crit = NO_TEMPERATURE_DATA;
+ c->temp1_highest = NO_TEMPERATURE_DATA;
+ c->temp1_input = NO_TEMPERATURE_DATA;
+ c->temp1_lcrit = NO_TEMPERATURE_DATA;
+ c->temp1_lowest = NO_TEMPERATURE_DATA;
+ c->temp1_max = NO_TEMPERATURE_DATA;
+ c->temp1_min = NO_TEMPERATURE_DATA;
+ c->temp1_monitored_wipe_max = NO_TEMPERATURE_DATA;
+ c->temp1_monitored_wipe_min = NO_TEMPERATURE_DATA;
+ c->temp1_monitored_wipe_avg = NO_TEMPERATURE_DATA;
+ c->temp1_flash_rate = 0;
+ c->temp1_flash_rate_counter = 0;
+ c->temp1_path[0] = 0;
+ c->temp1_time = 0;
+
+ /* Each hwmonX directory is processed in turn and once a hwmonX directory has been
+ * found that is a block device and the block device name matches the drive
+ * name in the current context then the path to ../hwmonX is constructed and written
+ * to the drive context structure '* c'. This path is used in the nwipe_update_temperature
+ * function to retrieve temperature data and store it in the device context
+ */
+
+ if( ( dir = opendir( dirpath ) ) != NULL )
+ {
+ /* Process each hwmonX sub directory in turn */
+ while( ( dp = readdir( dir ) ) != NULL )
+ {
+ /* Does the directory start with 'hwmon' */
+ if( strstr( dp->d_name, "hwmon" ) != NULL )
+ {
+ if( nwipe_options.verbose )
+ {
+ /* print a empty line to separate the different hwmon sensors */
+ nwipe_log( NWIPE_LOG_DEBUG, "hwmon:" );
+ }
+ strcpy( dirpath_tmp, dirpath );
+ strcat( dirpath_tmp, "/" );
+ strcat( dirpath_tmp, dp->d_name );
+ strcpy( dirpath_hwmonX, dirpath_tmp );
+ strcat( dirpath_tmp, "/device/block" );
+
+ /* Depending on the class of block device, the device name may
+ * appear in different sub-directories. So we try to open each
+ * directory that are known to contain block devices. These are
+ * /sys/class/hwmon/hwmonX/device/block
+ * /sys/class/hwmon/hwmonX/device/nvme/nvme0
+ * /sys/class/hwmon/hwmonX/device/
+ */
+
+ if( ( dir2 = opendir( dirpath_tmp ) ) == NULL )
+ {
+ if( nwipe_options.verbose )
+ {
+ nwipe_log( NWIPE_LOG_DEBUG, "hwmon: %s doesn't exist, trying next path", dirpath_tmp );
+ }
+ strcpy( dirpath_tmp2, dirpath_hwmonX );
+ strcat( dirpath_tmp2, "/device/nvme/nvme0" );
+ strcpy( dirpath_tmp, dirpath_tmp2 );
+
+ if( ( dir2 = opendir( dirpath_tmp ) ) == NULL )
+ {
+ if( nwipe_options.verbose )
+ {
+ nwipe_log( NWIPE_LOG_DEBUG, "hwmon: %s doesn't exist, trying next path", dirpath_tmp );
+ }
+
+ strcpy( dirpath_tmp2, dirpath_hwmonX );
+ strcat( dirpath_tmp2, "/device" );
+ strcpy( dirpath_tmp, dirpath_tmp2 );
+
+ if( ( dir2 = opendir( dirpath_tmp ) ) == NULL )
+ {
+ if( nwipe_options.verbose )
+ {
+ nwipe_log(
+ NWIPE_LOG_DEBUG, "hwmon: %s doesn't exist, no more paths to try", dirpath_tmp );
+ }
+ continue;
+ }
+ }
+ }
+
+ if( dir2 != NULL )
+ {
+ if( nwipe_options.verbose )
+ {
+ nwipe_log( NWIPE_LOG_DEBUG, "hwmon: Found %s", dirpath_tmp );
+ }
+
+ /* Read the device name */
+ while( ( dp2 = readdir( dir2 ) ) != NULL )
+ {
+ if( nwipe_options.verbose )
+ {
+ nwipe_log( NWIPE_LOG_DEBUG, "hwmon: dirpath_tmp=%s/%s", dirpath_tmp, &dp2->d_name[0] );
+ }
+
+ /* Skip the '.' and '..' directories */
+ if( dp2->d_name[0] == '.' )
+ {
+ continue;
+ }
+ strcpy( device, dp2->d_name );
+
+ /* Create a copy of the device name from the context but strip the path from it, right justify
+ * device name, prefix with spaces so length is 8. */
+ nwipe_strip_path( device_context_name, c->device_name );
+
+ /* Remove leading/training whitespace from a string and left justify result */
+ trim( device_context_name );
+
+ /* Does the hwmon device match the device for this drive context */
+ if( strcmp( device, device_context_name ) != 0 )
+ {
+ /* No, so try next hwmon device */
+ continue;
+ }
+ else
+ {
+ /* Match ! This hwmon device matches this context, so write the hwmonX path to the context
+ */
+ nwipe_log( NWIPE_LOG_NOTICE, "hwmon: %s has temperature monitoring", device, dirpath_tmp );
+ if( nwipe_options.verbose )
+ {
+ nwipe_log( NWIPE_LOG_DEBUG, "hwmon: %s found in %s", device, dirpath_tmp );
+ }
+ /* Copy the hwmon path to the drive context structure */
+ strcpy( c->temp1_path, dirpath_hwmonX );
+ c->templ_has_hwmon_data = 1;
+ }
+ }
+ closedir( dir2 );
+ }
+ }
+ }
+ closedir( dir );
+ }
+ /* if no hwmon data available try scsi access (SAS Disks are known to be not working in hwmon */
+ if( c->templ_has_hwmon_data == 0 && ( c->device_type == NWIPE_DEVICE_SAS || c->device_type == NWIPE_DEVICE_SCSI ) )
+ {
+ nwipe_log( NWIPE_LOG_NOTICE, "no hwmon data for %s, try to get SCSI data", c->device_name );
+ if( nwipe_init_scsi_temperature( c ) == 0 )
+ {
+ c->templ_has_scsitemp_data = 1;
+ nwipe_log( NWIPE_LOG_INFO, "got SCSI temperature data for %s", c->device_name );
+ }
+ else
+ {
+ c->templ_has_scsitemp_data = 0;
+ nwipe_log( NWIPE_LOG_INFO, "got no SCSI temperature data for %s", c->device_name );
+ }
+ }
+
+ return 0;
+}
+
+float timedifference_msec( struct timeval tv_start, struct timeval tv_end )
+{
+ /* helper function for time measurement in msec */
+ return ( tv_end.tv_sec - tv_start.tv_sec ) * 1000.0f + ( tv_end.tv_usec - tv_start.tv_usec ) / 1000.0f;
+}
+
+void* nwipe_update_temperature_thread( void* ptr )
+{
+ int i;
+
+ /* Set up the structs we will use for the data required. */
+ nwipe_thread_data_ptr_t* nwipe_thread_data_ptr;
+ nwipe_context_t** c;
+ nwipe_misc_thread_data_t* nwipe_misc_thread_data;
+
+ /* Retrieve from the pointer passed to the function. */
+ nwipe_thread_data_ptr = (nwipe_thread_data_ptr_t*) ptr;
+ c = nwipe_thread_data_ptr->c;
+ nwipe_misc_thread_data = nwipe_thread_data_ptr->nwipe_misc_thread_data;
+
+ /* mark start second of update */
+ time_t nwipe_timemark = time( NULL );
+
+ /* update immediately on entry to thread */
+ for( i = 0; i < nwipe_misc_thread_data->nwipe_enumerated; i++ )
+ {
+ nwipe_update_temperature( c[i] );
+ if( terminate_signal == 1 )
+ {
+ break;
+ }
+ }
+
+ while( terminate_signal != 1 )
+ {
+ /* Update all drive/s but never repeat checking the
+ * set of drive/s faster than once every 2 seconds */
+ if( time( NULL ) > ( nwipe_timemark + 1 ) )
+ {
+ nwipe_timemark = time( NULL );
+
+ for( i = 0; i < nwipe_misc_thread_data->nwipe_enumerated; i++ )
+ {
+ nwipe_update_temperature( c[i] );
+ }
+ }
+ else
+ {
+ sleep( 1 );
+ }
+ }
+ return NULL;
+}
+
+void nwipe_update_temperature( nwipe_context_t* c )
+{
+ /* Warning !! This function should only be called by nwipe_update_temperature_thread()
+ * Due to delays of upto 2 seconds with some drives, especially SAS in obtaining
+ * temperatures while wiping, the delays being worse the more drives you are wiping. Updating
+ * temperatures are performed within it's own thread so it doesn't cause momentary freezes
+ * in the GUI interface.
+ *
+ * For the given drive context obtain the path to it's hwmon temperature settings
+ * and read then write the temperature values back to the context. A numeric ascii to integer conversion is
+ * performed. The temperaures should be updated no more frequently than every 60 seconds
+ */
+
+ char temperature_label[NUMBER_OF_FILES][20] = {
+ "temp1_crit", "temp1_highest", "temp1_input", "temp1_lcrit", "temp1_lowest", "temp1_max", "temp1_min" };
+ int* temperature_pcontext[NUMBER_OF_FILES] = {
+
+ &( c->temp1_crit ),
+ &( c->temp1_highest ),
+ &( c->temp1_input ),
+ &( c->temp1_lcrit ),
+ &( c->temp1_lowest ),
+ &( c->temp1_max ),
+ &( c->temp1_min ) };
+
+ char path[256];
+ char temperature[256];
+ FILE* fptr;
+ int idx;
+ int result;
+ struct timeval tv_start;
+ struct timeval tv_end;
+ float delta_t;
+
+ /* avoid being called more often than 1x per 60 seconds */
+ time_t nwipe_time_now = time( NULL );
+ if( nwipe_time_now - c->temp1_time < 60 )
+ {
+ return;
+ }
+
+ /* measure time it takes to get the temperatures */
+ gettimeofday( &tv_start, 0 );
+
+ /* try to get temperatures from hwmon, standard */
+ if( c->templ_has_hwmon_data == 1 )
+ {
+ for( idx = 0; idx < NUMBER_OF_FILES; idx++ )
+ {
+ /* Construct the full path including filename */
+ strcpy( path, c->temp1_path );
+ strcat( path, "/" );
+ strcat( path, &( temperature_label[idx][0] ) );
+
+ /* Open the file */
+ if( ( fptr = fopen( path, "r" ) ) != NULL )
+ {
+ /* Acquire data until we reach a newline */
+ result = fscanf( fptr, "%[^\n]", temperature );
+
+ /* Convert numeric ascii to binary integer */
+ *( temperature_pcontext[idx] ) = atoi( temperature );
+
+ /* Divide by 1000 to get degrees celsius */
+ *( temperature_pcontext[idx] ) = *( temperature_pcontext[idx] ) / 1000;
+
+ if( nwipe_options.verbose )
+ {
+ nwipe_log( NWIPE_LOG_NOTICE, "hwmon: %s %dC", path, *( temperature_pcontext[idx] ) );
+ }
+
+ fclose( fptr );
+ }
+ else
+ {
+ if( nwipe_options.verbose )
+ {
+ nwipe_log( NWIPE_LOG_NOTICE, "hwmon: Unable to open %s", path );
+ }
+ }
+ }
+ }
+ else
+ {
+ /* alternative method to get temperature from SCSI/SAS disks */
+ if( c->device_type == NWIPE_DEVICE_SAS || c->device_type == NWIPE_DEVICE_SCSI )
+ {
+ if( c->templ_has_scsitemp_data == 1 )
+ {
+ if( nwipe_options.verbose )
+ {
+ nwipe_log( NWIPE_LOG_NOTICE, "hddtemp: %s temp1_crit %dC", c->device_name, c->temp1_crit );
+ nwipe_log( NWIPE_LOG_NOTICE, "hddtemp: %s temp1_highest %dC", c->device_name, c->temp1_highest );
+ nwipe_log( NWIPE_LOG_NOTICE, "hddtemp: %s temp1_input %dC", c->device_name, c->temp1_input );
+ nwipe_log( NWIPE_LOG_NOTICE, "hddtemp: %s temp1_lcrit %dC", c->device_name, c->temp1_lcrit );
+ nwipe_log( NWIPE_LOG_NOTICE, "hddtemp: %s temp1_lowest %dC", c->device_name, c->temp1_lowest );
+ nwipe_log( NWIPE_LOG_NOTICE, "hddtemp: %s temp1_max %dC", c->device_name, c->temp1_max );
+ nwipe_log( NWIPE_LOG_NOTICE, "hddtemp: %s temp1_min %dC", c->device_name, c->temp1_min );
+ }
+ if( nwipe_get_scsi_temperature( c ) != 0 )
+ {
+ nwipe_log( NWIPE_LOG_ERROR, "get_scsi_temperature error" );
+ }
+ }
+ }
+ }
+
+ /* Update the time stamp that records when we checked the temperature,
+ * this is used by the GUI to check temperatures periodically, typically
+ * every 60 seconds */
+ c->temp1_time = time( NULL );
+
+ gettimeofday( &tv_end, 0 );
+ delta_t = timedifference_msec( tv_start, tv_end );
+ if( nwipe_options.verbose )
+ {
+ nwipe_log( NWIPE_LOG_NOTICE, "get temperature for %s took %f ms", c->device_name, delta_t );
+ }
+
+ return;
+}
+
+void nwipe_log_drives_temperature_limits( nwipe_context_t* c )
+{
+ /* See header for description of function
+ */
+
+ char temperature_limits_txt[500];
+
+ int idx = 0;
+
+ /*
+ * Initialise the character string, as we are building it a few
+ * characters at a time and it's important there it is populated
+ * with all zeros as we are using strlen() as we build the line up.
+ */
+ memset( &temperature_limits_txt, 0, sizeof( temperature_limits_txt ) );
+
+ if( c->temp1_crit != NO_TEMPERATURE_DATA )
+ {
+ snprintf( temperature_limits_txt,
+ sizeof( temperature_limits_txt ),
+ "Temperature limits for %s, critical=%ic, ",
+ c->device_name,
+ c->temp1_crit );
+ }
+ else
+ {
+ snprintf( temperature_limits_txt,
+ sizeof( temperature_limits_txt ),
+ "Temperature limits for %s, critical=N/A, ",
+ c->device_name );
+ }
+
+ idx = strlen( temperature_limits_txt );
+
+ if( c->temp1_max != NO_TEMPERATURE_DATA )
+ {
+ snprintf( &temperature_limits_txt[idx], ( sizeof( temperature_limits_txt ) - idx ), "max=%ic, ", c->temp1_max );
+ }
+ else
+ {
+ snprintf( &temperature_limits_txt[idx], ( sizeof( temperature_limits_txt ) - idx ), "max=N/A, " );
+ }
+
+ idx = strlen( temperature_limits_txt );
+
+ if( c->temp1_highest != NO_TEMPERATURE_DATA )
+ {
+ snprintf( &temperature_limits_txt[idx],
+ ( sizeof( temperature_limits_txt ) - idx ),
+ "highest=%ic, ",
+ c->temp1_highest );
+ }
+ else
+ {
+ snprintf( &temperature_limits_txt[idx], ( sizeof( temperature_limits_txt ) - idx ), "highest=N/A, " );
+ }
+
+ idx = strlen( temperature_limits_txt );
+
+ if( c->temp1_lowest != NO_TEMPERATURE_DATA )
+ {
+ snprintf(
+ &temperature_limits_txt[idx], ( sizeof( temperature_limits_txt ) - idx ), "lowest=%ic, ", c->temp1_lowest );
+ }
+ else
+ {
+ snprintf( &temperature_limits_txt[idx], ( sizeof( temperature_limits_txt ) - idx ), "lowest=N/A, " );
+ }
+
+ idx = strlen( temperature_limits_txt );
+
+ if( c->temp1_min != NO_TEMPERATURE_DATA )
+ {
+ snprintf( &temperature_limits_txt[idx], ( sizeof( temperature_limits_txt ) - idx ), "min=%ic, ", c->temp1_min );
+ }
+ else
+ {
+ snprintf( &temperature_limits_txt[idx], ( sizeof( temperature_limits_txt ) - idx ), "min=N/A, " );
+ }
+
+ idx = strlen( temperature_limits_txt );
+
+ if( c->temp1_lcrit != NO_TEMPERATURE_DATA )
+ {
+ snprintf( &temperature_limits_txt[idx],
+ ( sizeof( temperature_limits_txt ) - idx ),
+ "low critical=%ic.",
+ c->temp1_lcrit );
+ }
+ else
+ {
+ snprintf( &temperature_limits_txt[idx], ( sizeof( temperature_limits_txt ) - idx ), "low critical=N/A. " );
+ }
+
+ nwipe_log( NWIPE_LOG_INFO, "%s", temperature_limits_txt );
+
+ return;
+}
diff --git a/src/temperature.h b/src/temperature.h
new file mode 100644
index 0000000..b07b091
--- /dev/null
+++ b/src/temperature.h
@@ -0,0 +1,61 @@
+/*
+ * temperature.h: The header file for disk drive temperature sensing
+ *
+ * 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, version 2.
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#ifndef TEMPERATURE_H_
+#define TEMPERATURE_H_
+
+#include <sys/types.h>
+#include "context.h"
+
+/**
+ * This function is called after each nwipe_context_t has been created.
+ * It initialises the temperature variables in each context and then
+ * constructs a path that is placed in the context that points to the
+ * appropriate /sys/class/hwmon/hwmonX directory that corresponds with
+ * the particular drive represented in the context structure.
+ * @param pointer to a drive context
+ * @return returns 0 on success < 1 on error
+ */
+int nwipe_init_temperature( nwipe_context_t* );
+
+void nwipe_update_temperature( nwipe_context_t* );
+
+/**
+ * Workaround for obtaining temperatures from SCSI/SAS drives
+ * @param pointer to a drive context
+ * @return returns 0 on success < 1 on error
+ */
+int nwipe_init_scsi_temperature( nwipe_context_t* );
+int nwipe_get_scsi_temperature( nwipe_context_t* );
+void nwipe_shut_scsi_temperature( nwipe_context_t* );
+void* nwipe_update_temperature_thread( void* ptr );
+
+/**
+ * This function is normally called only once. It's called after both the
+ * nwipe_init_temperature() function and nwipe_update_temperature()
+ * functions have been called. It logs the drives critical, highest, lowest
+ * and lowest critical temperatures. Not all drives report four temperatures.
+ * @param pointer to a drive context
+ */
+void nwipe_log_drives_temperature_limits( nwipe_context_t* );
+
+#define NUMBER_OF_FILES 7
+
+#define NO_TEMPERATURE_DATA 1000000
+
+#endif /* TEMPERATURE_H_ */
diff --git a/src/version.c b/src/version.c
new file mode 100644
index 0000000..cbaa106
--- /dev/null
+++ b/src/version.c
@@ -0,0 +1,17 @@
+/**
+ * version_string and program_name are used by siege
+ * and configure; author_name and email_address are
+ * used by configure to dynamically assign those values
+ * to documentation files.
+ */
+const char* version_string = "0.36";
+const char* program_name = "nwipe";
+const char* author_name = "Martijn van Brummelen";
+const char* email_address = "git@brumit.nl";
+const char* years = "2024";
+const char* copyright = "Copyright Darik Horn <dajhorn-dban@vanadac.com>\n\
+Modifications to original dwipe Copyright Andy Beverley <andy@andybev.com>\n\
+This is free software; see the source for copying conditions.\n\
+There is NO warranty; not even for MERCHANTABILITY or FITNESS\n\
+FOR A PARTICULAR PURPOSE.\n";
+const char* banner = "nwipe 0.36";
diff --git a/src/version.h b/src/version.h
new file mode 100644
index 0000000..b1214b2
--- /dev/null
+++ b/src/version.h
@@ -0,0 +1,11 @@
+#ifndef __VERSION_H
+#define __VERSION_H
+
+extern char* version_string;
+extern char* program_name;
+extern char* author_name;
+extern char* email_address;
+extern char* copyright;
+extern char* banner;
+
+#endif /*__VERSION_H*/